Transform YANG when expressions to canonical xpath form
This commit is contained in:
parent
71e921520e
commit
26ca872b0c
8 changed files with 262 additions and 87 deletions
|
|
@ -149,7 +149,10 @@ cxobj *xpath_first(cxobj *xcur, cvec *nsc, const char *xpformat, ...) __attribu
|
|||
cxobj *xpath_first_localonly(cxobj *xcur, const char *xpformat, ...) __attribute__ ((format (printf, 2, 3)));
|
||||
int xpath_vec(cxobj *xcur, cvec *nsc, const char *xpformat, cxobj ***vec, size_t *veclen, ...) __attribute__ ((format (printf, 3, 6)));
|
||||
|
||||
int xpath2canonical(const char *xpath0, cvec *nsc0, yang_stmt *yspec, char **xpath1, cvec **nsc1, cbuf **cbreason);
|
||||
int xpath2canonical(const char *xpath0, cvec *nsc0, yang_stmt *yspec,
|
||||
char **xpath1, cvec **nsc1, cbuf **cbreason);
|
||||
int xpath2canonical1(const char *xpath0, cvec *nsc0, yang_stmt *yspec, int exprstr,
|
||||
char **xpath1, cvec **nsc1, cbuf **cbreason);
|
||||
int xpath_count(cxobj *xcur, cvec *nsc, const char *xpath, uint32_t *count);
|
||||
int xml2xpath(cxobj *x, cvec *nsc, int spec, int apostrophe, char **xpath);
|
||||
int xpath2xml(char *xpath, cvec *nsc, cxobj *xtop, yang_stmt *ytop,
|
||||
|
|
|
|||
|
|
@ -261,8 +261,8 @@ int yang_flag_set(yang_stmt *ys, uint16_t flag);
|
|||
int yang_flag_reset(yang_stmt *ys, uint16_t flag);
|
||||
yang_stmt *yang_when_get(clixon_handle h, yang_stmt *ys);
|
||||
int yang_when_set(clixon_handle h, yang_stmt *ys, yang_stmt *ywhen);
|
||||
char *yang_when_xpath_get(yang_stmt *ys);
|
||||
cvec *yang_when_nsc_get(yang_stmt *ys);
|
||||
int yang_when_xpath_get(yang_stmt *ys, char **xpath, cvec **nsc);
|
||||
int yang_when_canonical_xpath_get(yang_stmt *ys, char **xpath, cvec **nsc);
|
||||
const char *yang_filename_get(yang_stmt *ys);
|
||||
int yang_filename_set(yang_stmt *ys, const char *filename);
|
||||
uint32_t yang_linenum_get(yang_stmt *ys);
|
||||
|
|
|
|||
|
|
@ -281,56 +281,94 @@ check_body_namespace(cxobj *x0,
|
|||
* @retval 1 OK
|
||||
* @retval 0 Failed (cbret set)
|
||||
* @retval -1 Error
|
||||
* @note There may be some combination cases (x0+x1) that are not covered in this function.
|
||||
* XXX this code is a mess. It tries multiple methods, one after the other. The solution
|
||||
* is probably to make xpath evaluation namespace aware in combination with XML evaluation
|
||||
* (3+4)
|
||||
*/
|
||||
static int
|
||||
check_when_condition(cxobj *x0p,
|
||||
cxobj *x1,
|
||||
yang_stmt *y0,
|
||||
cbuf *cbret)
|
||||
check_when_condition(cxobj *x0p,
|
||||
cxobj *x1,
|
||||
yang_stmt *y0,
|
||||
cbuf *cbret)
|
||||
{
|
||||
int retval = -1;
|
||||
int retval = -1;
|
||||
char *xpath = NULL;
|
||||
cvec *nsc = NULL;
|
||||
int nr;
|
||||
yang_stmt *y = NULL;
|
||||
cbuf *cberr = NULL;
|
||||
cxobj *x1p;
|
||||
cvec *cnsc = NULL;
|
||||
cvec *nnsc = NULL;
|
||||
char *cxpath = NULL;
|
||||
|
||||
if ((y = y0) != NULL ||
|
||||
(y = (yang_stmt*)xml_spec(x1)) != NULL){
|
||||
if ((xpath = yang_when_xpath_get(y)) != NULL){
|
||||
nsc = yang_when_nsc_get(y);
|
||||
x1p = xml_parent(x1);
|
||||
if ((nr = xpath_vec_bool(x1p, nsc, "%s", xpath)) < 0) /* Try request */
|
||||
goto done;
|
||||
if (nr == 0){
|
||||
/* Try existing tree */
|
||||
if ((nr = xpath_vec_bool(x0p, nsc, "%s", xpath)) < 0)
|
||||
goto done;
|
||||
if (nr == 0){
|
||||
if ((cberr = cbuf_new()) == NULL){
|
||||
clixon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cberr, "Node '%s' tagged with 'when' condition '%s' in module '%s' evaluates to false in edit-config operation (see RFC 7950 Sec 8.3.2)",
|
||||
yang_argument_get(y),
|
||||
xpath,
|
||||
yang_argument_get(ys_module(y)));
|
||||
if (netconf_unknown_element(cbret, "application", yang_argument_get(y),
|
||||
cbuf_get(cberr)) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
x1p = xml_parent(x1);
|
||||
if (yang_when_xpath_get(y, &xpath, &nsc) < 0)
|
||||
goto done;
|
||||
if (xpath == NULL)
|
||||
goto ok;
|
||||
/* 1. Try yang context for existing xml
|
||||
* Sufficient for all clixon/controller tests.
|
||||
* Required for test_augment */
|
||||
if ((nr = xpath_vec_bool(x0p, nsc, "%s", xpath)) < 0)
|
||||
goto done;
|
||||
if (nr != 0)
|
||||
goto ok;
|
||||
if (xml_nsctx_node(x1p, &nnsc) < 0)
|
||||
goto done;
|
||||
/* 2. Try yang context for incoming xml */
|
||||
if ((nr = xpath_vec_bool(x1p, nsc, "%s", xpath)) < 0)
|
||||
goto done;
|
||||
if (nr != 0)
|
||||
goto ok;
|
||||
/* 3. Try xml context for incoming xml */
|
||||
if ((nr = xpath_vec_bool(x1p, nnsc, "%s", xpath)) < 0) /* Try request */
|
||||
goto done;
|
||||
if (nr != 0)
|
||||
goto ok;
|
||||
/* 4. Try xml context for existing xml */
|
||||
if ((nr = xpath_vec_bool(x0p, nnsc, "%s", xpath)) < 0) /* Try request */
|
||||
goto done;
|
||||
if (nr != 0)
|
||||
goto ok;
|
||||
if (yang_when_canonical_xpath_get(y, &cxpath, &cnsc) < 0)
|
||||
goto done;
|
||||
/* 5. Try yang canonical context for incoming xml */
|
||||
if ((nr = xpath_vec_bool(x1p, cnsc, "%s", cxpath)) < 0)
|
||||
goto done;
|
||||
if (nr != 0)
|
||||
goto ok;
|
||||
/* 6. Try yang canonical context for existing xml */
|
||||
if ((nr = xpath_vec_bool(x0p, cnsc, "%s", cxpath)) < 0)
|
||||
goto done;
|
||||
if (nr != 0)
|
||||
goto ok;
|
||||
if ((cberr = cbuf_new()) == NULL){
|
||||
clixon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cberr, "Node '%s' tagged with 'when' condition '%s' in module '%s' evaluates to false in edit-config operation (see RFC 7950 Sec 8.3.2)",
|
||||
yang_argument_get(y),
|
||||
xpath,
|
||||
yang_argument_get(ys_module(y)));
|
||||
if (netconf_unknown_element(cbret, "application", yang_argument_get(y),
|
||||
cbuf_get(cberr)) < 0)
|
||||
goto done;
|
||||
goto fail;
|
||||
}
|
||||
ok:
|
||||
retval = 1;
|
||||
done:
|
||||
if (nsc)
|
||||
cvec_free(nsc);
|
||||
if (cberr)
|
||||
cbuf_free(cberr);
|
||||
if (cxpath)
|
||||
free(cxpath);
|
||||
if (nnsc)
|
||||
cvec_free(nnsc);
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
|
|
@ -466,7 +504,6 @@ choice_other_match(cxobj *x0,
|
|||
goto done;
|
||||
}
|
||||
|
||||
|
||||
/*! Modify a base tree x0 with x1 with yang spec y according to operation op
|
||||
*
|
||||
* @param[in] h Clixon handle
|
||||
|
|
|
|||
|
|
@ -2091,9 +2091,10 @@ yang_check_when_xpath(cxobj *xn,
|
|||
int xmalloc = 0; /* ugly help variable to clean temporary object */
|
||||
|
||||
/* First variant */
|
||||
if ((xpath = yang_when_xpath_get(yn)) != NULL){
|
||||
if (yang_when_canonical_xpath_get(yn, &xpath, &nsc) < 0)
|
||||
goto done;
|
||||
if (xpath != NULL){
|
||||
x = xp;
|
||||
nsc = yang_when_nsc_get(yn);
|
||||
*hit = 1;
|
||||
}
|
||||
/* Second variant */
|
||||
|
|
|
|||
|
|
@ -974,21 +974,78 @@ xpath_vec_bool(cxobj *xcur,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Translate literal string to "canonical" form
|
||||
*
|
||||
* the prefix according to actual namespace.
|
||||
* Actually it depends on context if the rewrite should be made.
|
||||
*/
|
||||
static int
|
||||
traverse_canonical_str(xpath_tree *xs,
|
||||
yang_stmt *yspec,
|
||||
cvec *nsc0,
|
||||
cvec *nsc1)
|
||||
{
|
||||
int retval = -1;
|
||||
char *name = NULL;
|
||||
char *prefix0 = NULL;
|
||||
char *prefix1 = NULL;
|
||||
char *namespace;
|
||||
yang_stmt *ymod;
|
||||
cbuf *cb = NULL;
|
||||
|
||||
if (nodeid_split(xs->xs_s0, &prefix0, &name) < 0)
|
||||
goto done;
|
||||
if (prefix0 != NULL && name != NULL &&
|
||||
(namespace = xml_nsctx_get(nsc0, prefix0)) != NULL) {
|
||||
if ((ymod = yang_find_module_by_namespace(yspec, namespace)) != NULL &&
|
||||
(prefix1 = yang_find_myprefix(ymod)) != NULL){
|
||||
if (xml_nsctx_get(nsc1, prefix1) == NULL)
|
||||
if (xml_nsctx_add(nsc1, prefix1, namespace) < 0)
|
||||
goto done;
|
||||
free(xs->xs_s0);
|
||||
xs->xs_s0 = NULL;
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clixon_err(OE_UNIX, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
cprintf(cb, "%s:%s", prefix1, name);
|
||||
if ((xs->xs_s0 = strdup(cbuf_get(cb))) == NULL){
|
||||
clixon_err(OE_UNIX, errno, "strdup");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (name)
|
||||
free(name);
|
||||
if (prefix0)
|
||||
free(prefix0);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Translate an xpath/nsc pair to a "canonical" form using yang prefixes
|
||||
*
|
||||
* Returns new namespace context and rewrites the xpath tree
|
||||
* @param[in] xs Parsed xpath - xpath_tree
|
||||
* @param[in] yspec Yang spec containing all modules, associated with namespaces
|
||||
* @param[in] nsc0 Input namespace context
|
||||
* @param[in] exprstr Interpret strings as <prefix>:<name> (primaryexpr/literal/string)
|
||||
* @param[out] nsc1 Output namespace context. Free after use with xml_nsctx_free
|
||||
* @param[out] reason Error reason if result is 0 - failed
|
||||
* @retval 1 OK with nsc1 containing the transformed nsc
|
||||
* @retval 0 XPath failure with reason set to why
|
||||
* @retval -1 Fatal Error
|
||||
* @note Setting str to 1 requires a knowledge of the context of the xpath, ie that strings are
|
||||
* something like identityref and is safe to translate into canonical form
|
||||
*/
|
||||
static int
|
||||
xpath_traverse_canonical(xpath_tree *xs,
|
||||
yang_stmt *yspec,
|
||||
cvec *nsc0,
|
||||
int exprstr,
|
||||
cvec *nsc1,
|
||||
cbuf **reason)
|
||||
{
|
||||
|
|
@ -999,9 +1056,13 @@ xpath_traverse_canonical(xpath_tree *xs,
|
|||
yang_stmt *ymod;
|
||||
cbuf *cb = NULL;
|
||||
int ret;
|
||||
// char *name;
|
||||
|
||||
switch (xs->xs_type){
|
||||
case XP_PRIME_STR:
|
||||
if (exprstr != 0)
|
||||
if (traverse_canonical_str(xs, yspec, nsc0, nsc1) < 0)
|
||||
goto done;
|
||||
break;
|
||||
case XP_NODE: /* s0 is namespace prefix, s1 is name */
|
||||
/* Nodetest = * needs no prefix */
|
||||
if (xs->xs_s1 && strcmp(xs->xs_s1, "*") == 0)
|
||||
|
|
@ -1061,13 +1122,13 @@ xpath_traverse_canonical(xpath_tree *xs,
|
|||
break;
|
||||
}
|
||||
if (xs->xs_c0){
|
||||
if ((ret = xpath_traverse_canonical(xs->xs_c0, yspec, nsc0, nsc1, reason)) < 0)
|
||||
if ((ret = xpath_traverse_canonical(xs->xs_c0, yspec, nsc0, exprstr, nsc1, reason)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
}
|
||||
if (xs->xs_c1){
|
||||
if ((ret = xpath_traverse_canonical(xs->xs_c1, yspec, nsc0, nsc1, reason)) < 0)
|
||||
if ((ret = xpath_traverse_canonical(xs->xs_c1, yspec, nsc0, exprstr, nsc1, reason)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -1085,8 +1146,10 @@ xpath_traverse_canonical(xpath_tree *xs,
|
|||
* @param[in] xpath0 Input xpath
|
||||
* @param[in] nsc0 Input namespace context
|
||||
* @param[in] yspec Yang spec containing all modules, associated with namespaces
|
||||
* @param[in] exprstr Interpret strings as <prefix>:<name> (primaryexpr/literal/string)
|
||||
* @param[out] xpath1 Output xpath. Free after use with free
|
||||
* @param[out] nsc1 Output namespace context. Free after use with xml_nsctx_free
|
||||
* @param[out] cbreason reason if retval = 0
|
||||
* @retval 1 OK, xpath1 and nsc1 allocated
|
||||
* @retval 0 XPath failure with reason set to why
|
||||
* @retval -1 Fatal Error
|
||||
|
|
@ -1116,12 +1179,13 @@ xpath_traverse_canonical(xpath_tree *xs,
|
|||
* @see xpath2xml
|
||||
*/
|
||||
int
|
||||
xpath2canonical(const char *xpath0,
|
||||
cvec *nsc0,
|
||||
yang_stmt *yspec,
|
||||
char **xpath1,
|
||||
cvec **nsc1p,
|
||||
cbuf **cbreason)
|
||||
xpath2canonical1(const char *xpath0,
|
||||
cvec *nsc0,
|
||||
yang_stmt *yspec,
|
||||
int exprstr,
|
||||
char **xpath1,
|
||||
cvec **nsc1p,
|
||||
cbuf **cbreason)
|
||||
{
|
||||
int retval = -1;
|
||||
xpath_tree *xpt = NULL;
|
||||
|
|
@ -1133,13 +1197,14 @@ xpath2canonical(const char *xpath0,
|
|||
/* Parse input xpath into an xpath-tree */
|
||||
if (xpath_parse(xpath0, &xpt) < 0)
|
||||
goto done;
|
||||
// xpath_tree_print(stderr, xpt);
|
||||
/* Create new nsc */
|
||||
if ((nsc1 = xml_nsctx_init(NULL, NULL)) == NULL)
|
||||
goto done;
|
||||
/* Traverse tree to find prefixes, transform them to canonical form and
|
||||
* create a canonical network namespace
|
||||
*/
|
||||
if ((ret = xpath_traverse_canonical(xpt, yspec, nsc0, nsc1, cbreason)) < 0)
|
||||
if ((ret = xpath_traverse_canonical(xpt, yspec, nsc0, exprstr, nsc1, cbreason)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
goto fail;
|
||||
|
|
@ -1174,6 +1239,17 @@ xpath2canonical(const char *xpath0,
|
|||
goto done;
|
||||
}
|
||||
|
||||
int
|
||||
xpath2canonical(const char *xpath0,
|
||||
cvec *nsc0,
|
||||
yang_stmt *yspec,
|
||||
char **xpath1,
|
||||
cvec **nsc1p,
|
||||
cbuf **cbreason)
|
||||
{
|
||||
return xpath2canonical1(xpath0, nsc0, yspec, 0, xpath1, nsc1p, cbreason);
|
||||
}
|
||||
|
||||
/*! Return a count(xpath)
|
||||
*
|
||||
* @param[in] xcur xml-tree where to search
|
||||
|
|
|
|||
|
|
@ -77,6 +77,8 @@
|
|||
#include "clixon_log.h"
|
||||
#include "clixon_debug.h"
|
||||
#include "clixon_xml_nsctx.h"
|
||||
#include "clixon_xpath_ctx.h"
|
||||
#include "clixon_xpath.h"
|
||||
#include "clixon_yang_module.h"
|
||||
#include "clixon_plugin.h"
|
||||
#include "clixon_data.h"
|
||||
|
|
@ -524,45 +526,68 @@ yang_when_set(clixon_handle h,
|
|||
return retval;
|
||||
}
|
||||
|
||||
/*! Get yang xpath for "when"-associated augment
|
||||
/*! Get 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)
|
||||
* Inserts 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
|
||||
* @note xpath context is PARENT which is different from when actual when child which is
|
||||
* child itself
|
||||
* @param[out] xpath
|
||||
* @param[out] nsc
|
||||
*/
|
||||
char*
|
||||
yang_when_xpath_get(yang_stmt *ys)
|
||||
int
|
||||
yang_when_xpath_get(yang_stmt *ys,
|
||||
char **xpath,
|
||||
cvec **nsc)
|
||||
{
|
||||
yang_stmt *ywhen;
|
||||
|
||||
if ((ywhen = yang_when_get(NULL, ys)) != NULL)
|
||||
return yang_argument_get(ywhen);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*! 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 (caller frees with cvec_free)
|
||||
* @note retval is direct pointer, may need to be copied
|
||||
*/
|
||||
cvec *
|
||||
yang_when_nsc_get(yang_stmt *ys)
|
||||
{
|
||||
yang_stmt *ywhen;
|
||||
cvec *wnsc = NULL;
|
||||
int retval = -1;
|
||||
yang_stmt *ywhen;
|
||||
cvec *nsc0 = NULL;
|
||||
|
||||
if ((ywhen = yang_when_get(NULL, ys)) != NULL) {
|
||||
if (xml_nsctx_yang(ywhen, &wnsc) < 0)
|
||||
wnsc = NULL;
|
||||
if (xml_nsctx_yang(ywhen, nsc) < 0)
|
||||
goto done;
|
||||
if (xpath)
|
||||
*xpath = yang_argument_get(ywhen);
|
||||
}
|
||||
return wnsc;
|
||||
retval = 0;
|
||||
done:
|
||||
if (nsc0)
|
||||
cvec_free(nsc0);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Get canonical xpath and namespace context for "when"-associated augment
|
||||
*
|
||||
* Ie, for yang structures like: augment <path> { when <xpath>; ... }
|
||||
* Inserts new yang nodes at <path> with this special "when" struct (not yang node)
|
||||
* @param[in] ys Yang statement
|
||||
* @param[out] xpath
|
||||
* @param[out] nsc
|
||||
*/
|
||||
int
|
||||
yang_when_canonical_xpath_get(yang_stmt *ys,
|
||||
char **xpath,
|
||||
cvec **nsc)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *ywhen;
|
||||
char *xpath0;
|
||||
cvec *nsc0 = NULL;
|
||||
|
||||
if ((ywhen = yang_when_get(NULL, ys)) != NULL) {
|
||||
if (xml_nsctx_yang(ywhen, &nsc0) < 0)
|
||||
goto done;
|
||||
xpath0 = yang_argument_get(ywhen);
|
||||
if (xpath0 && nsc0){
|
||||
if (xpath2canonical1(xpath0, nsc0, ys_spec(ys), 1, xpath, nsc, NULL) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (nsc0)
|
||||
cvec_free(nsc0);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*! Get yang filename for error/debug purpose (only modules)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue