mirror_frr/lib/yang.c
Christian Hopps 2375a11b08 lib: yang: add yang_parse_data function
This is similar to notify and RPC parsers, but this is for normal datastore
data. This is initially used in handling datastore notifications being sent to
another backend client[s].

Signed-off-by: Christian Hopps <chopps@labn.net>
2025-01-18 16:13:53 +00:00

1696 lines
38 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2018 NetDEF, Inc.
* Renato Westphal
*/
#include <zebra.h>
#include "darr.h"
#include "log.h"
#include "lib_errors.h"
#include "yang.h"
#include "yang_translator.h"
#include <libyang/version.h>
#include "northbound.h"
#include "frrstr.h"
#include "darr.h"
#include "lib/config_paths.h"
DEFINE_MTYPE_STATIC(LIB, YANG_MODULE, "YANG module");
DEFINE_MTYPE_STATIC(LIB, YANG_DATA, "YANG data structure");
/* Safe to remove after libyang 2.2.8 */
#if (LY_VERSION_MAJOR < 3)
#define yang_lyd_find_xpath3(ctx_node, tree, xpath, format, prefix_data, vars, \
set) \
lyd_find_xpath3(ctx_node, tree, xpath, vars, set)
#ifndef LYD_NEW_VAL_OUTPUT
#define LYD_NEW_VAL_OUTPUT LYD_NEW_PATH_OUTPUT
#endif
#else
#define yang_lyd_find_xpath3(ctx_node, tree, xpath, format, prefix_data, vars, \
set) \
lyd_find_xpath3(ctx_node, tree, xpath, LY_VALUE_JSON, NULL, vars, set)
#endif
/* libyang container. */
struct ly_ctx *ly_native_ctx;
static struct yang_module_embed *embeds, **embedupd = &embeds;
void yang_module_embed(struct yang_module_embed *embed)
{
embed->next = NULL;
*embedupd = embed;
embedupd = &embed->next;
}
static LY_ERR yang_module_imp_clb(const char *mod_name, const char *mod_rev,
const char *submod_name,
const char *submod_rev, void *user_data,
LYS_INFORMAT *format,
const char **module_data,
void (**free_module_data)(void *, void *))
{
struct yang_module_embed *e;
if (!strcmp(mod_name, "ietf-inet-types") ||
!strcmp(mod_name, "ietf-yang-types") ||
!strcmp(mod_name, "ietf-yang-metadata"))
/* libyang has these built in, don't try finding them here */
return LY_ENOTFOUND;
for (e = embeds; e; e = e->next) {
if (e->sub_mod_name && submod_name) {
if (strcmp(e->sub_mod_name, submod_name))
continue;
if (submod_rev && strcmp(e->sub_mod_rev, submod_rev))
continue;
} else {
if (strcmp(e->mod_name, mod_name))
continue;
if (mod_rev && strcmp(e->mod_rev, mod_rev))
continue;
}
*format = e->format;
*module_data = e->data;
return LY_SUCCESS;
}
/* We get here for indirect modules like ietf-inet-types */
zlog_debug(
"YANG model \"%s@%s\" \"%s@%s\"not embedded, trying external file",
mod_name, mod_rev ? mod_rev : "*",
submod_name ? submod_name : "*", submod_rev ? submod_rev : "*");
return LY_ENOTFOUND;
}
/* clang-format off */
static const char *const frr_native_modules[] = {
"frr-interface",
"frr-vrf",
"frr-routing",
"frr-affinity-map",
"frr-route-map",
"frr-nexthop",
"frr-ripd",
"frr-ripngd",
"frr-isisd",
"frr-vrrpd",
"frr-zebra",
"frr-pathd",
};
/* clang-format on */
/* Generate the yang_modules tree. */
static inline int yang_module_compare(const struct yang_module *a,
const struct yang_module *b)
{
return strcmp(a->name, b->name);
}
RB_GENERATE(yang_modules, yang_module, entry, yang_module_compare)
struct yang_modules yang_modules = RB_INITIALIZER(&yang_modules);
struct yang_module *yang_module_load(const char *module_name,
const char **features)
{
struct yang_module *module;
const struct lys_module *module_info;
module_info = ly_ctx_load_module(ly_native_ctx, module_name, NULL,
features);
if (!module_info) {
flog_err(EC_LIB_YANG_MODULE_LOAD,
"%s: failed to load data model: %s", __func__,
module_name);
exit(1);
}
module = XCALLOC(MTYPE_YANG_MODULE, sizeof(*module));
module->name = module_name;
module->info = module_info;
if (RB_INSERT(yang_modules, &yang_modules, module) != NULL) {
flog_err(EC_LIB_YANG_MODULE_LOADED_ALREADY,
"%s: YANG module is loaded already: %s", __func__,
module_name);
exit(1);
}
return module;
}
void yang_module_load_all(void)
{
static const char * const all_features[] = { "*", NULL };
for (size_t i = 0; i < array_size(frr_native_modules); i++)
yang_module_load(frr_native_modules[i], (const char **)all_features);
}
struct yang_module *yang_module_find(const char *module_name)
{
struct yang_module s;
s.name = module_name;
return RB_FIND(yang_modules, &yang_modules, &s);
}
int yang_snodes_iterate_subtree(const struct lysc_node *snode,
const struct lys_module *module,
yang_iterate_cb cb, uint16_t flags, void *arg)
{
const struct lysc_node *child;
int ret = YANG_ITER_CONTINUE;
if (module && snode->module != module)
goto next;
switch (snode->nodetype) {
case LYS_CONTAINER:
if (CHECK_FLAG(flags, YANG_ITER_FILTER_NPCONTAINERS)) {
if (!CHECK_FLAG(snode->flags, LYS_PRESENCE))
goto next;
}
break;
case LYS_LEAF:
if (CHECK_FLAG(flags, YANG_ITER_FILTER_LIST_KEYS)) {
/* Ignore list keys. */
if (lysc_is_key(snode))
goto next;
}
break;
case LYS_INPUT:
case LYS_OUTPUT:
if (CHECK_FLAG(flags, YANG_ITER_FILTER_INPUT_OUTPUT))
goto next;
break;
default:
assert(snode->nodetype != LYS_AUGMENT
&& snode->nodetype != LYS_GROUPING
&& snode->nodetype != LYS_USES);
break;
}
ret = (*cb)(snode, arg);
if (ret == YANG_ITER_STOP)
return ret;
next:
/*
* YANG leafs and leaf-lists can't have child nodes.
*/
if (CHECK_FLAG(snode->nodetype, LYS_LEAF | LYS_LEAFLIST))
return YANG_ITER_CONTINUE;
LY_LIST_FOR (lysc_node_child(snode), child) {
ret = yang_snodes_iterate_subtree(child, module, cb, flags,
arg);
if (ret == YANG_ITER_STOP)
return ret;
}
LY_LIST_FOR ((const struct lysc_node *)lysc_node_notifs(snode), child) {
ret = yang_snodes_iterate_subtree(child, module, cb, flags, arg);
if (ret == YANG_ITER_STOP)
return ret;
}
LY_LIST_FOR ((const struct lysc_node *)lysc_node_actions(snode), child) {
ret = yang_snodes_iterate_subtree(child, module, cb, flags, arg);
if (ret == YANG_ITER_STOP)
return ret;
}
return ret;
}
int yang_snodes_iterate(const struct lys_module *module, yang_iterate_cb cb,
uint16_t flags, void *arg)
{
const struct lys_module *module_iter;
uint32_t idx = 0;
int ret = YANG_ITER_CONTINUE;
idx = ly_ctx_internal_modules_count(ly_native_ctx);
while ((module_iter = ly_ctx_get_module_iter(ly_native_ctx, &idx))) {
struct lysc_node *snode;
if (!module_iter->implemented)
continue;
LY_LIST_FOR (module_iter->compiled->data, snode) {
ret = yang_snodes_iterate_subtree(snode, module, cb,
flags, arg);
if (ret == YANG_ITER_STOP)
return ret;
}
LY_LIST_FOR (&module_iter->compiled->rpcs->node, snode) {
ret = yang_snodes_iterate_subtree(snode, module, cb,
flags, arg);
if (ret == YANG_ITER_STOP)
return ret;
}
LY_LIST_FOR (&module_iter->compiled->notifs->node, snode) {
ret = yang_snodes_iterate_subtree(snode, module, cb,
flags, arg);
if (ret == YANG_ITER_STOP)
return ret;
}
}
return ret;
}
void yang_snode_get_path(const struct lysc_node *snode,
enum yang_path_type type, char *xpath,
size_t xpath_len)
{
switch (type) {
case YANG_PATH_SCHEMA:
(void)lysc_path(snode, LYSC_PATH_LOG, xpath, xpath_len);
break;
case YANG_PATH_DATA:
(void)lysc_path(snode, LYSC_PATH_DATA, xpath, xpath_len);
break;
default:
flog_err(EC_LIB_DEVELOPMENT, "%s: unknown yang path type: %u",
__func__, type);
exit(1);
}
}
LY_ERR yang_resolve_snode_xpath(struct ly_ctx *ly_ctx, const char *xpath,
const struct lysc_node ***snodes, bool *simple)
{
struct lysc_node *snode;
struct ly_set *set;
LY_ERR err;
/* lys_find_path will not resolve complex xpaths */
snode = (struct lysc_node *)lys_find_path(ly_ctx, NULL, xpath, 0);
if (snode) {
*darr_append(*snodes) = snode;
*simple = true;
return LY_SUCCESS;
}
/* Try again to catch complex query cases */
err = lys_find_xpath(ly_native_ctx, NULL, xpath, 0, &set);
if (err)
return err;
if (!set->count) {
ly_set_free(set, NULL);
return LY_ENOTFOUND;
}
*simple = false;
darr_ensure_i(*snodes, set->count - 1);
memcpy(*snodes, set->snodes, set->count * sizeof(set->snodes[0]));
ly_set_free(set, NULL);
return LY_SUCCESS;
}
struct lysc_node *yang_find_snode(struct ly_ctx *ly_ctx, const char *xpath,
uint32_t options)
{
struct lysc_node *snode;
snode = (struct lysc_node *)lys_find_path(ly_ctx, NULL, xpath, 0);
return snode;
}
struct lysc_node *yang_snode_real_parent(const struct lysc_node *snode)
{
struct lysc_node *parent = snode->parent;
while (parent) {
switch (parent->nodetype) {
case LYS_CONTAINER:
if (CHECK_FLAG(parent->flags, LYS_PRESENCE))
return parent;
break;
case LYS_LIST:
return parent;
default:
break;
}
parent = parent->parent;
}
return NULL;
}
struct lysc_node *yang_snode_parent_list(const struct lysc_node *snode)
{
struct lysc_node *parent = snode->parent;
while (parent) {
switch (parent->nodetype) {
case LYS_LIST:
return parent;
default:
break;
}
parent = parent->parent;
}
return NULL;
}
bool yang_snode_is_typeless_data(const struct lysc_node *snode)
{
const struct lysc_node_leaf *sleaf;
switch (snode->nodetype) {
case LYS_LEAF:
sleaf = (struct lysc_node_leaf *)snode;
if (sleaf->type->basetype == LY_TYPE_EMPTY)
return true;
return false;
case LYS_LEAFLIST:
return false;
default:
return true;
}
}
const char *yang_snode_get_default(const struct lysc_node *snode)
{
const struct lysc_node_leaf *sleaf;
switch (snode->nodetype) {
case LYS_LEAF:
sleaf = (const struct lysc_node_leaf *)snode;
return sleaf->dflt ? lyd_value_get_canonical(sleaf->module->ctx,
sleaf->dflt)
: NULL;
case LYS_LEAFLIST:
/* TODO: check leaf-list default values */
return NULL;
default:
return NULL;
}
}
const struct lysc_type *yang_snode_get_type(const struct lysc_node *snode)
{
struct lysc_node_leaf *sleaf = (struct lysc_node_leaf *)snode;
struct lysc_type *type;
if (!CHECK_FLAG(sleaf->nodetype, LYS_LEAF | LYS_LEAFLIST))
return NULL;
type = sleaf->type;
while (type->basetype == LY_TYPE_LEAFREF)
type = ((struct lysc_type_leafref *)type)->realtype;
return type;
}
unsigned int yang_snode_num_keys(const struct lysc_node *snode)
{
const struct lysc_node_leaf *skey;
uint count = 0;
if (!CHECK_FLAG(snode->nodetype, LYS_LIST))
return 0;
/* Walk list of children */
LY_FOR_KEYS (snode, skey) {
count++;
}
return count;
}
char *yang_dnode_get_path(const struct lyd_node *dnode, char *xpath,
size_t xpath_len)
{
return lyd_path(dnode, LYD_PATH_STD, xpath, xpath_len);
}
struct lyd_node *yang_dnode_get(const struct lyd_node *dnode, const char *xpath)
{
struct ly_set *set = NULL;
struct lyd_node *dnode_ret = NULL;
/*
* XXX a lot of the code uses this for style I guess. It shouldn't, as
* it adds to the xpath parsing complexity in libyang.
*/
if (xpath[0] == '.' && xpath[1] == '/')
xpath += 2;
if (lyd_find_xpath(dnode, xpath, &set)) {
/*
* Commenting out the below assert failure as it crashes mgmtd
* when bad xpath is passed.
*
* assert(0); XXX replicates old libyang1 base code
*/
goto exit;
}
if (set->count == 0)
goto exit;
if (set->count > 1) {
flog_warn(EC_LIB_YANG_DNODE_NOT_FOUND,
"%s: found %u elements (expected 0 or 1) [xpath %s]",
__func__, set->count, xpath);
goto exit;
}
dnode_ret = set->dnodes[0];
exit:
ly_set_free(set, NULL);
return dnode_ret;
}
struct lyd_node *yang_dnode_getf(const struct lyd_node *dnode,
const char *xpath_fmt, ...)
{
va_list ap;
char xpath[XPATH_MAXLEN];
va_start(ap, xpath_fmt);
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
va_end(ap);
return yang_dnode_get(dnode, xpath);
}
bool yang_dnode_exists(const struct lyd_node *dnode, const char *xpath)
{
struct ly_set *set = NULL;
bool exists = false;
if (xpath[0] == '.' && xpath[1] == '/')
xpath += 2;
if (lyd_find_xpath(dnode, xpath, &set))
return false;
exists = set->count > 0;
ly_set_free(set, NULL);
return exists;
}
bool yang_dnode_existsf(const struct lyd_node *dnode, const char *xpath_fmt,
...)
{
va_list ap;
char xpath[XPATH_MAXLEN];
va_start(ap, xpath_fmt);
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
va_end(ap);
return yang_dnode_exists(dnode, xpath);
}
void yang_dnode_iterate(yang_dnode_iter_cb cb, void *arg,
const struct lyd_node *dnode, const char *xpath_fmt,
...)
{
va_list ap;
char xpath[XPATH_MAXLEN];
struct ly_set *set;
va_start(ap, xpath_fmt);
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
va_end(ap);
if (lyd_find_xpath(dnode, xpath, &set)) {
assert(0); /* XXX libyang2: ly1 code asserted success */
return;
}
for (unsigned int i = 0; i < set->count; i++) {
int ret;
ret = (*cb)(set->dnodes[i], arg);
if (ret == YANG_ITER_STOP)
break;
}
ly_set_free(set, NULL);
}
uint32_t yang_dnode_count(const struct lyd_node *dnode, const char *xpath_fmt,
...)
{
va_list ap;
char xpath[XPATH_MAXLEN];
struct ly_set *set;
uint32_t count;
va_start(ap, xpath_fmt);
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
va_end(ap);
if (lyd_find_xpath(dnode, xpath, &set)) {
assert(0);
return 0;
}
count = set->count;
ly_set_free(set, NULL);
return count;
}
bool yang_dnode_is_default(const struct lyd_node *dnode, const char *xpath)
{
const struct lysc_node *snode;
struct lysc_node_leaf *sleaf;
if (xpath)
dnode = yang_dnode_get(dnode, xpath);
assert(dnode);
snode = dnode->schema;
switch (snode->nodetype) {
case LYS_LEAF:
sleaf = (struct lysc_node_leaf *)snode;
if (sleaf->type->basetype == LY_TYPE_EMPTY)
return false;
return lyd_is_default(dnode);
case LYS_LEAFLIST:
/* TODO: check leaf-list default values */
return false;
case LYS_CONTAINER:
if (CHECK_FLAG(snode->flags, LYS_PRESENCE))
return false;
return true;
default:
return false;
}
}
bool yang_dnode_is_defaultf(const struct lyd_node *dnode, const char *xpath_fmt,
...)
{
if (!xpath_fmt)
return yang_dnode_is_default(dnode, NULL);
else {
va_list ap;
char xpath[XPATH_MAXLEN];
va_start(ap, xpath_fmt);
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
va_end(ap);
return yang_dnode_is_default(dnode, xpath);
}
}
bool yang_dnode_is_default_recursive(const struct lyd_node *dnode)
{
struct lyd_node *root, *dnode_iter;
if (!yang_dnode_is_default(dnode, NULL))
return false;
if (CHECK_FLAG(dnode->schema->nodetype, LYS_LEAF | LYS_LEAFLIST))
return true;
LY_LIST_FOR (lyd_child(dnode), root) {
LYD_TREE_DFS_BEGIN (root, dnode_iter) {
if (!yang_dnode_is_default(dnode_iter, NULL))
return false;
LYD_TREE_DFS_END(root, dnode_iter);
}
}
return true;
}
void yang_dnode_change_leaf(struct lyd_node *dnode, const char *value)
{
assert(dnode->schema->nodetype == LYS_LEAF);
lyd_change_term(dnode, value);
}
struct lyd_node *yang_dnode_new(struct ly_ctx *ly_ctx, bool config_only)
{
struct lyd_node *dnode = NULL;
int options = config_only ? LYD_VALIDATE_NO_STATE : 0;
if (lyd_validate_all(&dnode, ly_ctx, options, NULL) != 0) {
/* Should never happen. */
flog_err(EC_LIB_LIBYANG, "%s: lyd_validate() failed", __func__);
exit(1);
}
return dnode;
}
struct lyd_node *yang_dnode_dup(const struct lyd_node *dnode)
{
struct lyd_node *dup = NULL;
LY_ERR err;
err = lyd_dup_siblings(dnode, NULL,
LYD_DUP_RECURSIVE | LYD_DUP_WITH_FLAGS, &dup);
assert(!err);
return dup;
}
void yang_dnode_free(struct lyd_node *dnode)
{
while (dnode->parent)
dnode = lyd_parent(dnode);
lyd_free_all(dnode);
}
void yang_dnode_rpc_output_add(struct lyd_node *output, const char *xpath,
const char *value)
{
LY_ERR err;
err = lyd_new_path(output, ly_native_ctx, xpath, value,
LYD_NEW_VAL_OUTPUT | LYD_NEW_PATH_UPDATE, NULL);
assert(err == LY_SUCCESS);
}
struct lyd_node *yang_state_new(struct lyd_node *tree, const char *path, const char *value)
{
struct lyd_node *dnode, *parent;
LY_ERR err;
err = lyd_new_path2(tree, ly_native_ctx, path, value, 0, 0, LYD_NEW_PATH_UPDATE, &parent,
&dnode);
assert(err == LY_SUCCESS);
/*
* If the node exists and isn't updated returned dnode will be NULL, so
* we need to find it. But even if returned it can be the first newly
* created node (could be container of path) not the actual path dnode.
* So we always find.
*/
err = lyd_find_path(tree ?: parent, path, false, &dnode);
assert(err == LY_SUCCESS);
return dnode;
}
void yang_state_delete(struct lyd_node *tree, const char *path)
{
LY_ERR err;
if (!tree)
return;
if (path) {
err = lyd_find_path(tree, path, false, &tree);
if (err != LY_SUCCESS) {
zlog_info("State %s has already been deleted", path);
return;
}
}
lyd_free_tree(tree);
}
PRINTFRR(2, 0)
struct lyd_node *yang_state_new_vpathf(struct lyd_node *tree, const char *path_fmt,
const char *value, va_list ap)
{
struct lyd_node *dnode;
char *path;
path = darr_vsprintf(path_fmt, ap);
dnode = yang_state_new(tree, path, value);
darr_free(path);
return dnode;
}
struct lyd_node *yang_state_new_pathf(struct lyd_node *tree, const char *path_fmt,
const char *value, ...)
{
struct lyd_node *dnode;
va_list ap;
va_start(ap, value);
dnode = yang_state_new_vpathf(tree, path_fmt, value, ap);
va_end(ap);
return dnode;
}
PRINTFRR(2, 0)
void yang_state_delete_vpathf(struct lyd_node *tree, const char *path_fmt, va_list ap)
{
char *path;
path = darr_vsprintf(path_fmt, ap);
yang_state_delete(tree, path);
darr_free(path);
}
void yang_state_delete_pathf(struct lyd_node *tree, const char *path_fmt, ...)
{
va_list ap;
va_start(ap, path_fmt);
yang_state_delete_vpathf(tree, path_fmt, ap);
va_end(ap);
}
PRINTFRR(3, 0)
struct lyd_node *yang_state_vnewf(struct lyd_node *tree, const char *path, const char *val_fmt,
va_list ap)
{
struct lyd_node *dnode;
char *value;
value = darr_vsprintf(val_fmt, ap);
dnode = yang_state_new(tree, path, value);
darr_free(value);
return dnode;
}
struct lyd_node *yang_state_newf(struct lyd_node *tree, const char *path, const char *val_fmt, ...)
{
struct lyd_node *dnode;
va_list ap;
va_start(ap, val_fmt);
dnode = yang_state_vnewf(tree, path, val_fmt, ap);
va_end(ap);
return dnode;
}
struct yang_data *yang_data_new(const char *xpath, const char *value)
{
struct yang_data *data;
data = XCALLOC(MTYPE_YANG_DATA, sizeof(*data));
strlcpy(data->xpath, xpath, sizeof(data->xpath));
if (value)
data->value = strdup(value);
return data;
}
void yang_data_free(struct yang_data *data)
{
if (data->value)
free(data->value);
XFREE(MTYPE_YANG_DATA, data);
}
struct list *yang_data_list_new(void)
{
struct list *list;
list = list_new();
list->del = (void (*)(void *))yang_data_free;
return list;
}
struct yang_data *yang_data_list_find(const struct list *list,
const char *xpath_fmt, ...)
{
char xpath[XPATH_MAXLEN];
struct yang_data *data;
struct listnode *node;
va_list ap;
va_start(ap, xpath_fmt);
vsnprintf(xpath, sizeof(xpath), xpath_fmt, ap);
va_end(ap);
for (ALL_LIST_ELEMENTS_RO(list, node, data))
if (strmatch(data->xpath, xpath))
return data;
return NULL;
}
/* Make libyang log its errors using FRR logging infrastructure. */
static void ly_zlog_cb(LY_LOG_LEVEL level, const char *msg, const char *data_path
#if !(LY_VERSION_MAJOR < 3)
,
const char *schema_path, uint64_t line
#endif
)
{
int priority = LOG_ERR;
switch (level) {
case LY_LLERR:
priority = LOG_ERR;
break;
case LY_LLWRN:
priority = LOG_WARNING;
break;
case LY_LLVRB:
case LY_LLDBG:
priority = LOG_DEBUG;
break;
}
if (data_path)
zlog(priority, "libyang: %s (%s)", msg, data_path);
#if !(LY_VERSION_MAJOR < 3)
else if (schema_path)
zlog(priority, "libyang %s (%s)\n", msg, schema_path);
else if (line)
zlog(priority, "libyang %s (line %" PRIu64 ")\n", msg, line);
#endif
else
zlog(priority, "libyang: %s", msg);
}
LY_ERR yang_parse_data(const char *xpath, LYD_FORMAT format, bool as_subtree, bool is_oper,
bool validate, const char *data, struct lyd_node **tree)
{
struct ly_in *in = NULL;
struct lyd_node *subtree = NULL;
uint32_t parse_options = LYD_PARSE_STRICT | LYD_PARSE_ONLY;
uint32_t validate_options = LYD_VALIDATE_PRESENT;
LY_ERR err;
err = ly_in_new_memory(data, &in);
if (err != LY_SUCCESS)
return err;
if (as_subtree) {
struct lyd_node *parent;
/*
* Create the subtree branch from root using the xpath. This
* will be used below to parse the data rooted at the subtree --
* a common YANG JSON technique (vs XML which starts all
* data trees from the root).
*/
err = lyd_new_path2(NULL, ly_native_ctx, xpath, NULL, 0, 0, 0, &parent, &subtree);
if (err != LY_SUCCESS)
goto done;
err = lyd_find_path(parent, xpath, false, &subtree);
if (err != LY_SUCCESS)
goto done;
}
if (is_oper)
validate_options |= LYD_VALIDATE_OPERATIONAL;
#ifdef LYD_VALIDATE_NOT_FINAL
if (!validate)
validate_options |= LYD_VALIDATE_NOT_FINAL;
#endif
err = lyd_parse_data(ly_native_ctx, subtree, in, format, parse_options, validate_options,
tree);
if (err == LY_SUCCESS && subtree)
*tree = subtree;
done:
ly_in_free(in, 0);
if (err != LY_SUCCESS) {
if (*tree)
lyd_free_all(*tree);
else if (subtree)
lyd_free_all(subtree);
*tree = NULL;
}
return err;
}
LY_ERR yang_parse_notification(const char *xpath, LYD_FORMAT format,
const char *data, struct lyd_node **notif)
{
struct lyd_node *tree;
struct ly_set *set = NULL;
struct ly_in *in = NULL;
LY_ERR err;
err = ly_in_new_memory(data, &in);
if (err) {
zlog_err("Failed to initialize ly_in: %s", ly_last_errmsg());
return err;
}
err = lyd_parse_op(ly_native_ctx, NULL, in, format, LYD_TYPE_NOTIF_YANG,
&tree, NULL);
ly_in_free(in, 0);
if (err) {
zlog_err("Failed to parse notification: %s", ly_last_errmsg());
return err;
}
err = yang_lyd_find_xpath3(NULL, tree, xpath, LY_VALUE_JSON, NULL, NULL,
&set);
if (err) {
zlog_err("Failed to parse notification: %s", ly_last_errmsg());
lyd_free_all(tree);
return err;
}
if (set->count == 0) {
zlog_err("Notification not found in the parsed tree: %s", xpath);
ly_set_free(set, NULL);
lyd_free_all(tree);
return LY_ENOTFOUND;
}
*notif = set->dnodes[0];
ly_set_free(set, NULL);
return LY_SUCCESS;
}
LY_ERR yang_parse_rpc(const char *xpath, LYD_FORMAT format, const char *data,
bool reply, struct lyd_node **rpc)
{
const struct lysc_node *snode;
struct lyd_node *parent = NULL;
struct ly_in *in = NULL;
LY_ERR err;
snode = lys_find_path(ly_native_ctx, NULL, xpath, 0);
if (!snode) {
zlog_err("Failed to find RPC/action schema node: %s", xpath);
return LY_ENOTFOUND;
}
/* If it's an action, create its parent */
if (snode->nodetype == LYS_ACTION) {
char *parent_xpath = XSTRDUP(MTYPE_TMP, xpath);
if (yang_xpath_pop_node(parent_xpath) != NB_OK) {
XFREE(MTYPE_TMP, parent_xpath);
zlog_err("Invalid action xpath: %s", xpath);
return LY_EINVAL;
}
err = lyd_new_path2(NULL, ly_native_ctx, parent_xpath, NULL, 0,
0, 0, NULL, &parent);
XFREE(MTYPE_TMP, parent_xpath);
if (err) {
zlog_err("Failed to create parent node for action: %s",
ly_last_errmsg());
return err;
}
} else if (snode->nodetype != LYS_RPC) {
zlog_err("Schema node is not an RPC/action: %s", xpath);
return LY_EINVAL;
}
err = ly_in_new_memory(data, &in);
if (err) {
lyd_free_all(parent);
zlog_err("Failed to initialize ly_in: %s", ly_last_errmsg());
return err;
}
err = lyd_parse_op(ly_native_ctx, parent, in, format,
reply ? LYD_TYPE_REPLY_YANG : LYD_TYPE_RPC_YANG,
NULL, rpc);
ly_in_free(in, 0);
if (err) {
lyd_free_all(parent);
zlog_err("Failed to parse RPC/action: %s", ly_last_errmsg());
return err;
}
return LY_SUCCESS;
}
static ssize_t yang_print_darr(void *arg, const void *buf, size_t count)
{
uint8_t *dst = darr_append_n(*(uint8_t **)arg, count);
memcpy(dst, buf, count);
return count;
}
LY_ERR yang_print_tree_append(uint8_t **darr, const struct lyd_node *root,
LYD_FORMAT format, uint32_t options)
{
LY_ERR err;
err = lyd_print_clb(yang_print_darr, darr, root, format, options);
if (err)
zlog_err("Failed to save yang tree: %s", ly_last_errmsg());
else if (format != LYD_LYB)
*darr_append(*darr) = 0;
return err;
}
uint8_t *yang_print_tree(const struct lyd_node *root, LYD_FORMAT format,
uint32_t options)
{
uint8_t *darr = NULL;
if (yang_print_tree_append(&darr, root, format, options))
return NULL;
return darr;
}
char *yang_convert_lyd_format(const char *data, size_t data_len,
LYD_FORMAT in_format, LYD_FORMAT out_format,
bool shrink)
{
struct lyd_node *tree = NULL;
uint32_t options = LYD_PRINT_WD_EXPLICIT | LYD_PRINT_WITHSIBLINGS;
uint8_t *result = NULL;
LY_ERR err;
assert(out_format != LYD_LYB);
if (in_format != LYD_LYB && (!data_len || data[data_len - 1] != 0)) {
zlog_err("Corrupt input data, no NUL terminating byte");
return NULL;
}
if (in_format == out_format)
return darr_strdup((const char *)data);
err = lyd_parse_data_mem(ly_native_ctx, (const char *)data, in_format,
LYD_PARSE_ONLY, 0, &tree);
if (err) {
flog_err_sys(EC_LIB_LIBYANG,
"cannot parse input data to convert: %s",
ly_last_errmsg());
return NULL;
}
if (shrink)
options |= LYD_PRINT_SHRINK;
/* Take a guess at the initial capacity based on input data size */
darr_ensure_cap(result, data_len);
err = yang_print_tree_append(&result, tree, out_format, options);
lyd_free_all(tree);
if (err) {
darr_free(result);
return NULL;
}
return (char *)result;
}
const char *yang_print_errors(struct ly_ctx *ly_ctx, char *buf, size_t buf_len)
{
const struct ly_err_item *ei;
ei = ly_err_first(ly_ctx);
if (!ei)
return "";
strlcpy(buf, "YANG error(s):\n", buf_len);
#if (LY_VERSION_MAJOR < 3)
#define data_path path
#else
#define data_path data_path
#endif
for (; ei; ei = ei->next) {
if (ei->data_path) {
strlcat(buf, " Path: ", buf_len);
strlcat(buf, ei->data_path, buf_len);
strlcat(buf, "\n", buf_len);
}
strlcat(buf, " Error: ", buf_len);
strlcat(buf, ei->msg, buf_len);
strlcat(buf, "\n", buf_len);
}
#undef data_path
ly_err_clean(ly_ctx, NULL);
return buf;
}
void yang_debugging_set(bool enable)
{
if (enable) {
ly_log_level(LY_LLDBG);
ly_log_dbg_groups(0xFF);
} else {
ly_log_level(LY_LLERR);
ly_log_dbg_groups(0);
}
}
struct ly_ctx *yang_ctx_new_setup(bool embedded_modules, bool explicit_compile, bool load_library)
{
struct ly_ctx *ctx = NULL;
const char *yang_models_path = YANG_MODELS_PATH;
uint options;
LY_ERR err;
if (access(yang_models_path, R_OK | X_OK)) {
yang_models_path = NULL;
if (errno == ENOENT)
zlog_info("yang model directory \"%s\" does not exist",
YANG_MODELS_PATH);
else
flog_err_sys(EC_LIB_LIBYANG,
"cannot access yang model directory \"%s\"",
YANG_MODELS_PATH);
}
options = LY_CTX_DISABLE_SEARCHDIR_CWD;
if (!load_library)
options |= LY_CTX_NO_YANGLIBRARY;
if (explicit_compile)
options |= LY_CTX_EXPLICIT_COMPILE;
err = ly_ctx_new(yang_models_path, options, &ctx);
if (err)
return NULL;
if (embedded_modules)
ly_ctx_set_module_imp_clb(ctx, yang_module_imp_clb, NULL);
return ctx;
}
void yang_init(bool embedded_modules, bool defer_compile, bool load_library)
{
/* Initialize libyang global parameters that affect all containers. */
ly_set_log_clb(ly_zlog_cb
#if (LY_VERSION_MAJOR < 3)
,
1
#endif
);
ly_log_options(LY_LOLOG | LY_LOSTORE);
/* Initialize libyang container for native models. */
ly_native_ctx = yang_ctx_new_setup(embedded_modules, defer_compile, load_library);
if (!ly_native_ctx) {
flog_err(EC_LIB_LIBYANG, "%s: ly_ctx_new() failed", __func__);
exit(1);
}
yang_translator_init();
}
void yang_init_loading_complete(void)
{
/* Compile everything */
if (ly_ctx_compile(ly_native_ctx) != LY_SUCCESS) {
flog_err(EC_LIB_YANG_MODULE_LOAD,
"%s: failed to compile loaded modules: %s", __func__,
ly_errmsg(ly_native_ctx));
exit(1);
}
}
void yang_terminate(void)
{
struct yang_module *module;
yang_translator_terminate();
while (!RB_EMPTY(yang_modules, &yang_modules)) {
module = RB_ROOT(yang_modules, &yang_modules);
/*
* We shouldn't call ly_ctx_remove_module() here because this
* function also removes other modules that depend on it.
*
* ly_ctx_destroy() will release all memory for us.
*/
RB_REMOVE(yang_modules, &yang_modules, module);
XFREE(MTYPE_YANG_MODULE, module);
}
ly_ctx_destroy(ly_native_ctx);
}
const struct lyd_node *yang_dnode_get_parent(const struct lyd_node *dnode,
const char *name)
{
const struct lyd_node *orig_dnode = dnode;
while (orig_dnode) {
switch (orig_dnode->schema->nodetype) {
case LYS_LIST:
case LYS_CONTAINER:
if (!strcmp(orig_dnode->schema->name, name))
return orig_dnode;
break;
default:
break;
}
orig_dnode = lyd_parent(orig_dnode);
}
return NULL;
}
bool yang_is_last_list_dnode(const struct lyd_node *dnode)
{
return (((dnode->next == NULL)
|| (dnode->next
&& (strcmp(dnode->next->schema->name, dnode->schema->name)
!= 0)))
&& dnode->prev
&& ((dnode->prev == dnode)
|| (strcmp(dnode->prev->schema->name, dnode->schema->name)
!= 0)));
}
bool yang_is_last_level_dnode(const struct lyd_node *dnode)
{
const struct lyd_node *parent;
const struct lyd_node *key_leaf;
uint8_t keys_size;
switch (dnode->schema->nodetype) {
case LYS_LIST:
assert(dnode->parent);
parent = lyd_parent(dnode);
uint snode_num_keys = yang_snode_num_keys(parent->schema);
/* XXX libyang2: q: really don't understand this code. */
key_leaf = dnode->prev;
for (keys_size = 1; keys_size < snode_num_keys; keys_size++)
key_leaf = key_leaf->prev;
if (key_leaf->prev == dnode)
return true;
break;
case LYS_CONTAINER:
return true;
default:
break;
}
return false;
}
const struct lyd_node *
yang_get_subtree_with_no_sibling(const struct lyd_node *dnode)
{
bool parent = true;
const struct lyd_node *node;
node = dnode;
if (node->schema->nodetype != LYS_LIST)
return node;
while (parent) {
switch (node->schema->nodetype) {
case LYS_CONTAINER:
if (!CHECK_FLAG(node->schema->flags, LYS_PRESENCE)) {
if (node->parent
&& (node->parent->schema->module
== dnode->schema->module))
node = lyd_parent(node);
else
parent = false;
} else
parent = false;
break;
case LYS_LIST:
if (yang_is_last_list_dnode(node)
&& yang_is_last_level_dnode(node)) {
if (node->parent
&& (node->parent->schema->module
== dnode->schema->module))
node = lyd_parent(node);
else
parent = false;
} else
parent = false;
break;
default:
parent = false;
break;
}
}
return node;
}
uint32_t yang_get_list_pos(const struct lyd_node *node)
{
return lyd_list_pos(node);
}
uint32_t yang_get_list_elements_count(const struct lyd_node *node)
{
unsigned int count;
const struct lysc_node *schema;
if (!node
|| ((node->schema->nodetype != LYS_LIST)
&& (node->schema->nodetype != LYS_LEAFLIST))) {
return 0;
}
schema = node->schema;
count = 0;
do {
if (node->schema == schema)
++count;
node = node->next;
} while (node);
return count;
}
int yang_get_key_preds(char *s, const struct lysc_node *snode,
struct yang_list_keys *keys, ssize_t space)
{
const struct lysc_node_leaf *skey;
ssize_t len2, len = 0;
ssize_t i = 0;
LY_FOR_KEYS (snode, skey) {
assert(i < keys->num);
len2 = snprintf(s + len, space - len, "[%s='%s']", skey->name,
keys->key[i]);
if (len2 > space - len)
len = space;
else
len += len2;
i++;
}
assert(i == keys->num);
return i;
}
int yang_get_node_keys(struct lyd_node *node, struct yang_list_keys *keys)
{
struct lyd_node *child = lyd_child(node);
keys->num = 0;
for (; child && lysc_is_key(child->schema); child = child->next) {
const char *value = lyd_get_value(child);
if (!value)
return NB_ERR;
strlcpy(keys->key[keys->num], value,
sizeof(keys->key[keys->num]));
keys->num++;
}
return NB_OK;
}
int yang_xpath_pop_node(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;
}
/*
* ------------------------
* Libyang Future Functions
* ------------------------
*
* All these functions are implemented in libyang versions (perhaps unreleased)
* beyond what we require currently so we must supply the functionality.
*/
/*
* Safe to remove after libyang v2.1.xxx is required (.144 has a bug so
* something > .144) https://github.com/CESNET/libyang/issues/2149
*/
LY_ERR yang_lyd_new_list(struct lyd_node_inner *parent,
const struct lysc_node *snode,
const struct yang_list_keys *list_keys,
struct lyd_node **node)
{
#if defined(HAVE_LYD_NEW_LIST3) && 0
LY_ERR err;
const char *keys[LIST_MAXKEYS];
assert(list_keys->num <= LIST_MAXKEYS);
for (int i = 0; i < list_keys->num; i++)
keys[i] = list_keys->key[i];
err = lyd_new_list3(&parent->node, snode->module, snode->name, keys,
NULL, 0, node);
return err;
#else
struct lyd_node *pnode = &parent->node;
const char(*keys)[LIST_MAXKEYLEN] = list_keys->key;
assert(list_keys->num <= 8);
switch (list_keys->num) {
case 0:
return lyd_new_list(pnode, snode->module, snode->name, false,
node);
case 1:
return lyd_new_list(pnode, snode->module, snode->name, false,
node, keys[0]);
case 2:
return lyd_new_list(pnode, snode->module, snode->name, false,
node, keys[0], keys[1]);
case 3:
return lyd_new_list(pnode, snode->module, snode->name, false,
node, keys[0], keys[1], keys[2]);
case 4:
return lyd_new_list(pnode, snode->module, snode->name, false,
node, keys[0], keys[1], keys[2], keys[3]);
case 5:
return lyd_new_list(pnode, snode->module, snode->name, false,
node, keys[0], keys[1], keys[2], keys[3],
keys[4]);
case 6:
return lyd_new_list(pnode, snode->module, snode->name, false,
node, keys[0], keys[1], keys[2], keys[3],
keys[4], keys[5]);
case 7:
return lyd_new_list(pnode, snode->module, snode->name, false,
node, keys[0], keys[1], keys[2], keys[3],
keys[4], keys[5], keys[6]);
case 8:
return lyd_new_list(pnode, snode->module, snode->name, false,
node, keys[0], keys[1], keys[2], keys[3],
keys[4], keys[5], keys[6], keys[7]);
}
_Static_assert(LIST_MAXKEYS == 8, "max key mismatch in switch unroll");
/*NOTREACHED*/
return LY_EINVAL;
#endif
}
/*
* Safe to remove after libyang v2.1.144 is required
*/
LY_ERR yang_lyd_trim_xpath(struct lyd_node **root, const char *xpath)
{
LY_ERR err;
#ifdef HAVE_LYD_TRIM_XPATH
err = lyd_trim_xpath(root, xpath, NULL);
if (err) {
flog_err_sys(EC_LIB_LIBYANG,
"cannot obtain specific result for xpath \"%s\": %s",
xpath, yang_ly_strerrcode(err));
return err;
}
return LY_SUCCESS;
#else
struct lyd_node *node, *sib;
struct lyd_node **remove = NULL;
struct ly_set *set = NULL;
uint32_t i;
*root = lyd_first_sibling(*root);
err = yang_lyd_find_xpath3(NULL, *root, xpath, LY_VALUE_JSON, NULL,
NULL, &set);
if (err) {
flog_err_sys(EC_LIB_LIBYANG,
"cannot obtain specific result for xpath \"%s\": %s",
xpath, yang_ly_strerrcode(err));
return err;
}
/*
* Mark keepers and sweep deleting non-keepers.
*
* NOTE: We assume the data-nodes have NULL priv pointers and use that
* for our mark.
*/
/* Mark */
for (i = 0; i < set->count; i++) {
for (node = set->dnodes[i]; node; node = &node->parent->node) {
if (node->priv)
break;
if (node == set->dnodes[i])
node->priv = (void *)2;
else
node->priv = (void *)1;
}
}
darr_ensure_cap(remove, 128);
LY_LIST_FOR(*root, sib) {
LYD_TREE_DFS_BEGIN (sib, node) {
/*
* If this is a direct matching node then include its
* subtree which won't be marked and would otherwise
* be removed.
*/
if (node->priv == (void *)2)
LYD_TREE_DFS_continue = 1;
else if (!node->priv) {
*darr_append(remove) = node;
LYD_TREE_DFS_continue = 1;
}
LYD_TREE_DFS_END(sib, node);
}
}
darr_foreach_i (remove, i) {
if (remove[i] == *root) {
assert(*root);
*root = (*root)->next;
}
lyd_free_tree(remove[i]);
}
darr_free(remove);
ly_set_free(set, NULL);
return LY_SUCCESS;
#endif
}
/* Can be replaced by `lyd_parse_data` with libyang >= 2.1.156 */
LY_ERR yang_lyd_parse_data(const struct ly_ctx *ctx, struct lyd_node *parent,
struct ly_in *in, LYD_FORMAT format,
uint32_t parse_options, uint32_t validate_options,
struct lyd_node **tree)
{
struct lyd_node *child;
LY_ERR err;
err = lyd_parse_data(ctx, parent, in, format, parse_options,
validate_options, tree);
if (err)
return err;
if (!parent || !(parse_options & LYD_PARSE_ONLY))
return LY_SUCCESS;
/*
* Versions prior to 2.1.156 don't return `tree` if `parent` is not NULL
* and validation is disabled (`LYD_PARSE_ONLY`). To work around this,
* go through the children and find the one with `LYD_NEW` flag set.
*/
*tree = NULL;
LY_LIST_FOR (lyd_child_no_keys(parent), child) {
if (child->flags & LYD_NEW) {
*tree = child;
break;
}
}
assert(tree);
return LY_SUCCESS;
}
/*
* Safe to remove after libyang v2.1.128 is required
*/
const char *yang_ly_strerrcode(LY_ERR err)
{
#ifdef HAVE_LY_STRERRCODE
return ly_strerrcode(err);
#else
switch (err) {
case LY_SUCCESS:
return "ok";
case LY_EMEM:
return "out of memory";
case LY_ESYS:
return "system error";
case LY_EINVAL:
return "invalid value given";
case LY_EEXIST:
return "item exists";
case LY_ENOTFOUND:
return "item not found";
case LY_EINT:
return "operation interrupted";
case LY_EVALID:
return "validation failed";
case LY_EDENIED:
return "access denied";
case LY_EINCOMPLETE:
return "incomplete";
case LY_ERECOMPILE:
return "compile error";
case LY_ENOT:
return "not";
case LY_EPLUGIN:
case LY_EOTHER:
return "other";
default:
return "unknown";
}
#endif
}
/*
* Safe to remove after libyang v2.1.128 is required
*/
const char *yang_ly_strvecode(LY_VECODE vecode)
{
#ifdef HAVE_LY_STRVECODE
return ly_strvecode(vecode);
#else
switch (vecode) {
case LYVE_SUCCESS:
return "";
case LYVE_SYNTAX:
return "syntax";
case LYVE_SYNTAX_YANG:
return "yang-syntax";
case LYVE_SYNTAX_YIN:
return "yin-syntax";
case LYVE_REFERENCE:
return "reference";
case LYVE_XPATH:
return "xpath";
case LYVE_SEMANTICS:
return "semantics";
case LYVE_SYNTAX_XML:
return "xml-syntax";
case LYVE_SYNTAX_JSON:
return "json-syntax";
case LYVE_DATA:
return "data";
case LYVE_OTHER:
return "other";
default:
return "unknown";
}
#endif
}