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. * You need to define state data in a backend callback. See the example and documentation for more details.
### Minor changes: ### 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. * 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 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. * 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_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 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 xml_merge(cxobj *x0, cxobj *x1, yang_spec *yspec);
int yang_enum_int_value(cxobj *node, int32_t *val);
#endif /* _CLIXON_XML_MAP_H_ */ #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); const char *module, const char *revision, yang_spec *ysp);
int yang_apply(yang_node *yn, enum rfc_6020 key, yang_applyfn_t fn, int yang_apply(yang_node *yn, enum rfc_6020 key, yang_applyfn_t fn,
void *arg); void *arg);
yang_node *yang_abs_schema_nodeid(yang_node *yn, char *schema_nodeid); int yang_abs_schema_nodeid(yang_spec *yspec, char *schema_nodeid,
yang_node *yang_desc_schema_nodeid(yang_node *yn, char *schema_nodeid); yang_stmt **yres);
yang_node *yang_schema_nodeid(yang_node *yn, char *xpath); 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); cg_var *ys_parse(yang_stmt *ys, enum cv_type cvtype);
int ys_parse_sub(yang_stmt *ys); int ys_parse_sub(yang_stmt *ys);
int yang_mandatory(yang_stmt *ys); int yang_mandatory(yang_stmt *ys);

View file

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

View file

@ -478,41 +478,6 @@ yang_find_topnode(yang_spec *ysp,
return NULL; 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 */ /*! Reset flag in complete tree, arg contains flag */
static int static int
ys_flag_reset(yang_stmt *ys, 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, The target node MUST be either a container, list, choice, case, input,
output, or notification node. output, or notification node.
If the "augment" statement is on the top level the absolute form MUST be used. 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 static int
yang_augment_node(yang_stmt *ys, yang_augment_node(yang_stmt *ys,
@ -1082,7 +1047,7 @@ yang_augment_node(yang_stmt *ys,
{ {
int retval = -1; int retval = -1;
char *schema_nodeid; char *schema_nodeid;
yang_node *yn; yang_stmt *yss = NULL;
yang_stmt *yc; yang_stmt *yc;
int i; int i;
@ -1090,21 +1055,21 @@ yang_augment_node(yang_stmt *ys,
clicon_debug(1, "%s %s", __FUNCTION__, schema_nodeid); clicon_debug(1, "%s %s", __FUNCTION__, schema_nodeid);
/* Find the target */ /* Find the target */
if ((yn = yang_abs_schema_nodeid((yang_node*)ys, schema_nodeid)) == NULL){ if (yang_abs_schema_nodeid(ysp, schema_nodeid, &yss) < 0)
clicon_err(OE_YANG, 0, "Augment schema_nodeid %s not found", schema_nodeid);
// retval = 0; /* Ignore, continue */
goto done; goto done;
} if (yss == NULL)
/* Extend yn with ys' children goto ok;
* First enlarge yn vector /* Extend yss with ys' children
* First enlarge yss vector
*/ */
for (i=0; i<ys->ys_len; i++){ for (i=0; i<ys->ys_len; i++){
if ((yc = ys_dup(ys->ys_stmt[i])) == NULL) if ((yc = ys_dup(ys->ys_stmt[i])) == NULL)
goto done; goto done;
/* XXX: use prefix of origin */ /* XXX: use prefix of origin */
if (yn_insert(yn, yc) < 0) if (yn_insert((yang_node*)yss, yc) < 0)
goto done; goto done;
} }
ok:
retval = 0; retval = 0;
done: done:
return retval; return retval;
@ -1126,10 +1091,14 @@ yang_augment_spec(yang_spec *ysp)
j = 0; j = 0;
while (j<ym->ys_len){ /* Top-level symbols in modules */ while (j<ym->ys_len){ /* Top-level symbols in modules */
ys = ym->ys_stmt[j++]; ys = ym->ys_stmt[j++];
if (ys->ys_keyword != Y_AUGMENT) switch (ys->ys_keyword){
continue; case Y_AUGMENT: /* top-level */
if (yang_augment_node(ys, ysp) < 0) if (yang_augment_node(ys, ysp) < 0)
goto done; goto done;
break;
default:
break;
}
} }
} }
retval = 0; retval = 0;
@ -1498,6 +1467,49 @@ yang_parse_recurse(clicon_handle h,
return ymod; /* top-level (sub)module */ 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 /*! Parse top yang module including all its sub-modules. Expand and populate yang tree
* *
* @param[in] h CLICON handle * @param[in] h CLICON handle
@ -1552,12 +1564,14 @@ yang_parse(clicon_handle h,
goto done; goto done;
yang_apply((yang_node*)ymod, -1, ys_flag_reset, (void*)YANG_FLAG_MARK); yang_apply((yang_node*)ymod, -1, ys_flag_reset, (void*)YANG_FLAG_MARK);
/* Step 4: Top-level augmentation of all modules */ /* Step 4: Top-level augmentation of all modules */
if (yang_augment_spec(ysp) < 0) if (yang_augment_spec(ysp) < 0)
goto done; 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; retval = 0;
done: done:
return retval; return retval;
@ -1621,21 +1635,26 @@ yang_apply(yang_node *yn,
/*! All the work for schema_nodeid functions both absolute and descendant /*! All the work for schema_nodeid functions both absolute and descendant
* Ignore prefixes, see _abs * 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] vec Vector of nodeid's in a schema node identifier, eg a/b
* @param[in] nvec Length of vec * @param[in] nvec Length of vec
* @retval NULL Error, with clicon_err called * @param[out] yres Result yang statement node, or NULL if not found
* @retval yres First yang node matching schema nodeid * @retval -1 Error, with clicon_err called
* @retval 0 OK
*/ */
static yang_node * static int
schema_nodeid_vec(yang_node *yn, schema_nodeid_vec(yang_node *yn,
char **vec, char **vec,
int nvec) int nvec,
yang_stmt **yres)
{ {
int retval = -1;
char *arg; char *arg;
yang_node *ynext;
char *nodeid;
int i;
yang_stmt *ys; yang_stmt *ys;
yang_node *yres = NULL; int match;
char *id;
if (nvec <= 0) if (nvec <= 0)
goto done; goto done;
@ -1644,88 +1663,121 @@ schema_nodeid_vec(yang_node *yn,
__FUNCTION__, yang_key2str(yn->yn_keyword), yn->yn_argument, __FUNCTION__, yang_key2str(yn->yn_keyword), yn->yn_argument,
arg, yn->yn_len); arg, yn->yn_len);
if (strcmp(arg, "..") == 0) if (strcmp(arg, "..") == 0)
ys = (yang_stmt*)yn->yn_parent; ynext = yn->yn_parent; /* This could actually be a MODULE */
else{ else{
/* ignore prefixes */ /* ignore prefixes */
if ((id = strchr(arg, ':')) == NULL) if ((nodeid = strchr(arg, ':')) == NULL)
id = arg; nodeid = arg;
else else
id++; nodeid++;
if ((ys = schema_nodeid_stmt(yn, id)) == NULL){ match = 0;
clicon_debug(1, "%s %s not found", __FUNCTION__, id); ys = NULL;
goto done; 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){ if (nvec == 1){ /* match */
yres = (yang_node*)ys; 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; goto done;
} ok:
yres = schema_nodeid_vec((yang_node*)ys, vec+1, nvec-1); retval = 0;
done: done:
return yres; return retval;
} }
/*! Given an absolute schema-nodeid (eg /a/b/c) find matching yang spec /*! 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 * @param[in] schema_nodeid Absolute schema-node-id, ie /a/b
* @retval NULL Error, with clicon_err called * @retval NULL Error, with clicon_err called
* @retval yres First yang node matching schema nodeid * @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 * int
yang_abs_schema_nodeid(yang_node *yn, yang_abs_schema_nodeid(yang_spec *yspec,
char *schema_nodeid) char *schema_nodeid,
yang_stmt **yres)
{ {
int retval = -1;
char **vec = NULL; char **vec = NULL;
int nvec; int nvec;
yang_node *yres = NULL;
yang_stmt *ymod; yang_stmt *ymod;
char *id; char *id;
char *prefix = NULL; 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){ if ((vec = clicon_strsep(schema_nodeid, "/", &nvec)) == NULL){
clicon_err(OE_YANG, errno, "%s: strsep", __FUNCTION__); clicon_err(OE_YANG, errno, "%s: strsep", __FUNCTION__);
goto done; goto done;
} }
/* Assume schame nodeid looks like: "/prefix:id[/prefix:id]*" */ /* Assume schema nodeid looks like: "/prefix:id[/prefix:id]*" */
if (nvec < 2){ if (nvec < 2){
clicon_err(OE_YANG, 0, "%s: NULL or truncated path: %s", clicon_err(OE_YANG, 0, "%s: NULL or truncated path: %s",
__FUNCTION__, schema_nodeid); __FUNCTION__, schema_nodeid);
goto done; 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> */ /* split <prefix>:<id> */
if ((id = strchr(vec[1], ':')) == NULL){ if ((id = strchr(vec[1], ':')) == NULL){ /* no prefix */
id = vec[1]; 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){
if ((prefix = strdup(vec[1])) == NULL){ clicon_err(OE_UNIX, errno, "%s: strdup", __FUNCTION__);
clicon_err(OE_UNIX, errno, "%s: strdup", __FUNCTION__); goto done;
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: done:
if (vec) if (vec)
free(vec); free(vec);
if (prefix) if (prefix)
free(prefix); free(prefix);
return yres; return retval;
} }
/*! Given a descendant schema-nodeid (eg a/b/c) find matching yang spec /*! 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 NULL Error, with clicon_err called
* @retval yres First yang node matching schema nodeid * @retval yres First yang node matching schema nodeid
* @see yang_schema_nodeid * @see yang_schema_nodeid
* Used in yang: unique, refine, uses augment
*/ */
yang_node * int
yang_desc_schema_nodeid(yang_node *yn, yang_desc_schema_nodeid(yang_node *yn,
char *schema_nodeid) char *schema_nodeid,
yang_stmt **yres)
{ {
yang_node *yres = NULL; int retval = -1;
char **vec = NULL; char **vec = NULL;
int nvec; int nvec;
@ -1747,47 +1801,18 @@ yang_desc_schema_nodeid(yang_node *yn,
goto done; goto done;
/* check absolute schema_nodeid */ /* check absolute schema_nodeid */
if (schema_nodeid[0] == '/'){ 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; goto done;
} }
if ((vec = clicon_strsep(schema_nodeid, "/", &nvec)) == NULL) if ((vec = clicon_strsep(schema_nodeid, "/", &nvec)) == NULL)
goto done; 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: done:
if (vec) if (vec)
free(vec); free(vec);
return yres; return retval;
}
/*! 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;
} }
/*! Parse argument as CV and save result in yang cv variable /*! Parse argument as CV and save result in yang cv variable

View file

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

View file

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

View file

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