summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/frrstr.c21
-rw-r--r--lib/frrstr.h8
-rw-r--r--lib/northbound.c96
3 files changed, 109 insertions, 16 deletions
diff --git a/lib/frrstr.c b/lib/frrstr.c
index bb112afef7..1e743d4b0c 100644
--- a/lib/frrstr.c
+++ b/lib/frrstr.c
@@ -249,3 +249,24 @@ const char *frrstr_skip_over_char(const char *s, int skipc)
}
return NULL;
}
+
+/*
+ * Advance backward in string until reaching the char `toc`
+ * if beginning of string is reached w/o finding char return NULL
+ *
+ * /foo/bar'baz/booz'/foo
+ */
+const char *frrstr_back_to_char(const char *s, int toc)
+{
+ const char *next = s;
+ const char *prev = NULL;
+
+ if (s[0] == 0)
+ return NULL;
+ if (!strpbrk(s, "'\"\\"))
+ return strrchr(s, toc);
+ while ((next = frrstr_skip_over_char(next, toc)))
+ prev = next - 1;
+ return prev;
+}
+
diff --git a/lib/frrstr.h b/lib/frrstr.h
index 9a4fe257a2..33a4992001 100644
--- a/lib/frrstr.h
+++ b/lib/frrstr.h
@@ -167,13 +167,19 @@ int all_digit(const char *str);
*/
char *frrstr_hex(char *buff, size_t bufsiz, const uint8_t *str, size_t num);
-
/*
* Advance past a given char `skipc` in a string, while honoring quoting and
* backslash escapes (i.e., ignore `skipc` which occur in quoted sections).
*/
const char *frrstr_skip_over_char(const char *s, int skipc);
+/*
+ * Advance back from end to a given char `toc` in a string, while honoring
+ * quoting and backslash escapes. `toc` chars inside quote or escaped are
+ * ignored.
+ */
+const char *frrstr_back_to_char(const char *s, int toc);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/northbound.c b/lib/northbound.c
index 32988dfc15..e70d05cea9 100644
--- a/lib/northbound.c
+++ b/lib/northbound.c
@@ -2032,6 +2032,62 @@ static int nb_oper_data_iter_node(const struct lysc_node *snode,
return ret;
}
+static int nb_xpath_dirname(char *xpath)
+{
+ int len = strlen(xpath);
+ bool abs = xpath[0] == '/';
+ char *slash;
+
+ /* "//" or "/" => NULL */
+ if (abs && (len == 1 || (len == 2 && xpath[1] == '/')))
+ return NB_ERR_NOT_FOUND;
+ slash = (char *)frrstr_back_to_char(xpath, '/');
+ /* "/foo/bar/" or "/foo/bar//" => "/foo " */
+ if (slash && slash == &xpath[len - 1]) {
+ xpath[--len] = 0;
+ slash = (char *)frrstr_back_to_char(xpath, '/');
+ if (slash && slash == &xpath[len - 1]) {
+ xpath[--len] = 0;
+ slash = (char *)frrstr_back_to_char(xpath, '/');
+ }
+ }
+ if (!slash)
+ return NB_ERR_NOT_FOUND;
+ *slash = 0;
+ return NB_OK;
+}
+
+static int nb_oper_data_xpath_to_tree(const char *xpath_in,
+ struct lyd_node **dnode,
+ bool is_top_node_list)
+{
+ /* Eventually this function will loop until it finds a concrete path */
+ char *xpath;
+ LY_ERR err;
+ int ret;
+
+ err = lyd_new_path2(NULL, ly_native_ctx, xpath_in, NULL, 0, 0,
+ LYD_NEW_PATH_UPDATE, NULL, dnode);
+ if (err == LY_SUCCESS)
+ return NB_OK;
+ if (!is_top_node_list)
+ return NB_ERR_NOT_FOUND;
+
+ xpath = XSTRDUP(MTYPE_TMP, xpath_in);
+ ret = nb_xpath_dirname(xpath);
+ if (ret != NB_OK)
+ goto done;
+
+ err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0,
+ LYD_NEW_PATH_UPDATE, NULL, dnode);
+ if (err != LY_SUCCESS)
+ ret = NB_ERR_NOT_FOUND;
+done:
+ XFREE(MTYPE_TMP, xpath);
+ return ret;
+}
+
+
int nb_oper_data_iterate(const char *xpath, struct yang_translator *translator,
uint32_t flags, nb_oper_data_cb cb, void *arg)
{
@@ -2064,25 +2120,23 @@ int nb_oper_data_iterate(const char *xpath, struct yang_translator *translator,
* all YANG lists (if any).
*/
- LY_ERR err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0,
- LYD_NEW_PATH_UPDATE, NULL, &dnode);
- if (err || !dnode) {
- const char *errmsg =
- err ? ly_errmsg(ly_native_ctx) : "node not found";
- flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed %s",
- __func__, errmsg);
- return NB_ERR;
+ ret = nb_oper_data_xpath_to_tree(xpath, &dnode,
+ nb_node->snode->nodetype == LYS_LIST);
+ if (ret) {
+ flog_warn(EC_LIB_LIBYANG,
+ "%s: can't instantiate concrete path using xpath: %s",
+ __func__, xpath);
+ return ret;
}
/*
* Create a linked list to sort the data nodes starting from the root.
*/
list_dnodes = list_new();
- for (dn = dnode; dn; dn = lyd_parent(dn)) {
- if (dn->schema->nodetype != LYS_LIST || !lyd_child(dn))
- continue;
- listnode_add_head(list_dnodes, dn);
- }
+ for (dn = dnode; dn; dn = lyd_parent(dn))
+ if (dn->schema->nodetype == LYS_LIST)
+ listnode_add_head(list_dnodes, dn);
+
/*
* Use the northbound callbacks to find list entry pointer corresponding
* to the given XPath.
@@ -2104,6 +2158,10 @@ int nb_oper_data_iterate(const char *xpath, struct yang_translator *translator,
}
list_keys.num = n;
if (list_keys.num != yang_snode_num_keys(dn->schema)) {
+ flog_warn(
+ EC_LIB_NB_OPERATIONAL_DATA,
+ "%s: internal list entry '%s' missing required key values predicates in xpath: %s",
+ __func__, dn->schema->name, xpath);
list_delete(&list_dnodes);
yang_dnode_free(dnode);
return NB_ERR_NOT_FOUND;
@@ -2121,6 +2179,11 @@ int nb_oper_data_iterate(const char *xpath, struct yang_translator *translator,
return NB_ERR;
}
+ /* NOTE: To add support for multiple levels of unspecified keys
+ * we need to loop here using the list entry's get_next to work
+ * with each "existing in the data" list entry. It will be a bit
+ * tricky b/c we are inside a loop here.
+ */
list_entry =
nb_callback_lookup_entry(nn, list_entry, &list_keys);
if (list_entry == NULL) {
@@ -2130,8 +2193,11 @@ int nb_oper_data_iterate(const char *xpath, struct yang_translator *translator,
}
}
- /* If a list entry was given, iterate over that list entry only. */
- if (dnode->schema->nodetype == LYS_LIST && lyd_child(dnode))
+ /* If a list entry was given with keys as the last node in the path,
+ * iterate over that list entry only.
+ */
+ if (dnode->schema->nodetype == LYS_LIST && lyd_child(dnode)
+ && dnode->schema == nb_node->snode)
ret = nb_oper_data_iter_children(
nb_node->snode, xpath, list_entry, &list_keys,
translator, true, flags, cb, arg);