diff --git a/CHANGELOG.md b/CHANGELOG.md index 940860b8..26b9d196 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/lib/clixon/clixon_xml_map.h b/lib/clixon/clixon_xml_map.h index 36cfda82..bfd27302 100644 --- a/lib/clixon/clixon_xml_map.h +++ b/lib/clixon/clixon_xml_map.h @@ -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_ */ diff --git a/lib/clixon/clixon_yang.h b/lib/clixon/clixon_yang.h index fb8d49a8..9593181c 100644 --- a/lib/clixon/clixon_yang.h +++ b/lib/clixon/clixon_yang.h @@ -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); diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index dccf0fa5..449adc23 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -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; +} diff --git a/lib/src/clixon_xsl.c b/lib/src/clixon_xsl.c index 0d3146d4..ff21d008 100644 --- a/lib/src/clixon_xsl.c +++ b/lib/src/clixon_xsl.c @@ -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; jxe_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-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; iyn_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; iys_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 (jys_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; iyn_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 : */ - 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 diff --git a/lib/src/clixon_yang_type.c b/lib/src/clixon_yang_type.c index 79c4bea1..72a2f83c 100644 --- a/lib/src/clixon_yang_type.c +++ b/lib/src/clixon_yang_type.c @@ -1041,3 +1041,4 @@ yang_type_get(yang_stmt *ys, done: return retval; } + diff --git a/test/test_leafref.sh b/test/test_leafref.sh index e500d5b4..eea36dbc 100755 --- a/test/test_leafref.sh +++ b/test/test_leafref.sh @@ -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" diff --git a/test/test_type.sh b/test/test_type.sh index 97170294..8cfb3305 100755 --- a/test/test_type.sh +++ b/test/test_type.sh @@ -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" "]]>]]>" "^]]>]]>$" +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`