Added xpath support for predicate: current(); added by-reference return for schema_nodeid functions; prototype enum2int function

This commit is contained in:
Olof hagsand 2017-07-30 11:00:02 +02:00
parent 89cfe2a9b3
commit d0660bf611
9 changed files with 368 additions and 208 deletions

View file

@ -86,6 +86,7 @@ If you submit "nopresence" without a leaf, it will automatically be removed:
* You need to define state data in a backend callback. See the example and documentation for more details.
### Minor changes:
* Added xpath support for predicate: current(), eg /interface[name=current()/../name]
* Added prefix parsing of xpath, allowing eg /p:x/p:y, but prefix ignored.
* Corrected Yang union CLI generation and type validation. Recursive unions did not work.
* Corrected Yang pattern type escaping problem, ie '\.' did not work properly. This requires update of cligen as well.

View file

@ -75,5 +75,6 @@ int api_path2xpath_cvv(yang_spec *yspec, cvec *cvv, int offset, cbuf *xpath);
int api_path2xpath(yang_spec *yspec, char *api_path, cbuf *xpath);
int api_path2xml(char *api_path, yang_spec *yspec, cxobj *xtop, cxobj **xpathp, yang_node **ypathp);
int xml_merge(cxobj *x0, cxobj *x1, yang_spec *yspec);
int yang_enum_int_value(cxobj *node, int32_t *val);
#endif /* _CLIXON_XML_MAP_H_ */

View file

@ -213,9 +213,10 @@ int yang_parse(clicon_handle h, const char *yang_dir,
const char *module, const char *revision, yang_spec *ysp);
int yang_apply(yang_node *yn, enum rfc_6020 key, yang_applyfn_t fn,
void *arg);
yang_node *yang_abs_schema_nodeid(yang_node *yn, char *schema_nodeid);
yang_node *yang_desc_schema_nodeid(yang_node *yn, char *schema_nodeid);
yang_node *yang_schema_nodeid(yang_node *yn, char *xpath);
int yang_abs_schema_nodeid(yang_spec *yspec, char *schema_nodeid,
yang_stmt **yres);
int yang_desc_schema_nodeid(yang_node *yn, char *schema_nodeid,
yang_stmt **yres);
cg_var *ys_parse(yang_stmt *ys, enum cv_type cvtype);
int ys_parse_sub(yang_stmt *ys);
int yang_mandatory(yang_stmt *ys);

View file

@ -1999,3 +1999,54 @@ xml_merge(cxobj *x0,
return retval;
}
/*! Get integer value from xml node from yang enumeration
* @param[in] node XML node in a tree
* @param[out] val Integer value returned
* @retval 0 OK, value parsed
* @retval -1 Error, value not obtained or parsed, no reason given
* @note assume yang spec set
* @note The function only returns assigned values, but according to RFC:
If a value is not specified, then one will be automatically assigned.
If the "enum" substatement is the first one defined, the assigned
value is zero (0); otherwise, the assigned value is one greater than
the current highest enum value (i.e., the highest enum value,
* Thanks: Matthew Smith
*/
int
yang_enum_int_value(cxobj *node,
int32_t *val)
{
int retval = -1;
yang_spec *yspec;
yang_stmt *ys;
yang_stmt *ytype;
yang_stmt *yrestype; /* resolved type */
yang_stmt *yenum;
yang_stmt *yval;
char *reason = NULL;
if (node == NULL)
goto done;
if ((ys = (yang_stmt *) xml_spec(node)) == NULL)
goto done;
if ((yspec = ys_spec(ys)) == NULL)
goto done;
if ((ytype = yang_find((yang_node *)ys, Y_TYPE, NULL)) == NULL)
goto done;
if (yang_type_resolve(ys, ytype, &yrestype,
NULL, NULL, NULL, NULL, NULL) < 0)
goto done;
if (yrestype==NULL || strcmp(yrestype->ys_argument, "enumeration"))
goto done;
if ((yenum = yang_find((yang_node *)yrestype, Y_ENUM, xml_body(node))) == NULL)
goto done;
/* Should assign value if yval not found */
if ((yval = yang_find((yang_node *)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)
goto done;
retval = 0;
done:
return retval;
}

View file

@ -37,7 +37,7 @@
* Look at the end of the file for a test unit program
*/
/*
https://www.w3.org/TR/xpath/
See https://www.w3.org/TR/xpath/
Implementation of a limited xslt xpath syntax. Some examples. Given the following
xml tree:
@ -172,6 +172,9 @@ xpath_print(FILE *f, struct xpath_element *xplist)
return 0;
}
/*! Extract PredicateExpr (Expr) from a Predicate within []
* @see xpath_expr For evaluation of predicate
*/
static int
xpath_parse_predicate(struct xpath_element *xe,
char *pred)
@ -439,7 +442,13 @@ recursive_find(cxobj *xn,
return retval;
}
/* forward */
static int
xpath_exec(cxobj *xcur, char *xpath, cxobj **vec0, size_t vec0len,
uint16_t flags, cxobj ***vec2, size_t *vec2len);
/*! XPath predicate expression check
* @param[in] xcur xml-tree where to search
* @param[in] predicate_expression xpath expression as a string
* @param[in] flags Extra xml flag checks that must match (apart from predicate)
* @param[in,out] vec0 Vector or xml nodes that are checked. Not matched are filtered
@ -454,7 +463,8 @@ recursive_find(cxobj *xn,
* @see https://www.w3.org/TR/xpath/#predicates
*/
static int
xpath_expr(char *predicate_expression,
xpath_expr(cxobj *xcur,
char *predicate_expression,
uint16_t flags,
cxobj ***vec0,
size_t *vec0len)
@ -462,6 +472,7 @@ xpath_expr(char *predicate_expression,
char *e_a;
char *e_v;
int i;
int j;
int retval = -1;
cxobj *x;
cxobj *xv;
@ -520,25 +531,72 @@ xpath_expr(char *predicate_expression,
goto done;
}
}
else{
else{ /* name = expr */
if ((tag = strsep(&e, "=")) == NULL){
clicon_err(OE_XML, errno, "%s: malformed expression: [%s]",
__FUNCTION__, e);
goto done;
}
for (i=0; i<*vec0len; i++){
xv = (*vec0)[i];
/* Check if more may match,... */
x = NULL;
while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) {
if (strcmp(tag, xml_name(x)) != 0)
continue;
if ((val = xml_body(x)) != NULL &&
strcmp(val, e) == 0){
clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xv, flags));
if (flags==0x0 || xml_flag(xv, flags))
if (cxvec_append(xv, &vec, &veclen) < 0)
goto done;
/* Strip trailing spaces */
while (tag[strlen(tag)-1] == ' ')
tag[strlen(tag)-1] = '\0';
/* Strip heading spaces */
while (e[0]==' ')
e++;
if (strncmp(e, "current()", strlen("current()")) == 0){
/* name = current()xpath */
cxobj **svec0 = NULL;
size_t svec0len = 0;
cxobj **svec1 = NULL;
size_t svec1len = 0;
char *ebody;
e += strlen("current("); /* e is path expression */
*e = '.';
if ((svec0 = calloc(1, sizeof(cxobj *))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
svec0[0] = xcur;
svec0len++;
/* Recursive invocation */
if (xpath_exec(xcur, e, svec0, svec0len,
flags, &svec1, &svec1len) < 0)
goto done;
for (j=0; j<svec1len; j++){
ebody = xml_body(svec1[j]);
for (i=0; i<*vec0len; i++){
xv = (*vec0)[i];
x = NULL;
while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) {
if (strcmp(tag, xml_name(x)) != 0)
continue;
if ((val = xml_body(x)) != NULL &&
strcmp(val, ebody) == 0){
clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xv, flags));
if (flags==0x0 || xml_flag(xv, flags))
if (cxvec_append(xv, &vec, &veclen) < 0)
goto done;
}
}
}
}
}
else { /* name = value */
for (i=0; i<*vec0len; i++){
xv = (*vec0)[i];
/* Check if more may match,... */
x = NULL;
while ((x = xml_child_each(xv, x, CX_ELMNT)) != NULL) {
if (strcmp(tag, xml_name(x)) != 0)
continue;
if ((val = xml_body(x)) != NULL &&
strcmp(val, e) == 0){
clicon_debug(2, "%s %x %x", __FUNCTION__, flags, xml_flag(xv, flags));
if (flags==0x0 || xml_flag(xv, flags))
if (cxvec_append(xv, &vec, &veclen) < 0)
goto done;
}
}
}
}
@ -556,6 +614,7 @@ xpath_expr(char *predicate_expression,
}
/*! Given vec0, add matches to vec1
* @param[in] xcur xml-tree where to search
* @param[in] xe XPATH in structured (parsed) form
* @param[in] descendants0
* @param[in] vec0 vector of XML trees
@ -565,7 +624,8 @@ xpath_expr(char *predicate_expression,
* @param[out] vec2len Length of result vector.
*/
static int
xpath_find(struct xpath_element *xe,
xpath_find(cxobj *xcur,
struct xpath_element *xe,
int descendants0,
cxobj **vec0,
size_t vec0len,
@ -669,10 +729,10 @@ xpath_find(struct xpath_element *xe,
}
for (xp = xe->xe_predicate; xp; xp = xp->xp_next){
if (xpath_expr(xp->xp_expr, flags, &vec0, &vec0len) < 0)
if (xpath_expr(xcur, xp->xp_expr, flags, &vec0, &vec0len) < 0)
goto done;
}
if (xpath_find(xe->xe_next, descendants,
if (xpath_find(xcur, xe->xe_next, descendants,
vec0, vec0len, flags,
vec2, vec2len) < 0)
goto done;
@ -719,6 +779,7 @@ xpath_split(char *xpathstr,
}
/*! Process single xpath expression on xml tree
* @param[in] xcur xml-tree where to search
* @param[in] xpath string with XPATH syntax
* @param[in] vec0 vector of XML trees
* @param[in] vec0len length of XML trees
@ -727,7 +788,8 @@ xpath_split(char *xpathstr,
* @param[out] vec2len Length of result vector.
*/
static int
xpath_exec(char *xpath,
xpath_exec(cxobj *xcur,
char *xpath,
cxobj **vec0,
size_t vec0len,
uint16_t flags,
@ -744,7 +806,7 @@ xpath_exec(char *xpath,
goto done;
if (debug > 1)
xpath_print(stderr, xplist);
if (xpath_find(xplist, 0, vec1, vec1len, flags, vec2, vec2len) < 0)
if (xpath_find(xcur, xplist, 0, vec1, vec1len, flags, vec2, vec2len) < 0)
goto done;
if (xpath_free(xplist) < 0)
goto done;
@ -754,6 +816,11 @@ xpath_exec(char *xpath,
/*! Intermediate xpath function to handle 'conditional' cases.
* @param[in] xcur xml-tree where to search
* @param[in] xpath string with XPATH syntax
* @param[in] flags if != 0, only match xml nodes matching flags
* @param[in] vec1 vector of XML trees
* @param[in] vec1len length of XML trees
* For example: xpath = //a | //b.
* xpath_first+ splits xpath up in several subcalls
* (eg xpath=//a and xpath=//b) and collects the results.
@ -762,7 +829,7 @@ xpath_exec(char *xpath,
* Note, this could be 'folded' into xpath1 but I judged it too complex.
*/
static int
xpath_choice(cxobj *xtop,
xpath_choice(cxobj *xcur,
char *xpath0,
uint16_t flags,
cxobj ***vec1,
@ -776,7 +843,6 @@ xpath_choice(cxobj *xtop,
cxobj **vec0 = NULL;
size_t vec0len = 0;
if ((s0 = strdup(xpath0)) == NULL){
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
goto done;
@ -786,7 +852,7 @@ xpath_choice(cxobj *xtop,
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
vec0[0] = xtop;
vec0[0] = xcur;
vec0len++;
while (s1 != NULL){
s2 = strstr(s1, " | ");
@ -796,7 +862,7 @@ xpath_choice(cxobj *xtop,
}
xpath = s1;
s1 = s2;
if (xpath_exec(xpath, vec0, vec0len, flags, vec1, vec1len) < 0)
if (xpath_exec(xcur, xpath, vec0, vec0len, flags, vec1, vec1len) < 0)
goto done;
}
retval = 0;
@ -808,32 +874,35 @@ xpath_choice(cxobj *xtop,
return retval;
}
/*! Help function to xpath_first
*/
static cxobj *
xpath_first0(cxobj *cxtop,
char *xpath)
xpath_first0(cxobj *xcur,
char *xpath)
{
cxobj **vec0 = NULL;
size_t vec0len = 0;
cxobj **vec1 = NULL;
size_t vec1len = 0;
cxobj *xn = NULL;
if (xpath_choice(cxtop, xpath, 0, &vec0, &vec0len) < 0)
if (xpath_choice(xcur, xpath, 0, &vec1, &vec1len) < 0)
goto done;
if (vec0len)
xn = vec0[0];
if (vec1len)
xn = vec1[0];
else
xn = NULL;
done:
if (vec0)
free(vec0);
if (vec1)
free(vec1);
return xn;
}
/*! A restricted xpath function where the first matching entry is returned
* See xpath1() on details for subset.
* args:
* @param[in] cxtop xml-tree where to search
* @param[in] xcur xml-tree where to search
* @param[in] xpath string with XPATH syntax
* @retval xml-tree of first match, or NULL on error.
* @retval xml-tree of first match
* @retval NULL Error or not found
*
* @code
* cxobj *x;
@ -846,7 +915,7 @@ xpath_first0(cxobj *cxtop,
* @see also xpath_vec.
*/
cxobj *
xpath_first(cxobj *cxtop,
xpath_first(cxobj *xcur,
char *format,
...)
{
@ -871,7 +940,7 @@ xpath_first(cxobj *cxtop,
goto done;
}
va_end(ap);
retval = xpath_first0(cxtop, xpath);
retval = xpath_first0(xcur, xpath);
done:
if (xpath)
free(xpath);
@ -881,14 +950,14 @@ xpath_first(cxobj *cxtop,
/*! A restricted xpath iterator that loops over all matching entries. Dont use.
*
* See xpath1() on details for subset.
* @param[in] cxtop xml-tree where to search
* @param[in] xcur xml-tree where to search
* @param[in] xpath string with XPATH syntax
* @param[in] xprev iterator/result should be initiated to NULL
* @retval xml-tree of n:th match, or NULL on error.
*
* @code
* cxobj *x = NULL;
* while ((x = xpath_each(cxtop, "//symbol/foo", x)) != NULL) {
* while ((x = xpath_each(xcur, "//symbol/foo", x)) != NULL) {
* ...
* }
* @endcode
@ -899,33 +968,33 @@ xpath_first(cxobj *cxtop,
* NOTE: uses a static variable: consider replacing with xpath_vec() instead
*/
cxobj *
xpath_each(cxobj *cxtop,
xpath_each(cxobj *xcur,
char *xpath,
cxobj *xprev)
{
static cxobj **vec0 = NULL; /* XXX */
static size_t vec0len = 0;
static cxobj **vec1 = NULL; /* XXX */
static size_t vec1len = 0;
cxobj *xn = NULL;
int i;
if (xprev == NULL){
if (vec0) // XXX
free(vec0); // XXX
vec0len = 0;
if (xpath_choice(cxtop, xpath, 0, &vec0, &vec0len) < 0)
if (vec1) // XXX
free(vec1); // XXX
vec1len = 0;
if (xpath_choice(xcur, xpath, 0, &vec1, &vec1len) < 0)
goto done;
}
if (vec0len){
if (vec1len){
if (xprev==NULL)
xn = vec0[0];
xn = vec1[0];
else{
for (i=0; i<vec0len; i++)
if (vec0[i] == xprev)
for (i=0; i<vec1len; i++)
if (vec1[i] == xprev)
break;
if (i>=vec0len-1)
if (i>=vec1len-1)
xn = NULL;
else
xn = vec0[i+1];
xn = vec1[i+1];
}
}
else
@ -937,7 +1006,7 @@ xpath_each(cxobj *cxtop,
/*! A restricted xpath that returns a vector of matches
*
* See xpath1() on details for subset
. * @param[in] cxtop xml-tree where to search
. * @param[in] xcur xml-tree where to search
* @param[in] xpath string with XPATH syntax
* @param[out] vec vector of xml-trees. Vector must be free():d after use
* @param[out] veclen returns length of vector in return value
@ -947,7 +1016,7 @@ xpath_each(cxobj *cxtop,
* @code
* cxobj **xvec;
* size_t xlen;
* if (xpath_vec(cxtop, "//symbol/foo", &xvec, &xlen) < 0)
* if (xpath_vec(xcur, "//symbol/foo", &xvec, &xlen) < 0)
* goto err;
* for (i=0; i<xlen; i++){
* xn = xvec[i];
@ -960,7 +1029,7 @@ xpath_each(cxobj *cxtop,
* @see also xpath_first, xpath_each.
*/
int
xpath_vec(cxobj *cxtop,
xpath_vec(cxobj *xcur,
char *format,
cxobj ***vec,
size_t *veclen,
@ -989,7 +1058,7 @@ xpath_vec(cxobj *cxtop,
va_end(ap);
*vec = NULL;
*veclen = 0;
retval = xpath_choice(cxtop, xpath, 0x0, vec, veclen);
retval = xpath_choice(xcur, xpath, 0x0, vec, veclen);
done:
if (xpath)
free(xpath);
@ -997,7 +1066,7 @@ xpath_vec(cxobj *cxtop,
}
/* A restricted xpath that returns a vector of matches (only nodes marked with flags)
* @param[in] cxtop xml-tree where to search
* @param[in] xcur xml-tree where to search
* @param[in] xpath string with XPATH syntax
* @param[in] flags Set of flags that return nodes must match (0 if all)
* @param[out] vec vector of xml-trees. Vector must be free():d after use
@ -1007,7 +1076,7 @@ xpath_vec(cxobj *cxtop,
* @code
* cxobj **vec;
* size_t veclen;
* if (xpath_vec_flag(cxtop, "//symbol/foo", XML_FLAG_ADD, &vec, &veclen) < 0)
* if (xpath_vec_flag(xcur, "//symbol/foo", XML_FLAG_ADD, &vec, &veclen) < 0)
* goto err;
* for (i=0; i<veclen; i++){
* xn = vec[i];
@ -1020,7 +1089,7 @@ xpath_vec(cxobj *cxtop,
* @see also xpath_vec This is a specialized version.
*/
int
xpath_vec_flag(cxobj *cxtop,
xpath_vec_flag(cxobj *xcur,
char *format,
uint16_t flags,
cxobj ***vec,
@ -1050,7 +1119,7 @@ xpath_vec_flag(cxobj *cxtop,
va_end(ap);
*vec=NULL;
*veclen = 0;
retval = xpath_choice(cxtop, xpath, flags, vec, veclen);
retval = xpath_choice(xcur, xpath, flags, vec, veclen);
done:
if (xpath)
free(xpath);

View file

@ -478,41 +478,6 @@ yang_find_topnode(yang_spec *ysp,
return NULL;
}
/*! Find a child spec-node yang_stmt with matching argument for schema-nodid
*
* See also yang_find() but this looks only for the yang specification nodes with
* the following keyword: container, leaf, list, leaf-list
* That is, basic syntax nodes.
* @see yang_find_datanode
* @see clicon_dbget_xpath
*/
static yang_stmt *
schema_nodeid_stmt(yang_node *yn,
char *argument)
{
yang_stmt *ys = NULL;
int i;
int match = 0;
for (i=0; i<yn->yn_len; i++){
ys = yn->yn_stmt[i];
if (!yang_schemanode(ys))
continue;
/* some keys dont have arguments, match on key */
if (ys->ys_keyword == Y_INPUT || ys->ys_keyword == Y_OUTPUT){
if (strcmp(argument, yang_key2str(ys->ys_keyword)) == 0){
match++;
break;
}
} else
if (ys->ys_argument && strcmp(argument, ys->ys_argument) == 0){
match++;
break;
}
}
return match ? ys : NULL;
}
/*! Reset flag in complete tree, arg contains flag */
static int
ys_flag_reset(yang_stmt *ys,
@ -1074,7 +1039,7 @@ ys_grouping_resolve(yang_stmt *ys,
The target node MUST be either a container, list, choice, case, input,
output, or notification node.
If the "augment" statement is on the top level the absolute form MUST be used.
XXX: Destructively changing a datamodel may affect outlying loop?
@note Destructively changing a datamodel may affect outlying loop?
*/
static int
yang_augment_node(yang_stmt *ys,
@ -1082,7 +1047,7 @@ yang_augment_node(yang_stmt *ys,
{
int retval = -1;
char *schema_nodeid;
yang_node *yn;
yang_stmt *yss = NULL;
yang_stmt *yc;
int i;
@ -1090,21 +1055,21 @@ yang_augment_node(yang_stmt *ys,
clicon_debug(1, "%s %s", __FUNCTION__, schema_nodeid);
/* Find the target */
if ((yn = yang_abs_schema_nodeid((yang_node*)ys, schema_nodeid)) == NULL){
clicon_err(OE_YANG, 0, "Augment schema_nodeid %s not found", schema_nodeid);
// retval = 0; /* Ignore, continue */
if (yang_abs_schema_nodeid(ysp, schema_nodeid, &yss) < 0)
goto done;
}
/* Extend yn with ys' children
* First enlarge yn vector
if (yss == NULL)
goto ok;
/* Extend yss with ys' children
* First enlarge yss vector
*/
for (i=0; i<ys->ys_len; i++){
if ((yc = ys_dup(ys->ys_stmt[i])) == NULL)
goto done;
/* XXX: use prefix of origin */
if (yn_insert(yn, yc) < 0)
if (yn_insert((yang_node*)yss, yc) < 0)
goto done;
}
ok:
retval = 0;
done:
return retval;
@ -1126,10 +1091,14 @@ yang_augment_spec(yang_spec *ysp)
j = 0;
while (j<ym->ys_len){ /* Top-level symbols in modules */
ys = ym->ys_stmt[j++];
if (ys->ys_keyword != Y_AUGMENT)
continue;
if (yang_augment_node(ys, ysp) < 0)
goto done;
switch (ys->ys_keyword){
case Y_AUGMENT: /* top-level */
if (yang_augment_node(ys, ysp) < 0)
goto done;
break;
default:
break;
}
}
}
retval = 0;
@ -1498,6 +1467,49 @@ yang_parse_recurse(clicon_handle h,
return ymod; /* top-level (sub)module */
}
int
ys_schemanode_check(yang_stmt *ys,
void *arg)
{
int retval = -1;
yang_spec *yspec;
yang_stmt *yres;
yang_node *yp;
yp = ys->ys_parent;
switch (ys->ys_keyword){
case Y_AUGMENT:
if (yp->yn_keyword == Y_MODULE) /* Not top-level */
break;
/* fallthru */
case Y_REFINE:
case Y_UNIQUE:
if (yang_desc_schema_nodeid(yp, ys->ys_argument, &yres) < 0)
goto done;
if (yres == NULL){
clicon_err(OE_YANG, 0, "schemanode sanity check of %d %s",
ys->ys_keyword,
ys->ys_argument);
goto done;
}
break;
case Y_DEVIATION:
yspec = ys_spec(ys);
if (yang_abs_schema_nodeid(yspec, ys->ys_argument, &yres) < 0)
goto done;
if (yres == NULL){
clicon_err(OE_YANG, 0, "schemanode sanity check of %s", ys->ys_argument);
goto done;
}
break;
default:
break;
}
retval = 0;
done:
return retval;
}
/*! Parse top yang module including all its sub-modules. Expand and populate yang tree
*
* @param[in] h CLICON handle
@ -1552,12 +1564,14 @@ yang_parse(clicon_handle h,
goto done;
yang_apply((yang_node*)ymod, -1, ys_flag_reset, (void*)YANG_FLAG_MARK);
/* Step 4: Top-level augmentation of all modules */
if (yang_augment_spec(ysp) < 0)
goto done;
/* sanity check of schemanode references, need more here */
if (yang_apply((yang_node*)ysp, -1, ys_schemanode_check, NULL) < 0)
goto done;
retval = 0;
done:
return retval;
@ -1621,21 +1635,26 @@ yang_apply(yang_node *yn,
/*! All the work for schema_nodeid functions both absolute and descendant
* Ignore prefixes, see _abs
* @param[in] yn Yang node
* @param[in] yn Yang node. Find next yang stmt and return that if match.
* @param[in] vec Vector of nodeid's in a schema node identifier, eg a/b
* @param[in] nvec Length of vec
* @retval NULL Error, with clicon_err called
* @retval yres First yang node matching schema nodeid
* @param[out] yres Result yang statement node, or NULL if not found
* @retval -1 Error, with clicon_err called
* @retval 0 OK
*/
static yang_node *
schema_nodeid_vec(yang_node *yn,
char **vec,
int nvec)
static int
schema_nodeid_vec(yang_node *yn,
char **vec,
int nvec,
yang_stmt **yres)
{
int retval = -1;
char *arg;
yang_node *ynext;
char *nodeid;
int i;
yang_stmt *ys;
yang_node *yres = NULL;
char *id;
int match;
if (nvec <= 0)
goto done;
@ -1644,88 +1663,121 @@ schema_nodeid_vec(yang_node *yn,
__FUNCTION__, yang_key2str(yn->yn_keyword), yn->yn_argument,
arg, yn->yn_len);
if (strcmp(arg, "..") == 0)
ys = (yang_stmt*)yn->yn_parent;
ynext = yn->yn_parent; /* This could actually be a MODULE */
else{
/* ignore prefixes */
if ((id = strchr(arg, ':')) == NULL)
id = arg;
if ((nodeid = strchr(arg, ':')) == NULL)
nodeid = arg;
else
id++;
if ((ys = schema_nodeid_stmt(yn, id)) == NULL){
clicon_debug(1, "%s %s not found", __FUNCTION__, id);
goto done;
nodeid++;
match = 0;
ys = NULL;
for (i=0; i<yn->yn_len; i++){
ys = yn->yn_stmt[i];
if (!yang_schemanode(ys))
continue;
/* some keys dont have arguments, match on key */
if (ys->ys_keyword == Y_INPUT || ys->ys_keyword == Y_OUTPUT){
if (strcmp(nodeid, yang_key2str(ys->ys_keyword)) == 0){
match++;
break;
}
} else
if (ys->ys_argument && strcmp(nodeid, ys->ys_argument) == 0){
match++;
break;
}
}
if (!match){
clicon_debug(1, "%s not found", nodeid);
goto ok;
}
ynext = (yang_node*)ys;
}
if (nvec == 1){
yres = (yang_node*)ys;
if (nvec == 1){ /* match */
if (yang_schemanode((yang_stmt*)ynext))
*yres = (yang_stmt*)ynext;
else
clicon_debug(1, "%s not schema node", nodeid);
goto ok;
}
/* recursive call using ynext */
if (schema_nodeid_vec(ynext, vec+1, nvec-1, yres) < 0)
goto done;
}
yres = schema_nodeid_vec((yang_node*)ys, vec+1, nvec-1);
ok:
retval = 0;
done:
return yres;
return retval;
}
/*! Given an absolute schema-nodeid (eg /a/b/c) find matching yang spec
* @param[in] yn Yang node
* @param[in] yspec Yang specification.
* @param[in] schema_nodeid Absolute schema-node-id, ie /a/b
* @retval NULL Error, with clicon_err called
* @retval yres First yang node matching schema nodeid
* @see yang_schema_nodeid
* Assume schema nodeid:s have prefixes, (actually the first).
* @see yang_desc_schema_nodeid
* Used in yang: deviation, top-level augment
*/
yang_node *
yang_abs_schema_nodeid(yang_node *yn,
char *schema_nodeid)
int
yang_abs_schema_nodeid(yang_spec *yspec,
char *schema_nodeid,
yang_stmt **yres)
{
int retval = -1;
char **vec = NULL;
int nvec;
yang_node *yres = NULL;
yang_stmt *ymod;
char *id;
char *prefix = NULL;
yang_stmt *yprefix;
/* check absolute schema_nodeid */
if (schema_nodeid[0] != '/'){
clicon_err(OE_YANG, EINVAL, "absolute schema nodeid should start with /");
goto done;
}
if ((vec = clicon_strsep(schema_nodeid, "/", &nvec)) == NULL){
clicon_err(OE_YANG, errno, "%s: strsep", __FUNCTION__);
goto done;
}
/* Assume schame nodeid looks like: "/prefix:id[/prefix:id]*" */
/* Assume schema nodeid looks like: "/prefix:id[/prefix:id]*" */
if (nvec < 2){
clicon_err(OE_YANG, 0, "%s: NULL or truncated path: %s",
__FUNCTION__, schema_nodeid);
goto done;
}
/* Check for absolute path */
if (strcmp(vec[0], "") != 0){
clicon_err(OE_YANG, errno, "%s: %s: expected absolute path",
__FUNCTION__, vec[0]);
goto done;
}
/* Find correct module */
if ((ymod = ys_module((yang_stmt*)yn)) == NULL){
clicon_err(OE_YANG, 0, "Could not find top-level");
goto done;
}
/* split <prefix>:<id> */
if ((id = strchr(vec[1], ':')) == NULL){
id = vec[1];
if ((id = strchr(vec[1], ':')) == NULL){ /* no prefix */
clicon_err(OE_YANG, 0, "Absolute schema nodeid must have prefix");
goto done;
}
else{ /* other module - peek into first element to find module */
if ((prefix = strdup(vec[1])) == NULL){
clicon_err(OE_UNIX, errno, "%s: strdup", __FUNCTION__);
goto done;
if ((prefix = strdup(vec[1])) == NULL){
clicon_err(OE_UNIX, errno, "%s: strdup", __FUNCTION__);
goto done;
}
prefix[id-vec[1]] = '\0';
id++;
ymod = NULL;
while ((ymod = yn_each((yang_node*)yspec, ymod)) != NULL) {
if ((yprefix = yang_find((yang_node*)ymod, Y_PREFIX, NULL)) != NULL &&
strcmp(yprefix->ys_argument, prefix) == 0){
break;
}
prefix[id-vec[1]] = '\0';
id++;
if ((ymod = yang_find_module_by_prefix((yang_stmt*)yn, prefix)) == NULL)
goto done;
}
yres = schema_nodeid_vec((yang_node*)ymod, vec+1, nvec-1);
if (ymod == NULL){
clicon_err(OE_YANG, 0, "Module with prefix %s not found", prefix);
goto done;
}
if (schema_nodeid_vec((yang_node*)ymod, vec+1, nvec-1, yres) < 0)
goto done;
retval = 0;
done:
if (vec)
free(vec);
if (prefix)
free(prefix);
return yres;
return retval;
}
/*! Given a descendant schema-nodeid (eg a/b/c) find matching yang spec
@ -1734,12 +1786,14 @@ yang_abs_schema_nodeid(yang_node *yn,
* @retval NULL Error, with clicon_err called
* @retval yres First yang node matching schema nodeid
* @see yang_schema_nodeid
* Used in yang: unique, refine, uses augment
*/
yang_node *
yang_desc_schema_nodeid(yang_node *yn,
char *schema_nodeid)
int
yang_desc_schema_nodeid(yang_node *yn,
char *schema_nodeid,
yang_stmt **yres)
{
yang_node *yres = NULL;
int retval = -1;
char **vec = NULL;
int nvec;
@ -1747,47 +1801,18 @@ yang_desc_schema_nodeid(yang_node *yn,
goto done;
/* check absolute schema_nodeid */
if (schema_nodeid[0] == '/'){
clicon_err(OE_YANG, EINVAL, "descenadant schema nodeid should not start with /");
clicon_err(OE_YANG, EINVAL, "descendant schema nodeid should not start with /");
goto done;
}
if ((vec = clicon_strsep(schema_nodeid, "/", &nvec)) == NULL)
goto done;
yres = schema_nodeid_vec((yang_node*)yn, vec, nvec);
if (schema_nodeid_vec(yn, vec, nvec, yres) < 0)
goto done;
retval = 0;
done:
if (vec)
free(vec);
return yres;
}
/*! Given a schema-node-id (eg /a/b/c or a/b/c) find matching yang specification
*
* They are either absolute (start with /) or descendant (no /)
* Compare with XPATH for XML, this is for YANG spec
* @param[in] yn Yang node tree
* @param[in] schema_nodeid schema-node-id, ie a/b or /a/b
* @retval NULL Error, with clicon_err called
* @retval yres First yang node matching schema nodeid
* @note: the identifiers in the xpath (eg a, b in a/b) can match the nodes
* defined in yang_xpath: container, leaf,list,leaf-list, modules, sub-modules
* Example:
* yn : module m { prefix b; container b { list c { key d; leaf d; }} }
* schema-node-id = m/b/c, returns the list 'c'.
*/
yang_node *
yang_schema_nodeid(yang_node *yn,
char *schema_nodeid)
{
yang_node *yres = NULL;
if (strlen(schema_nodeid) == 0)
goto done;
/* check absolute schema_nodeid */
if (schema_nodeid[0] == '/')
yres = yang_abs_schema_nodeid(yn, schema_nodeid);
else
yres = yang_desc_schema_nodeid(yn, schema_nodeid);
done:
return yres;
return retval;
}
/*! Parse argument as CV and save result in yang cv variable

View file

@ -1041,3 +1041,4 @@ yang_type_get(yang_stmt *ys,
done:
return retval;
}

View file

@ -27,14 +27,13 @@ module example{
}
leaf address {
type leafref {
path "../../interfaces/interface[name=eth0]"
+ "/address/ip";
}
path "../../interfaces/interface[name = current()/../relname]"
+ "/ipv4/address/ip";
}
}
}
}
EOF
# XXX not eth0 path "../../interface[name = current()/../ifname]"
# kill old backend (if any)
new "kill old backend"

View file

@ -1,5 +1,6 @@
#!/bin/bash
# Advanced union types and generated code
# and enum w values
# include err() and new() functions
. ./lib.sh
@ -48,6 +49,14 @@ module example{
type af;
}
}
leaf status {
type enumeration {
enum up {
value 1;
}
enum down;
}
}
}
EOF
@ -101,6 +110,9 @@ expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/type.yang" "<rpc><discard-chan
new "netconf commit"
expecteof "$clixon_netconf -qf $clixon_cf -y /tmp/type.yang" "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "cli enum value"
expectfn "$clixon_cli -1f $clixon_cf -l o -y /tmp/type.yang set status down" "^$"
new "Kill backend"
# Check if still alive
pid=`pgrep clixon_backend`