Very large commit for upcoming 4.4 release
Major New features
* New and updated search functions using xpath, api-path and instance-id
* New search functions using api-path and instance_id:
* C search functions: `clixon_find_instance_id()` and `clixon_find_api_path()`
* Binary search optimization in lists for indexed leafs in all three formats.
* This improves search performance to O(logN) which is drastical improvements for large lists.
* You can also register explicit indexes for making binary search (not only list keys)
* For more info, see docs at [paths](https://clixon-docs.readthedocs.io/en/latest/paths.html) and
[search](https://clixon-docs.readthedocs.io/en/latest/xml.html#searching-in-xml)
API changes on existing features (you may need to change your code)
* On failed validation of leafrefs, error message changed from: `No such leaf` to `No leaf <name> matching path <path>`.
* CLI Error message (clicon_rpc_generate_error()) changed when backend returns netconf error to be more descriptive:
* Original: `Config error: Validate failed. Edit and try again or discard changes: Invalid argument`
* New (example): `Netconf error: application operation-failed Identityref validation failed, undefined not derived from acl-base . Validate failed. Edit and try again or discard changes"
Minor changes
* Test framework
* Added `-- -S <file>` command-line to main example to be able to return any state to main example.
* Added `test/cicd` test scripts for running on a set of other hosts
* C-code restructuring
* clixon_yang.c partitioned and moved code into clixon_yang_parse_lib.c and clixon_yang_module.c and move back some code from clixon_yang_type.c.
* partly to reduce size, but most important to limit code that accesses internal yang structures, only clixon_yang.c does this now.
This commit is contained in:
parent
e8ae628d06
commit
19e21be0bc
132 changed files with 6241 additions and 2332 deletions
|
|
@ -1,7 +1,8 @@
|
|||
#
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
#
|
||||
# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
# Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
# Copyright (C) 2017-2020 Olof Hagsand
|
||||
#
|
||||
# This file is part of CLIXON
|
||||
#
|
||||
|
|
@ -68,10 +69,10 @@ 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_sort.c clixon_xml_map.c \
|
||||
clixon_json.c clixon_yang.c clixon_yang_type.c clixon_yang_module.c \
|
||||
clixon_xml.c clixon_xml_sort.c clixon_xml_map.c clixon_json.c \
|
||||
clixon_yang.c clixon_yang_type.c clixon_yang_module.c clixon_yang_parse_lib.c \
|
||||
clixon_yang_cardinality.c clixon_xml_changelog.c clixon_xml_nsctx.c \
|
||||
clixon_api_path.c clixon_validate.c \
|
||||
clixon_path.c clixon_validate.c \
|
||||
clixon_hash.c clixon_options.c clixon_data.c clixon_plugin.c \
|
||||
clixon_proto.c clixon_proto_client.c \
|
||||
clixon_xpath.c clixon_xpath_ctx.c clixon_xpath_eval.c clixon_xpath_optimize.c \
|
||||
|
|
@ -82,7 +83,9 @@ SRC = clixon_sig.c clixon_uid.c clixon_log.c clixon_err.c clixon_event.c \
|
|||
YACCOBJS := lex.clixon_xml_parse.o clixon_xml_parse.tab.o \
|
||||
lex.clixon_yang_parse.o clixon_yang_parse.tab.o \
|
||||
lex.clixon_json_parse.o clixon_json_parse.tab.o \
|
||||
lex.clixon_xpath_parse.o clixon_xpath_parse.tab.o
|
||||
lex.clixon_xpath_parse.o clixon_xpath_parse.tab.o \
|
||||
lex.clixon_api_path_parse.o clixon_api_path_parse.tab.o \
|
||||
lex.clixon_instance_id_parse.o clixon_instance_id_parse.tab.o
|
||||
|
||||
# Generated src
|
||||
GENSRC = build.c
|
||||
|
|
@ -105,10 +108,14 @@ clean:
|
|||
rm -f clixon_yang_parse.tab.[ch] clixon_yang_parse.[co]
|
||||
rm -f clixon_json_parse.tab.[ch] clixon_json_parse.[co]
|
||||
rm -f clixon_xpath_parse.tab.[ch] clixon_xpath_parse.[co]
|
||||
rm -f clixon_api_path_parse.tab.[ch] clixon_api_path_parse.[co]
|
||||
rm -f clixon_instance_id_parse.tab.[ch] clixon_instance_id_parse.[co]
|
||||
rm -f lex.clixon_xml_parse.c
|
||||
rm -f lex.clixon_yang_parse.c
|
||||
rm -f lex.clixon_json_parse.c
|
||||
rm -f lex.clixon_xpath_parse.c
|
||||
rm -f lex.clixon_api_path_parse.c
|
||||
rm -f lex.clixon_instance_id_parse.c
|
||||
|
||||
#############################################################################
|
||||
# Implicit rules for lex and yacc.
|
||||
|
|
@ -174,6 +181,32 @@ clixon_xpath_parse.tab.c: clixon_xpath_parse.tab.h
|
|||
lex.clixon_xpath_parse.o : lex.clixon_xpath_parse.c clixon_xpath_parse.tab.h
|
||||
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -Wno-error -c $<
|
||||
|
||||
# api-path parser
|
||||
lex.clixon_api_path_parse.c : clixon_api_path_parse.l clixon_api_path_parse.tab.h
|
||||
$(LEX) -Pclixon_api_path_parse clixon_api_path_parse.l # -d is debug
|
||||
|
||||
clixon_api_path_parse.tab.h: clixon_api_path_parse.y
|
||||
$(YACC) -l -d -b clixon_api_path_parse -p clixon_api_path_parse clixon_api_path_parse.y # -t is debug
|
||||
|
||||
# extra rule to avoid parallell yaccs
|
||||
clixon_api_path_parse.tab.c: clixon_api_path_parse.tab.h
|
||||
|
||||
lex.clixon_api_path_parse.o : lex.clixon_api_path_parse.c clixon_api_path_parse.tab.h
|
||||
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -Wno-error -c $<
|
||||
|
||||
# instance-identifier parser
|
||||
lex.clixon_instance_id_parse.c : clixon_instance_id_parse.l clixon_instance_id_parse.tab.h
|
||||
$(LEX) -Pclixon_instance_id_parse clixon_instance_id_parse.l # -d is debug
|
||||
|
||||
clixon_instance_id_parse.tab.h: clixon_instance_id_parse.y
|
||||
$(YACC) -l -d -b clixon_instance_id_parse -p clixon_instance_id_parse clixon_instance_id_parse.y # -t is debug
|
||||
|
||||
# extra rule to avoid parallell yaccs
|
||||
clixon_instance_id_parse.tab.c: clixon_instance_id_parse.tab.h
|
||||
|
||||
lex.clixon_instance_id_parse.o : lex.clixon_instance_id_parse.c clixon_instance_id_parse.tab.h
|
||||
$(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -Wno-error -c $<
|
||||
|
||||
distclean: clean
|
||||
rm -f Makefile *~ .depend
|
||||
|
||||
|
|
|
|||
68
lib/src/clixon_api_path_parse.h
Normal file
68
lib/src/clixon_api_path_parse.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
|
||||
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 *****
|
||||
|
||||
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
|
||||
*/
|
||||
#ifndef _CLIXON_API_PATH_PARSE_H_
|
||||
#define _CLIXON_API_PATH_PARSE_H_
|
||||
|
||||
/*
|
||||
* Types
|
||||
*/
|
||||
struct clicon_api_path_yacc_arg{
|
||||
const char *ay_name; /* Name of syntax (for error string) */
|
||||
int ay_linenum; /* Number of \n in parsed buffer */
|
||||
char *ay_parse_string; /* original (copy of) parse string */
|
||||
void *ay_lexbuf; /* internal parse buffer from lex */
|
||||
clixon_path *ay_top;
|
||||
};
|
||||
|
||||
/*
|
||||
* Variables
|
||||
*/
|
||||
extern char *clixon_api_path_parsetext;
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int api_path_scan_init(struct clicon_api_path_yacc_arg *);
|
||||
int api_path_scan_exit(struct clicon_api_path_yacc_arg *);
|
||||
|
||||
int api_path_parse_init(struct clicon_api_path_yacc_arg *);
|
||||
int api_path_parse_exit(struct clicon_api_path_yacc_arg *);
|
||||
|
||||
int clixon_api_path_parselex(void *);
|
||||
int clixon_api_path_parseparse(void *);
|
||||
void clixon_api_path_parseerror(void *, char*);
|
||||
|
||||
#endif /* _CLIXON_API_PATH_PARSE_H_ */
|
||||
150
lib/src/clixon_api_path_parse.l
Normal file
150
lib/src/clixon_api_path_parse.l
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
|
||||
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 *****
|
||||
*
|
||||
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.53.
|
||||
* BNF:
|
||||
* <api-path> := <root> ("/" (<api-identifier> | <list-instance>))*
|
||||
* <root> := <string>
|
||||
* <api-identifier> := [<module-name> ":"] <identifier>
|
||||
* <module-name> := <identifier>
|
||||
* <list-instance> := <api-identifier> "=" key-value *("," key-value)
|
||||
* <key-value> := <string>
|
||||
* <string> := <an unquoted string>
|
||||
* <identifier> := (<ALPHA> | "_") (<ALPHA> | <DIGIT> | "_" | "-" | ".")
|
||||
* @note 1. <root> is the RESTCONF root resource (Sec 3.3) omitted in all calls below, it is
|
||||
* assumed to be stripped from api-path before calling these functions.
|
||||
* @note 2. characters in a key value string are constrained, and some characters need to be
|
||||
* percent-encoded,
|
||||
* XXX For some reason, I cant use "return *string" in these rules, so I resort to symbols
|
||||
* (eg slash)
|
||||
*/
|
||||
|
||||
%{
|
||||
|
||||
#include "clixon_config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "clixon_api_path_parse.tab.h" /* generated */
|
||||
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
#include "clixon_queue.h"
|
||||
#include "clixon_hash.h"
|
||||
#include "clixon_handle.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_log.h"
|
||||
#include "clixon_string.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_path.h"
|
||||
#include "clixon_api_path_parse.h"
|
||||
|
||||
/* Redefine main lex function so that you can send arguments to it: _yy is added to arg list */
|
||||
#define YY_DECL int clixon_api_path_parselex(void *_ay)
|
||||
|
||||
/* Dont use input function (use user-buffer) */
|
||||
#define YY_NO_INPUT
|
||||
|
||||
/* typecast macro */
|
||||
#define _AY ((struct clicon_api_path_yacc_arg *)_ay)
|
||||
|
||||
#define MAXBUF 4*4*64*1024
|
||||
|
||||
#undef clixon_api_path_parsewrap
|
||||
int
|
||||
clixon_api_path_parsewrap(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
namestart [A-Z_a-z]
|
||||
namechar [A-Z_a-z\-\.0-9]
|
||||
identifier {namestart}{namechar}*
|
||||
|
||||
%x INIT
|
||||
%s KEYV
|
||||
|
||||
%%
|
||||
|
||||
<INIT,KEYV>[ \t]
|
||||
<INIT,KEYV>\n { _AY->ay_linenum++; }
|
||||
<INIT,KEYV>\r
|
||||
<INIT,KEYV><<EOF>> { return X_EOF; }
|
||||
|
||||
<INIT>\/ { return SLASH;}
|
||||
<INIT>\= { BEGIN(KEYV); return EQUAL; }
|
||||
<INIT>\: { return COLON; }
|
||||
<INIT>{identifier} { clixon_api_path_parselval.string = strdup(yytext);
|
||||
return IDENTIFIER; }
|
||||
<<INIT>. { clixon_api_path_parseerror(_AY, "LEXICAL ERROR\n"); return -1; }
|
||||
|
||||
<KEYV>\, { return COMMA; }
|
||||
<KEYV>\/ { BEGIN(INIT); return SLASH; }
|
||||
<KEYV>[^:/?#\[\]@,]+ { clixon_api_path_parselval.string = strdup(yytext);
|
||||
return STRING;}
|
||||
<KEYV>. { clixon_api_path_parseerror(_AY, "LEXICAL ERROR\n"); return -1; }
|
||||
|
||||
%%
|
||||
|
||||
/*! Initialize scanner.
|
||||
*/
|
||||
int
|
||||
api_path_scan_init(struct clicon_api_path_yacc_arg *ay)
|
||||
{
|
||||
BEGIN(INIT);
|
||||
ay->ay_lexbuf = yy_scan_string(ay->ay_parse_string);
|
||||
#if 1 /* XXX: just to use unput to avoid warning */
|
||||
if (0)
|
||||
yyunput(0, "");
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* free buffers
|
||||
* Even within Flex version 2.5 (this is assumed), freeing buffers is different.
|
||||
*/
|
||||
int
|
||||
api_path_scan_exit(struct clicon_api_path_yacc_arg *ay)
|
||||
{
|
||||
yy_delete_buffer(ay->ay_lexbuf);
|
||||
clixon_api_path_parselex_destroy(); /* modern */
|
||||
return 0;
|
||||
}
|
||||
|
||||
298
lib/src/clixon_api_path_parse.y
Normal file
298
lib/src/clixon_api_path_parse.y
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
|
||||
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 *****
|
||||
|
||||
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
|
||||
* BNF:
|
||||
* <api-path> := <root> ("/" (<api-identifier> | <list-instance>))*
|
||||
* <root> := <string> # See note 1 below
|
||||
* <root> := <string>
|
||||
* <api-identifier> := [<module-name> ":"] <identifier>
|
||||
* <module-name> := <identifier>
|
||||
* <list-instance> := <api-identifier> "=" key-value *("," key-value)
|
||||
* <key-value> := <string>
|
||||
* <string> := <an unquoted string>
|
||||
* <identifier> := (<ALPHA> | "_") (<ALPHA> | <DIGIT> | "_" | "-" | ".")
|
||||
* @note 1. <root> is the RESTCONF root resource (Sec 3.3) omitted in all calls below, it is
|
||||
* assumed to be stripped from api-path before calling these functions.
|
||||
* @note 2. characters in a key value string are constrained, and some characters need to be
|
||||
* percent-encoded,
|
||||
*/
|
||||
|
||||
%start start
|
||||
|
||||
/* Must be here to define YYSTYPE */
|
||||
%union {
|
||||
char *string;
|
||||
void *stack; /* cv / cvec */
|
||||
}
|
||||
|
||||
%token <string> IDENTIFIER
|
||||
%token <string> STRING
|
||||
%token <string> SLASH
|
||||
%token <string> COLON
|
||||
%token <string> COMMA
|
||||
%token <string> EQUAL
|
||||
%token <string> X_EOF
|
||||
|
||||
%type <stack> list
|
||||
%type <stack> element
|
||||
%type <stack> api_identifier
|
||||
%type <string> module_name
|
||||
%type <stack> list_instance
|
||||
%type <stack> key_values
|
||||
%type <stack> key_value
|
||||
|
||||
|
||||
%lex-param {void *_ay} /* Add this argument to parse() and lex() function */
|
||||
%parse-param {void *_ay}
|
||||
|
||||
%{
|
||||
/* Here starts user C-code */
|
||||
|
||||
/* typecast macro */
|
||||
#define _AY ((struct clicon_api_path_yacc_arg *)_ay)
|
||||
|
||||
#define _YYERROR(msg) {clicon_err(OE_XML, 0, "YYERROR %s '%s' %d", (msg), clixon_api_path_parsetext, _AY->ay_linenum); YYERROR;}
|
||||
|
||||
/* add _yy to error parameters */
|
||||
#define YY_(msgid) msgid
|
||||
|
||||
#include "clixon_config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <net/if.h>
|
||||
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
#include "clixon_err.h"
|
||||
#include "clixon_log.h"
|
||||
#include "clixon_queue.h"
|
||||
#include "clixon_string.h"
|
||||
#include "clixon_hash.h"
|
||||
#include "clixon_handle.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_path.h"
|
||||
#include "clixon_api_path_parse.h"
|
||||
|
||||
/*
|
||||
also called from yacc generated code *
|
||||
*/
|
||||
|
||||
void
|
||||
clixon_api_path_parseerror(void *_ay,
|
||||
char *s)
|
||||
{
|
||||
clicon_err(OE_XML, 0, "%s on line %d: %s at or before: '%s'",
|
||||
_AY->ay_name,
|
||||
_AY->ay_linenum ,
|
||||
s,
|
||||
clixon_api_path_parsetext);
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
api_path_parse_init(struct clicon_api_path_yacc_arg *ay)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
api_path_parse_exit(struct clicon_api_path_yacc_arg *ay)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Append new path structure to clixon path list
|
||||
*/
|
||||
static clixon_path *
|
||||
path_append(clixon_path *list,
|
||||
clixon_path *new)
|
||||
{
|
||||
clicon_debug(1, "%s()", __FUNCTION__);
|
||||
if (new == NULL)
|
||||
return NULL;
|
||||
ADDQ(new, list);
|
||||
return list;
|
||||
}
|
||||
|
||||
/*! Add keyvalue to existing clixon path
|
||||
*/
|
||||
static clixon_path *
|
||||
path_add_keyvalue(clixon_path *cp,
|
||||
cvec *cvk)
|
||||
{
|
||||
clicon_debug(1, "%s()", __FUNCTION__);
|
||||
if (cp)
|
||||
cp->cp_cvk = cvk;
|
||||
return cp;
|
||||
}
|
||||
|
||||
static clixon_path *
|
||||
path_new(char *module_name,
|
||||
char *id)
|
||||
{
|
||||
clixon_path *cp = NULL;
|
||||
|
||||
clicon_debug(1, "%s(%s,%s)", __FUNCTION__, module_name, id);
|
||||
if ((cp = malloc(sizeof(*cp))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "malloc");
|
||||
goto done;
|
||||
}
|
||||
memset(cp, 0, sizeof(*cp));
|
||||
if (module_name)
|
||||
if ((cp->cp_prefix = strdup(module_name)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
if ((cp->cp_id = strdup(id)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
return cp;
|
||||
done:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Append a key-value cv to a cvec, create the cvec if not exist
|
||||
* @param[in] cvv Either created cvv or NULL, in whihc case it is created
|
||||
* @param[in] cv Is consumed by thius function (if appended)
|
||||
* @retval NULL Error
|
||||
* @retval cvv Cvec
|
||||
*/
|
||||
static cvec *
|
||||
keyval_add(cvec *cvv,
|
||||
cg_var *cv)
|
||||
{
|
||||
clicon_debug(1, "%s()", __FUNCTION__);
|
||||
if (cv == NULL)
|
||||
goto done;
|
||||
if (cvv == NULL &&
|
||||
(cvv = cvec_new(0)) == NULL) {
|
||||
clicon_err(OE_UNIX, errno, "cvec_new");
|
||||
goto done;
|
||||
}
|
||||
if (cvec_append_var(cvv, cv) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cvec_append_var");
|
||||
cvv = NULL;
|
||||
goto done;
|
||||
}
|
||||
cv_free(cv);
|
||||
done:
|
||||
return cvv;
|
||||
}
|
||||
|
||||
/*! Create a single key-value as cv and return it
|
||||
*/
|
||||
static cg_var *
|
||||
keyval_set(char *name,
|
||||
char *val)
|
||||
{
|
||||
cg_var *cv = NULL;
|
||||
|
||||
clicon_debug(1, "%s(%s=%s)", __FUNCTION__, name, val);
|
||||
if ((cv = cv_new(CGV_STRING)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cv_new");
|
||||
goto done;
|
||||
}
|
||||
if (name && cv_name_set(cv, name) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cv_string_set");
|
||||
cv = NULL;
|
||||
goto done;
|
||||
}
|
||||
if (cv_string_set(cv, val) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cv_string_set");
|
||||
cv = NULL;
|
||||
goto done;
|
||||
}
|
||||
done:
|
||||
return cv;
|
||||
}
|
||||
|
||||
%}
|
||||
|
||||
|
||||
%%
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
start : list X_EOF {clicon_debug(2,"top");_AY->ay_top=$1; YYACCEPT; }
|
||||
;
|
||||
|
||||
list : list SLASH element { if (($$ = path_append($1, $3)) == NULL) YYABORT;
|
||||
clicon_debug(2,"list = list / element");}
|
||||
| { $$ = NULL;
|
||||
clicon_debug(2,"list = ");}
|
||||
;
|
||||
|
||||
element : api_identifier { $$=$1;
|
||||
clicon_debug(2,"element = api_identifier");}
|
||||
| list_instance { $$=$1;
|
||||
clicon_debug(2,"element = list_instance");}
|
||||
;
|
||||
|
||||
api_identifier : module_name COLON IDENTIFIER { $$ = path_new($1, $3); free($1); free($3);
|
||||
clicon_debug(2,"api_identifier = module_name : IDENTIFIER");}
|
||||
| IDENTIFIER { $$ = path_new(NULL, $1); free($1);
|
||||
clicon_debug(2,"api_identifier = IDENTIFIER");}
|
||||
;
|
||||
|
||||
module_name : IDENTIFIER { $$ = $1;
|
||||
clicon_debug(2,"module_name = IDENTIFIER");}
|
||||
;
|
||||
|
||||
list_instance : api_identifier EQUAL key_values { $$ = path_add_keyvalue($1, $3);
|
||||
clicon_debug(2,"list_instance->api_identifier = key_values");}
|
||||
;
|
||||
|
||||
key_values : key_values COMMA key_value { if (($$ = keyval_add($1, $3)) == NULL) YYABORT;
|
||||
clicon_debug(2,"key_values->key_values , key_value");}
|
||||
| key_value { if (($$ = keyval_add(NULL, $1)) == NULL) YYABORT;
|
||||
clicon_debug(2,"key_values->key_value");}
|
||||
;
|
||||
|
||||
key_value : STRING { $$ = keyval_set(NULL, $1); free($1); clicon_debug(2,"keyvalue->STRING"); }
|
||||
| { $$ = keyval_set(NULL, ""); clicon_debug(2,"keyvalue->"); }
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -853,7 +853,6 @@ xmldb_put(clicon_handle h,
|
|||
xml_name(x1));
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ((de = clicon_db_elmnt_get(h, db)) != NULL){
|
||||
if (clicon_datastore_cache(h) != DATASTORE_NOCACHE)
|
||||
x0 = de->de_xml;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2017-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -88,6 +89,7 @@ static struct errvec EV[] = {
|
|||
{"Daemon error", OE_DAEMON},
|
||||
{"Event error", OE_EVENTS},
|
||||
{"Config error", OE_CFG},
|
||||
{"Netconf error", OE_NETCONF},
|
||||
{"Protocol error", OE_PROTO},
|
||||
{"Regexp error", OE_REGEX},
|
||||
{"UNIX error", OE_UNIX},
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -60,10 +60,11 @@
|
|||
#include "clixon_string.h"
|
||||
#include "clixon_hash.h"
|
||||
#include "clixon_handle.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_yang_type.h"
|
||||
#include "clixon_options.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_yang_type.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_xml_sort.h"
|
||||
#include "clixon_xml_map.h"
|
||||
#include "clixon_xml_nsctx.h" /* namespace context */
|
||||
|
|
@ -331,7 +332,7 @@ json2xml_decode_identityref(cxobj *x,
|
|||
if (yang_find_prefix_by_namespace(y, namespace, &prefix2) == 0){
|
||||
#ifndef IDENTITYREF_KLUDGE
|
||||
/* Just get the prefix from the module's own namespace */
|
||||
if (netconf_unknown_namespace_xml(xerr, "application",
|
||||
if (xerr && netconf_unknown_namespace_xml(xerr, "application",
|
||||
namespace,
|
||||
"No local prefix corresponding to namespace") < 0)
|
||||
goto done;
|
||||
|
|
@ -365,7 +366,7 @@ json2xml_decode_identityref(cxobj *x,
|
|||
goto done;
|
||||
}
|
||||
else{
|
||||
if (netconf_unknown_namespace_xml(xerr, "application",
|
||||
if (xerr && netconf_unknown_namespace_xml(xerr, "application",
|
||||
prefix,
|
||||
"No module corresponding to prefix") < 0)
|
||||
goto done;
|
||||
|
|
@ -1187,7 +1188,7 @@ json_parse(char *str,
|
|||
*
|
||||
* @code
|
||||
* cxobj *cx = NULL;
|
||||
* if (json_parse_str(str, &cx) < 0)
|
||||
* if (json_parse_str(str, yspec, &cx, &xerr) < 0)
|
||||
* err;
|
||||
* xml_free(cx);
|
||||
* @endcode
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -82,7 +82,7 @@ integer {digit}+
|
|||
real ({digit}+[.]{digit}*)|({digit}*[.]{digit}+)
|
||||
exp ({integer}|{real})[eE][+-]{integer}
|
||||
|
||||
%x START
|
||||
%x START§
|
||||
%s STRING
|
||||
%s ESCAPE
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -245,7 +245,7 @@ json_current_body(struct clicon_json_yacc_arg *jy,
|
|||
*/
|
||||
|
||||
/* top: json -> value is also possible */
|
||||
json : value J_EOF { clicon_debug(2,"json->object"); YYACCEPT; }
|
||||
§json : value J_EOF { clicon_debug(2,"json->object"); YYACCEPT; }
|
||||
;
|
||||
|
||||
value : J_TRUE { json_current_body(_JY, "true");}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -498,7 +498,7 @@ nacm_data_read_xr(cxobj *xt,
|
|||
* For NETCONF filtering purposes, the selection criteria are applied to the
|
||||
* subset of nodes that the user is authorized to read, not the entire datastore.
|
||||
* @note assume mode is internal or external, not disabled
|
||||
* @node There is unclarity on what "a data node" means wrt a read operation.
|
||||
* @note There is unclarity on what "a data node" means wrt a read operation.
|
||||
* Suppose a tree is accessed. Is "the data node" just the top of the tree?
|
||||
* (1) Or is it all nodes, recursively, in the data-tree?
|
||||
* (2) Or is the datanode only the requested tree, NOT the whole datatree?
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2017-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -66,6 +67,7 @@
|
|||
#include "clixon_xpath_ctx.h"
|
||||
#include "clixon_xpath.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_yang_parse_lib.h"
|
||||
#include "clixon_netconf_lib.h"
|
||||
|
||||
/*! Create Netconf in-use error XML tree according to RFC 6241 Appendix A
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2017-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -72,6 +73,7 @@
|
|||
#include "clixon_data.h"
|
||||
#include "clixon_xpath_ctx.h"
|
||||
#include "clixon_xpath.h"
|
||||
#include "clixon_yang_parse_lib.h"
|
||||
#include "clixon_netconf_lib.h"
|
||||
#include "clixon_xml_nsctx.h"
|
||||
#include "clixon_validate.h"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -31,17 +31,30 @@
|
|||
|
||||
***** END LICENSE BLOCK *****
|
||||
|
||||
* This file has code for several variants of paths in cxobj trees:
|
||||
* - api-path as defined by RESTCONF
|
||||
* - instance-identifier as defined by YANG
|
||||
* - clixon-path is an internal format which both ^ use as internal representation
|
||||
*
|
||||
* "Instance-identifier" is a subset of XML Xpaths and defined in Yang, used in NACM for example.
|
||||
* and defined in RF7950 Sections 9.13 and 14.
|
||||
* To note: prefixes depend on the XML context in which the value occurs,
|
||||
*
|
||||
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
|
||||
* BNF:
|
||||
* <api-path> := <root> ("/" (<api-identifier> | <list-instance>))*
|
||||
* <root> := <string>
|
||||
* <root> := <string> # See note 1 below
|
||||
* <api-identifier> := [<module-name> ":"] <identifier>
|
||||
* <module-name> := <identifier>
|
||||
* <list-instance> := <api-identifier> "=" key-value *("," key-value)
|
||||
* <key-value> := <string>
|
||||
* <string> := <an unquoted string>
|
||||
* <identifier> := (<ALPHA> | "_") (<ALPHA> | <DIGIT> | "_" | "-" | ".")
|
||||
|
||||
*
|
||||
* @note 1. <root> is the RESTCONF root resource (Sec 3.3) omitted in all calls below, it is
|
||||
* assumed to be stripped from api-path before calling these functions.
|
||||
* @note 2. characters in a key value string are constrained, and some characters need to be
|
||||
* percent-encoded,
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "clixon_config.h" /* generated by config & autoconf */
|
||||
|
|
@ -75,9 +88,158 @@
|
|||
#include "clixon_yang.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_xml_nsctx.h"
|
||||
#include "clixon_xml_sort.h"
|
||||
#include "clixon_netconf_lib.h"
|
||||
#include "clixon_xml_map.h"
|
||||
#include "clixon_api_path.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_path.h"
|
||||
#include "clixon_api_path_parse.h"
|
||||
#include "clixon_instance_id_parse.h"
|
||||
|
||||
/*! Given api-path, parse it, and return a clixon-path struct
|
||||
*
|
||||
* @param[in] api_path String with api-path syntax according to RESTCONF RFC8040
|
||||
* @param[out] cplist Structured internal clixon-path
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @code
|
||||
* clixon_path *cplist = NULL;
|
||||
* if (api_path_parse(api_path, &cplist) < 0)
|
||||
* err;
|
||||
* if (api_path_resolve(cplist, yt) < 0)
|
||||
* err;
|
||||
* ...
|
||||
* if (cplist)
|
||||
* clixon_path_free(cplist);
|
||||
* @endcode
|
||||
* @see clixon_path_free
|
||||
*/
|
||||
static int
|
||||
api_path_parse(char *api_path,
|
||||
clixon_path **cplist)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_api_path_yacc_arg ay = {0,};
|
||||
|
||||
clicon_debug(1, "%s api_path:%s", __FUNCTION__, api_path);
|
||||
ay.ay_parse_string = api_path;
|
||||
ay.ay_name = "api-path parser";
|
||||
ay.ay_linenum = 1;
|
||||
if (api_path_scan_init(&ay) < 0)
|
||||
goto done;
|
||||
if (api_path_parse_init(&ay) < 0)
|
||||
goto done;
|
||||
if (clixon_api_path_parseparse(&ay) != 0) { /* yacc returns 1 on error */
|
||||
clicon_log(LOG_NOTICE, "API-PATH error: on line %d", ay.ay_linenum);
|
||||
if (clicon_errno == 0)
|
||||
clicon_err(OE_XML, 0, "API-PATH parser error with no error code (should not happen)");
|
||||
goto done;
|
||||
}
|
||||
api_path_parse_exit(&ay);
|
||||
api_path_scan_exit(&ay);
|
||||
*cplist = ay.ay_top;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Given instance-id path, parse it, and return an clixon-path struct
|
||||
*
|
||||
* @param[in] api_path String with syntax according to YANG RFC7980
|
||||
* @param[out] cplist Structured internal clixon-path
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @code
|
||||
* clixon_path *cplist = NULL;
|
||||
* if (instance_id_parse(api_path, &cplist) < 0)
|
||||
* err;
|
||||
* ...
|
||||
* if (cplist)
|
||||
* clixon_path_free(cplist);
|
||||
* @endcode
|
||||
* @see clixon_path_free
|
||||
*/
|
||||
static int
|
||||
instance_id_parse(char *path,
|
||||
clixon_path **cplist)
|
||||
{
|
||||
int retval = -1;
|
||||
struct clicon_instance_id_yacc_arg iy = {0,};
|
||||
|
||||
clicon_debug(1, "%s path:%s", __FUNCTION__, path);
|
||||
iy.iy_parse_string = path;
|
||||
iy.iy_name = "instance-id parser";
|
||||
iy.iy_linenum = 1;
|
||||
if (instance_id_scan_init(&iy) < 0)
|
||||
goto done;
|
||||
if (instance_id_parse_init(&iy) < 0)
|
||||
goto done;
|
||||
if (clixon_instance_id_parseparse(&iy) != 0) { /* yacc returns 1 on error */
|
||||
clicon_log(LOG_NOTICE, "Instance-id error: on line %d", iy.iy_linenum);
|
||||
if (clicon_errno == 0)
|
||||
clicon_err(OE_XML, 0, "Instance-id parser error with no error code (should not happen)");
|
||||
goto done;
|
||||
}
|
||||
instance_id_parse_exit(&iy);
|
||||
instance_id_scan_exit(&iy);
|
||||
*cplist = iy.iy_top;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int
|
||||
clixon_path_free(clixon_path *cplist)
|
||||
{
|
||||
clixon_path *cp;
|
||||
|
||||
while ((cp = cplist) != NULL){
|
||||
DELQ(cp, cplist, clixon_path *);
|
||||
if (cp->cp_prefix)
|
||||
free(cp->cp_prefix);
|
||||
if (cp->cp_id)
|
||||
free(cp->cp_id);
|
||||
if (cp->cp_cvk)
|
||||
cvec_free(cp->cp_cvk);
|
||||
free(cp);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Print path on instance-id/xpath form
|
||||
*/
|
||||
static int
|
||||
clixon_path_print(FILE *f,
|
||||
clixon_path *cplist)
|
||||
{
|
||||
clixon_path *cp;
|
||||
cg_var *cv;
|
||||
|
||||
if ((cp = cplist) != NULL){
|
||||
do {
|
||||
fprintf(f, "/");
|
||||
if (cp->cp_prefix)
|
||||
fprintf(f, "%s:", cp->cp_prefix);
|
||||
fprintf(f, "%s", cp->cp_id);
|
||||
if (cp->cp_cvk){
|
||||
fprintf(f, "=");
|
||||
cv = NULL;
|
||||
while ((cv = cvec_each(cp->cp_cvk, cv)) != NULL){
|
||||
fprintf(f, "[");
|
||||
/* If cvk has one integer argument, interpret as position, eg x/y[42] */
|
||||
if (cvec_len(cp->cp_cvk) == 1 && (cv_type_get(cv) == CGV_UINT32))
|
||||
fprintf(f, "%u", cv_uint32_get(cv));
|
||||
else
|
||||
fprintf(f, "%s=\"%s\"", cv_name_get(cv), cv_string_get(cv));
|
||||
fprintf(f, "]");
|
||||
}
|
||||
}
|
||||
cp = NEXTQ(clixon_path *, cp);
|
||||
} while (cp && cp != cplist);
|
||||
}
|
||||
fprintf(f, "\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Given an XML node, return root node
|
||||
* A root node is an ancestor xr of x with one or both of the following properties
|
||||
|
|
@ -936,7 +1098,7 @@ api_path2xml(char *api_path,
|
|||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if (*api_path!='/'){
|
||||
if (*api_path != '/'){
|
||||
cprintf(cberr, "Invalid api-path: %s (must start with '/')", api_path);
|
||||
if (netconf_invalid_value_xml(xerr, "application", cbuf_get(cberr)) < 0)
|
||||
goto done;
|
||||
|
|
@ -955,8 +1117,8 @@ api_path2xml(char *api_path,
|
|||
}
|
||||
nvec--; /* NULL-terminated */
|
||||
if ((retval = api_path2xml_vec(vec+1, nvec,
|
||||
xtop, yspec, nodeclass, strict,
|
||||
xbotp, ybotp, xerr)) < 1)
|
||||
xtop, yspec, nodeclass, strict,
|
||||
xbotp, ybotp, xerr)) < 1)
|
||||
goto done;
|
||||
xml_yang_root(*xbotp, &xroot);
|
||||
if (xmlns_assign(xroot) < 0)
|
||||
|
|
@ -1062,3 +1224,409 @@ xml2api_path_1(cxobj *x,
|
|||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Resolve api-path module:names to yang statements
|
||||
* @param[in] cplist Lisp of clixon-path
|
||||
* @param[in] yt Yang statement of top symbol (can be yang-spec if top-level)
|
||||
* @retval -1 Error
|
||||
* @retval 0 Fail
|
||||
* @retval 1 OK
|
||||
* Reasons for fail (retval = 0) are: XXX
|
||||
* - Modulename in api-path does not correspond to existing module
|
||||
* - Modulename not defined for top-level id.
|
||||
* - Corresponding yang node for id not found
|
||||
* - Number of keys in key-value list does not match Yang list
|
||||
* - Key-values only defined for list or leaf-list
|
||||
* @see instance_id_resolve
|
||||
*/
|
||||
static int
|
||||
api_path_resolve(clixon_path *cplist,
|
||||
yang_stmt *yt)
|
||||
{
|
||||
int retval = -1;
|
||||
clixon_path *cp;
|
||||
yang_stmt *yc;
|
||||
int i;
|
||||
cg_var *cva;
|
||||
cg_var *cvy;
|
||||
|
||||
if ((cp = cplist) != NULL){
|
||||
do {
|
||||
if (yang_keyword_get(yt) == Y_SPEC){
|
||||
if (cp->cp_prefix == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "Modulename not defined for top-level id.");
|
||||
goto fail;
|
||||
}
|
||||
if ((yt = yang_find_module_by_name(yt, cp->cp_prefix)) == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "Modulename in api-path does not correspond to existing module");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if ((yc = yang_find_datanode(yt, cp->cp_id)) == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "Corresponding yang node for id not found");
|
||||
goto fail;
|
||||
}
|
||||
cp->cp_yang = yc;
|
||||
if (cp->cp_cvk){
|
||||
/* Iterate over yang list keys and assign as names (or "." for leaf-list) in cvk */
|
||||
if (yang_keyword_get(yc) == Y_LEAF_LIST){
|
||||
cva = NULL;
|
||||
while ((cva = cvec_each(cp->cp_cvk, cva)) != NULL) {
|
||||
if (cv_name_get(cva) == NULL)
|
||||
cv_name_set(cva, ".");
|
||||
}
|
||||
}
|
||||
else if (yang_keyword_get(yc) == Y_LIST){
|
||||
if (cvec_len(cp->cp_cvk) > cvec_len(yang_cvec_get(yc))){
|
||||
clicon_err(OE_YANG, ENOENT, "Number of keys in key-value list does not match Yang list");
|
||||
goto fail;
|
||||
}
|
||||
i = 0;
|
||||
cva = NULL;
|
||||
while ((cva = cvec_each(cp->cp_cvk, cva)) != NULL) {
|
||||
if (cv_name_get(cva) == NULL){
|
||||
cvy = cvec_i(yang_cvec_get(yc), i);
|
||||
cv_name_set(cva, cv_string_get(cvy));
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else{
|
||||
clicon_err(OE_YANG, ENOENT, "key-values only defined for list or leaf-list");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
yt = yc;
|
||||
cp = NEXTQ(clixon_path *, cp);
|
||||
} while (cp && cp != cplist);
|
||||
}
|
||||
retval = 1;
|
||||
done:
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Resolve instance-id prefix:names to yang statements
|
||||
* @param[in] cplist Lisp of clixon-path
|
||||
* @param[in] yt Yang statement of top symbol (can be yang-spec if top-level)
|
||||
* @param[in] xt XML statement for prefix context
|
||||
* @retval -1 Error
|
||||
* @retval 0 Fail
|
||||
* @retval 1 OK
|
||||
* @note: The spec says: prefixes depend on the XML context in which the value occurs. However,
|
||||
* canonical prefixes/namespaces are used based on loaded yang modules.
|
||||
* This means that prefix=NULL is not allowed.
|
||||
* Reasons for fail (retval = 0) are:
|
||||
* - No prefix of identifier (keynames may omit prefix)
|
||||
* - Prefix does not correspond to existing module.
|
||||
* - Corresponding yang node for id not found
|
||||
* - Number of keys in key-value list does not match Yang list
|
||||
* - Key-values only defined for list or leaf-list
|
||||
* @see api_path_resolve
|
||||
*/
|
||||
static int
|
||||
instance_id_resolve(clixon_path *cplist,
|
||||
yang_stmt *yt)
|
||||
{
|
||||
int retval = -1;
|
||||
clixon_path *cp;
|
||||
yang_stmt *yc;
|
||||
int i;
|
||||
cg_var *cva;
|
||||
cg_var *cvy;
|
||||
yang_stmt *yspec;
|
||||
|
||||
yspec = ys_spec(yt);
|
||||
if ((cp = cplist) != NULL){
|
||||
do {
|
||||
if (cp->cp_prefix == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "No prefix of identifier (keynames may omit prefix)");
|
||||
goto fail;
|
||||
}
|
||||
if (yang_keyword_get(yt) == Y_SPEC){
|
||||
if ((yt = yang_find_module_by_prefix_yspec(yspec, cp->cp_prefix)) == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "Prefix does not correspond to existing module.");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if ((yc = yang_find_datanode(yt, cp->cp_id)) == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "Corresponding yang node for id not found");
|
||||
goto fail;
|
||||
}
|
||||
cp->cp_yang = yc;
|
||||
if (cp->cp_cvk){
|
||||
/* Iterate over yang list keys and assign as names in cvk */
|
||||
if (yang_keyword_get(yc) == Y_LEAF_LIST)
|
||||
;
|
||||
else if (yang_keyword_get(yc) == Y_LIST){
|
||||
#ifdef notused
|
||||
if (cvec_len(cp->cp_cvk) > cvec_len(yang_cvec_get(yc))){
|
||||
clicon_err(OE_YANG, ENOENT, "Number of keys in key-value list does not match Yang list ");
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
i = 0;
|
||||
cva = NULL;
|
||||
while ((cva = cvec_each(cp->cp_cvk, cva)) != NULL) {
|
||||
if (cv_name_get(cva) == NULL){
|
||||
cvy = cvec_i(yang_cvec_get(yc), i);
|
||||
cv_name_set(cva, cv_string_get(cvy));
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
else{
|
||||
clicon_err(OE_YANG, ENOENT, "key-values only defined for list or leaf-list");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
yt = yc;
|
||||
cp = NEXTQ(clixon_path *, cp);
|
||||
} while (cp && cp != cplist);
|
||||
}
|
||||
retval = 1;
|
||||
done:
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Search XML tree using Clixon path struct
|
||||
*
|
||||
* @param[in] xt Top xml-tree where to search
|
||||
* @param[in] yt Yang statement of top symbol (can be yang-spec if top-level)
|
||||
* @param[in] cplist Lisp of clixon-path
|
||||
* @param[out] xvec Vector of xml-trees. Vector must be free():d after use
|
||||
* @param[out] xlen Returns length of vector in return value
|
||||
* @retval -1 Error
|
||||
* @retval 0 Fail fail: eg no yang
|
||||
* @retval 1 OK with found xml nodes in xvec (if any)
|
||||
*/
|
||||
static int
|
||||
clixon_path_search(cxobj *xt,
|
||||
yang_stmt *yt,
|
||||
clixon_path *cplist,
|
||||
cxobj ***xvec0,
|
||||
size_t *xlen0)
|
||||
{
|
||||
int retval = -1;
|
||||
char *modns; /* Module namespace of api-path */
|
||||
clixon_path *cp;
|
||||
cxobj **xvecp = NULL; /* parent set */
|
||||
size_t xlenp = 0;
|
||||
cxobj **xvecc = NULL; /* child set */
|
||||
size_t xlenc = 0;
|
||||
yang_stmt *yc;
|
||||
cxobj *xp;
|
||||
int i;
|
||||
cg_var *cv;
|
||||
|
||||
modns = NULL;
|
||||
if ((cp = cplist) != NULL){
|
||||
cxvec_append(xt, &xvecp, &xlenp); /* Initialize parent xml set */
|
||||
do {
|
||||
yc = cp->cp_yang;
|
||||
if ((modns = yang_find_mynamespace(yc)) == NULL)
|
||||
goto fail;
|
||||
for (i=0; i<xlenp; i++){
|
||||
xp = xvecp[i]; /* Iterate over parent set */
|
||||
if (cp->cp_cvk && /* cornercase for instance-id [<pos>] special case */
|
||||
(yang_keyword_get(yc) == Y_LIST || yang_keyword_get(yc) == Y_LEAF_LIST) &&
|
||||
cvec_len(cp->cp_cvk) == 1 && (cv = cvec_i(cp->cp_cvk,0)) &&
|
||||
(cv_type_get(cv) == CGV_UINT32)){
|
||||
if (clixon_xml_find_pos(xp, yc, cv_uint32_get(cv), &xvecc, &xlenc) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (clixon_xml_find_index(xp, yang_parent_get(yc), modns, yang_argument_get(yc),
|
||||
cp->cp_cvk, &xvecc, &xlenc) < 0)
|
||||
goto done;
|
||||
}
|
||||
if (xvecp)
|
||||
free(xvecp);
|
||||
xvecp = xvecc; xlenp = xlenc;
|
||||
xvecc = NULL, xlenc = 0;
|
||||
cp = NEXTQ(clixon_path *, cp);
|
||||
} while (cp && cp != cplist);
|
||||
*xvec0 = xvecp; xvecp = NULL;
|
||||
*xlen0 = xlenp;
|
||||
}
|
||||
retval = 1;
|
||||
done:
|
||||
if (xvecp)
|
||||
free(xvecp);
|
||||
if (xvecc)
|
||||
free(xvecc);
|
||||
return retval;
|
||||
fail: /* eg no yang for api-path, no match */
|
||||
*xvec0 = NULL;
|
||||
*xlen0 = 0;
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Given RESTCONF api-path and an XML tree, return matching xml node vector using stdarg
|
||||
*
|
||||
* @param[in] xt Top xml-tree where to search
|
||||
* @param[in] yt Yang statement of top symbol (can be yang-spec if top-level)
|
||||
* @param[out] xvec Vector of xml-trees. Vector must be free():d after use
|
||||
* @param[out] xlen Returns length of vector in return value
|
||||
* @param[in] format Format string for api-path syntax
|
||||
* @retval -1 Error
|
||||
* @retval 0 Nomatch
|
||||
* @retval 1 OK with found xml nodes in xvec (if any)
|
||||
* Reasons for nomatch (retval = 0) are:
|
||||
* - Modulename in api-path does not correspond to existing module
|
||||
* - Modulename not defined for top-level id.
|
||||
* - Number of keys in key-value list does not match Yang list
|
||||
* @code
|
||||
* cxobj **xvec = NULL;
|
||||
* size_t xlen;
|
||||
* if (clixon_xml_find_api_path(x, &xvec, &xlen, "/symbol/%s", "foo") < 0)
|
||||
* goto err;
|
||||
* for (i=0; i<xlen; i++){
|
||||
* xn = xvec[i];
|
||||
* ...
|
||||
* }
|
||||
* free(xvec);
|
||||
* @endcode
|
||||
* @see xpath_vec
|
||||
*/
|
||||
int
|
||||
clixon_xml_find_api_path(cxobj *xt,
|
||||
yang_stmt *yt,
|
||||
cxobj ***xvec,
|
||||
size_t *xlen,
|
||||
char *format,
|
||||
...)
|
||||
{
|
||||
int retval = -1;
|
||||
va_list ap;
|
||||
size_t len;
|
||||
char *api_path = NULL;
|
||||
clixon_path *cplist = NULL;
|
||||
int ret;
|
||||
|
||||
va_start(ap, format);
|
||||
len = vsnprintf(NULL, 0, format, ap);
|
||||
va_end(ap);
|
||||
/* allocate an api-path string exactly fitting the length */
|
||||
if ((api_path = malloc(len+1)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "malloc");
|
||||
goto done;
|
||||
}
|
||||
/* second round: actually compute api-path string content */
|
||||
va_start(ap, format);
|
||||
if (vsnprintf(api_path, len+1, format, ap) < 0){
|
||||
clicon_err(OE_UNIX, errno, "vsnprintf");
|
||||
va_end(ap);
|
||||
goto done;
|
||||
}
|
||||
va_end(ap);
|
||||
/* Parse api-path string to structured clixon-path data */
|
||||
if (api_path_parse(api_path, &cplist) < 0)
|
||||
goto done;
|
||||
if (debug)
|
||||
clixon_path_print(stderr, cplist);
|
||||
/* Resolve module:name to yang-stmt, fail if not successful */
|
||||
if ((ret = api_path_resolve(cplist, yt)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
retval = clixon_path_search(xt, yt, cplist, xvec, xlen);
|
||||
done:
|
||||
if (cplist)
|
||||
clixon_path_free(cplist);
|
||||
if (api_path)
|
||||
free(api_path);
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Given (instance-id) path and an XML tree, return matching xml node vector using stdarg
|
||||
*
|
||||
* Instance-identifier is a subset of XML Xpaths and defined in Yang, used in NACM for example.
|
||||
* @param[in] xt Top xml-tree where to search
|
||||
* @param[in] yt Yang statement of top symbol (can be yang-spec if top-level)
|
||||
* @param[out] xvec Vector of xml-trees. Vector must be free():d after use
|
||||
* @param[out] xlen Returns length of vector in return value
|
||||
* @param[in] format Format string for api-path syntax
|
||||
* @retval -1 Error
|
||||
* @retval 0 Nomatch
|
||||
* @retval 1 OK with found xml nodes in xvec (if any)
|
||||
* Reasons for nomatch (retval = 0) are:
|
||||
* - Modulename in api-path does not correspond to existing module
|
||||
* - Modulename not defined for top-level id.
|
||||
* - Number of keys in key-value list does not match Yang list
|
||||
* @code
|
||||
* cxobj **xvec = NULL;
|
||||
* size_t xlen;
|
||||
* if (clixon_xml_find_instance_id(x, &xvec, &xlen, "/symbol/%s", "foo") < 0)
|
||||
* goto err;
|
||||
* for (i=0; i<xlen; i++){
|
||||
* xn = xvec[i];
|
||||
* ...
|
||||
* }
|
||||
* free(xvec);
|
||||
* @endcode
|
||||
* @see xpath_vec for full XML Xpaths
|
||||
* @see api_path_search for RESTCONF api-paths
|
||||
* @see RFC7950 Sec 9.13
|
||||
*/
|
||||
int
|
||||
clixon_xml_find_instance_id(cxobj *xt,
|
||||
yang_stmt *yt,
|
||||
cxobj ***xvec,
|
||||
size_t *xlen,
|
||||
char *format,
|
||||
...)
|
||||
{
|
||||
int retval = -1;
|
||||
va_list ap;
|
||||
size_t len;
|
||||
char *path = NULL;
|
||||
clixon_path *cplist = NULL;
|
||||
int ret;
|
||||
|
||||
va_start(ap, format);
|
||||
len = vsnprintf(NULL, 0, format, ap);
|
||||
va_end(ap);
|
||||
/* allocate a path string exactly fitting the length */
|
||||
if ((path = malloc(len+1)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "malloc");
|
||||
goto done;
|
||||
}
|
||||
/* second round: actually compute api-path string content */
|
||||
va_start(ap, format);
|
||||
if (vsnprintf(path, len+1, format, ap) < 0){
|
||||
clicon_err(OE_UNIX, errno, "vsnprintf");
|
||||
va_end(ap);
|
||||
goto done;
|
||||
}
|
||||
va_end(ap);
|
||||
if (instance_id_parse(path, &cplist) < 0)
|
||||
goto done;
|
||||
if (debug)
|
||||
clixon_path_print(stderr, cplist);
|
||||
/* Resolve module:name to yang-stmt, fail if not successful */
|
||||
if ((ret = instance_id_resolve(cplist, yt)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
if ((retval = clixon_path_search(xt, yt, cplist, xvec, xlen)) < 0)
|
||||
goto done;
|
||||
done:
|
||||
if (cplist)
|
||||
clixon_path_free(cplist);
|
||||
if (path)
|
||||
free(path);
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
|
@ -2,7 +2,8 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2017-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -247,12 +248,10 @@ clicon_rpc_generate_error(cxobj *xerr,
|
|||
}
|
||||
if (netconf_err2cb(xerr, cb) < 0)
|
||||
goto done;
|
||||
if (arg){
|
||||
cprintf(cb, "%s: \"%s\": ", msg, arg);
|
||||
clicon_err(OE_CFG, EINVAL, "%s", cbuf_get(cb));
|
||||
}
|
||||
else /* XXX: should really be clicon_err but dont do it yet for backward compatability */
|
||||
clicon_log(LOG_ERR, "%s: %s", msg, cbuf_get(cb));
|
||||
cprintf(cb, ". %s", msg);
|
||||
if (arg)
|
||||
cprintf(cb, " \"%s\" ", arg);
|
||||
clicon_err(OE_NETCONF, 0, "%s", cbuf_get(cb));
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2017-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -71,22 +72,14 @@
|
|||
#include "clixon_xml_nsctx.h"
|
||||
#include "clixon_xpath_ctx.h"
|
||||
#include "clixon_xpath.h"
|
||||
#if 0
|
||||
|
||||
#include "clixon_plugin.h"
|
||||
|
||||
|
||||
#include "clixon_log.h"
|
||||
#include "clixon_xml_sort.h"
|
||||
#include "clixon_yang_internal.h" /* internal */
|
||||
|
||||
#endif
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_yang_type.h"
|
||||
#include "clixon_xml_map.h"
|
||||
#include "clixon_validate.h"
|
||||
|
||||
/*! Validate xml node of type leafref, ensure the value is one of that path's reference
|
||||
* @param[in] xt XML leaf node of type leafref
|
||||
* @param[in] ys Yang spec of leaf
|
||||
* @param[in] ytype Yang type statement belonging to the XML node
|
||||
* @param[out] xret Error XML tree. Free with xml_free after use
|
||||
* @retval 1 Validation OK
|
||||
|
|
@ -97,17 +90,19 @@
|
|||
* in addition to the definition in Section 6.4.1:
|
||||
* o If the "path" statement is defined within a typedef, the context
|
||||
* node is the leaf or leaf-list node in the data tree that
|
||||
* references the typedef.
|
||||
* references the typedef. (ie ys)
|
||||
* o Otherwise, the context node is the node in the data tree for which
|
||||
* the "path" statement is defined.
|
||||
* the "path" statement is defined. (ie yc)
|
||||
*/
|
||||
static int
|
||||
validate_leafref(cxobj *xt,
|
||||
yang_stmt *ys,
|
||||
yang_stmt *ytype,
|
||||
cxobj **xret)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *ypath;
|
||||
yang_stmt *yp;
|
||||
cxobj **xvec = NULL;
|
||||
cxobj *x;
|
||||
int i;
|
||||
|
|
@ -115,6 +110,8 @@ validate_leafref(cxobj *xt,
|
|||
char *leafrefbody;
|
||||
char *leafbody;
|
||||
cvec *nsc = NULL;
|
||||
cbuf *cberr = NULL;
|
||||
char *path;
|
||||
|
||||
if ((leafrefbody = xml_body(xt)) == NULL)
|
||||
goto ok;
|
||||
|
|
@ -123,10 +120,17 @@ validate_leafref(cxobj *xt,
|
|||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
/* XXX see comment above regarding typeref or not */
|
||||
if (xml_nsctx_yang(ytype, &nsc) < 0)
|
||||
goto done;
|
||||
if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, yang_argument_get(ypath)) < 0)
|
||||
/* See comment^: If path is defined in typedef or not */
|
||||
if ((yp = yang_parent_get(ytype)) != NULL &&
|
||||
yang_keyword_get(yp) == Y_TYPEDEF){
|
||||
if (xml_nsctx_yang(ys, &nsc) < 0)
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
if (xml_nsctx_yang(ytype, &nsc) < 0)
|
||||
goto done;
|
||||
path = yang_argument_get(ypath);
|
||||
if (xpath_vec(xt, nsc, "%s", &xvec, &xlen, path) < 0)
|
||||
goto done;
|
||||
for (i = 0; i < xlen; i++) {
|
||||
x = xvec[i];
|
||||
|
|
@ -136,13 +140,20 @@ validate_leafref(cxobj *xt,
|
|||
break;
|
||||
}
|
||||
if (i==xlen){
|
||||
if (netconf_bad_element_xml(xret, "application", leafrefbody, "Leafref validation failed: No such leaf") < 0)
|
||||
if ((cberr = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cberr, "Leafref validation failed: No leaf %s matching path %s", leafrefbody, path);
|
||||
if (netconf_bad_element_xml(xret, "application", leafrefbody, cbuf_get(cberr)) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
ok:
|
||||
retval = 1;
|
||||
done:
|
||||
if (cberr)
|
||||
cbuf_free(cberr);
|
||||
if (nsc)
|
||||
xml_nsctx_free(nsc);
|
||||
if (xvec)
|
||||
|
|
@ -153,7 +164,6 @@ validate_leafref(cxobj *xt,
|
|||
goto done;
|
||||
}
|
||||
|
||||
|
||||
/*! Validate xml node of type identityref, ensure value is a defined identity
|
||||
* Check if a given node has value derived from base identity. This is
|
||||
* a run-time check necessary when validating eg netconf.
|
||||
|
|
@ -963,7 +973,6 @@ xml_yang_validate_add(clicon_handle h,
|
|||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if ((ys_cv_validate(h, cv, yt, &reason)) != 1){
|
||||
if (netconf_bad_element_xml(xret, "application", yang_argument_get(yt), reason) < 0)
|
||||
goto done;
|
||||
|
|
@ -1064,6 +1073,7 @@ xml_yang_validate_all(clicon_handle h,
|
|||
cxobj *x;
|
||||
char *namespace = NULL;
|
||||
cbuf *cb = NULL;
|
||||
cvec *nsc = NULL;
|
||||
|
||||
/* if not given by argument (overide) use default link
|
||||
and !Node has a config sub-statement and it is false */
|
||||
|
|
@ -1102,7 +1112,7 @@ xml_yang_validate_all(clicon_handle h,
|
|||
if (yang_type_get(ys, NULL, &yc, NULL, NULL, NULL, NULL, NULL) < 0)
|
||||
goto done;
|
||||
if (strcmp(yang_argument_get(yc), "leafref") == 0){
|
||||
if ((ret = validate_leafref(xt, yc, xret)) < 0)
|
||||
if ((ret = validate_leafref(xt, ys, yc, xret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -1124,7 +1134,9 @@ xml_yang_validate_all(clicon_handle h,
|
|||
if (yang_keyword_get(yc) != Y_MUST)
|
||||
continue;
|
||||
xpath = yang_argument_get(yc); /* "must" has xpath argument */
|
||||
if ((nr = xpath_vec_bool(xt, NULL, "%s", xpath)) < 0)
|
||||
if (xml_nsctx_yang(yc, &nsc) < 0)
|
||||
goto done;
|
||||
if ((nr = xpath_vec_bool(xt, nsc, "%s", xpath)) < 0)
|
||||
goto done;
|
||||
if (!nr){
|
||||
ye = yang_find(yc, Y_ERROR_MESSAGE, NULL);
|
||||
|
|
@ -1133,6 +1145,10 @@ xml_yang_validate_all(clicon_handle h,
|
|||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
if (nsc){
|
||||
xml_nsctx_free(nsc);
|
||||
nsc = NULL;
|
||||
}
|
||||
}
|
||||
/* "when" sub-node RFC 7950 Sec 7.21.5. Can only be one. */
|
||||
if ((yc = yang_find(ys, Y_WHEN, NULL)) != NULL){
|
||||
|
|
@ -1167,6 +1183,8 @@ xml_yang_validate_all(clicon_handle h,
|
|||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (nsc)
|
||||
xml_nsctx_free(nsc);
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2017-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -62,6 +63,7 @@
|
|||
#include "clixon_yang.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_options.h" /* xml_spec_populate */
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_xml_map.h" /* xml_spec_populate */
|
||||
#include "clixon_xml_sort.h"
|
||||
#include "clixon_xml_parse.h"
|
||||
|
|
@ -263,7 +265,7 @@ nscache_get_prefix(cxobj *x,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! Dump whole namespacet context cache of one xml node
|
||||
/*! Dump whole namespace context cache of one xml node
|
||||
* @param[in] x XML node
|
||||
* @retval nsc Whole namespace context of x
|
||||
* @retval NULL Empty nsc
|
||||
|
|
@ -581,7 +583,7 @@ xml_parent_set(cxobj *xn,
|
|||
|
||||
/*! Get xml node flags, used for internal algorithms
|
||||
* @param[in] xn xml node
|
||||
* @retval flag Flags value, see XML_FLAG_*
|
||||
* @retval flag Flag value(s), see XML_FLAG_*
|
||||
*/
|
||||
uint16_t
|
||||
xml_flag(cxobj *xn,
|
||||
|
|
@ -592,7 +594,7 @@ xml_flag(cxobj *xn,
|
|||
|
||||
/*! Set xml node flags, used for internal algorithms
|
||||
* @param[in] xn xml node
|
||||
* @param[in] flag Flags value to set, see XML_FLAG_*
|
||||
* @param[in] flag Flag values to set, see XML_FLAG_*
|
||||
*/
|
||||
int
|
||||
xml_flag_set(cxobj *xn,
|
||||
|
|
@ -604,7 +606,7 @@ xml_flag_set(cxobj *xn,
|
|||
|
||||
/*! Reset xml node flags, used for internal algorithms
|
||||
* @param[in] xn xml node
|
||||
* @param[in] flag Flags value to reset, see XML_FLAG_*
|
||||
* @param[in] flag Flag value(s) to reset, see XML_FLAG_*
|
||||
*/
|
||||
int
|
||||
xml_flag_reset(cxobj *xn,
|
||||
|
|
@ -1048,8 +1050,12 @@ xml_cv_set(cxobj *x,
|
|||
*
|
||||
* @retval xmlobj if found.
|
||||
* @retval NULL if no such node found.
|
||||
* @see xml_find_type A more generic function
|
||||
* @note Linear scalability and relies on strcmp
|
||||
* There are several issues with this function:
|
||||
* @note (1) Ignores prefix which means namespaces are ignored
|
||||
* @note (2) Does not differentiate between element,attributes and body. You usually want elements.
|
||||
* @note (3) Linear scalability and relies on strcmp, does not use search/key indexes
|
||||
* @note (4) Only returns first match, eg a list/leaf-list may have several children with same name
|
||||
* @see xml_find_type A more generic function fixes (1) and (2) above
|
||||
*/
|
||||
cxobj *
|
||||
xml_find(cxobj *x_up,
|
||||
|
|
@ -1494,9 +1500,10 @@ xml_find_type(cxobj *xt,
|
|||
char *xprefix; /* xprefix */
|
||||
|
||||
while ((x = xml_child_each(xt, x, type)) != NULL) {
|
||||
xprefix = xml_prefix(x);
|
||||
if (prefix)
|
||||
pmatch = xprefix?strcmp(prefix,xprefix)==0:0;
|
||||
if (prefix){
|
||||
xprefix = xml_prefix(x);
|
||||
pmatch = xprefix ? strcmp(prefix,xprefix)==0 : 0;
|
||||
}
|
||||
else
|
||||
pmatch = 1;
|
||||
if (pmatch && strcmp(name, xml_name(x)) == 0)
|
||||
|
|
@ -1980,7 +1987,9 @@ FSM(char *tag,
|
|||
*
|
||||
* @code
|
||||
* cxobj *xt = NULL;
|
||||
* xml_parse_file(0, "</config>", yspec, &xt);
|
||||
* int fd;
|
||||
* fd = open(filename, O_RDONLY);
|
||||
* xml_parse_file(fd, "</config>", yspec, &xt);
|
||||
* xml_free(xt);
|
||||
* @endcode
|
||||
* @see xml_parse_string
|
||||
|
|
@ -2246,10 +2255,23 @@ cxvec_dup(cxobj **vec0,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Append a new xml tree to an existing xml vector
|
||||
/*! Append a new xml tree to an existing xml vector last in the list
|
||||
* @param[in] x XML tree (append this to vector)
|
||||
* @param[in,out] vec XML tree vector
|
||||
* @param[in,out] len Length of XML tree vector
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @code
|
||||
* cxobj **xvec = NULL;
|
||||
* size_t xlen = 0;
|
||||
* cxobj *x;
|
||||
*
|
||||
* if (cxvec_append(x, &xvec, &xlen) < 0)
|
||||
* err;
|
||||
* if (xvec)
|
||||
* free(xvec);
|
||||
* @endcode
|
||||
* @see cxvec_prepend
|
||||
*/
|
||||
int
|
||||
cxvec_append(cxobj *x,
|
||||
|
|
@ -2268,6 +2290,44 @@ cxvec_append(cxobj *x,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Prepend a new xml tree to an existing xml vector first in the list
|
||||
* @param[in] x XML tree (append this to vector)
|
||||
* @param[in,out] vec XML tree vector
|
||||
* @param[in,out] len Length of XML tree vector
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @code
|
||||
* cxobj **xvec = NULL;
|
||||
* size_t xlen = 0;
|
||||
* cxobj *x;
|
||||
*
|
||||
* if (cxvec_append(x, &xvec, &xlen) < 0)
|
||||
* err;
|
||||
* if (xvec)
|
||||
* free(xvec);
|
||||
* @endcode
|
||||
* @see cxvec_prepend
|
||||
*/
|
||||
int
|
||||
cxvec_prepend(cxobj *x,
|
||||
cxobj ***vec,
|
||||
size_t *len)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
if ((*vec = realloc(*vec, sizeof(cxobj *) * (*len+1))) == NULL){
|
||||
clicon_err(OE_XML, errno, "realloc");
|
||||
goto done;
|
||||
}
|
||||
memmove(&(*vec)[1], &(*vec)[0], sizeof(cxobj *) * (*len));
|
||||
(*vec)[0] = x;
|
||||
(*len)++;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/*! Apply a function call recursively on all xml node children recursively
|
||||
* Recursively traverse all xml nodes in a parse-tree and apply fn(arg) for
|
||||
* each object found. The function is called with the xml node and an
|
||||
|
|
@ -2548,8 +2608,6 @@ xml_operation(char *opstr,
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*! Map xml operation from enumeration to string
|
||||
* @param[in] op enumeration operation, eg OP_MERGE,...
|
||||
* @retval str String, eg "merge". Static string, no free necessary
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2017-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -66,6 +67,7 @@
|
|||
#include "clixon_options.h"
|
||||
#include "clixon_data.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_yang_parse_lib.h"
|
||||
#include "clixon_netconf_lib.h"
|
||||
#include "clixon_xml_nsctx.h"
|
||||
#include "clixon_xml_map.h"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2017-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -72,7 +73,6 @@
|
|||
#include "clixon_err.h"
|
||||
#include "clixon_netconf_lib.h"
|
||||
#include "clixon_xml_sort.h"
|
||||
#include "clixon_yang_internal.h" /* internal */
|
||||
#include "clixon_yang_type.h"
|
||||
#include "clixon_xml_map.h"
|
||||
|
||||
|
|
@ -296,7 +296,7 @@ xml2cvec(cxobj *xt,
|
|||
name = xml_name(xc);
|
||||
if ((ys = yang_find_datanode(yt, name)) == NULL){
|
||||
clicon_debug(0, "%s: yang sanity problem: %s in xml but not present in yang under %s",
|
||||
__FUNCTION__, name, yt->ys_argument);
|
||||
__FUNCTION__, name, yang_argument_get(yt));
|
||||
if ((body = xml_body(xc)) != NULL){
|
||||
if ((cv = cv_new(CGV_STRING)) == NULL){
|
||||
clicon_err(OE_PLUGIN, errno, "cv_new");
|
||||
|
|
@ -476,7 +476,7 @@ xml_diff1(yang_stmt *ys,
|
|||
continue;
|
||||
}
|
||||
/* Both x0c and x1c exists, check if they are equal. */
|
||||
eq = xml_cmp(x0c, x1c, 0);
|
||||
eq = xml_cmp(x0c, x1c, 0, 0);
|
||||
if (eq < 0){
|
||||
if (cxvec_append(x0c, x0vec, x0veclen) < 0)
|
||||
goto done;
|
||||
|
|
@ -502,7 +502,7 @@ xml_diff1(yang_stmt *ys,
|
|||
if (cxvec_append(x1c, changed_x1, changedlen) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (yc->ys_keyword == Y_LEAF){
|
||||
else if (yang_keyword_get(yc) == Y_LEAF){
|
||||
/* if x0c and x1c are leafs w bodies, then they are changed */
|
||||
if ((b1 = xml_body(x0c)) == NULL) /* empty type */
|
||||
break;
|
||||
|
|
@ -761,7 +761,7 @@ xml_default(cxobj *xt,
|
|||
int retval = -1;
|
||||
yang_stmt *ys;
|
||||
yang_stmt *y;
|
||||
int i;
|
||||
// int i; // XXX
|
||||
cxobj *xc;
|
||||
cxobj *xb;
|
||||
char *str;
|
||||
|
|
@ -775,15 +775,15 @@ xml_default(cxobj *xt,
|
|||
goto done;
|
||||
}
|
||||
/* Check leaf defaults */
|
||||
if (ys->ys_keyword == Y_CONTAINER || ys->ys_keyword == Y_LIST ||
|
||||
ys->ys_keyword == Y_INPUT){
|
||||
for (i=0; i<ys->ys_len; i++){
|
||||
y = ys->ys_stmt[i];
|
||||
if (y->ys_keyword != Y_LEAF)
|
||||
if (yang_keyword_get(ys) == Y_CONTAINER || yang_keyword_get(ys) == Y_LIST ||
|
||||
yang_keyword_get(ys) == Y_INPUT){
|
||||
y = NULL;
|
||||
while ((y = yn_each(ys, y)) != NULL) {
|
||||
if (yang_keyword_get(y) != Y_LEAF)
|
||||
continue;
|
||||
if (!cv_flag(yang_cv_get(y), V_UNSET)){ /* Default value exists */
|
||||
if (!xml_find(xt, y->ys_argument)){
|
||||
if ((xc = xml_new(y->ys_argument, NULL, y)) == NULL)
|
||||
if (!xml_find(xt, yang_argument_get(y))){
|
||||
if ((xc = xml_new(yang_argument_get(y), NULL, y)) == NULL)
|
||||
goto done;
|
||||
/* assign right prefix */
|
||||
if ((namespace = yang_find_mynamespace(y)) != NULL){
|
||||
|
|
@ -843,9 +843,9 @@ xml_sanity(cxobj *xt,
|
|||
goto done;
|
||||
}
|
||||
name = xml_name(xt);
|
||||
if (strstr(ys->ys_argument, name)==NULL){
|
||||
if (strstr(yang_argument_get(ys), name)==NULL){
|
||||
clicon_err(OE_XML, 0, "xml node name '%s' does not match yang spec arg '%s'",
|
||||
name, ys->ys_argument);
|
||||
name, yang_argument_get(ys));
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
|
|
@ -1126,7 +1126,7 @@ xmlns_assign(cxobj *x)
|
|||
}
|
||||
/* 1. Check which namespace x should have (via yang). This is correct namespace. */
|
||||
if ((ns_correct = yang_find_mynamespace(y)) == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "yang node %s does not have namespace", y->ys_argument);
|
||||
clicon_err(OE_YANG, ENOENT, "yang node %s does not have namespace", yang_argument_get(y));
|
||||
goto done;
|
||||
}
|
||||
/* 2. Check which namespace x has via its XML tree */
|
||||
|
|
@ -1293,7 +1293,7 @@ xml_merge1(cxobj *x0, /* the target */
|
|||
assert(y0);
|
||||
|
||||
x1name = xml_name(x1);
|
||||
if (y0->ys_keyword == Y_LEAF_LIST || y0->ys_keyword == Y_LEAF){
|
||||
if (yang_keyword_get(y0) == Y_LEAF_LIST || yang_keyword_get(y0) == Y_LEAF){
|
||||
x1bstr = xml_body(x1);
|
||||
if (x0==NULL){
|
||||
if ((x0 = xml_new(x1name, x0p, (yang_stmt*)y0)) == NULL)
|
||||
|
|
@ -1479,7 +1479,7 @@ yang_enum_int_value(cxobj *node,
|
|||
if (yang_type_resolve(ys, ys, ytype, &yrestype,
|
||||
NULL, NULL, NULL, NULL, NULL) < 0)
|
||||
goto done;
|
||||
if (yrestype==NULL || strcmp(yrestype->ys_argument, "enumeration"))
|
||||
if (yrestype==NULL || strcmp(yang_argument_get(yrestype), "enumeration"))
|
||||
goto done;
|
||||
if ((yenum = yang_find(yrestype, Y_ENUM, xml_body(node))) == NULL)
|
||||
goto done;
|
||||
|
|
@ -1487,7 +1487,7 @@ yang_enum_int_value(cxobj *node,
|
|||
if ((yval = yang_find(yenum, Y_VALUE, NULL)) == NULL)
|
||||
goto done;
|
||||
/* reason is string containing why int could not be parsed */
|
||||
if (parse_int32(yval->ys_argument, val, &reason) < 0)
|
||||
if (parse_int32(yang_argument_get(yval), val, &reason) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2017-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -51,7 +52,7 @@ struct xml_parse_yacc_arg{
|
|||
cxobj *ya_xparent; /* xml parent element*/
|
||||
int ya_skipspace; /* If set, remove all non-terminal bodies (strip pretty-print) */
|
||||
yang_stmt *ya_yspec; /* If set, top-level yang-spec */
|
||||
int ya_lex_state; /* lex start condition (AMPERSAND) */
|
||||
int ya_lex_state; /* lex return state */
|
||||
};
|
||||
|
||||
extern char *clixon_xml_parsetext;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2017-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -31,7 +32,7 @@
|
|||
|
||||
***** END LICENSE BLOCK *****
|
||||
|
||||
* XML search functions when used with YANG
|
||||
* XML sort and search functions when used with YANG
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
|
|
@ -65,6 +66,7 @@
|
|||
#include "clixon_options.h"
|
||||
#include "clixon_xml_map.h"
|
||||
#include "clixon_yang_type.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_xml_sort.h"
|
||||
|
||||
/*! Get xml body value as cligen variable
|
||||
|
|
@ -193,10 +195,10 @@ xml_child_spec(cxobj *x,
|
|||
* @param[in] x2 object 2
|
||||
* @param[in] same If set, x1 and x2 are member of same parent & enumeration
|
||||
* is used (see explanation below)
|
||||
* @param[in] skip1 Key matching skipped for keys not in x1 (see explanation)
|
||||
* @retval 0 If equal
|
||||
* @retval <0 If x1 is less than x2
|
||||
* @retval >0 If x1 is greater than x2
|
||||
* @see xml_cmp1 Similar, but for one object
|
||||
*
|
||||
* There are distinct calls for this function:
|
||||
* 1. For sorting in an existing list of XML children
|
||||
|
|
@ -205,11 +207,19 @@ xml_child_spec(cxobj *x,
|
|||
* if they have the same yang-spec, the existing order is used as tie-breaker.
|
||||
* In other words, if order-by-system, or if the case (2) above, the existing
|
||||
* order is ignored and the actual xml element contents is examined.
|
||||
*
|
||||
* Also, if pattern is set, list matching is not made so that x1 and x2 must have same keys.
|
||||
* instead, x1 and x2 are considered equal if the keys that x1 have match. Keys that x2 but not
|
||||
* x1 has are ignored.
|
||||
* Example: x1: <x><k1>1</k1></x> and x2: <x><k1>1</k1><k2>2</k2></x> are considered equal.
|
||||
* This is useful in searching for indexes in trees, where x1 is a search index and not a
|
||||
* complete tree.
|
||||
*
|
||||
* @note empty value/NULL is smallest value
|
||||
* @note some error cases return as -1 (qsort cant handle errors)
|
||||
* @note some error cases return as -1 (qsort cant handle errors)
|
||||
*
|
||||
* NOTE: "comparing" x1 and x2 here judges equality from a structural (model)
|
||||
* @note "comparing" x1 and x2 here judges equality from a structural (model)
|
||||
* perspective, ie both have the same yang spec, if they are lists, they have the
|
||||
* the same keys. NOT that the values are equal!
|
||||
* In other words, if x is a leaf with the same yang spec, <x>1</x> and <x>2</x> are
|
||||
|
|
@ -221,7 +231,8 @@ xml_child_spec(cxobj *x,
|
|||
int
|
||||
xml_cmp(cxobj *x1,
|
||||
cxobj *x2,
|
||||
int same)
|
||||
int same,
|
||||
int skip1)
|
||||
{
|
||||
yang_stmt *y1;
|
||||
yang_stmt *y2;
|
||||
|
|
@ -281,9 +292,13 @@ xml_cmp(cxobj *x1,
|
|||
}
|
||||
switch (yang_keyword_get(y1)){
|
||||
case Y_LEAF_LIST: /* Match with name and value */
|
||||
if ((b1 = xml_body(x1)) == NULL)
|
||||
b1 = xml_body(x1);
|
||||
b2 = xml_body(x2);
|
||||
if (b1 == NULL && b2 == NULL)
|
||||
;
|
||||
else if (b1 == NULL)
|
||||
equal = -1;
|
||||
else if ((b2 = xml_body(x2)) == NULL)
|
||||
else if (b2 == NULL)
|
||||
equal = 1;
|
||||
else{
|
||||
if (xml_cv_cache(x1, &cv1) < 0) /* error case */
|
||||
|
|
@ -301,33 +316,47 @@ xml_cmp(cxobj *x1,
|
|||
}
|
||||
break;
|
||||
case Y_LIST: /* Match with key values
|
||||
* Use Y_LIST cache (see struct yang_stmt)
|
||||
*/
|
||||
* Use Y_LIST cache (see struct yang_stmt) */
|
||||
cvk = yang_cvec_get(y1); /* Use Y_LIST cache, see ys_populate_list() */
|
||||
cvi = NULL;
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||
keyname = cv_string_get(cvi); /* operational data may have NULL keys*/
|
||||
if ((x1b = xml_find(x1, keyname)) == NULL ||
|
||||
xml_body(x1b) == NULL)
|
||||
x1b = xml_find(x1, keyname);
|
||||
/* match1: key matching skipped for keys not in x1 (see explanation) */
|
||||
if (skip1 && x1b == NULL)
|
||||
continue;
|
||||
x2b = xml_find(x2, keyname);
|
||||
if (x1b == NULL && x2b == NULL)
|
||||
;
|
||||
else if (x1b == NULL)
|
||||
equal = -1;
|
||||
else if ((x2b = xml_find(x2, keyname)) == NULL ||
|
||||
xml_body(x2b) == NULL)
|
||||
else if (x2b == NULL)
|
||||
equal = 1;
|
||||
else{
|
||||
if (xml_cv_cache(x1b, &cv1) < 0) /* error case */
|
||||
goto done;
|
||||
if (xml_cv_cache(x2b, &cv2) < 0) /* error case */
|
||||
goto done;
|
||||
assert(cv1 && cv2);
|
||||
if ((equal = cv_cmp(cv1, cv2)) != 0)
|
||||
goto done;
|
||||
b1 = xml_body(x1b);
|
||||
b2 = xml_body(x2b);
|
||||
if (b1 == NULL && b2 == NULL)
|
||||
;
|
||||
else if (b1 == NULL)
|
||||
equal = -1;
|
||||
else if (b2 == NULL)
|
||||
equal = 1;
|
||||
else{
|
||||
if (xml_cv_cache(x1b, &cv1) < 0) /* error case */
|
||||
goto done;
|
||||
if (xml_cv_cache(x2b, &cv2) < 0) /* error case */
|
||||
goto done;
|
||||
assert(cv1 && cv2);
|
||||
equal = cv_cmp(cv1, cv2);
|
||||
}
|
||||
}
|
||||
}
|
||||
equal = 0;
|
||||
if (equal)
|
||||
break;
|
||||
} /* while cvi */
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} /* switch */
|
||||
done:
|
||||
clicon_debug(2, "%s %s %s %d nr: %d %d yi: %d %d", __FUNCTION__, xml_name(x1), xml_name(x2), equal, nr1, nr2, yi1, yi2);
|
||||
return equal;
|
||||
|
|
@ -340,7 +369,7 @@ static int
|
|||
xml_cmp_qsort(const void* arg1,
|
||||
const void* arg2)
|
||||
{
|
||||
return xml_cmp(*(struct xml**)arg1, *(struct xml**)arg2, 1);
|
||||
return xml_cmp(*(struct xml**)arg1, *(struct xml**)arg2, 1, 0);
|
||||
}
|
||||
|
||||
/*! Sort children of an XML node
|
||||
|
|
@ -366,21 +395,28 @@ xml_sort(cxobj *x,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! Special case search for ordered-by user where linear sort is used
|
||||
/*! Special case search for ordered-by user or state data where linear sort is used
|
||||
*
|
||||
* @param[in] xp Parent XML node (go through its childre)
|
||||
* @param[in] x1 XML node to match
|
||||
* @param[in] yangi Yang order number (according to spec)
|
||||
* @param[in] mid Where to start from (may be in middle of interval)
|
||||
* @param[out] xretp XML return object, or NULL
|
||||
* @retval 0 OK, see xretp
|
||||
* @param[out] xvec Vector of matching XML return objects (can be empty)
|
||||
* @param[out] xlen Length of xvec
|
||||
* @retval 0 OK, see xvec (may be empty)
|
||||
* @retval -1 Error
|
||||
* XXX: only first match
|
||||
*/
|
||||
static int
|
||||
xml_search_userorder(cxobj *xp,
|
||||
cxobj *x1,
|
||||
int yangi,
|
||||
int mid,
|
||||
cxobj **xretp)
|
||||
xml_find_keys_notsorted(cxobj *xp,
|
||||
cxobj *x1,
|
||||
int yangi,
|
||||
int mid,
|
||||
int skip1,
|
||||
cxobj ***xvec,
|
||||
size_t *xlen)
|
||||
{
|
||||
int retval = -1;
|
||||
int i;
|
||||
cxobj *xc;
|
||||
yang_stmt *yc;
|
||||
|
|
@ -388,113 +424,186 @@ xml_search_userorder(cxobj *xp,
|
|||
for (i=mid+1; i<xml_child_nr(xp); i++){ /* First increment */
|
||||
xc = xml_child_i(xp, i);
|
||||
yc = xml_spec(xc);
|
||||
if (yangi!=yang_order(yc))
|
||||
if (yangi != yang_order(yc)) /* wrong yang */
|
||||
break;
|
||||
if (xml_cmp(xc, x1, 0) == 0){
|
||||
*xretp = xc;
|
||||
goto done;
|
||||
if (xml_cmp(xc, x1, 0, skip1) == 0){
|
||||
if (cxvec_append(xc, xvec, xlen) < 0)
|
||||
goto done;
|
||||
goto ok; /* found */
|
||||
}
|
||||
}
|
||||
for (i=mid-1; i>=0; i--){ /* Then decrement */
|
||||
xc = xml_child_i(xp, i);
|
||||
yc = xml_spec(xc);
|
||||
if (yangi!=yang_order(yc))
|
||||
if (yangi != yang_order(yc)) /* wrong yang */
|
||||
break;
|
||||
if (xml_cmp(xc, x1, 0) == 0){
|
||||
*xretp = xc;
|
||||
goto done;
|
||||
if (xml_cmp(xc, x1, 0, skip1) == 0){
|
||||
if (cxvec_append(xc, xvec, xlen) < 0)
|
||||
goto done;
|
||||
goto ok; /* found */
|
||||
}
|
||||
}
|
||||
*xretp = NULL;
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
return 0;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Find more equal objects in a vector up and down in the array of the present
|
||||
* @param[in] xp Parent XML node (go through its childre)
|
||||
* @param[in] x1 XML node to match
|
||||
* @param[in] yangi Yang order number (according to spec)
|
||||
* @param[in] mid Where to start from (may be in middle of interval)
|
||||
* @param[out] xvec Vector of matching XML return objects (can be empty)
|
||||
* @param[out] xlen Length of xvec
|
||||
* @retval 0 OK, see xvec (may be empty)
|
||||
* @retval -1 Error
|
||||
*/
|
||||
static int
|
||||
more_equals(cxobj *xp,
|
||||
cxobj *x1,
|
||||
int yangi,
|
||||
int mid,
|
||||
int skip1,
|
||||
cxobj ***xvec,
|
||||
size_t *xlen)
|
||||
{
|
||||
int retval = -1;
|
||||
int i;
|
||||
cxobj *xc;
|
||||
yang_stmt *yc;
|
||||
|
||||
for (i=mid-1; i>=0; i--){ /* First decrement */
|
||||
xc = xml_child_i(xp, i);
|
||||
yc = xml_spec(xc);
|
||||
if (yangi != yang_order(yc)) /* wrong yang */
|
||||
break;
|
||||
if (xml_cmp(x1, xc, 0, skip1) != 0)
|
||||
break;
|
||||
if (cxvec_prepend(xc, xvec, xlen) < 0)
|
||||
goto done;
|
||||
}
|
||||
for (i=mid+1; i<xml_child_nr(xp); i++){ /* Then increment */
|
||||
xc = xml_child_i(xp, i);
|
||||
yc = xml_spec(xc);
|
||||
if (yangi != yang_order(yc)) /* wrong yang */
|
||||
break;
|
||||
if (xml_cmp(x1, xc, 0, skip1) != 0)
|
||||
break;
|
||||
if (cxvec_append(xc, xvec, xlen) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Find XML child under xp matching x1 using binary search
|
||||
* @param[in] xp Parent xml node.
|
||||
* @param[in] x1 Find this object among xp:s children
|
||||
* @param[in] userorder If x1 is ordered by user
|
||||
* @param[in] sorted If x1 is ordered by user or statedata, the list is not sorted
|
||||
* @param[in] yangi Yang order
|
||||
* @param[in] low Lower bound of childvec search interval
|
||||
* @param[in] upper Lower bound of childvec search interval
|
||||
* @param[out] xretp XML return object, or NULL
|
||||
* @retval 0 OK, see xretp
|
||||
* @param[in] skip1 Key matching skipped for keys not in x1
|
||||
* @param[out] xvec Vector of matching XML return objects (can be empty)
|
||||
* @param[out] xlen Length of xvec
|
||||
* @retval 0 OK, see xvec (may be empty)
|
||||
* @retval -1 Error
|
||||
*/
|
||||
static int
|
||||
xml_search1(cxobj *xp,
|
||||
cxobj *x1,
|
||||
int userorder,
|
||||
int yangi,
|
||||
int low,
|
||||
int upper,
|
||||
cxobj **xretp)
|
||||
xml_find_keys1(cxobj *xp,
|
||||
cxobj *x1,
|
||||
int sorted,
|
||||
int yangi,
|
||||
int low,
|
||||
int upper,
|
||||
int skip1,
|
||||
cxobj ***xvec,
|
||||
size_t *xlen)
|
||||
{
|
||||
int retval = -1;
|
||||
int mid;
|
||||
int cmp;
|
||||
cxobj *xc;
|
||||
yang_stmt *y;
|
||||
|
||||
if (upper < low)
|
||||
goto notfound;
|
||||
goto ok;
|
||||
mid = (low + upper) / 2;
|
||||
if (mid >= xml_child_nr(xp)) /* beyond range */
|
||||
goto notfound;
|
||||
goto ok;
|
||||
xc = xml_child_i(xp, mid);
|
||||
if ((y = xml_spec(xc)) == NULL)
|
||||
goto notfound;
|
||||
goto ok;
|
||||
cmp = yangi-yang_order(y);
|
||||
/* Here is right yang order == same yang? */
|
||||
if (cmp == 0){
|
||||
cmp = xml_cmp(x1, xc, 0);
|
||||
if (cmp && userorder){ /* Ordered by user (if not equal) */
|
||||
xml_search_userorder(xp, x1, yangi, mid, xretp);
|
||||
cmp = xml_cmp(x1, xc, 0, skip1);
|
||||
if (cmp && !sorted){ /* Ordered by user (if not equal) */
|
||||
retval = xml_find_keys_notsorted(xp, x1, yangi, mid, skip1, xvec, xlen);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
if (cmp == 0)
|
||||
*xretp = xc;
|
||||
if (cmp == 0){
|
||||
if (cxvec_append(xc, xvec, xlen) < 0)
|
||||
goto done;
|
||||
/* there may be more? */
|
||||
if (more_equals(xp, x1, yangi, mid, skip1, xvec, xlen) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (cmp < 0)
|
||||
xml_search1(xp, x1, userorder, yangi, low, mid-1, xretp);
|
||||
xml_find_keys1(xp, x1, sorted, yangi, low, mid-1, skip1, xvec, xlen);
|
||||
else
|
||||
xml_search1(xp, x1, userorder, yangi, mid+1, upper, xretp);
|
||||
xml_find_keys1(xp, x1, sorted, yangi, mid+1, upper, skip1, xvec, xlen);
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
return 0;
|
||||
notfound:
|
||||
*xretp = NULL;
|
||||
goto done;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Find XML child under xp matching x1 using binary search
|
||||
/*! Find XML child under xp matching x1 using binary search for list/leaf-list keys
|
||||
*
|
||||
* Match is tried xp with x1 with either name only (container/leaf) or using keys (list/leaf-lists)
|
||||
* Any non-key leafs or other structure of x1 is not matched.
|
||||
*
|
||||
* @param[in] xp Parent xml node.
|
||||
* @param[in] x1 Find this object among xp:s children
|
||||
* @param[in] yc Yang spec of x1
|
||||
* @param[out] xretp XML return object, or NULL
|
||||
* @retval 0 OK, see xretp
|
||||
* @param[in] skip1 Key matching skipped for keys not in x1
|
||||
* @param[out] xvec Vector of matching XML return objects (can be empty)
|
||||
* @param[out] xlen Length of xvec
|
||||
* @retval 0 OK, see xvec (may be empty)
|
||||
* @retval -1 Error
|
||||
* @see xml_find_index for a generic search function
|
||||
*/
|
||||
static int
|
||||
xml_search(cxobj *xp,
|
||||
cxobj *x1,
|
||||
yang_stmt *yc,
|
||||
cxobj **xretp)
|
||||
xml_find_keys(cxobj *xp,
|
||||
cxobj *x1,
|
||||
yang_stmt *yc,
|
||||
int skip1,
|
||||
cxobj ***xvec,
|
||||
size_t *xlen)
|
||||
{
|
||||
cxobj *xa;
|
||||
int low = 0;
|
||||
int upper = xml_child_nr(xp);
|
||||
int userorder=0;
|
||||
int yangi;
|
||||
cxobj *xa;
|
||||
int low = 0;
|
||||
int upper = xml_child_nr(xp);
|
||||
int sorted = 1;
|
||||
int yangi;
|
||||
|
||||
/* Assume if there are any attributes, they are first in the list, mask
|
||||
them by raising low to skip them */
|
||||
for (low=0; low<upper; low++)
|
||||
if ((xa = xml_child_i(xp, low)) == NULL || xml_type(xa)!=CX_ATTR)
|
||||
if ((xa = xml_child_i(xp, low)) == NULL || xml_type(xa) != CX_ATTR)
|
||||
break;
|
||||
/* Find if non-config and if ordered-by-user */
|
||||
if (yang_config(yc)==0)
|
||||
userorder = 1;
|
||||
sorted = 0;
|
||||
else if (yang_keyword_get(yc) == Y_LIST || yang_keyword_get(yc) == Y_LEAF_LIST)
|
||||
userorder = (yang_find(yc, Y_ORDERED_BY, "user") != NULL);
|
||||
sorted = (yang_find(yc, Y_ORDERED_BY, "user") == NULL);
|
||||
yangi = yang_order(yc);
|
||||
return xml_search1(xp, x1, userorder, yangi, low, upper, xretp);
|
||||
return xml_find_keys1(xp, x1, sorted, yangi, low, upper, skip1, xvec, xlen);
|
||||
}
|
||||
|
||||
/*! Insert xn in xp:s sorted child list (special case of ordered-by user)
|
||||
|
|
@ -652,7 +761,7 @@ xml_insert2(cxobj *xp,
|
|||
goto done;
|
||||
}
|
||||
else /* Ordered by system */
|
||||
cmp = xml_cmp(xn, xc, 0);
|
||||
cmp = xml_cmp(xn, xc, 0, 0);
|
||||
}
|
||||
else{ /* Not equal yang - compute diff */
|
||||
cmp = yni - yang_order(yc);
|
||||
|
|
@ -769,7 +878,7 @@ xml_sort_verify(cxobj *x0,
|
|||
xml_enumerate_children(x0);
|
||||
while ((x = xml_child_each(x0, x, -1)) != NULL) {
|
||||
if (xprev != NULL){ /* Check xprev <= x */
|
||||
if (xml_cmp(xprev, x, 1) > 0)
|
||||
if (xml_cmp(xprev, x, 1, 0) > 0)
|
||||
goto done;
|
||||
}
|
||||
xprev = x;
|
||||
|
|
@ -779,13 +888,14 @@ xml_sort_verify(cxobj *x0,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Given child tree x1c, find matching child in base tree x0 and return as x0cp
|
||||
/*! Given child tree x1c, find (first) matching child in base tree x0 and return as x0cp
|
||||
* @param[in] x0 Base tree node
|
||||
* @param[in] x1c Modification tree child
|
||||
* @param[in] yc Yang spec of tree child
|
||||
* @param[out] x0cp Matching base tree child (if any)
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* XXX: only handles first match
|
||||
*/
|
||||
int
|
||||
match_base_child(cxobj *x0,
|
||||
|
|
@ -802,6 +912,8 @@ match_base_child(cxobj *x0,
|
|||
yang_stmt *y0c;
|
||||
yang_stmt *y0p;
|
||||
yang_stmt *yp; /* yang parent */
|
||||
cxobj **xvec = NULL;
|
||||
size_t xlen = 0;
|
||||
|
||||
*x0cp = NULL; /* init return value */
|
||||
/* Special case is if yc parent (yp) is choice/case
|
||||
|
|
@ -816,6 +928,7 @@ match_base_child(cxobj *x0,
|
|||
y0p == yp)
|
||||
break; /* x0c will have a value */
|
||||
}
|
||||
*x0cp = x0c;
|
||||
goto ok; /* What to do if not found? */
|
||||
}
|
||||
switch (yang_keyword_get(yc)){
|
||||
|
|
@ -823,58 +936,146 @@ match_base_child(cxobj *x0,
|
|||
case Y_LEAF: /* Equal regardless */
|
||||
break;
|
||||
case Y_LEAF_LIST: /* Match with name and value */
|
||||
if (xml_body(x1c) == NULL){ /* Treat as empty string */
|
||||
// assert(0);
|
||||
if (xml_body(x1c) == NULL) /* Treat as empty string */
|
||||
goto ok;
|
||||
}
|
||||
break;
|
||||
case Y_LIST: /* Match with key values */
|
||||
cvk = yang_cvec_get(yc); /* Use Y_LIST cache, see ys_populate_list() */
|
||||
/* Count number of key indexes
|
||||
* Then create two vectors one with names and one with values of x1c,
|
||||
* ec: keyvec: [a,b,c] keyval: [1,2,3]
|
||||
*/
|
||||
cvi = NULL;
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||
keyname = cv_string_get(cvi);
|
||||
// keyvec[i] = keyname;
|
||||
if ((xb = xml_find(x1c, keyname)) == NULL){
|
||||
if ((xb = xml_find(x1c, keyname)) == NULL)
|
||||
goto ok;
|
||||
}
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* Get match. */
|
||||
xml_search(x0, x1c, yc, &x0c);
|
||||
if (xml_find_keys(x0, x1c, yc, 0, &xvec, &xlen) < 0)
|
||||
goto done;
|
||||
if (xlen)
|
||||
*x0cp = xvec[0];
|
||||
ok:
|
||||
*x0cp = x0c;
|
||||
retval = 0;
|
||||
done:
|
||||
if (xvec)
|
||||
free(xvec);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Experimental API for binary search
|
||||
*
|
||||
* Create a temporary search object: a list (xc) with a key (xk) and call the binary search.
|
||||
* @param[in] xp Parent xml node.
|
||||
* @param[in] yc Yang spec of list child
|
||||
* @param[in] cvk List of keys and values as CLIgen vector
|
||||
* @param[out] xret Found XML object, NULL if not founs
|
||||
* @retval 0 OK, see xret
|
||||
* @code
|
||||
* cvec *cvk = NULL; vector of index keys
|
||||
* ... Populate cvk with key/values eg a:5 b:6
|
||||
* if (xml_binsearch(xp, yc, cvk, xp) < 0)
|
||||
* err;
|
||||
* @endcode
|
||||
* @retval -1 Error
|
||||
* Can extend to leaf-list?
|
||||
|
||||
/*! API for search in XML child list with non-indexed variables
|
||||
*/
|
||||
int
|
||||
xml_binsearch(cxobj *xp,
|
||||
yang_stmt *yc,
|
||||
cvec *cvk,
|
||||
cxobj **xretp)
|
||||
static int
|
||||
xml_find_noyang_cvk(char *ns0,
|
||||
cxobj *xc,
|
||||
cvec *cvk,
|
||||
cxobj ***xvec,
|
||||
size_t *xlen)
|
||||
{
|
||||
int retval = -1;
|
||||
cg_var *cvi;
|
||||
char *ns;
|
||||
cxobj *xcc;
|
||||
char *keyname;
|
||||
char *keyval;
|
||||
char *body;
|
||||
|
||||
cvi = NULL;
|
||||
/* Loop through index variables. xc should match all, on exit if cvi=NULL it macthes */
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||
keyname = cv_name_get(cvi);
|
||||
keyval = cv_string_get(cvi);
|
||||
if (keyname==NULL || strcmp(keyname, ".")==0){ /* Index variable on form .=<val> (self) */
|
||||
body = xml_body(xc);
|
||||
if ((body==NULL && (keyval==NULL || strlen(keyval) == 0)) || /* both null, break */
|
||||
(body != NULL && strcmp(body, keyval) == 0)) /* Name match, break */
|
||||
continue; /* match */
|
||||
break; /* nomatch */
|
||||
}
|
||||
else{
|
||||
/* Index variable on form <id>=<val>
|
||||
* Loop through children of the matched x (to match keyname and value) */
|
||||
xcc = NULL;
|
||||
while ((xcc = xml_child_each(xc, xcc, CX_ELMNT)) != NULL) {
|
||||
if (xml2ns(xcc, xml_prefix(xcc), &ns) < 0)
|
||||
goto done;
|
||||
if (strcmp(ns0, ns) != 0) /* Namespace does not match, skip */
|
||||
continue;
|
||||
if (strcmp(keyname, xml_name(xcc)) != 0) /* Name does not match, skip */
|
||||
continue;
|
||||
body = xml_body(xcc);
|
||||
if (body==NULL && (keyval==NULL || strlen(keyval) == 0)) /* both null, break */
|
||||
break;
|
||||
if (body != NULL && strcmp(body, keyval) == 0) /* Name match, break */
|
||||
break;
|
||||
}
|
||||
if (xcc == NULL)
|
||||
break; /* No match found */
|
||||
}
|
||||
}
|
||||
if (cvi == NULL) /* means we iterated through all indexes without breaks */
|
||||
if (cxvec_append(xc, xvec, xlen) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! API for search in XML child list with no yang available
|
||||
* Fallback if no yang available. Only linear search for matching name, and eventual index match
|
||||
*/
|
||||
static int
|
||||
xml_find_noyang_name(cxobj *xp,
|
||||
char *ns0,
|
||||
char *name,
|
||||
cvec *cvk,
|
||||
cxobj ***xvec,
|
||||
size_t *xlen)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xc;
|
||||
char *ns;
|
||||
|
||||
if (name == NULL || ns0 == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "name and namespace required");
|
||||
goto done;
|
||||
}
|
||||
/* Go through children linearly */
|
||||
xc = NULL;
|
||||
while ((xc = xml_child_each(xp, xc, CX_ELMNT)) != NULL) {
|
||||
ns = NULL;
|
||||
if (xml2ns(xc, xml_prefix(xc), &ns) < 0)
|
||||
goto done;
|
||||
if (ns == NULL)
|
||||
continue;
|
||||
if (strcmp(ns0, ns) != 0) /* Namespace does not match, skip */
|
||||
continue;
|
||||
if (strcmp(name, xml_name(xc)) != 0) /* Namespace does not match, skip */
|
||||
continue;
|
||||
if (cvk){ /* Check indexes */
|
||||
if (xml_find_noyang_cvk(ns0, xc, cvk, xvec, xlen) < 0)
|
||||
goto done;
|
||||
}
|
||||
else /* No index variables: just add node to found list */
|
||||
if (cxvec_append(xc, xvec, xlen) < 0)
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! API for search in XML child list with yang available
|
||||
* @retval 1 OK
|
||||
* @retval 0 Revert, try again with no-yang search
|
||||
* @retval -1 Error
|
||||
*/
|
||||
static int
|
||||
xml_find_index_yang(cxobj *xp,
|
||||
yang_stmt *yc,
|
||||
cvec *cvk,
|
||||
cxobj ***xvec,
|
||||
size_t *xlen)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xc = NULL;
|
||||
|
|
@ -882,30 +1083,58 @@ xml_binsearch(cxobj *xp,
|
|||
cg_var *cvi = NULL;
|
||||
cbuf *cb = NULL;
|
||||
yang_stmt *yk;
|
||||
char *kname;
|
||||
cvec *ycvk;
|
||||
cg_var *ycv = NULL;
|
||||
int i;
|
||||
char *name;
|
||||
|
||||
if (yc == NULL || xml_spec(xp) == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "yang spec not found");
|
||||
goto done;
|
||||
}
|
||||
|
||||
name = yang_argument_get(yc);
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cb, "<%s>", name);
|
||||
cvi = NULL;
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||
cprintf(cb, "<%s>%s</%s>",
|
||||
cv_name_get(cvi),
|
||||
cv_string_get(cvi),
|
||||
cv_name_get(cvi));
|
||||
switch (yang_keyword_get(yc)){
|
||||
case Y_LIST:
|
||||
cprintf(cb, "<%s>", name);
|
||||
ycvk = yang_cvec_get(yc); /* Check that those are proper index keys */
|
||||
cvi = NULL;
|
||||
i = 0;
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||
if ((kname = cv_name_get(cvi)) == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "missing yang key name in cvk");
|
||||
goto done;
|
||||
}
|
||||
/* Parameter in cvk is not key or not in right key order, then we cannot call
|
||||
* xml_find_keys and we need to revert to noyang
|
||||
*/
|
||||
if ((ycv = cvec_i(ycvk, i)) == NULL || strcmp(kname, cv_string_get(ycv))){
|
||||
goto revert;
|
||||
break;
|
||||
}
|
||||
cprintf(cb, "<%s>%s</%s>", kname, cv_string_get(cvi), kname);
|
||||
i++;
|
||||
}
|
||||
cprintf(cb, "</%s>", name);
|
||||
break;
|
||||
case Y_LEAF_LIST:
|
||||
if (cvec_len(cvk) != 1){
|
||||
clicon_err(OE_YANG, ENOENT, "expected exactly one leaf-list key");
|
||||
goto done;
|
||||
}
|
||||
cvi = cvec_i(cvk, 0);
|
||||
cprintf(cb, "<%s>%s</%s>", name, cv_string_get(cvi), name);
|
||||
break;
|
||||
default:
|
||||
cprintf(cb, "<%s/>", name);
|
||||
break;
|
||||
}
|
||||
cprintf(cb, "</%s>", name);
|
||||
clicon_debug(1, "%s XML:%s", __FUNCTION__, cbuf_get(cb));
|
||||
if (xml_parse_string(cbuf_get(cb), yc, &xc) < 0)
|
||||
goto done;
|
||||
if (xml_rootchild(xc, 0, &xc) < 0)
|
||||
goto done;
|
||||
/* Populate created XML tree with yang specs */
|
||||
if (xml_spec_set(xc, yc) < 0)
|
||||
goto done;
|
||||
xk = NULL;
|
||||
|
|
@ -917,11 +1146,150 @@ xml_binsearch(cxobj *xp,
|
|||
if (xml_spec_set(xk, yk) < 0)
|
||||
goto done;
|
||||
}
|
||||
retval = xml_search(xp, xc, yc, xretp);
|
||||
if (xml_find_keys(xp, xc, yc, 1, xvec, xlen) < 0)
|
||||
goto done;
|
||||
retval = 1; /* OK */
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (xc)
|
||||
xml_free(xc);
|
||||
return retval;
|
||||
revert: /* means try name-only*/
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! API for search in XML child list using indexes and binary search if applicable
|
||||
*
|
||||
* Generic search function as alternative to xml_find() and others that finds YANG associated
|
||||
* children of an XML tree node using indexes and optimized lookup. Optimized lookup is used only
|
||||
* if an associated YANG exists. The optimized lookup is based on binary trees but is used
|
||||
* The function takes as input:
|
||||
* 1) An XML tree (parent) node (cxobj). See note(1) below
|
||||
* 2) A set of parameters to define wanted children by name or yang-spec using yc, name, ns, yp:
|
||||
* a. yp and ns:name given: YANG spec of parent + ns:name derives yc. Not found: only ns:name
|
||||
* b. name given: yp is derived from xp spec, then goto 2b above.
|
||||
* 3) A vector of index expressions on the form <id>=<val> refining the child set:
|
||||
* a. cvk is NULL, return all children matching according to (B)
|
||||
* b. cvk contains <id>=<val> entries, return children with <id> leafs matching <val>.
|
||||
* See "Optimized" section below.
|
||||
* 4) A return vector of XML nodes matching name and index variables
|
||||
*
|
||||
* # Optimized
|
||||
* The function makes the index matching <id>=<val> above optimized using binary search if:
|
||||
* - yc is defined AND one of the following
|
||||
* - if xp is leaf-list and "id" is "."
|
||||
* - if xp is a yang list and "id" is a registered index key
|
||||
* - if xp is a yang list and first "id" is first leaf key, second "id" is second leaf key, etc.
|
||||
* - Otherwise search is made using linear search
|
||||
*
|
||||
* @param[in] xp Parent xml node.
|
||||
* @param[in] yc Yang spec of list child (preferred) See rule (2) above
|
||||
* @param[in] yp Yang spec of parent node or yang-spec/yang (Alternative if yc not given).
|
||||
* @param[in] ns Namespace (needed only if name is derived from top-symbol)
|
||||
* @param[in] name Name of child (not required if yc given)
|
||||
* @param[in] cvk List of keys and values as CLIgen vector on the form k1=foo, k2=bar
|
||||
* @param[out] xvec Array of found nodes
|
||||
* @param[out] xlen Len of xvec
|
||||
* @retval 0 OK, see xret
|
||||
* @retval -1 Error
|
||||
* @code
|
||||
* cxobj **xvec = NULL;
|
||||
* size_t xlen = 0;
|
||||
* cvec *cvk = NULL; vector of index keys
|
||||
* ... Populate cvk with key/values eg a:5 b:6
|
||||
* if (clixon_xml_find_index(xp, yp, "a", ns, cvk, &xvec, &xlen) < 0)
|
||||
* err;
|
||||
* @endcode
|
||||
* Discussion:
|
||||
* (1) Rule 2 on how to get the child name election seems unecessary complex. First, it would be
|
||||
* nice to just state name and parent 2c. But xp as top-objects may sometimes be dummies, for
|
||||
* parsing, copy-buffers, or simply for XML nodes that do not have YANG.
|
||||
* (2) Short form could be: "first" only returning first object to get rid of vectors.
|
||||
* (3) Another short form could be: keyname,keyval instead of cvk for single keys.
|
||||
*/
|
||||
int
|
||||
clixon_xml_find_index(cxobj *xp,
|
||||
yang_stmt *yp,
|
||||
char *namespace,
|
||||
char *name,
|
||||
cvec *cvk,
|
||||
cxobj ***xvec,
|
||||
size_t *xlen)
|
||||
{
|
||||
int retval = -1;
|
||||
int ret;
|
||||
yang_stmt *yc = NULL;
|
||||
|
||||
if (name == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "name");
|
||||
goto done;
|
||||
}
|
||||
if (yp == NULL)
|
||||
yp = xml_spec(xp);
|
||||
if (yp){/* 2. YANG spec of parent + name derives yc. If not found use just name. */
|
||||
if (yang_keyword_get(yp) == Y_SPEC)
|
||||
yp = yang_find_module_by_namespace(yp, namespace);
|
||||
if (yp)
|
||||
yc = yang_find_datanode(yp, name);
|
||||
}
|
||||
if (yc){
|
||||
if ((ret = xml_find_index_yang(xp, yc, cvk, xvec, xlen)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
if (xml_find_noyang_name(xp, namespace, name, cvk, xvec, xlen) < 0)
|
||||
goto done;
|
||||
}
|
||||
else
|
||||
if (xml_find_noyang_name(xp, namespace, name, cvk, xvec, xlen) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Find positional parameter in xml child list, eg x/y[42]. Note, not optimized
|
||||
*
|
||||
* Create a temporary search object: a list (xc) with a key (xk) and call the binary search.
|
||||
* @param[in] xp Parent xml node.
|
||||
* @param[in] yc Yang spec of list child
|
||||
* @param[in] pos Position
|
||||
* @param[out] xvec Array of found nodes
|
||||
* @param[out] xlen Len of xvec
|
||||
* @retval 0 OK, see xret
|
||||
* @retval -1 Error
|
||||
*/
|
||||
int
|
||||
clixon_xml_find_pos(cxobj *xp,
|
||||
yang_stmt *yc,
|
||||
uint32_t pos,
|
||||
cxobj ***xvec,
|
||||
size_t *xlen)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xc = NULL;
|
||||
char *name;
|
||||
uint32_t u;
|
||||
|
||||
if (yc == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "yang spec not found");
|
||||
goto done;
|
||||
}
|
||||
name = yang_argument_get(yc);
|
||||
u = 0;
|
||||
xc = NULL;
|
||||
while ((xc = xml_child_each(xp, xc, CX_ELMNT)) != NULL) {
|
||||
if (strcmp(name, xml_name(xc)))
|
||||
continue;
|
||||
if (pos == u++){ /* Found */
|
||||
if (cxvec_append(xc, xvec, xlen) < 0)
|
||||
goto done;
|
||||
break;
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -83,6 +83,7 @@
|
|||
#include "clixon_yang.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_xml_nsctx.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_xpath_ctx.h"
|
||||
#include "clixon_xpath.h"
|
||||
#include "clixon_xpath_parse.h"
|
||||
|
|
@ -726,12 +727,12 @@ xpath_vec(cxobj *xcur,
|
|||
va_start(ap, veclen);
|
||||
len = vsnprintf(NULL, 0, xpformat, ap);
|
||||
va_end(ap);
|
||||
/* allocate a message string exactly fitting the message length */
|
||||
/* allocate an xpath string exactly fitting the length */
|
||||
if ((xpath = malloc(len+1)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "malloc");
|
||||
goto done;
|
||||
}
|
||||
/* second round: compute write message from reason and args */
|
||||
/* second round: actually compute xpath string content */
|
||||
va_start(ap, veclen);
|
||||
if (vsnprintf(xpath, len+1, xpformat, ap) < 0){
|
||||
clicon_err(OE_UNIX, errno, "vsnprintf");
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -117,8 +117,6 @@ ctx_dup(xp_ctx *xc0)
|
|||
* @param[in] xc XPATH evaluation context
|
||||
* @param[in] ind Indentation margin
|
||||
* @param[in] str Prefix string in printout
|
||||
|
||||
|
||||
*/
|
||||
int
|
||||
ctx_print_cb(cbuf *cb,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -339,25 +339,17 @@ xp_eval_step(xp_ctx *xc0,
|
|||
else for (i=0; i<xc->xc_size; i++){
|
||||
xv = xc->xc_nodeset[i];
|
||||
x = NULL;
|
||||
if ((ret = xpath_optimize_check(xs, xv, &x)) < 0)
|
||||
if ((ret = xpath_optimize_check(xs, xv, &vec, &veclen)) < 0)
|
||||
goto done;
|
||||
switch (ret){
|
||||
case 1: /* optimized */
|
||||
if (x) /* keep only x */
|
||||
if (cxvec_append(x, &vec, &veclen) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case 0: /* regular code */
|
||||
if (ret == 0){ /* No optimization made */
|
||||
while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) {
|
||||
/* xs->xs_c0 is nodetest */
|
||||
if (nodetest == NULL || nodetest_eval(x, nodetest, nsc, localonly) == 1){
|
||||
if (cxvec_append(x, &vec, &veclen) < 0)
|
||||
goto done;
|
||||
}
|
||||
if (ret == 0){/* regular code, no optimization made */
|
||||
while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) {
|
||||
/* xs->xs_c0 is nodetest */
|
||||
if (nodetest == NULL || nodetest_eval(x, nodetest, nsc, localonly) == 1){
|
||||
if (cxvec_append(x, &vec, &veclen) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
} /* switch */
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx_nodeset_replace(xc, vec, veclen);
|
||||
|
|
@ -932,7 +924,7 @@ xp_eval(xp_ctx *xc,
|
|||
xp_ctx *xr2 = NULL;
|
||||
int use_xr0 = 0; /* In 2nd child use transitively result of 1st child */
|
||||
|
||||
if (debug)
|
||||
if (debug > 1)
|
||||
ctx_print(stderr, xc, xpath_tree_int2str(xs->xs_type));
|
||||
/* Pre-actions before check first child c0
|
||||
*/
|
||||
|
|
@ -1088,7 +1080,7 @@ xp_eval(xp_ctx *xc,
|
|||
xr0 = NULL;
|
||||
}
|
||||
ok:
|
||||
if (debug)
|
||||
if (debug>1)
|
||||
ctx_print(stderr, *xrp, xpath_tree_int2str(xs->xs_type));
|
||||
retval = 0;
|
||||
done:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -68,7 +68,6 @@
|
|||
#include "clixon_xpath.h"
|
||||
#include "clixon_xpath_optimize.h"
|
||||
|
||||
|
||||
#ifdef XPATH_LIST_OPTIMIZE
|
||||
static xpath_tree *_xmtop = NULL; /* pattern match tree top */
|
||||
static xpath_tree *_xm = NULL;
|
||||
|
|
@ -77,7 +76,6 @@ static int _optimize_enable = 1;
|
|||
static int _optimize_hits = 0;
|
||||
#endif /* XPATH_LIST_OPTIMIZE */
|
||||
|
||||
|
||||
/* XXX development in clixon_xpath_eval */
|
||||
int
|
||||
xpath_list_optimize_stats(int *hits)
|
||||
|
|
@ -89,6 +87,9 @@ xpath_list_optimize_stats(int *hits)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! Enable xpath optimize
|
||||
* Cant replace this with optin since there is no handle in xpath functions,...
|
||||
*/
|
||||
int
|
||||
xpath_list_optimize_set(int enable)
|
||||
{
|
||||
|
|
@ -213,7 +214,8 @@ loop_preds(xpath_tree *xt,
|
|||
*
|
||||
* @param[in] xt XPath tree
|
||||
* @param[in] xv XML base node
|
||||
* @param[out] xp XML found node (retval == 1)
|
||||
* @param[out] xvec Array of found nodes
|
||||
* @param[out] xlen Len of xvec
|
||||
* @param[out] key
|
||||
* @param[out] keyval
|
||||
* @retval -1 Error
|
||||
|
|
@ -225,7 +227,8 @@ loop_preds(xpath_tree *xt,
|
|||
static int
|
||||
xpath_list_optimize_fn(xpath_tree *xt,
|
||||
cxobj *xv,
|
||||
cxobj **xp)
|
||||
cxobj ***xvec,
|
||||
size_t *xlen)
|
||||
{
|
||||
int retval = -1;
|
||||
xpath_tree *xm = NULL;
|
||||
|
|
@ -262,9 +265,9 @@ xpath_list_optimize_fn(xpath_tree *xt,
|
|||
if (veclen != 2)
|
||||
goto ok;
|
||||
name = vec[0]->xs_s1;
|
||||
/* Extract variables XXX strdups */
|
||||
/* Extract variables */
|
||||
if ((yc = yang_find(yp, Y_LIST, name)) == NULL)
|
||||
#ifdef NOTYET
|
||||
#ifdef NOTYET /* leaf-list is not detected by xpath optimize detection */
|
||||
if ((yc = yang_find(yp, Y_LEAF_LIST, name)) == NULL) /* XXX */
|
||||
#endif
|
||||
goto ok;
|
||||
|
|
@ -286,14 +289,12 @@ xpath_list_optimize_fn(xpath_tree *xt,
|
|||
i = 0;
|
||||
cvi = NULL;
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||
if (strcmp(cv_name_get(cvi), cv_string_get(cvec_i(cvv,i)))){
|
||||
fprintf(stderr, "%s key name mismatch %s vs %s\n",
|
||||
__FUNCTION__, cv_name_get(cvi), cv_string_get(cvec_i(cvv,i)));
|
||||
if (strcmp(cv_name_get(cvi), cv_string_get(cvec_i(cvv,i))))
|
||||
goto ok;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (xml_binsearch(xv, yc, cvk, xp) < 0)
|
||||
/* Use 2a form since yc allready given to compute cvk */
|
||||
if (clixon_xml_find_index(xv, yp, NULL, name, cvk, xvec, xlen) < 0)
|
||||
goto done;
|
||||
retval = 1; /* match */
|
||||
done:
|
||||
|
|
@ -316,8 +317,9 @@ xpath_list_optimize_fn(xpath_tree *xt,
|
|||
*/
|
||||
int
|
||||
xpath_optimize_check(xpath_tree *xs,
|
||||
cxobj *xv,
|
||||
cxobj **xp)
|
||||
cxobj *xv,
|
||||
cxobj ***xvec,
|
||||
size_t *xlen)
|
||||
|
||||
{
|
||||
#ifdef XPATH_LIST_OPTIMIZE
|
||||
|
|
@ -325,7 +327,7 @@ xpath_optimize_check(xpath_tree *xs,
|
|||
|
||||
if (!_optimize_enable)
|
||||
return 0; /* use regular code */
|
||||
if ((ret = xpath_list_optimize_fn(xs, xv, xp)) < 0)
|
||||
if ((ret = xpath_list_optimize_fn(xs, xv, xvec, xlen)) < 0)
|
||||
return -1;
|
||||
if (ret == 1){
|
||||
_optimize_hits++;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -39,7 +39,7 @@
|
|||
/*
|
||||
* Types
|
||||
*/
|
||||
struct clicon_xpath_yacc_arg{ /* XXX: mostly unrelevant */
|
||||
struct clicon_xpath_yacc_arg{
|
||||
const char *xy_name; /* Name of syntax (for error string) */
|
||||
int xy_linenum; /* Number of \n in parsed buffer */
|
||||
char *xy_parse_string; /* original (copy of) parse string */
|
||||
|
|
@ -55,11 +55,11 @@ extern char *clixon_xpath_parsetext;
|
|||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int xpath_scan_init(struct clicon_xpath_yacc_arg *jy);
|
||||
int xpath_scan_exit(struct clicon_xpath_yacc_arg *jy);
|
||||
int xpath_scan_init(struct clicon_xpath_yacc_arg *xy);
|
||||
int xpath_scan_exit(struct clicon_xpath_yacc_arg *xy);
|
||||
|
||||
int xpath_parse_init(struct clicon_xpath_yacc_arg *jy);
|
||||
int xpath_parse_exit(struct clicon_xpath_yacc_arg *jy);
|
||||
int xpath_parse_init(struct clicon_xpath_yacc_arg *xy);
|
||||
int xpath_parse_exit(struct clicon_xpath_yacc_arg *xy);
|
||||
|
||||
int clixon_xpath_parselex(void *);
|
||||
int clixon_xpath_parseparse(void *);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -77,8 +77,6 @@ clixon_xpath_parsewrap(void)
|
|||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
%}
|
||||
|
||||
digit [0-9]
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -124,7 +124,7 @@
|
|||
|
||||
#include "clixon_xpath_parse.h"
|
||||
|
||||
extern int clixon_xpath_parseget_lineno (void);
|
||||
extern int clixon_xpath_parseget_lineno (void); /*XXX obsolete ? */
|
||||
|
||||
/*
|
||||
also called from yacc generated code *
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -63,7 +63,6 @@
|
|||
#include "clixon_handle.h"
|
||||
#include "clixon_err.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_yang_internal.h" /* internal */
|
||||
#include "clixon_yang_cardinality.h"
|
||||
|
||||
/*
|
||||
|
|
@ -545,8 +544,8 @@ yang_cardinality(clicon_handle h,
|
|||
|
||||
/* 4) Recurse */
|
||||
i = 0;
|
||||
while (i<yt->ys_len){ /* Note, children may be removed cant use yn_each */
|
||||
ys = yt->ys_stmt[i++];
|
||||
while (i< yang_len_get(yt)){ /* Note, children may be removed cant use yn_each */
|
||||
ys = yang_child_i(yt, i++);
|
||||
if (yang_cardinality(h, ys, modname) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -31,24 +31,15 @@
|
|||
|
||||
***** END LICENSE BLOCK *****
|
||||
|
||||
* Yang functions
|
||||
* @see https://tools.ietf.org/html/rfc6020 YANG 1.0
|
||||
* @see https://tools.ietf.org/html/rfc7950 YANG 1.1
|
||||
*/
|
||||
*
|
||||
* This file defines the internal YANG data structures used by the Clixon implementation
|
||||
* This file SHOULD ONLY be included by clixon_yang.h.
|
||||
* Accesses should be made via the API defined in clixon_yang.h
|
||||
*/
|
||||
|
||||
#ifndef _CLIXON_YANG_INTERNAL_H_
|
||||
#define _CLIXON_YANG_INTERNAL_H_
|
||||
|
||||
/*
|
||||
* Clixon-specific cligen variable (cv) flags
|
||||
* CLIgen flags defined are in the range 0x01 -0x0f
|
||||
* An application can use any flags above that
|
||||
* @see cv_flag
|
||||
*/
|
||||
#define V_UNSET 0x10 /* Used by XML code to denote a value is not default */
|
||||
|
||||
#define YANG_FLAG_MARK 0x01 /* Marker for dynamic algorithms, eg expand */
|
||||
|
||||
/*! Yang type cache. Yang type statements can cache all typedef info here
|
||||
* @note unions not cached
|
||||
*/
|
||||
|
|
@ -76,7 +67,7 @@ struct yang_stmt{
|
|||
enum rfc_6020 ys_keyword; /* See clicon_yang_parse.tab.h */
|
||||
|
||||
char *ys_argument; /* String / argument depending on keyword */
|
||||
int ys_flags; /* Flags according to YANG_FLAG_* above */
|
||||
uint16_t ys_flags; /* Flags according to YANG_FLAG_* */
|
||||
yang_stmt *ys_mymodule; /* Shortcut to "my" module. Augmented
|
||||
nodes can belong to other
|
||||
modules than the ancestor module */
|
||||
|
|
@ -116,3 +107,4 @@ struct yang_stmt{
|
|||
#define yang_schemanode(y) (yang_datanode(y) || (y)->ys_keyword == Y_RPC || (y)->ys_keyword == Y_CHOICE || (y)->ys_keyword == Y_CASE || (y)->ys_keyword == Y_INPUT || (y)->ys_keyword == Y_OUTPUT || (y)->ys_keyword == Y_NOTIFICATION)
|
||||
|
||||
#endif /* _CLIXON_YANG_INTERNAL_H_ */
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -77,8 +77,8 @@
|
|||
#include "clixon_plugin.h"
|
||||
#include "clixon_netconf_lib.h"
|
||||
#include "clixon_xml_map.h"
|
||||
#include "clixon_yang_parse_lib.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_yang_internal.h" /* internal */
|
||||
|
||||
modstate_diff_t *
|
||||
modstate_diff_new(void)
|
||||
|
|
@ -155,7 +155,7 @@ yang_modules_revision(clicon_handle h)
|
|||
if ((ymod = yang_find(yspec, Y_MODULE, "ietf-yang-library")) != NULL ||
|
||||
(ymod = yang_find(yspec, Y_SUBMODULE, "ietf-yang-library")) != NULL){
|
||||
if ((yrev = yang_find(ymod, Y_REVISION, NULL)) != NULL){
|
||||
revision = yrev->ys_argument;
|
||||
revision = yang_argument_get(yrev);
|
||||
}
|
||||
}
|
||||
return revision;
|
||||
|
|
@ -189,25 +189,25 @@ yms_build(clicon_handle h,
|
|||
goto done;
|
||||
}
|
||||
|
||||
cprintf(cb,"<modules-state xmlns=\"%s\">", yns->ys_argument);
|
||||
cprintf(cb,"<modules-state xmlns=\"%s\">", yang_argument_get(yns));
|
||||
cprintf(cb,"<module-set-id>%s</module-set-id>", msid);
|
||||
|
||||
ymod = NULL;
|
||||
while ((ymod = yn_each(yspec, ymod)) != NULL) {
|
||||
if (ymod->ys_keyword != Y_MODULE &&
|
||||
ymod->ys_keyword != Y_SUBMODULE)
|
||||
if (yang_keyword_get(ymod) != Y_MODULE &&
|
||||
yang_keyword_get(ymod) != Y_SUBMODULE)
|
||||
continue;
|
||||
cprintf(cb,"<module>");
|
||||
cprintf(cb,"<name>%s</name>", ymod->ys_argument);
|
||||
cprintf(cb,"<name>%s</name>", yang_argument_get(ymod));
|
||||
if ((ys = yang_find(ymod, Y_REVISION, NULL)) != NULL)
|
||||
cprintf(cb,"<revision>%s</revision>", ys->ys_argument);
|
||||
cprintf(cb,"<revision>%s</revision>", yang_argument_get(ys));
|
||||
else{
|
||||
/* RFC7895 1 If no (such) revision statement exists, the module's or
|
||||
submodule's revision is the zero-length string. */
|
||||
cprintf(cb,"<revision></revision>");
|
||||
}
|
||||
if ((ys = yang_find(ymod, Y_NAMESPACE, NULL)) != NULL)
|
||||
cprintf(cb,"<namespace>%s</namespace>", ys->ys_argument);
|
||||
cprintf(cb,"<namespace>%s</namespace>", yang_argument_get(ys));
|
||||
else
|
||||
cprintf(cb,"<namespace></namespace>");
|
||||
/* This follows order in rfc 7895: feature, conformance-type,
|
||||
|
|
@ -215,10 +215,10 @@ yms_build(clicon_handle h,
|
|||
if (!brief){
|
||||
yc = NULL;
|
||||
while ((yc = yn_each(ymod, yc)) != NULL) {
|
||||
switch(yc->ys_keyword){
|
||||
switch(yang_keyword_get(yc)){
|
||||
case Y_FEATURE:
|
||||
if (yc->ys_cv && cv_bool_get(yc->ys_cv))
|
||||
cprintf(cb,"<feature>%s</feature>", yc->ys_argument);
|
||||
if (yang_cv_get(yc) && cv_bool_get(yang_cv_get(yc)))
|
||||
cprintf(cb,"<feature>%s</feature>", yang_argument_get(yc));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -228,12 +228,12 @@ yms_build(clicon_handle h,
|
|||
}
|
||||
yc = NULL;
|
||||
while ((yc = yn_each(ymod, yc)) != NULL) {
|
||||
switch(yc->ys_keyword){
|
||||
switch(yang_keyword_get(yc)){
|
||||
case Y_SUBMODULE:
|
||||
cprintf(cb,"<submodule>");
|
||||
cprintf(cb,"<name>%s</name>", yc->ys_argument);
|
||||
cprintf(cb,"<name>%s</name>", yang_argument_get(yc));
|
||||
if ((ys = yang_find(yc, Y_REVISION, NULL)) != NULL)
|
||||
cprintf(cb,"<revision>%s</revision>", ys->ys_argument);
|
||||
cprintf(cb,"<revision>%s</revision>", yang_argument_get(ys));
|
||||
else
|
||||
cprintf(cb,"<revision></revision>");
|
||||
cprintf(cb,"</submodule>");
|
||||
|
|
@ -413,7 +413,7 @@ mod_ns_upgrade(clicon_handle h,
|
|||
goto fail;
|
||||
if ((yrev = yang_find(ymod, Y_REVISION, NULL)) == NULL)
|
||||
goto fail;
|
||||
if (ys_parse_date_arg(yrev->ys_argument, &to) < 0)
|
||||
if (ys_parse_date_arg(yang_argument_get(yrev), &to) < 0)
|
||||
goto done;
|
||||
}
|
||||
if ((ret = upgrade_callback_call(h, xt, ns, from, to, cbret)) < 0)
|
||||
|
|
@ -482,3 +482,125 @@ clixon_module_upgrade(clicon_handle h,
|
|||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Given a yang statement and a prefix, return yang module to that relative prefix
|
||||
* Note, not the other module but the proxy import statement only
|
||||
* @param[in] ys A yang statement
|
||||
* @param[in] prefix prefix
|
||||
* @retval ymod Yang module statement if found
|
||||
* @retval NULL not found
|
||||
* @note Prefixes are relative to the module they are defined
|
||||
* @see yang_find_module_by_name
|
||||
* @see yang_find_module_by_namespace
|
||||
*/
|
||||
yang_stmt *
|
||||
yang_find_module_by_prefix(yang_stmt *ys,
|
||||
char *prefix)
|
||||
{
|
||||
yang_stmt *yimport;
|
||||
yang_stmt *yprefix;
|
||||
yang_stmt *my_ymod;
|
||||
yang_stmt *ymod = NULL;
|
||||
yang_stmt *yspec;
|
||||
char *myprefix;
|
||||
|
||||
if ((yspec = ys_spec(ys)) == NULL){
|
||||
clicon_err(OE_YANG, 0, "My yang spec not found");
|
||||
goto done;
|
||||
}
|
||||
if ((my_ymod = ys_module(ys)) == NULL){
|
||||
clicon_err(OE_YANG, 0, "My yang module not found");
|
||||
goto done;
|
||||
}
|
||||
myprefix = yang_find_myprefix(ys);
|
||||
if (myprefix && strcmp(myprefix, prefix) == 0){
|
||||
ymod = my_ymod;
|
||||
goto done;
|
||||
}
|
||||
yimport = NULL;
|
||||
while ((yimport = yn_each(my_ymod, yimport)) != NULL) {
|
||||
if (yang_keyword_get(yimport) != Y_IMPORT)
|
||||
continue;
|
||||
if ((yprefix = yang_find(yimport, Y_PREFIX, NULL)) != NULL &&
|
||||
strcmp(yang_argument_get(yprefix), prefix) == 0){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (yimport){
|
||||
if ((ymod = yang_find(yspec, Y_MODULE, yang_argument_get(yimport))) == NULL){
|
||||
clicon_err(OE_YANG, 0, "No module or sub-module found with prefix %s",
|
||||
prefix);
|
||||
yimport = NULL;
|
||||
goto done; /* unresolved */
|
||||
}
|
||||
}
|
||||
done:
|
||||
return ymod;
|
||||
}
|
||||
|
||||
/* Get module from its own prefix
|
||||
* This is really not a valid usecase, a kludge for the identityref derived
|
||||
* list workaround (IDENTITYREF_KLUDGE)
|
||||
* Actually, for canonical prefixes it is!
|
||||
*/
|
||||
yang_stmt *
|
||||
yang_find_module_by_prefix_yspec(yang_stmt *yspec,
|
||||
char *prefix)
|
||||
{
|
||||
yang_stmt *ymod = NULL;
|
||||
yang_stmt *yprefix;
|
||||
|
||||
while ((ymod = yn_each(yspec, ymod)) != NULL)
|
||||
if (yang_keyword_get(ymod) == Y_MODULE &&
|
||||
(yprefix = yang_find(ymod, Y_PREFIX, NULL)) != NULL &&
|
||||
strcmp(yang_argument_get(yprefix), prefix) == 0)
|
||||
return ymod;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! Given a yang spec and a namespace, return yang module
|
||||
*
|
||||
* @param[in] yspec A yang specification
|
||||
* @param[in] namespace namespace
|
||||
* @retval ymod Yang module statement if found
|
||||
* @retval NULL not found
|
||||
* @see yang_find_module_by_name
|
||||
* @see yang_find_module_by_prefix module-specific prefix
|
||||
*/
|
||||
yang_stmt *
|
||||
yang_find_module_by_namespace(yang_stmt *yspec,
|
||||
char *namespace)
|
||||
{
|
||||
yang_stmt *ymod = NULL;
|
||||
|
||||
if (namespace == NULL)
|
||||
goto done;
|
||||
while ((ymod = yn_each(yspec, ymod)) != NULL) {
|
||||
if (yang_find(ymod, Y_NAMESPACE, namespace) != NULL)
|
||||
break;
|
||||
}
|
||||
done:
|
||||
return ymod;
|
||||
}
|
||||
|
||||
/*! Given a yang spec and a module name, return yang module or submodule
|
||||
*
|
||||
* @param[in] yspec A yang specification
|
||||
* @param[in] name Name of module
|
||||
* @retval ymod Yang module statement if found
|
||||
* @retval NULL not found
|
||||
* @see yang_find_module_by_namespace
|
||||
* @see yang_find_module_by_prefix module-specific prefix
|
||||
*/
|
||||
yang_stmt *
|
||||
yang_find_module_by_name(yang_stmt *yspec,
|
||||
char *name)
|
||||
{
|
||||
yang_stmt *ymod = NULL;
|
||||
|
||||
while ((ymod = yn_each(yspec, ymod)) != NULL)
|
||||
if ((yang_keyword_get(ymod) == Y_MODULE || yang_keyword_get(ymod) == Y_SUBMODULE) &&
|
||||
strcmp(yang_argument_get(ymod), name)==0)
|
||||
return ymod;
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -51,7 +51,6 @@
|
|||
%start file
|
||||
|
||||
%union {
|
||||
int intval;
|
||||
char *string;
|
||||
}
|
||||
|
||||
|
|
@ -190,8 +189,8 @@
|
|||
#include "clixon_err.h"
|
||||
#include "clixon_log.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_yang_parse_lib.h"
|
||||
#include "clixon_yang_parse.h"
|
||||
#include "clixon_yang_internal.h" /* internal */
|
||||
|
||||
extern int clixon_yang_parseget_lineno (void);
|
||||
|
||||
|
|
@ -300,7 +299,7 @@ ysp_add(struct clicon_yang_yacc_arg *yy,
|
|||
if ((ys = ys_new(keyword)) == NULL)
|
||||
goto err;
|
||||
/* NOTE: does not make a copy of string, ie argument is 'consumed' here */
|
||||
ys->ys_argument = argument;
|
||||
yang_argument_set(ys, argument);
|
||||
if (yn_insert(yn, ys) < 0) /* Insert into hierarchy */
|
||||
goto err;
|
||||
if (ys_parse_sub(ys, extra) < 0) /* Check statement-specific syntax */
|
||||
|
|
|
|||
1360
lib/src/clixon_yang_parse_lib.c
Normal file
1360
lib/src/clixon_yang_parse_lib.c
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -2,7 +2,7 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -91,13 +91,12 @@
|
|||
#include "clixon_hash.h"
|
||||
#include "clixon_handle.h"
|
||||
#include "clixon_regex.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_hash.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_plugin.h"
|
||||
#include "clixon_options.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_yang_internal.h" /* internal */
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_yang_type.h"
|
||||
|
||||
/*
|
||||
|
|
@ -166,176 +165,6 @@ yang_builtin(char *type)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! Set type cache for yang type
|
||||
* @param[in] rxmode Kludge to know which regexp engine is used
|
||||
* @see yang_type_cache_regexp_set where cache is extended w compiled regexps
|
||||
*/
|
||||
static int
|
||||
yang_type_cache_set(yang_type_cache **ycache0,
|
||||
yang_stmt *resolved,
|
||||
int options,
|
||||
cvec *cvv,
|
||||
cvec *patterns,
|
||||
uint8_t fraction)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_type_cache *ycache = *ycache0;
|
||||
|
||||
assert (ycache == NULL);
|
||||
if ((ycache = (yang_type_cache *)malloc(sizeof(*ycache))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "malloc");
|
||||
goto done;
|
||||
}
|
||||
memset(ycache, 0, sizeof(*ycache));
|
||||
*ycache0 = ycache;
|
||||
ycache->yc_resolved = resolved;
|
||||
ycache->yc_options = options;
|
||||
if (cvv){
|
||||
if ((ycache->yc_cvv = cvec_dup(cvv)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cvec_dup");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
if (patterns && (ycache->yc_patterns = cvec_dup(patterns)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cvec_dup");
|
||||
goto done;
|
||||
}
|
||||
ycache->yc_fraction = fraction;
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Extend yang type cache with compiled regexps
|
||||
* Compiled Regexps are computed in validate code - after initial cache set
|
||||
* @param[in] regexps
|
||||
*/
|
||||
static int
|
||||
yang_type_cache_regexp_set(yang_stmt *ytype,
|
||||
int rxmode,
|
||||
cvec *regexps)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_type_cache *ycache;
|
||||
|
||||
assert(regexps);
|
||||
assert(yang_keyword_get(ytype) == Y_TYPE);
|
||||
assert((ycache = ytype->ys_typecache) != NULL);
|
||||
assert(ycache->yc_regexps == NULL);
|
||||
ycache->yc_rxmode = rxmode;
|
||||
if ((ycache->yc_regexps = cvec_dup(regexps)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cvec_dup");
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Get individual fields (direct/destructively) from yang type cache.
|
||||
* @param[out] patterns Initialized cvec of regexp patterns strings
|
||||
*/
|
||||
static int
|
||||
yang_type_cache_get(yang_type_cache *ycache,
|
||||
yang_stmt **resolved,
|
||||
int *options,
|
||||
cvec **cvv,
|
||||
cvec *patterns,
|
||||
int *rxmode,
|
||||
cvec *regexps,
|
||||
uint8_t *fraction)
|
||||
{
|
||||
int retval = -1;
|
||||
cg_var *cv = NULL;
|
||||
|
||||
if (resolved)
|
||||
*resolved = ycache->yc_resolved;
|
||||
if (options)
|
||||
*options = ycache->yc_options;
|
||||
if (cvv)
|
||||
*cvv = ycache->yc_cvv;
|
||||
if (patterns){
|
||||
cv = NULL;
|
||||
while ((cv = cvec_each(ycache->yc_patterns, cv)) != NULL)
|
||||
cvec_append_var(patterns, cv);
|
||||
}
|
||||
if (regexps){
|
||||
cv = NULL;
|
||||
while ((cv = cvec_each(ycache->yc_regexps, cv)) != NULL)
|
||||
cvec_append_var(regexps, cv);
|
||||
}
|
||||
if (rxmode)
|
||||
*rxmode = ycache->yc_rxmode;
|
||||
if (fraction)
|
||||
*fraction = ycache->yc_fraction;
|
||||
retval = 0;
|
||||
// done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
yang_type_cache_cp(yang_type_cache **ycnew,
|
||||
yang_type_cache *ycold)
|
||||
{
|
||||
int retval = -1;
|
||||
int options;
|
||||
cvec *cvv;
|
||||
cvec *patterns = NULL;
|
||||
uint8_t fraction;
|
||||
yang_stmt *resolved;
|
||||
|
||||
if ((patterns = cvec_new(0)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cvec_new");
|
||||
goto done;
|
||||
}
|
||||
/* Note, regexps are not copied since they are voids, if they were, they
|
||||
* could not be freed in a simple way since copies are made at augment/group
|
||||
*/
|
||||
yang_type_cache_get(ycold, &resolved, &options, &cvv, patterns, NULL, NULL, &fraction);
|
||||
if (yang_type_cache_set(ycnew, resolved, options, cvv, patterns, fraction) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
if (patterns)
|
||||
cvec_free(patterns);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int
|
||||
yang_type_cache_free(yang_type_cache *ycache)
|
||||
{
|
||||
cg_var *cv;
|
||||
void *p;
|
||||
|
||||
if (ycache->yc_cvv)
|
||||
cvec_free(ycache->yc_cvv);
|
||||
if (ycache->yc_patterns)
|
||||
cvec_free(ycache->yc_patterns);
|
||||
if (ycache->yc_regexps){
|
||||
cv = NULL;
|
||||
while ((cv = cvec_each(ycache->yc_regexps, cv)) != NULL){
|
||||
/* need to store mode since clicon_handle is not available */
|
||||
switch (ycache->yc_rxmode){
|
||||
case REGEXP_POSIX:
|
||||
cligen_regex_posix_free(cv_void_get(cv));
|
||||
break;
|
||||
case REGEXP_LIBXML2:
|
||||
cligen_regex_libxml2_free(cv_void_get(cv));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if ((p = cv_void_get(cv)) != NULL){
|
||||
free(p);
|
||||
cv_void_set(cv, NULL);
|
||||
}
|
||||
}
|
||||
cvec_free(ycache->yc_regexps);
|
||||
}
|
||||
free(ycache);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! Compile yang patterns in string form to regex compiled void* form
|
||||
* and re-store into "patterns" cvec.
|
||||
* This is done here instead of deep in resolve code (resolve_restrictions)
|
||||
|
|
@ -415,7 +244,7 @@ ys_resolve_type(yang_stmt *ys,
|
|||
/* Recursively resolve ys -> resolve with restrictions(options, etc)
|
||||
* Note that the resolved type could be ys itself.
|
||||
*/
|
||||
if (yang_type_resolve(ys->ys_parent, ys->ys_parent,
|
||||
if (yang_type_resolve(yang_parent_get(ys), yang_parent_get(ys),
|
||||
ys, &resolved,
|
||||
&options, &cvv, patterns, NULL, &fraction) < 0)
|
||||
goto done;
|
||||
|
|
@ -423,8 +252,7 @@ ys_resolve_type(yang_stmt *ys,
|
|||
/* Cache the type resolving locally. Only place where this is done.
|
||||
* Why not do it in yang_type_resolve? (compile regexps needs clicon_handle)
|
||||
*/
|
||||
if (yang_type_cache_set(&ys->ys_typecache,
|
||||
resolved, options, cvv,
|
||||
if (yang_type_cache_set(ys, resolved, options, cvv,
|
||||
patterns, fraction) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
|
|
@ -512,13 +340,15 @@ clicon_type2cv(char *origtype,
|
|||
enum cv_type *cvtype)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *ym;
|
||||
|
||||
*cvtype = CGV_ERR;
|
||||
ym = ys_module(ys);
|
||||
if (restype != NULL){
|
||||
yang2cv_type(restype, cvtype);
|
||||
if (*cvtype == CGV_ERR){
|
||||
clicon_err(OE_YANG, 0, "%s: \"%s\" type not translated",
|
||||
ys_module(ys)->ys_argument, restype);
|
||||
yang_argument_get(ym), restype);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
|
@ -530,7 +360,7 @@ clicon_type2cv(char *origtype,
|
|||
yang2cv_type(origtype, cvtype);
|
||||
if (*cvtype == CGV_ERR){
|
||||
clicon_err(OE_YANG, 0, "%s:\"%s\": type not resolved",
|
||||
ys_module(ys)->ys_argument, origtype);
|
||||
yang_argument_get(ym), origtype);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
|
@ -832,9 +662,9 @@ cv_validate1(clicon_handle h,
|
|||
yi = NULL;
|
||||
if (str != NULL)
|
||||
while ((yi = yn_each(yrestype, yi)) != NULL){
|
||||
if (yi->ys_keyword != Y_ENUM)
|
||||
if (yang_keyword_get(yi) != Y_ENUM)
|
||||
continue;
|
||||
if (strcmp(yi->ys_argument, str) == 0){
|
||||
if (strcmp(yang_argument_get(yi), str) == 0){
|
||||
found++;
|
||||
break;
|
||||
}
|
||||
|
|
@ -860,9 +690,9 @@ cv_validate1(clicon_handle h,
|
|||
found = 0;
|
||||
yi = NULL;
|
||||
while ((yi = yn_each(yrestype, yi)) != NULL){
|
||||
if (yi->ys_keyword != Y_BIT)
|
||||
if (yang_keyword_get(yi) != Y_BIT)
|
||||
continue;
|
||||
if (strcmp(yi->ys_argument, v) == 0){
|
||||
if (strcmp(yang_argument_get(yi), v) == 0){
|
||||
found++;
|
||||
break;
|
||||
}
|
||||
|
|
@ -944,7 +774,7 @@ ys_cv_validate_union_one(clicon_handle h,
|
|||
if (yang_type_resolve(ys, ys, yt, &yrt, &options, &cvv, patterns, regexps,
|
||||
&fraction) < 0)
|
||||
goto done;
|
||||
restype = yrt?yrt->ys_argument:NULL;
|
||||
restype = yrt?yang_argument_get(yrt):NULL;
|
||||
if (restype && strcmp(restype, "union") == 0){ /* recursive union */
|
||||
if ((retval = ys_cv_validate_union(h, ys, reason, yrt, type, val)) < 0)
|
||||
goto done;
|
||||
|
|
@ -957,6 +787,10 @@ ys_cv_validate_union_one(clicon_handle h,
|
|||
clicon_err(OE_UNIX, errno, "cv_new");
|
||||
goto done;
|
||||
}
|
||||
if (val == NULL){ /* Fail validation on NULL */
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
if ((retval = cv_parse1(val, cvt, reason)) < 0){
|
||||
clicon_err(OE_UNIX, errno, "cv_parse");
|
||||
goto done;
|
||||
|
|
@ -988,7 +822,7 @@ ys_cv_validate_union_one(clicon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*!
|
||||
/*! Validate union
|
||||
* @param[out] reason If given, and return value is 0, contains malloced string
|
||||
* @retval -1 Error (fatal), with errno set to indicate error
|
||||
* @retval 0 Validation not OK, malloced reason is returned. Free reason with free()
|
||||
|
|
@ -1007,7 +841,7 @@ ys_cv_validate_union(clicon_handle h,
|
|||
char *reason1 = NULL; /* saved reason */
|
||||
|
||||
while ((yt = yn_each(yrestype, yt)) != NULL){
|
||||
if (yt->ys_keyword != Y_TYPE)
|
||||
if (yang_keyword_get(yt) != Y_TYPE)
|
||||
continue;
|
||||
if ((retval = ys_cv_validate_union_one(h, ys, reason, yt, type, val)) < 0)
|
||||
goto done;
|
||||
|
|
@ -1069,11 +903,11 @@ ys_cv_validate(clicon_handle h,
|
|||
|
||||
if (reason)
|
||||
*reason=NULL;
|
||||
if (ys->ys_keyword != Y_LEAF && ys->ys_keyword != Y_LEAF_LIST){
|
||||
if (yang_keyword_get(ys) != Y_LEAF && yang_keyword_get(ys) != Y_LEAF_LIST){
|
||||
retval = 1;
|
||||
goto done;
|
||||
}
|
||||
ycv = ys->ys_cv;
|
||||
ycv = yang_cv_get(ys);
|
||||
if ((regexps = cvec_new(0)) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cvec_new");
|
||||
goto done;
|
||||
|
|
@ -1087,7 +921,7 @@ ys_cv_validate(clicon_handle h,
|
|||
patterns, regexps,
|
||||
&fraction) < 0)
|
||||
goto done;
|
||||
restype = yrestype?yrestype->ys_argument:NULL;
|
||||
restype = yrestype?yang_argument_get(yrestype):NULL;
|
||||
if (clicon_type2cv(origtype, restype, ys, &cvtype) < 0)
|
||||
goto done;
|
||||
|
||||
|
|
@ -1104,7 +938,11 @@ ys_cv_validate(clicon_handle h,
|
|||
/* Note restype can be NULL here for example with unresolved hardcoded uuid */
|
||||
if (restype && strcmp(restype, "union") == 0){
|
||||
assert(cvtype == CGV_REST);
|
||||
val = cv_string_get(cv);
|
||||
/* Instead of NULL, give an empty string to validate, this is to avoid cv_parse
|
||||
* errors and may actually be the wrong thing to do.
|
||||
*/
|
||||
if ((val = cv_string_get(cv)) == NULL)
|
||||
val = "";
|
||||
if ((retval2 = ys_cv_validate_union(h, ys, reason, yrestype, origtype, val)) < 0)
|
||||
goto done;
|
||||
retval = retval2; /* invalid (0) with latest reason or valid 1 */
|
||||
|
|
@ -1146,8 +984,8 @@ ys_cv_validate(clicon_handle h,
|
|||
static inline int
|
||||
ys_typedef(yang_stmt *ys)
|
||||
{
|
||||
return ys->ys_keyword == Y_MODULE || ys->ys_keyword == Y_SUBMODULE ||
|
||||
ys->ys_keyword == Y_CONTAINER || ys->ys_keyword == Y_LIST;
|
||||
return yang_keyword_get(ys) == Y_MODULE || yang_keyword_get(ys) == Y_SUBMODULE ||
|
||||
yang_keyword_get(ys) == Y_CONTAINER || yang_keyword_get(ys) == Y_LIST;
|
||||
}
|
||||
|
||||
/* find next ys up which can contain a typedef */
|
||||
|
|
@ -1157,9 +995,9 @@ ys_typedef_up(yang_stmt *ys)
|
|||
yang_stmt *yn;
|
||||
|
||||
while (ys != NULL && !ys_typedef(ys)){
|
||||
yn = ys->ys_parent;
|
||||
yn = yang_parent_get(ys);
|
||||
/* Some extra stuff to ensure ys is a stmt */
|
||||
if (yn && yn->ys_keyword == Y_SPEC)
|
||||
if (yn && yang_keyword_get(yn) == Y_SPEC)
|
||||
yn = NULL;
|
||||
ys = (yang_stmt*)yn;
|
||||
}
|
||||
|
|
@ -1222,8 +1060,8 @@ yang_find_identity(yang_stmt *ys,
|
|||
if ((yid = yang_find(ys, Y_IDENTITY, id)) != NULL)
|
||||
break;
|
||||
/* Did not find a matching typedef there, proceed to next level */
|
||||
yn = ys->ys_parent;
|
||||
if (yn && yn->ys_keyword == Y_SPEC)
|
||||
yn = yang_parent_get(ys);
|
||||
if (yn && yang_keyword_get(yn) == Y_SPEC)
|
||||
yn = NULL;
|
||||
ys = (yang_stmt*)yn;
|
||||
}
|
||||
|
|
@ -1263,12 +1101,12 @@ yang_type_resolve_restrictions(yang_stmt *ytype,
|
|||
|
||||
if (options && cvv &&
|
||||
(ys = yang_find(ytype, Y_RANGE, NULL)) != NULL){
|
||||
*cvv = ys->ys_cvec;
|
||||
*cvv = yang_cvec_get(ys);
|
||||
*options |= YANG_OPTIONS_RANGE;
|
||||
}
|
||||
if (options && cvv &&
|
||||
(ys = yang_find(ytype, Y_LENGTH, NULL)) != NULL){
|
||||
*cvv = ys->ys_cvec;
|
||||
*cvv = yang_cvec_get(ys);
|
||||
*options |= YANG_OPTIONS_LENGTH;
|
||||
}
|
||||
/* Find all patterns */
|
||||
|
|
@ -1281,7 +1119,7 @@ yang_type_resolve_restrictions(yang_stmt *ytype,
|
|||
clicon_err(OE_UNIX, errno, "cvec_add");
|
||||
goto done;
|
||||
}
|
||||
pattern = ys->ys_argument; /* clear text pattern */
|
||||
pattern = yang_argument_get(ys); /* clear text pattern */
|
||||
/* Check 1.1 invert pattern */
|
||||
if (yang_find(ys, Y_MODIFIER, "invert-match") != NULL)
|
||||
cv_flag_set(cv, V_INVERT);
|
||||
|
|
@ -1290,7 +1128,7 @@ yang_type_resolve_restrictions(yang_stmt *ytype,
|
|||
}
|
||||
if (options && fraction &&
|
||||
(ys = yang_find(ytype, Y_FRACTION_DIGITS, NULL)) != NULL){
|
||||
*fraction = cv_uint8_get(ys->ys_cv);
|
||||
*fraction = cv_uint8_get(yang_cv_get(ys));
|
||||
*options |= YANG_OPTIONS_FRACTION_DIGITS;
|
||||
}
|
||||
retval = 0;
|
||||
|
|
@ -1339,6 +1177,7 @@ yang_type_resolve(yang_stmt *yorig,
|
|||
int retval = -1;
|
||||
yang_stmt *yn;
|
||||
yang_stmt *yrmod; /* module where resolved type is looked for */
|
||||
int ret;
|
||||
|
||||
if (options)
|
||||
*options = 0x0;
|
||||
|
|
@ -1347,13 +1186,20 @@ yang_type_resolve(yang_stmt *yorig,
|
|||
if (nodeid_split(yang_argument_get(ytype), &prefix, &type) < 0)
|
||||
goto done;
|
||||
/* Cache does not work for eg string length 32? */
|
||||
#if 1
|
||||
if ((ret = yang_type_cache_get(ytype, yrestype,
|
||||
options, cvv, patterns, NULL, regexps, fraction)) < 0)
|
||||
goto done;
|
||||
if (ret == 1)
|
||||
goto ok;
|
||||
#else
|
||||
if (ytype->ys_typecache != NULL){
|
||||
if (yang_type_cache_get(ytype->ys_typecache, yrestype,
|
||||
if (yang_type_cache_get(ytype, yrestype,
|
||||
options, cvv, patterns, NULL, regexps, fraction) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
|
||||
#endif
|
||||
/* Check if type is basic type. If so, return that */
|
||||
if ((prefix == NULL && yang_builtin(type))){
|
||||
*yrestype = ytype;
|
||||
|
|
@ -1366,7 +1212,7 @@ yang_type_resolve(yang_stmt *yorig,
|
|||
if (prefix){ /* Go to top and find import that matches */
|
||||
if ((yrmod = yang_find_module_by_prefix(ytype, prefix)) == NULL){
|
||||
clicon_err(OE_DB, 0, "Type not resolved: \"%s:%s\" in module %s",
|
||||
prefix, type, ys_module(yorig)->ys_argument);
|
||||
prefix, type, yang_argument_get(ys_module(yorig)));
|
||||
goto done;
|
||||
}
|
||||
if ((rytypedef = yang_find(yrmod, Y_TYPEDEF, type)) == NULL)
|
||||
|
|
@ -1384,8 +1230,8 @@ yang_type_resolve(yang_stmt *yorig,
|
|||
if ((rytypedef = yang_find(ys, Y_TYPEDEF, type)) != NULL)
|
||||
break;
|
||||
/* Did not find a matching typedef there, proceed to next level */
|
||||
yn = ys->ys_parent;
|
||||
if (yn && (yn->ys_keyword == Y_SPEC))
|
||||
yn = yang_parent_get(ys);
|
||||
if (yn && (yang_keyword_get(yn) == Y_SPEC))
|
||||
yn = NULL;
|
||||
ys = (yang_stmt*)yn;
|
||||
}
|
||||
|
|
@ -1488,8 +1334,6 @@ yang_type_get(yang_stmt *ys,
|
|||
if (yang_type_resolve(ys, ys, ytype, yrestype,
|
||||
options, cvv, patterns, regexps, fraction) < 0)
|
||||
goto done;
|
||||
clicon_debug(3, "%s: %s %s->%s", __FUNCTION__, ys->ys_argument, type,
|
||||
*yrestype?(*yrestype)->ys_argument:"null");
|
||||
retval = 0;
|
||||
done:
|
||||
if (type)
|
||||
|
|
@ -1512,7 +1356,7 @@ yang_type2cv(yang_stmt *ys)
|
|||
if (yang_type_get(ys, &origtype, &yrestype, NULL, NULL, NULL, NULL, NULL)
|
||||
< 0)
|
||||
goto done;
|
||||
restype = yrestype?yrestype->ys_argument:NULL;
|
||||
restype = yrestype?yang_argument_get(yrestype):NULL;
|
||||
if (clicon_type2cv(origtype, restype, ys, &cvtype) < 0) /* This handles non-resolved also */
|
||||
goto done;
|
||||
done:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue