Added support for XPATH functions:
* `contains`,
* `derived-from` and `derived-from-or-self`
* in particular in augment/when statements as shown in eg RFC 7950.
This commit is contained in:
parent
6d7b76550f
commit
c616aa1569
18 changed files with 800 additions and 48 deletions
|
|
@ -27,6 +27,13 @@
|
|||
## 4.8.0
|
||||
Expected: October 2020
|
||||
|
||||
### New features
|
||||
|
||||
* Added support for the following XPATH functions:
|
||||
* `contains`, see https://www.w3.org/TR/xpath-10
|
||||
* `derived-from` and `derived-from-or-self`
|
||||
* in particular in augment/when statements as shown in eg RFC 7950.
|
||||
|
||||
## 4.7.0
|
||||
14 September 2020
|
||||
|
||||
|
|
|
|||
|
|
@ -200,6 +200,10 @@ int yang_cvec_set(yang_stmt *ys, cvec *cvv);
|
|||
uint16_t yang_flag_get(yang_stmt *ys, uint16_t flag);
|
||||
int yang_flag_set(yang_stmt *ys, uint16_t flag);
|
||||
int yang_flag_reset(yang_stmt *ys, uint16_t flag);
|
||||
char *yang_when_xpath_get(yang_stmt *ys);
|
||||
int yang_when_xpath_set(yang_stmt *ys, char *xpath);
|
||||
cvec *yang_when_nsc_get(yang_stmt *ys);
|
||||
int yang_when_nsc_set(yang_stmt *ys, cvec *nsc);
|
||||
|
||||
/* Other functions */
|
||||
yang_stmt *yspec_new(void);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2017-2019 Olof Hagsand
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -58,6 +60,7 @@ int ys_resolve_type(yang_stmt *ys, void *arg);
|
|||
int yang2cv_type(char *ytype, enum cv_type *cv_type);
|
||||
char *cv2yang_type(enum cv_type cv_type);
|
||||
yang_stmt *yang_find_identity(yang_stmt *ys, char *identity);
|
||||
yang_stmt *yang_find_identity_nsc(yang_stmt *yspec, char *identity, cvec *nsc);
|
||||
int ys_cv_validate(clicon_handle h, cg_var *cv, yang_stmt *ys, char **reason);
|
||||
int clicon_type2cv(char *type, char *rtype, yang_stmt *ys, enum cv_type *cvtype);
|
||||
int yang_type_get(yang_stmt *ys, char **otype, yang_stmt **restype,
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ SRC = clixon_sig.c clixon_uid.c clixon_log.c clixon_err.c clixon_event.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 \
|
||||
clixon_xpath.c clixon_xpath_ctx.c clixon_xpath_eval.c clixon_xpath_function.c clixon_xpath_optimize.c \
|
||||
clixon_sha1.c clixon_datastore.c clixon_datastore_write.c clixon_datastore_read.c \
|
||||
clixon_netconf_lib.c clixon_stream.c clixon_nacm.c
|
||||
|
||||
|
|
|
|||
|
|
@ -1174,11 +1174,45 @@ xml_yang_validate_all(clicon_handle h,
|
|||
/* "when" sub-node RFC 7950 Sec 7.21.5. Can only be one. */
|
||||
if ((yc = yang_find(ys, Y_WHEN, NULL)) != NULL){
|
||||
xpath = yang_argument_get(yc); /* "when" has xpath argument */
|
||||
if ((nr = xpath_vec_bool(xt, NULL, "%s", xpath)) < 0)
|
||||
/* WHEN xpath needs namespace context */
|
||||
if (xml_nsctx_yang(ys, &nsc) < 0)
|
||||
goto done;
|
||||
if (!nr){
|
||||
if ((nr = xpath_vec_bool(xt, nsc,
|
||||
"%s", xpath)) < 0)
|
||||
goto done;
|
||||
if (nsc){
|
||||
xml_nsctx_free(nsc);
|
||||
nsc = NULL;
|
||||
}
|
||||
if (nr == 0){
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cb, "Failed WHEN condition of %s in module %s",
|
||||
xml_name(xt),
|
||||
yang_argument_get(ys_module(ys)));
|
||||
if (netconf_operation_failed_xml(xret, "application",
|
||||
"when xpath validation failed") < 0)
|
||||
cbuf_get(cb)) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
/* Augmented when using special struct. */
|
||||
if ((xpath = yang_when_xpath_get(ys)) != NULL){
|
||||
if ((nr = xpath_vec_bool(xml_parent(xt), yang_when_nsc_get(ys),
|
||||
"%s", xpath)) < 0)
|
||||
goto done;
|
||||
if (nr == 0){
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cb, "Failed augmented WHEN condition of %s in module %s",
|
||||
xml_name(xt),
|
||||
yang_argument_get(ys_module(ys)));
|
||||
if (netconf_operation_failed_xml(xret, "application",
|
||||
cbuf_get(cb)) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2180,3 +2180,4 @@ xml_copy_marked(cxobj *x0,
|
|||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@
|
|||
#include "clixon_xpath_ctx.h"
|
||||
#include "clixon_xpath.h"
|
||||
#include "clixon_xpath_optimize.h"
|
||||
#include "clixon_xpath_function.h"
|
||||
#include "clixon_xpath_eval.h"
|
||||
|
||||
/* Mapping between XPATH operator string <--> int */
|
||||
|
|
@ -178,8 +179,8 @@ nodetest_eval_node(cxobj *x,
|
|||
*/
|
||||
static int
|
||||
nodetest_eval_node_localonly(cxobj *x,
|
||||
xpath_tree *xs,
|
||||
cvec *nsc)
|
||||
xpath_tree *xs,
|
||||
cvec *nsc)
|
||||
{
|
||||
int retval = -1;
|
||||
char *name1 = xml_name(x);
|
||||
|
|
@ -237,11 +238,11 @@ nodetest_eval(cxobj *x,
|
|||
|
||||
/*!
|
||||
* @param[in] xn
|
||||
* @param[in] nodetest XPATH stack
|
||||
* @param[in] nodetest XPATH stack
|
||||
* @param[in] node_type
|
||||
* @param[in] flags
|
||||
* @param[in] nsc XML Namespace context
|
||||
* @param[in] localonly Skip prefix and namespace tests (non-standard)
|
||||
* @param[in] localonly Skip prefix and namespace tests (non-standard)
|
||||
* @param[out] vec0
|
||||
* @param[out] vec0len
|
||||
*/
|
||||
|
|
@ -333,7 +334,7 @@ xp_eval_step(xp_ctx *xc0,
|
|||
else{
|
||||
if (nodetest->xs_type==XP_NODE_FN &&
|
||||
nodetest->xs_s0 &&
|
||||
strcmp(nodetest->xs_s0,"current")==0){
|
||||
strcmp(nodetest->xs_s0, "current")==0){
|
||||
if (cxvec_append(xc->xc_initial, &vec, &veclen) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -522,7 +523,6 @@ xp_eval_predicate(xp_ctx *xc,
|
|||
if (xrc)
|
||||
ctx_free(xrc);
|
||||
}
|
||||
|
||||
}
|
||||
assert(xr0||xr1);
|
||||
if (xr1){
|
||||
|
|
@ -972,6 +972,25 @@ xp_eval(xp_ctx *xc,
|
|||
goto done;
|
||||
goto ok;
|
||||
break;
|
||||
case XP_PRIME_FN:
|
||||
if (xs->xs_s0){
|
||||
if (strcmp(xs->xs_s0, "contains") == 0){
|
||||
if (xp_function_contains(xc, xs->xs_c0, nsc, localonly, xrp) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
else if (strcmp(xs->xs_s0, "derived-from") == 0){
|
||||
if (xp_function_derived_from(xc, xs->xs_c0, nsc, localonly, 0, xrp) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
else if (strcmp(xs->xs_s0, "derived-from-or-self") == 0){
|
||||
if (xp_function_derived_from(xc, xs->xs_c0, nsc, localonly, 1, xrp) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -1047,12 +1066,10 @@ xp_eval(xp_ctx *xc,
|
|||
xr0->xc_type = XT_STRING;
|
||||
xr0->xc_string = xs->xs_s0?strdup(xs->xs_s0):NULL;
|
||||
break;
|
||||
case XP_PRIME_FN:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* Eval second child c0
|
||||
/* Eval second child c1
|
||||
* Note, some operators like locationpath, need transitive context (use_xr0)
|
||||
*/
|
||||
if (xs->xs_c1)
|
||||
|
|
|
|||
295
lib/src/clixon_xpath_function.c
Normal file
295
lib/src/clixon_xpath_function.c
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of
|
||||
the GNU General Public License Version 3 or later (the "GPL"),
|
||||
in which case the provisions of the GPL are applicable instead
|
||||
of those above. If you wish to allow use of your version of this file only
|
||||
under the terms of the GPL, and not to allow others to
|
||||
use your version of this file under the terms of Apache License version 2, indicate
|
||||
your decision by deleting the provisions above and replace them with the
|
||||
notice and other provisions required by the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this file under
|
||||
the terms of any one of the Apache License version 2 or the GPL.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
|
||||
* Clixon XML XPATH 1.0 according to https://www.w3.org/TR/xpath-10
|
||||
* and rfc 7950
|
||||
*
|
||||
*/
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "clixon_config.h" /* generated by config & autoconf */
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <syslog.h>
|
||||
#include <fcntl.h>
|
||||
#include <math.h> /* NaN */
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
/* clicon */
|
||||
#include "clixon_err.h"
|
||||
#include "clixon_log.h"
|
||||
#include "clixon_string.h"
|
||||
#include "clixon_queue.h"
|
||||
#include "clixon_hash.h"
|
||||
#include "clixon_handle.h"
|
||||
#include "clixon_options.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_yang_type.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_xml_map.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_validate.h"
|
||||
#include "clixon_xpath_ctx.h"
|
||||
#include "clixon_xpath.h"
|
||||
#include "clixon_xpath_eval.h"
|
||||
#include "clixon_xpath_function.h"
|
||||
|
||||
/*! Eval xpath function contains
|
||||
* @param[in] xc Incoming context
|
||||
* @param[in] xs XPATH node tree
|
||||
* @param[in] nsc XML Namespace context
|
||||
* @param[in] localonly Skip prefix and namespace tests (non-standard)
|
||||
* @param[out] xrp Resulting context
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @see https://www.w3.org/TR/xpath-10/#NT-FunctionName 4.2 String Functions
|
||||
*/
|
||||
int
|
||||
xp_function_contains(xp_ctx *xc,
|
||||
struct xpath_tree *xs,
|
||||
cvec *nsc,
|
||||
int localonly,
|
||||
xp_ctx **xrp)
|
||||
{
|
||||
int retval = -1;
|
||||
xp_ctx *xr0 = NULL;
|
||||
xp_ctx *xr1 = NULL;
|
||||
xp_ctx *xr = NULL;
|
||||
char *s0 = NULL;
|
||||
char *s1 = NULL;
|
||||
|
||||
/* contains two arguments in xs: boolean contains(string, string) */
|
||||
if (xp_eval(xc, xs->xs_c0, nsc, localonly, &xr0) < 0)
|
||||
goto done;
|
||||
if (ctx2string(xr0, &s0) < 0)
|
||||
goto done;
|
||||
if (xp_eval(xc, xs->xs_c1, nsc, localonly, &xr1) < 0)
|
||||
goto done;
|
||||
if (ctx2string(xr1, &s1) < 0)
|
||||
goto done;
|
||||
if ((xr = malloc(sizeof(*xr))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "malloc");
|
||||
goto done;
|
||||
}
|
||||
memset(xr, 0, sizeof(*xr));
|
||||
xr->xc_type = XT_BOOL;
|
||||
xr->xc_bool = (strstr(s0, s1) != NULL);
|
||||
*xrp = xr;
|
||||
xr = NULL;
|
||||
retval = 0;
|
||||
done:
|
||||
if (xr0)
|
||||
ctx_free(xr0);
|
||||
if (xr1)
|
||||
ctx_free(xr1);
|
||||
if (s0)
|
||||
free(s0);
|
||||
if (s1)
|
||||
free(s1);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Helper function for derived-from(-and-self) - eval one node
|
||||
* @param[in] nsc XML Namespace context
|
||||
* @param[in] self If set, implements derived_from_or_self
|
||||
* @retval 1 OK and match
|
||||
* @retval 0 OK but not match
|
||||
* @retval -1 Error
|
||||
*/
|
||||
static int
|
||||
derived_from_one(char *baseidentity,
|
||||
cvec *nsc,
|
||||
cxobj *xleaf,
|
||||
int self)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *yleaf;
|
||||
yang_stmt *ytype;
|
||||
yang_stmt *ybaseid;
|
||||
yang_stmt *ymod;
|
||||
cvec *idrefvec; /* Derived identityref list: (module:id)**/
|
||||
char *node = NULL;
|
||||
char *prefix = NULL;
|
||||
char *id = NULL;
|
||||
cbuf *cb = NULL;
|
||||
char *baseid = NULL;
|
||||
|
||||
/* Split baseidentity to get its id (w/o prefix) */
|
||||
if (nodeid_split(baseidentity, NULL, &baseid) < 0)
|
||||
goto done;
|
||||
if ((yleaf = xml_spec(xleaf)) == NULL)
|
||||
goto nomatch;
|
||||
if (yang_keyword_get(yleaf) != Y_LEAF && yang_keyword_get(yleaf) != Y_LEAF_LIST)
|
||||
goto nomatch;
|
||||
/* Node is of type identityref */
|
||||
if (yang_type_get(yleaf, NULL, &ytype, NULL, NULL, NULL, NULL, NULL) < 0)
|
||||
goto done;
|
||||
if (ytype == NULL || strcmp(yang_argument_get(ytype), "identityref"))
|
||||
goto nomatch;
|
||||
/* Find if the derivation chain is: identity ->...-> ytype
|
||||
* Example:
|
||||
* identity is ex:ethernet
|
||||
* xleaf <type>fast-ethernet</type>
|
||||
* yleaf type identityref{base interface-type;}
|
||||
*/
|
||||
/* Just get the object corresponding to the base identity */
|
||||
if ((ybaseid = yang_find_identity_nsc(ys_spec(yleaf), baseidentity, nsc)) == NULL)
|
||||
goto nomatch;
|
||||
/* Get its list of derived identities */
|
||||
idrefvec = yang_cvec_get(ybaseid);
|
||||
/* Get and split the leaf id reference */
|
||||
if ((node = xml_body(xleaf)) == NULL) /* It may not be empty */
|
||||
goto nomatch;
|
||||
if (nodeid_split(node, &prefix, &id) < 0)
|
||||
goto done;
|
||||
/* Get its module (prefixes are not used here) */
|
||||
if (prefix == NULL)
|
||||
ymod = ys_module(yleaf);
|
||||
else{ /* from prefix to name */
|
||||
#if 1 /* IDENTITYREF_KLUDGE */
|
||||
ymod = yang_find_module_by_prefix_yspec(ys_spec(yleaf), prefix);
|
||||
#endif
|
||||
}
|
||||
if (ymod == NULL)
|
||||
goto nomatch;
|
||||
/* self special case, ie that the xleaf has a ref to itself */
|
||||
if (self &&
|
||||
ymod == ys_module(ybaseid) &&
|
||||
strcmp(baseid, id) == 0){
|
||||
; /* match */
|
||||
}
|
||||
else {
|
||||
/* Allocate cbuf */
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cb, "%s:%s", yang_argument_get(ymod), id);
|
||||
if (cvec_find(idrefvec, cbuf_get(cb)) == NULL)
|
||||
goto nomatch;
|
||||
}
|
||||
retval = 1;
|
||||
done:
|
||||
if (baseid)
|
||||
free(baseid);
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (id)
|
||||
free(id);
|
||||
if (prefix)
|
||||
free(prefix);
|
||||
return retval;
|
||||
nomatch:
|
||||
retval = 0;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*! Eval xpath function derived-from(-and-self)
|
||||
* @param[in] xc Incoming context
|
||||
* @param[in] xs XPATH node tree
|
||||
* @param[in] nsc XML Namespace context
|
||||
* @param[in] localonly Skip prefix and namespace tests (non-standard)
|
||||
* @param[in] self If set, implements derived_from_or_self
|
||||
* @param[out] xrp Resulting context
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
* @see rfc7950 10.4.1
|
||||
* Returns "true" if any node in the argument "nodes" is a node of type "identityref" and its
|
||||
* value is an identity that is derived from (see Section 7.18.2) the identity "identity"
|
||||
* boolean derived-from(node-set nodes, string identity)
|
||||
* @see validate_identityref for similar code other usage
|
||||
*/
|
||||
int
|
||||
xp_function_derived_from(xp_ctx *xc,
|
||||
struct xpath_tree *xs,
|
||||
cvec *nsc,
|
||||
int localonly,
|
||||
int self,
|
||||
xp_ctx **xrp)
|
||||
{
|
||||
int retval = -1;
|
||||
xp_ctx *xr0 = NULL;
|
||||
xp_ctx *xr1 = NULL;
|
||||
xp_ctx *xr = NULL;
|
||||
char *identity = NULL;
|
||||
int i;
|
||||
int ret = 0;
|
||||
|
||||
/* contains two arguments in xs: boolean derived-from(node-set, string) */
|
||||
/* This evolves to a set of (identityref) nodes */
|
||||
if (xp_eval(xc, xs->xs_c0, nsc, localonly, &xr0) < 0)
|
||||
goto done;
|
||||
if (xr0->xc_type != XT_NODESET)
|
||||
goto done;
|
||||
/* This evolves to a string identity */
|
||||
if (xp_eval(xc, xs->xs_c1, nsc, localonly, &xr1) < 0)
|
||||
goto done;
|
||||
if (ctx2string(xr1, &identity) < 0)
|
||||
goto done;
|
||||
/* Allocate a return struct of type boolean */
|
||||
if ((xr = malloc(sizeof(*xr))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "malloc");
|
||||
goto done;
|
||||
}
|
||||
memset(xr, 0, sizeof(*xr));
|
||||
xr->xc_type = XT_BOOL;
|
||||
/* ANY node is an identityref and its value an identity that is derived ... */
|
||||
for (i=0; i<xr0->xc_size; i++){
|
||||
if ((ret = derived_from_one(identity, nsc, xr0->xc_nodeset[i], self)) < 0)
|
||||
goto done;
|
||||
if (ret == 1)
|
||||
break;
|
||||
}
|
||||
xr->xc_bool = ret;
|
||||
*xrp = xr;
|
||||
xr = NULL;
|
||||
retval = 0;
|
||||
done:
|
||||
if (xr0)
|
||||
ctx_free(xr0);
|
||||
if (xr1)
|
||||
ctx_free(xr1);
|
||||
if (identity)
|
||||
free(identity);
|
||||
return retval;
|
||||
}
|
||||
47
lib/src/clixon_xpath_function.h
Normal file
47
lib/src/clixon_xpath_function.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2019 Olof Hagsand
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
Alternatively, the contents of this file may be used under the terms of
|
||||
the GNU General Public License Version 3 or later (the "GPL"),
|
||||
in which case the provisions of the GPL are applicable instead
|
||||
of those above. If you wish to allow use of your version of this file only
|
||||
under the terms of the GPL, and not to allow others to
|
||||
use your version of this file under the terms of Apache License version 2,
|
||||
indicate your decision by deleting the provisions above and replace them with
|
||||
the notice and other provisions required by the GPL. If you do not delete
|
||||
the provisions above, a recipient may use your version of this file under
|
||||
the terms of any one of the Apache License version 2 or the GPL.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
|
||||
* Clixon XML XPATH 1.0 according to https://www.w3.org/TR/xpath-10 (Base XML)
|
||||
* and rfc 7950 (YANG-specific)
|
||||
*/
|
||||
#ifndef _CLIXON_XPATH_FUNCTION_H
|
||||
#define _CLIXON_XPATH_FUNCTION_H
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
int xp_function_contains(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, xp_ctx **xrp);
|
||||
int xp_function_derived_from(xp_ctx *xc, struct xpath_tree *xs, cvec *nsc, int localonly, int self, xp_ctx **xrp);
|
||||
|
||||
#endif /* _CLIXON_XPATH_FUNCTION_H */
|
||||
|
|
@ -113,6 +113,7 @@ ncname {namestart}{namechar}*
|
|||
<TOKEN>last { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
|
||||
<TOKEN>position { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
|
||||
<TOKEN>count { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
|
||||
<TOKEN>contains { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
|
||||
<TOKEN>re-match { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
|
||||
<TOKEN>deref { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
|
||||
<TOKEN>derived-from { clixon_xpath_parselval.string = strdup(yytext); return FUNCTIONNAME; }
|
||||
|
|
|
|||
|
|
@ -316,6 +316,87 @@ yang_flag_reset(yang_stmt *ys,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*! Get yang xpath for "when"-associated augment
|
||||
*
|
||||
* Ie, for yang structures like: augment <path> { when <xpath>; ... }
|
||||
* Will insert new yang nodes at <path> with this special "when" struct (not yang node)
|
||||
* @param[in] ys Yang statement
|
||||
* @retval xpath xpath should evaluate to true at validation
|
||||
* @retval NULL Not set
|
||||
*/
|
||||
char*
|
||||
yang_when_xpath_get(yang_stmt *ys)
|
||||
{
|
||||
return ys->ys_when_xpath;
|
||||
}
|
||||
|
||||
/*! Set yang xpath and namespace context for "when"-associated augment
|
||||
*
|
||||
* Ie, for yang structures like: augment <path> { when <xpath>; ... }
|
||||
* Will insert new yang nodes at <path> with this special "when" struct (not yang node)
|
||||
* @param[in] ys Yang statement
|
||||
* @param[in] xpath If set, this xpath should evaluate to true at validation, copied
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*/
|
||||
int
|
||||
yang_when_xpath_set(yang_stmt *ys,
|
||||
char *xpath)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
if (xpath == NULL){
|
||||
clicon_err(OE_YANG, EINVAL, "xpath is NULL");
|
||||
goto done;
|
||||
}
|
||||
if ((ys->ys_when_xpath = strdup(xpath)) == NULL){
|
||||
clicon_err(OE_YANG, errno, "strdup");
|
||||
goto done;
|
||||
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Get yang namespace context for "when"-associated augment
|
||||
*
|
||||
* Ie, for yang structures like: augment <path> { when <xpath>; ... }
|
||||
* Will insert new yang nodes at <path> with this special "when" struct (not yang node)
|
||||
* @param[in] ys Yang statement
|
||||
* @retval nsc Namespace context
|
||||
* @note retval is direct pointer, may need to be copied
|
||||
*/
|
||||
cvec *
|
||||
yang_when_nsc_get(yang_stmt *ys)
|
||||
{
|
||||
return ys->ys_when_nsc;
|
||||
}
|
||||
|
||||
/*! Set yang namespace context for "when"-associated augment
|
||||
*
|
||||
* Ie, for yang structures like: augment <path> { when <xpath>; ... }
|
||||
* Will insert new yang nodes at <path> with this special "when" struct (not yang node)
|
||||
* @param[in] ys Yang statement
|
||||
* @param[in] nsc Namespace context for when xpath
|
||||
* @retval 0 OK
|
||||
* @retval -1 Error
|
||||
*/
|
||||
int
|
||||
yang_when_nsc_set(yang_stmt *ys,
|
||||
cvec *nsc)
|
||||
{
|
||||
int retval = -1;
|
||||
|
||||
if (nsc && (ys->ys_when_nsc = cvec_dup(nsc)) == NULL){
|
||||
clicon_err(OE_YANG, errno, "cvec_dup");
|
||||
goto done;
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* End access functions */
|
||||
|
||||
/*! Create new yang specification
|
||||
|
|
@ -390,6 +471,10 @@ ys_free1(yang_stmt *ys,
|
|||
yang_type_cache_free(ys->ys_typecache);
|
||||
ys->ys_typecache = NULL;
|
||||
}
|
||||
if (ys->ys_when_xpath)
|
||||
free(ys->ys_when_xpath);
|
||||
if (ys->ys_when_nsc)
|
||||
cvec_free(ys->ys_when_nsc);
|
||||
if (self)
|
||||
free(ys);
|
||||
return 0;
|
||||
|
|
@ -523,6 +608,17 @@ ys_cp(yang_stmt *ynew,
|
|||
if (yang_type_cache_cp(ynew, yold) < 0)
|
||||
goto done;
|
||||
}
|
||||
if (yold->ys_when_xpath)
|
||||
if ((ynew->ys_when_xpath = strdup(yold->ys_when_xpath)) == NULL){
|
||||
clicon_err(OE_YANG, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
if (yold->ys_when_nsc){
|
||||
if ((ynew->ys_when_nsc = cvec_dup(yold->ys_when_nsc)) == NULL){
|
||||
clicon_err(OE_YANG, errno, "cvec_dup");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
for (i=0; i<ynew->ys_len; i++){
|
||||
yco = yold->ys_stmt[i];
|
||||
if ((ycn = ys_dup(yco)) == NULL)
|
||||
|
|
|
|||
|
|
@ -67,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 */
|
||||
uint16_t ys_flags; /* Flags according to YANG_FLAG_* */
|
||||
uint16_t ys_flags; /* Flags according to YANG_FLAG_MARK and others */
|
||||
yang_stmt *ys_mymodule; /* Shortcut to "my" module. Augmented
|
||||
nodes can belong to other
|
||||
modules than the ancestor module */
|
||||
|
|
@ -87,7 +87,10 @@ struct yang_stmt{
|
|||
types as <module>:<id> list
|
||||
*/
|
||||
yang_type_cache *ys_typecache; /* If ys_keyword==Y_TYPE, cache all typedef data except unions */
|
||||
char *ys_when_xpath; /* Special conditional for a "when"-associated augment xpath */
|
||||
cvec *ys_when_nsc; /* Special conditional for a "when"-associated augment namespace ctx */
|
||||
int _ys_vector_i; /* internal use: yn_each */
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@
|
|||
#include "clixon_yang_internal.h"
|
||||
#include "clixon_hash.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_xml_nsctx.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_plugin.h"
|
||||
#include "clixon_data.h"
|
||||
|
|
@ -203,6 +204,10 @@ ys_grouping_resolve(yang_stmt *yuses,
|
|||
* All data nodes defined in the "augment" statement are defined as XML
|
||||
* elements in the XML namespace of the module where the "augment" is
|
||||
* specified.
|
||||
*
|
||||
* @note If the augment has a when statement, which is commonplace, the when statement is not copied as
|
||||
* datanodes are, since it should not apply to the target node. Instead it is added as a special "when"
|
||||
* struct to the yang statements being inserted.
|
||||
*/
|
||||
static int
|
||||
yang_augment_node(yang_stmt *ys,
|
||||
|
|
@ -214,6 +219,9 @@ yang_augment_node(yang_stmt *ys,
|
|||
yang_stmt *yc0;
|
||||
yang_stmt *yc;
|
||||
yang_stmt *ymod;
|
||||
yang_stmt *ywhen;
|
||||
char *wxpath = NULL; /* xpath of when statement */
|
||||
cvec *wnsc = NULL; /* namespace context of when statement */
|
||||
|
||||
if ((ymod = ys_module(ys)) == NULL){
|
||||
clicon_err(OE_YANG, 0, "My yang module not found");
|
||||
|
|
@ -226,6 +234,12 @@ yang_augment_node(yang_stmt *ys,
|
|||
goto done;
|
||||
if (ytarget == NULL)
|
||||
goto ok;
|
||||
/* Find when statement, if present */
|
||||
if ((ywhen = yang_find(ys, Y_WHEN, NULL)) != NULL){
|
||||
wxpath = yang_argument_get(ywhen);
|
||||
if (xml_nsctx_yang(ywhen, &wnsc) < 0)
|
||||
goto done;
|
||||
}
|
||||
/* Extend ytarget with ys' schemanode children */
|
||||
yc0 = NULL;
|
||||
while ((yc0 = yn_each(ys, yc0)) != NULL) {
|
||||
|
|
@ -236,10 +250,19 @@ yang_augment_node(yang_stmt *ys,
|
|||
yc->ys_mymodule = ymod;
|
||||
if (yn_insert(ytarget, yc) < 0)
|
||||
goto done;
|
||||
/* If there is an associated when statement, add a special when struct to the yang */
|
||||
if (ywhen){
|
||||
if (yang_when_xpath_set(yc, wxpath) < 0)
|
||||
goto done;
|
||||
if (yang_when_nsc_set(yc, wnsc) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
ok:
|
||||
retval = 0;
|
||||
done:
|
||||
if (wnsc)
|
||||
cvec_free(wnsc);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
/*
|
||||
*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (C) 2009-2020 Olof Hagsand
|
||||
|
||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2017-2019 Olof Hagsand
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate)
|
||||
|
||||
This file is part of CLIXON.
|
||||
|
||||
|
|
@ -94,6 +96,7 @@
|
|||
#include "clixon_hash.h"
|
||||
#include "clixon_yang.h"
|
||||
#include "clixon_xml.h"
|
||||
#include "clixon_xml_nsctx.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_plugin.h"
|
||||
#include "clixon_options.h"
|
||||
|
|
@ -1007,31 +1010,14 @@ ys_typedef_up(yang_stmt *ys)
|
|||
return (yang_stmt*)ys;
|
||||
}
|
||||
|
||||
/*! Find identity yang-stmt
|
||||
* This is a sanity check of base identity of identity-ref and for identity
|
||||
* statements when parsing.
|
||||
/*! Find identity yang-stmt given a name and a yang statement for prefix context
|
||||
*
|
||||
* Return true if node is identityref and is derived from identity_name
|
||||
* The derived-from() function returns true if the (first) node (in
|
||||
* document order in the argument "nodes") is a node of type identityref,
|
||||
* and its value is an identity that is derived from the identity
|
||||
* "identity-name" defined in the YANG module "module-name"; otherwise
|
||||
* it returns false.
|
||||
*
|
||||
* Valid values for an identityref are any identities derived from the
|
||||
* identityref's base identity.
|
||||
* 1. (base) identity must exist (be found). This is a sanity check
|
||||
* of the specification and also necessary for identity statements.
|
||||
* (This is what is done here)
|
||||
* 2. Check if a given node has value derived from base identity. This is
|
||||
* a run-time check necessary when validating eg netconf.
|
||||
* (This is validation)
|
||||
* 3. Find all valid derived identities from a identityref base identity.
|
||||
* (This is for cli generation)
|
||||
* @param[in] ys Yang spec of id statement
|
||||
* @param[in] identity Identity string -check if it exists
|
||||
* @retval 0 OK
|
||||
* @param[in] identity Identity string on the form <prefix>:<id>
|
||||
* @retval yid yang-stmt of type IDENTITY
|
||||
* @retval NULL Not found
|
||||
* @see validate_identityref for (2) above
|
||||
* @see xml_find_identity
|
||||
*/
|
||||
yang_stmt *
|
||||
yang_find_identity(yang_stmt *ys,
|
||||
|
|
@ -1076,6 +1062,44 @@ yang_find_identity(yang_stmt *ys,
|
|||
return yid;
|
||||
}
|
||||
|
||||
/*! Find identity yang-stmt given a name and a xml node for prefix context
|
||||
*
|
||||
* @param[in] yspec Top-level yang-spec
|
||||
* @param[in] identity Identity string on the form <prefix>:<id>
|
||||
* @param[in] nsc Namespace context for <prefix>
|
||||
* @retval yid yang-stmt of type IDENTITY
|
||||
* @retval NULL Not found
|
||||
* @see validate_identityref for (2) above
|
||||
* @see xml_find_identity
|
||||
*/
|
||||
yang_stmt *
|
||||
yang_find_identity_nsc(yang_stmt *yspec,
|
||||
char *identity,
|
||||
cvec *nsc)
|
||||
{
|
||||
char *id = NULL;
|
||||
char *prefix = NULL;
|
||||
yang_stmt *ymodule;
|
||||
yang_stmt *yid = NULL;
|
||||
char *ns = NULL;
|
||||
|
||||
if (nodeid_split(identity, &prefix, &id) < 0)
|
||||
goto done;
|
||||
if ((ns = xml_nsctx_get(nsc, prefix)) == NULL)
|
||||
goto done;
|
||||
if ((ymodule = yang_find_module_by_namespace(yspec, ns)) == NULL)
|
||||
goto done;
|
||||
/* if ymodule is a sub-module, the identity may be found in a
|
||||
* sub-module of ymod */
|
||||
yid = yang_find(ymodule, Y_IDENTITY, id);
|
||||
done:
|
||||
if (id)
|
||||
free(id);
|
||||
if (prefix)
|
||||
free(prefix);
|
||||
return yid;
|
||||
}
|
||||
|
||||
/*! Resolve type restrictions, return constraining parameters
|
||||
*
|
||||
* This is for types with range/length/regexp restrictions of the base type
|
||||
|
|
|
|||
|
|
@ -195,7 +195,7 @@ new "netconf set identity defined in other"
|
|||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">
|
||||
<interface xmlns:mymod=\"urn:example:augment\">
|
||||
<name>e2</name>
|
||||
<type>fddi</type>
|
||||
<type>mymod:some-new-iftype</type>
|
||||
<mymod:mandatory-leaf>true</mymod:mandatory-leaf>
|
||||
<mymod:other>if:fddi</mymod:other>
|
||||
</interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
|
@ -207,7 +207,7 @@ new "netconf set identity defined in main"
|
|||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><interfaces xmlns=\"urn:ietf:params:xml:ns:yang:ietf-interfaces\">
|
||||
<interface xmlns:mymod=\"urn:example:augment\">
|
||||
<name>e3</name>
|
||||
<type>fddi</type>
|
||||
<type>mymod:some-new-iftype</type>
|
||||
<mymod:mandatory-leaf>true</mymod:mandatory-leaf>
|
||||
<mymod:me>mymod:you</mymod:me>
|
||||
</interface></interfaces></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
|
@ -217,10 +217,11 @@ expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><commit/></rpc>]]>]]>" "
|
|||
|
||||
# restconf and augment
|
||||
new "restconf get augment json"
|
||||
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces)" 0 "HTTP/1.1 200 OK" '{"ietf-interfaces:interfaces":{"interface":\[{"name":"e1","type":"example-augment:some-new-iftype","example-augment:mandatory-leaf":"true","example-augment:port":80,"example-augment:lport":8080},{"name":"e2","type":"fddi","example-augment:mandatory-leaf":"true","example-augment:other":"ietf-interfaces:fddi","example-augment:port":80,"example-augment:lport":8080},{"name":"e3","type":"fddi","example-augment:mandatory-leaf":"true","example-augment:me":"you","example-augment:port":80,"example-augment:lport":8080}\]}}'
|
||||
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces)" 0 "HTTP/1.1 200 OK" '{"ietf-interfaces:interfaces":{"interface":\[{"name":"e1","type":"example-augment:some-new-iftype","example-augment:mandatory-leaf":"true","example-augment:port":80,"example-augment:lport":8080},{"name":"e2","type":"example-augment:some-new-iftype","example-augment:mandatory-leaf":"true","example-augment:other":"ietf-interfaces:fddi","example-augment:port":80,"example-augment:lport":8080},{"name":"e3","type":"example-augment:some-new-iftype","example-augment:mandatory-leaf":"true","example-augment:me":"you","example-augment:port":80,"example-augment:lport":8080}\]}}'
|
||||
|
||||
new "restconf get augment xml"
|
||||
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces)" 0 'HTTP/1.1 200 OK
' '<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface xmlns:mymod="urn:example:augment"><name>e1</name><type>mymod:some-new-iftype</type><mymod:mandatory-leaf>true</mymod:mandatory-leaf><mymod:port>80</mymod:port><mymod:lport>8080</mymod:lport></interface><interface><name>e2</name><type>fddi</type><mymod:mandatory-leaf xmlns:mymod="urn:example:augment">true</mymod:mandatory-leaf><mymod:other xmlns:mymod="urn:example:augment">if:fddi</mymod:other><mymod:port xmlns:mymod="urn:example:augment">80</mymod:port><mymod:lport xmlns:mymod="urn:example:augment">8080</mymod:lport></interface><interface><name>e3</name><type>fddi</type><mymod:mandatory-leaf xmlns:mymod="urn:example:augment">true</mymod:mandatory-leaf><mymod:me xmlns:mymod="urn:example:augment">mymod:you</mymod:me><mymod:port xmlns:mymod="urn:example:augment">80</mymod:port><mymod:lport xmlns:mymod="urn:example:augment">8080</mymod:lport></interface></interfaces>'
|
||||
expectpart "$(curl $CURLOPTS -X GET -H 'Accept: application/yang-data+xml' $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces)" 0 'HTTP/1.1 200 OK
' '<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces">
|
||||
<interface xmlns:mymod="urn:example:augment"><name>e1</name><type>mymod:some-new-iftype</type><mymod:mandatory-leaf>true</mymod:mandatory-leaf><mymod:port>80</mymod:port><mymod:lport>8080</mymod:lport></interface><interface xmlns:mymod="urn:example:augment"><name>e2</name><type>mymod:some-new-iftype</type><mymod:mandatory-leaf>true</mymod:mandatory-leaf><mymod:other>if:fddi</mymod:other><mymod:port>80</mymod:port><mymod:lport>8080</mymod:lport></interface><interface xmlns:mymod="urn:example:augment"><name>e3</name><type>mymod:some-new-iftype</type><mymod:mandatory-leaf>true</mymod:mandatory-leaf><mymod:me>mymod:you</mymod:me><mymod:port>80</mymod:port><mymod:lport>8080</mymod:lport></interface></interfaces>'
|
||||
|
||||
#<interface xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>e1</name><ospf xmlns="urn:example:augment"><reference-bandwidth>23</reference-bandwidth></ospf></interface>'
|
||||
|
||||
|
|
@ -243,7 +244,7 @@ new "restconf POST augment multi-namespace path e2 (middle path)"
|
|||
expectpart "$(curl $CURLOPTS -X POST -H 'Content-Type: application/yang-data+xml' $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces/interface=e2 -d "$XML" )" 0 "HTTP/1.1 201 Created"
|
||||
|
||||
new "restconf GET augment multi-namespace top"
|
||||
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces)" 0 'HTTP/1.1 200 OK' '{"ietf-interfaces:interfaces":{"interface":\[{"name":"e1","type":"example-augment:some-new-iftype","example-augment:ospf":{"reference-bandwidth":23},"example-augment:mandatory-leaf":"true","example-augment:port":80,"example-augment:lport":8080},{"name":"e2","type":"fddi","example-augment:ospf":{"reference-bandwidth":23},"example-augment:mandatory-leaf":"true","example-augment:other":"ietf-interfaces:fddi","example-augment:port":80,"example-augment:lport":8080},{"name":"e3","type":"fddi","example-augment:mandatory-leaf":"true","example-augment:me":"you","example-augment:port":80,"example-augment:lport":8080}\]}}'
|
||||
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces)" 0 'HTTP/1.1 200 OK' '{"ietf-interfaces:interfaces":{"interface":\[{"name":"e1","type":"example-augment:some-new-iftype","example-augment:ospf":{"reference-bandwidth":23},"example-augment:mandatory-leaf":"true","example-augment:port":80,"example-augment:lport":8080},{"name":"e2","type":"example-augment:some-new-iftype","example-augment:ospf":{"reference-bandwidth":23},"example-augment:mandatory-leaf":"true","example-augment:other":"ietf-interfaces:fddi","example-augment:port":80,"example-augment:lport":8080},{"name":"e3","type":"example-augment:some-new-iftype","example-augment:mandatory-leaf":"true","example-augment:me":"you","example-augment:port":80,"example-augment:lport":8080}\]}}'
|
||||
|
||||
new "restconf GET augment multi-namespace level 1"
|
||||
expectpart "$(curl $CURLOPTS -X GET $RCPROTO://localhost/restconf/data/ietf-interfaces:interfaces/interface=e1)" 0 'HTTP/1.1 200 OK' '{"ietf-interfaces:interface":\[{"name":"e1","type":"example-augment:some-new-iftype","example-augment:ospf":{"reference-bandwidth":23},"example-augment:mandatory-leaf":"true","example-augment:port":80,"example-augment:lport":8080}\]}'
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ new "when get config"
|
|||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><get-config><source><candidate/></source></get-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><data><whenex xmlns=\"urn:example:clixon\"><type>direct</type><name>r2</name><static-routes/></whenex><whenex xmlns=\"urn:example:clixon\"><type>static</type><name>r1</name><static-routes/></whenex></data></rpc-reply>]]>]]>$"
|
||||
|
||||
new "when: validate fail"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>when xpath validation failed</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Failed WHEN condition of static-routes in module example</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
|
||||
new "when: discard-changes"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><discard-changes/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ new "xpath ../../../rt:address-family = 'v6ur:ipv6-unicast'"
|
|||
expecteof "$clixon_util_xpath -f $xml2 -i /aaa/bbb/here2/here" 0 "../../../rt:address-family = 'v6ur:ipv6-unicast'" "^bool:true$"
|
||||
|
||||
new "xpath /if:interfaces/if:interface[if:name=current()/rt:name]/ip:ipv6/ip:enabled='true'"
|
||||
expecteof "$clixon_util_xpath -f $xml2" 0 "/if:interfaces/if:interface[if:name=current()/rt:name]/ip:ipv6/ip:enabled='true'" "^bool:true$"
|
||||
expecteof "$clixon_util_xpath -D 1 -f $xml2" 0 "/if:interfaces/if:interface[if:name=current()/rt:name]/ip:ipv6/ip:enabled='true'" "^bool:true$"
|
||||
|
||||
new "xpath rt:address-family='v6ur:ipv6-unicast'"
|
||||
expecteof "$clixon_util_xpath -f $xml2 -i /aaa" 0 "rt:address-family='v6ur:ipv6-unicast'" "^bool:true$"
|
||||
|
|
@ -174,7 +174,6 @@ new "xpath .[name='bar']"
|
|||
expecteof "$clixon_util_xpath -f $xml2 -p .[name='bar'] -i /aaa/bbb/routing/ribs/rib" 0 "" "^nodeset:0:<rib><name>bar</name><address-family>myfamily</address-family></rib>$"
|
||||
|
||||
new "xpath /aaa/bbb/namespace (namespace is xpath axisname)"
|
||||
echo "$clixon_util_xpath -f $xml2 -p /aaa/bbb/namespace"
|
||||
expecteof "$clixon_util_xpath -f $xml2 -p /aaa/bbb/namespace" 0 "" "^nodeset:0:<namespace>urn:example:foo</namespace>$"
|
||||
|
||||
# See https://github.com/clicon/clixon/issues/54
|
||||
|
|
@ -202,8 +201,17 @@ new "xpath bbb[ccc='fie']"
|
|||
expecteof "$clixon_util_xpath -f $xml3 -p bbb[ccc='fie']" 0 "" "^nodeset:$"
|
||||
|
||||
# Just syntax - no semantic meaning
|
||||
new "xpath derived-from 10.4.1"
|
||||
expectpart "$($clixon_util_xpath -f $xml3 -p 'derived-from(../../change-operation,"modify")')" 0 "bool:false"
|
||||
|
||||
new "xpath derived-from-or-self"
|
||||
expecteof "$clixon_util_xpath -f $xml3 -p 'derived-from-or-self(../../change-operation,modify)'" 0 "" "derived-from-or-self"
|
||||
expectpart "$($clixon_util_xpath -f $xml3 -p 'derived-from-or-self(../../change-operation,"modify")')" 0 "bool:false"
|
||||
|
||||
new "xpath contains"
|
||||
expectpart "$($clixon_util_xpath -f $xml3 -p "contains(../../objectClass,'BTSFunction') or contains(../../objectClass,'RNCFunction')")" 0 "bool:false"
|
||||
|
||||
# Just syntax - no semantic meaning
|
||||
|
||||
|
||||
rm -rf $dir
|
||||
|
||||
|
|
|
|||
188
test/test_xpath_functions.sh
Executable file
188
test/test_xpath_functions.sh
Executable file
|
|
@ -0,0 +1,188 @@
|
|||
#!/usr/bin/env bash
|
||||
# From both:
|
||||
# XPATH base https://www.w3.org/TR/xpath-10/
|
||||
# YANG XPATH functions: https://tools.ietf.org/html/rfc7950
|
||||
# Tests:
|
||||
# - Contains
|
||||
|
||||
# Magic line must be first in script (see README.md)
|
||||
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||
|
||||
APPNAME=example
|
||||
|
||||
cfg=$dir/conf_yang.xml
|
||||
fyang=$dir/$APPNAME.yang
|
||||
|
||||
cat <<EOF > $cfg
|
||||
<clixon-config xmlns="http://clicon.org/config">
|
||||
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
|
||||
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_DIR>$dir</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
|
||||
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
|
||||
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
||||
<CLICON_NETCONF_DIR>/usr/local/lib/$APPNAME/netconf</CLICON_NETCONF_DIR>
|
||||
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
|
||||
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
||||
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
|
||||
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
||||
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||
<CLICON_MODULE_LIBRARY_RFC7895>false</CLICON_MODULE_LIBRARY_RFC7895>
|
||||
</clixon-config>
|
||||
EOF
|
||||
|
||||
cat <<EOF > $fyang
|
||||
module $APPNAME{
|
||||
yang-version 1.1;
|
||||
prefix ex;
|
||||
namespace "urn:example:clixon";
|
||||
identity interface-type;
|
||||
|
||||
identity atm {
|
||||
base interface-type;
|
||||
}
|
||||
identity ethernet {
|
||||
base interface-type;
|
||||
}
|
||||
identity fast-ethernet {
|
||||
base ethernet;
|
||||
}
|
||||
identity gigabit-ethernet {
|
||||
base ethernet;
|
||||
}
|
||||
|
||||
container top{
|
||||
leaf class { /* contains */
|
||||
type string;
|
||||
}
|
||||
list mylist{ /* contains */
|
||||
key id;
|
||||
leaf id {
|
||||
type string;
|
||||
}
|
||||
leaf site {
|
||||
/* If the XPath expression is defined in a substatement to a data
|
||||
* node that represents configuration, the accessible tree is the
|
||||
* data in the datastore where the context node exists.
|
||||
* The "when" statement makes its parent data definition statement conditional.
|
||||
*/
|
||||
when "contains(../../class,'foo') or contains(../../class,'bar')";
|
||||
type int32;
|
||||
}
|
||||
}
|
||||
}
|
||||
list interface { /* derived-from */
|
||||
key name;
|
||||
leaf name{
|
||||
type string;
|
||||
}
|
||||
leaf type {
|
||||
type identityref {
|
||||
base interface-type;
|
||||
}
|
||||
}
|
||||
}
|
||||
augment "/ex:interface" {
|
||||
when 'derived-from(type, "ex:ethernet")';
|
||||
leaf mtu {
|
||||
type uint32;
|
||||
}
|
||||
}
|
||||
augment "/ex:interface" {
|
||||
when 'derived-from-or-self(type, "ex:ethernet")';
|
||||
leaf crc {
|
||||
type uint32;
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
new "test params: -f $cfg"
|
||||
|
||||
if [ $BE -ne 0 ]; then
|
||||
new "kill old backend"
|
||||
sudo clixon_backend -zf $cfg
|
||||
if [ $? -ne 0 ]; then
|
||||
err
|
||||
fi
|
||||
new "start backend -s init -f $cfg"
|
||||
start_backend -s init -f $cfg
|
||||
|
||||
new "waiting"
|
||||
wait_backend
|
||||
fi
|
||||
|
||||
# contains
|
||||
new "contains: Set site to foo that validates site OK"
|
||||
expecteof "$clixon_netconf -qf $cfg -D $DBG" 0 "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><top xmlns=\"urn:example:clixon\"><class>foo</class><mylist><id>12</id><site>42</site></mylist></top></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>"
|
||||
|
||||
new "netconf validate OK"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "^<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "Set site to fie which invalidates the when contains"
|
||||
expecteof "$clixon_netconf -qf $cfg -D $DBG" 0 "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><top xmlns=\"urn:example:clixon\"><class>fie</class></top></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>"
|
||||
|
||||
new "netconf validate not OK"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "^<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Failed WHEN condition of site in module example</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf discard-changes"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><discard-changes/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
# derived-from
|
||||
new "derived-from: Set mtu to interface OK on GE"
|
||||
expecteof "$clixon_netconf -qf $cfg -D $DBG" 0 "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><interface xmlns=\"urn:example:clixon\"><name>e0</name><type>fast-ethernet</type><mtu>1500</mtu></interface></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>"
|
||||
|
||||
new "netconf validate OK"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "^<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "Change type to atm"
|
||||
expecteof "$clixon_netconf -qf $cfg -D $DBG" 0 "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><interface xmlns=\"urn:example:clixon\"><name>e0</name><type>atm</type></interface></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>"
|
||||
|
||||
new "netconf validate not OK (mtu not allowed)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "^<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Failed augmented WHEN condition of mtu in module example</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
|
||||
new "Change type to ethernet (self)"
|
||||
expecteof "$clixon_netconf -qf $cfg -D $DBG" 0 "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><interface xmlns=\"urn:example:clixon\"><name>e0</name><type>ethernet</type></interface></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>"
|
||||
|
||||
new "netconf validate not OK (mtu not allowed on self)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "^<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Failed augmented WHEN condition of mtu in module example</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf discard-changes"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><discard-changes/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
# derived-from-or-self
|
||||
new "derived-from-or-self: Set crc to interface OK on GE"
|
||||
expecteof "$clixon_netconf -qf $cfg -D $DBG" 0 "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><interface xmlns=\"urn:example:clixon\"><name>e0</name><type>fast-ethernet</type><crc>42</crc></interface></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>"
|
||||
|
||||
new "netconf validate OK"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "^<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "Change type to atm"
|
||||
expecteof "$clixon_netconf -qf $cfg -D $DBG" 0 "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><interface xmlns=\"urn:example:clixon\"><name>e0</name><type>atm</type></interface></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>"
|
||||
|
||||
new "netconf validate not OK (crc not allowed)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "^<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><rpc-error><error-type>application</error-type><error-tag>operation-failed</error-tag><error-severity>error</error-severity><error-message>Failed augmented WHEN condition of crc in module example</error-message></rpc-error></rpc-reply>]]>]]>$"
|
||||
|
||||
new "Change type to ethernet (self)"
|
||||
expecteof "$clixon_netconf -qf $cfg -D $DBG" 0 "<rpc $DEFAULTNS><edit-config><target><candidate/></target><config><interface xmlns=\"urn:example:clixon\"><name>e0</name><type>ethernet</type></interface></config></edit-config></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>"
|
||||
|
||||
new "netconf validate OK (self)"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "^<rpc $DEFAULTNS><validate><source><candidate/></source></validate></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "netconf discard-changes"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc $DEFAULTNS><discard-changes/></rpc>]]>]]>" "^<rpc-reply $DEFAULTNS><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
if [ $BE -ne 0 ]; then
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
# kill backend
|
||||
stop_backend -f $cfg
|
||||
sudo pkill -u root -f clixon_backend
|
||||
fi
|
||||
|
||||
rm -rf $dir
|
||||
Loading…
Add table
Add a link
Reference in a new issue