Merge pull request #3342 from opensourcerouting/nb-operational-data

Northbound: improved support for YANG-modeled operational data
This commit is contained in:
Russ White 2018-11-29 15:19:38 -05:00 committed by GitHub
commit 9f7b49e105
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 1765 additions and 464 deletions

View file

@ -134,6 +134,12 @@ static struct log_ref ferr_lib_warn[] = {
.description = "The northbound subsystem failed to edit a candidate configuration",
.suggestion = "This is a bug; please report it"
},
{
.code = EC_LIB_NB_OPERATIONAL_DATA,
.title = "Failure to obtain operational data",
.description = "The northbound subsystem failed to obtain YANG-modeled operational data",
.suggestion = "This is a bug; please report it"
},
{
.code = EC_LIB_NB_TRANSACTION_CREATION_FAILED,
.title = "Failure to create a configuration transaction",

View file

@ -62,6 +62,7 @@ enum lib_log_refs {
EC_LIB_NB_CB_RPC,
EC_LIB_NB_CANDIDATE_INVALID,
EC_LIB_NB_CANDIDATE_EDIT_ERROR,
EC_LIB_NB_OPERATIONAL_DATA,
EC_LIB_NB_TRANSACTION_CREATION_FAILED,
EC_LIB_NB_TRANSACTION_RECORD_FAILED,
EC_LIB_LIBYANG,

View file

@ -50,8 +50,26 @@ static void nb_transaction_free(struct nb_transaction *transaction);
static int nb_transaction_process(enum nb_event event,
struct nb_transaction *transaction);
static void nb_transaction_apply_finish(struct nb_transaction *transaction);
static int nb_oper_data_iter_node(const struct lys_node *snode,
const char *xpath, const void *list_entry,
const struct yang_list_keys *list_keys,
struct yang_translator *translator,
bool first, uint32_t flags,
nb_oper_data_cb cb, void *arg);
static void nb_node_new_cb(const struct lys_node *snode, void *arg1, void *arg2)
static int nb_node_check_config_only(const struct lys_node *snode, void *arg)
{
bool *config_only = arg;
if (CHECK_FLAG(snode->flags, LYS_CONFIG_R)) {
*config_only = false;
return YANG_ITER_STOP;
}
return YANG_ITER_CONTINUE;
}
static int nb_node_new_cb(const struct lys_node *snode, void *arg)
{
struct nb_node *nb_node;
struct lys_node *sparent, *sparent_list;
@ -67,21 +85,46 @@ static void nb_node_new_cb(const struct lys_node *snode, void *arg1, void *arg2)
if (sparent_list)
nb_node->parent_list = sparent_list->priv;
/* Set flags. */
if (CHECK_FLAG(snode->nodetype, LYS_CONTAINER | LYS_LIST)) {
bool config_only = true;
yang_snodes_iterate_subtree(snode, nb_node_check_config_only,
YANG_ITER_ALLOW_AUGMENTATIONS,
&config_only);
if (config_only)
SET_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY);
}
/*
* Link the northbound node and the libyang schema node with one
* another.
*/
nb_node->snode = snode;
lys_set_private(snode, nb_node);
return YANG_ITER_CONTINUE;
}
static void nb_node_del_cb(const struct lys_node *snode, void *arg1, void *arg2)
static int nb_node_del_cb(const struct lys_node *snode, void *arg)
{
struct nb_node *nb_node;
nb_node = snode->priv;
lys_set_private(snode, NULL);
XFREE(MTYPE_NB_NODE, nb_node);
return YANG_ITER_CONTINUE;
}
void nb_nodes_create(void)
{
yang_snodes_iterate_all(nb_node_new_cb, 0, NULL);
}
void nb_nodes_delete(void)
{
yang_snodes_iterate_all(nb_node_del_cb, 0, NULL);
}
struct nb_node *nb_node_find(const char *xpath)
@ -170,15 +213,16 @@ static unsigned int nb_node_validate_priority(const struct nb_node *nb_node)
return 0;
}
static void nb_node_validate(const struct lys_node *snode, void *arg1,
void *arg2)
static int nb_node_validate(const struct lys_node *snode, void *arg)
{
struct nb_node *nb_node = snode->priv;
unsigned int *errors = arg1;
unsigned int *errors = arg;
/* Validate callbacks and priority. */
*errors += nb_node_validate_cbs(nb_node);
*errors += nb_node_validate_priority(nb_node);
return YANG_ITER_CONTINUE;
}
struct nb_config *nb_config_new(struct lyd_node *dnode)
@ -189,7 +233,7 @@ struct nb_config *nb_config_new(struct lyd_node *dnode)
if (dnode)
config->dnode = dnode;
else
config->dnode = yang_dnode_new(ly_native_ctx);
config->dnode = yang_dnode_new(ly_native_ctx, true);
config->version = 0;
return config;
@ -236,7 +280,8 @@ void nb_config_replace(struct nb_config *config_dst,
config_dst->version = config_src->version;
/* Update dnode. */
yang_dnode_free(config_dst->dnode);
if (config_dst->dnode)
yang_dnode_free(config_dst->dnode);
if (preserve_source) {
config_dst->dnode = yang_dnode_dup(config_src->dnode);
} else {
@ -380,7 +425,8 @@ static void nb_config_diff(const struct nb_config *config1,
nb_config_diff_add_change(changes, operation, dnode);
if (type == LYD_DIFF_CREATED
&& (dnode->schema->nodetype & (LYS_CONTAINER | LYS_LIST)))
&& CHECK_FLAG(dnode->schema->nodetype,
LYS_CONTAINER | LYS_LIST))
nb_config_diff_new_subtree(dnode, changes);
}
@ -890,15 +936,341 @@ static void nb_transaction_apply_finish(struct nb_transaction *transaction)
}
}
static int nb_oper_data_iter_children(const struct lys_node *snode,
const char *xpath, const void *list_entry,
const struct yang_list_keys *list_keys,
struct yang_translator *translator,
bool first, uint32_t flags,
nb_oper_data_cb cb, void *arg)
{
struct lys_node *child;
LY_TREE_FOR (snode->child, child) {
int ret;
ret = nb_oper_data_iter_node(child, xpath, list_entry,
list_keys, translator, false,
flags, cb, arg);
if (ret != NB_OK)
return ret;
}
return NB_OK;
}
static int nb_oper_data_iter_leaf(const struct nb_node *nb_node,
const char *xpath, const void *list_entry,
const struct yang_list_keys *list_keys,
struct yang_translator *translator,
uint32_t flags, nb_oper_data_cb cb, void *arg)
{
struct yang_data *data;
if (CHECK_FLAG(nb_node->snode->flags, LYS_CONFIG_W))
return NB_OK;
/* Ignore list keys. */
if (lys_is_key((struct lys_node_leaf *)nb_node->snode, NULL))
return NB_OK;
data = nb_node->cbs.get_elem(xpath, list_entry);
if (data == NULL)
/* Leaf of type "empty" is not present. */
return NB_OK;
return (*cb)(nb_node->snode, translator, data, arg);
}
static int nb_oper_data_iter_container(const struct nb_node *nb_node,
const char *xpath,
const void *list_entry,
const struct yang_list_keys *list_keys,
struct yang_translator *translator,
uint32_t flags, nb_oper_data_cb cb,
void *arg)
{
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
return NB_OK;
/* Presence containers. */
if (nb_node->cbs.get_elem) {
struct yang_data *data;
int ret;
data = nb_node->cbs.get_elem(xpath, list_entry);
if (data == NULL)
/* Presence container is not present. */
return NB_OK;
ret = (*cb)(nb_node->snode, translator, data, arg);
if (ret != NB_OK)
return ret;
}
/* Iterate over the child nodes. */
return nb_oper_data_iter_children(nb_node->snode, xpath, list_entry,
list_keys, translator, false, flags,
cb, arg);
}
static int
nb_oper_data_iter_leaflist(const struct nb_node *nb_node, const char *xpath,
const void *parent_list_entry,
const struct yang_list_keys *parent_list_keys,
struct yang_translator *translator, uint32_t flags,
nb_oper_data_cb cb, void *arg)
{
const void *list_entry = NULL;
if (CHECK_FLAG(nb_node->snode->flags, LYS_CONFIG_W))
return NB_OK;
do {
struct yang_data *data;
int ret;
list_entry =
nb_node->cbs.get_next(parent_list_entry, list_entry);
if (!list_entry)
/* End of the list. */
break;
data = nb_node->cbs.get_elem(xpath, list_entry);
if (data == NULL)
continue;
ret = (*cb)(nb_node->snode, translator, data, arg);
if (ret != NB_OK)
return ret;
} while (list_entry);
return NB_OK;
}
static int nb_oper_data_iter_list(const struct nb_node *nb_node,
const char *xpath_list,
const void *parent_list_entry,
const struct yang_list_keys *parent_list_keys,
struct yang_translator *translator,
uint32_t flags, nb_oper_data_cb cb, void *arg)
{
struct lys_node_list *slist = (struct lys_node_list *)nb_node->snode;
const void *list_entry = NULL;
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
return NB_OK;
/* Iterate over all list entries. */
do {
struct yang_list_keys list_keys;
char xpath[XPATH_MAXLEN];
int ret;
/* Obtain list entry. */
list_entry =
nb_node->cbs.get_next(parent_list_entry, list_entry);
if (!list_entry)
/* End of the list. */
break;
/* Obtain the list entry keys. */
if (nb_node->cbs.get_keys(list_entry, &list_keys) != NB_OK) {
flog_warn(EC_LIB_NB_CB_STATE,
"%s: failed to get list keys", __func__);
return NB_ERR;
}
/* Build XPath of the list entry. */
strlcpy(xpath, xpath_list, sizeof(xpath));
for (unsigned int i = 0; i < list_keys.num; i++) {
snprintf(xpath + strlen(xpath),
sizeof(xpath) - strlen(xpath), "[%s='%s']",
slist->keys[i]->name, list_keys.key[i]);
}
/* Iterate over the child nodes. */
ret = nb_oper_data_iter_children(
nb_node->snode, xpath, list_entry, &list_keys,
translator, false, flags, cb, arg);
if (ret != NB_OK)
return ret;
} while (list_entry);
return NB_OK;
}
static int nb_oper_data_iter_node(const struct lys_node *snode,
const char *xpath_parent,
const void *list_entry,
const struct yang_list_keys *list_keys,
struct yang_translator *translator,
bool first, uint32_t flags,
nb_oper_data_cb cb, void *arg)
{
struct nb_node *nb_node;
char xpath[XPATH_MAXLEN];
int ret = NB_OK;
if (!first && CHECK_FLAG(flags, NB_OPER_DATA_ITER_NORECURSE)
&& CHECK_FLAG(snode->nodetype, LYS_CONTAINER | LYS_LIST))
return NB_OK;
/* Update XPath. */
strlcpy(xpath, xpath_parent, sizeof(xpath));
if (!first && snode->nodetype != LYS_USES)
snprintf(xpath + strlen(xpath), sizeof(xpath) - strlen(xpath),
"/%s", snode->name);
nb_node = snode->priv;
switch (snode->nodetype) {
case LYS_CONTAINER:
ret = nb_oper_data_iter_container(nb_node, xpath, list_entry,
list_keys, translator, flags,
cb, arg);
break;
case LYS_LEAF:
ret = nb_oper_data_iter_leaf(nb_node, xpath, list_entry,
list_keys, translator, flags, cb,
arg);
break;
case LYS_LEAFLIST:
ret = nb_oper_data_iter_leaflist(nb_node, xpath, list_entry,
list_keys, translator, flags,
cb, arg);
break;
case LYS_LIST:
ret = nb_oper_data_iter_list(nb_node, xpath, list_entry,
list_keys, translator, flags, cb,
arg);
break;
case LYS_USES:
ret = nb_oper_data_iter_children(snode, xpath, list_entry,
list_keys, translator, false,
flags, cb, arg);
break;
default:
break;
}
return ret;
}
int nb_oper_data_iterate(const char *xpath, struct yang_translator *translator,
uint32_t flags, nb_oper_data_cb cb, void *arg)
{
struct nb_node *nb_node;
const void *list_entry = NULL;
struct yang_list_keys list_keys;
struct list *list_dnodes;
struct lyd_node *dnode, *dn;
struct listnode *ln;
int ret;
nb_node = nb_node_find(xpath);
if (!nb_node) {
flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
"%s: unknown data path: %s", __func__, xpath);
return NB_ERR;
}
/* For now this function works only with containers and lists. */
if (!CHECK_FLAG(nb_node->snode->nodetype, LYS_CONTAINER | LYS_LIST)) {
flog_warn(
EC_LIB_NB_OPERATIONAL_DATA,
"%s: can't iterate over YANG leaf or leaf-list [xpath %s]",
__func__, xpath);
return NB_ERR;
}
/*
* Create a data tree from the XPath so that we can parse the keys of
* all YANG lists (if any).
*/
ly_errno = 0;
dnode = lyd_new_path(NULL, ly_native_ctx, xpath, NULL, 0,
LYD_PATH_OPT_UPDATE);
if (!dnode && ly_errno) {
flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed",
__func__);
return NB_ERR;
}
/*
* We can remove the following two lines once we depend on
* libyang-v0.16-r2, which has the LYD_PATH_OPT_NOPARENTRET flag for
* lyd_new_path().
*/
dnode = yang_dnode_get(dnode, xpath);
assert(dnode);
/*
* Create a linked list to sort the data nodes starting from the root.
*/
list_dnodes = list_new();
for (dn = dnode; dn; dn = dn->parent) {
if (dn->schema->nodetype != LYS_LIST || !dn->child)
continue;
listnode_add_head(list_dnodes, dn);
}
/*
* Use the northbound callbacks to find list entry pointer corresponding
* to the given XPath.
*/
for (ALL_LIST_ELEMENTS_RO(list_dnodes, ln, dn)) {
struct lyd_node *child;
struct nb_node *nn;
unsigned int n = 0;
/* Obtain the list entry keys. */
memset(&list_keys, 0, sizeof(list_keys));
LY_TREE_FOR (dn->child, child) {
if (!lys_is_key((struct lys_node_leaf *)child->schema,
NULL))
continue;
strlcpy(list_keys.key[n],
yang_dnode_get_string(child, NULL),
sizeof(list_keys.key[n]));
n++;
}
list_keys.num = n;
assert(list_keys.num
== ((struct lys_node_list *)dn->schema)->keys_size);
/* Find the list entry pointer. */
nn = dn->schema->priv;
list_entry = nn->cbs.lookup_entry(list_entry, &list_keys);
if (list_entry == NULL) {
list_delete(&list_dnodes);
yang_dnode_free(dnode);
return NB_ERR_NOT_FOUND;
}
}
/* If a list entry was given, iterate over that list entry only. */
if (dnode->schema->nodetype == LYS_LIST && dnode->child)
ret = nb_oper_data_iter_children(
nb_node->snode, xpath, list_entry, &list_keys,
translator, true, flags, cb, arg);
else
ret = nb_oper_data_iter_node(nb_node->snode, xpath, list_entry,
&list_keys, translator, true,
flags, cb, arg);
list_delete(&list_dnodes);
yang_dnode_free(dnode);
return ret;
}
bool nb_operation_is_valid(enum nb_operation operation,
const struct lys_node *snode)
{
struct nb_node *nb_node = snode->priv;
struct lys_node_container *scontainer;
struct lys_node_leaf *sleaf;
switch (operation) {
case NB_OP_CREATE:
if (!(snode->flags & LYS_CONFIG_W))
if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
return false;
switch (snode->nodetype) {
@ -920,7 +1292,7 @@ bool nb_operation_is_valid(enum nb_operation operation,
}
return true;
case NB_OP_MODIFY:
if (!(snode->flags & LYS_CONFIG_W))
if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
return false;
switch (snode->nodetype) {
@ -938,7 +1310,7 @@ bool nb_operation_is_valid(enum nb_operation operation,
}
return true;
case NB_OP_DELETE:
if (!(snode->flags & LYS_CONFIG_W))
if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
return false;
switch (snode->nodetype) {
@ -957,7 +1329,8 @@ bool nb_operation_is_valid(enum nb_operation operation,
return true;
if (sleaf->when)
return true;
if ((sleaf->flags & LYS_MAND_TRUE) || sleaf->dflt)
if (CHECK_FLAG(sleaf->flags, LYS_MAND_TRUE)
|| sleaf->dflt)
return false;
break;
case LYS_CONTAINER:
@ -973,13 +1346,13 @@ bool nb_operation_is_valid(enum nb_operation operation,
}
return true;
case NB_OP_MOVE:
if (!(snode->flags & LYS_CONFIG_W))
if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
return false;
switch (snode->nodetype) {
case LYS_LIST:
case LYS_LEAFLIST:
if (!(snode->flags & LYS_USERORDERED))
if (!CHECK_FLAG(snode->flags, LYS_USERORDERED))
return false;
break;
default:
@ -987,15 +1360,16 @@ bool nb_operation_is_valid(enum nb_operation operation,
}
return true;
case NB_OP_APPLY_FINISH:
if (!(snode->flags & LYS_CONFIG_W))
if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
return false;
return true;
case NB_OP_GET_ELEM:
if (!(snode->flags & LYS_CONFIG_R))
if (!CHECK_FLAG(snode->flags, LYS_CONFIG_R))
return false;
switch (snode->nodetype) {
case LYS_LEAF:
case LYS_LEAFLIST:
break;
case LYS_CONTAINER:
scontainer = (struct lys_node_container *)snode;
@ -1007,20 +1381,32 @@ bool nb_operation_is_valid(enum nb_operation operation,
}
return true;
case NB_OP_GET_NEXT:
case NB_OP_GET_KEYS:
case NB_OP_LOOKUP_ENTRY:
if (!(snode->flags & LYS_CONFIG_R))
return false;
switch (snode->nodetype) {
case LYS_LIST:
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
return false;
break;
case LYS_LEAFLIST:
if (CHECK_FLAG(snode->flags, LYS_CONFIG_W))
return false;
break;
default:
return false;
}
return true;
case NB_OP_GET_KEYS:
case NB_OP_LOOKUP_ENTRY:
switch (snode->nodetype) {
case LYS_LIST:
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
return false;
break;
default:
return false;
}
return true;
case NB_OP_RPC:
if (snode->flags & (LYS_CONFIG_W | LYS_CONFIG_R))
if (CHECK_FLAG(snode->flags, LYS_CONFIG_W | LYS_CONFIG_R))
return false;
switch (snode->nodetype) {
@ -1162,14 +1548,14 @@ void nb_init(const struct frr_yang_module_info *modules[], size_t nmodules)
yang_module_load(modules[i]->name);
/* Create a nb_node for all YANG schema nodes. */
yang_all_snodes_iterate(nb_node_new_cb, 0, NULL, NULL);
nb_nodes_create();
/* Load northbound callbacks. */
for (size_t i = 0; i < nmodules; i++)
nb_load_callbacks(modules[i]);
/* Validate northbound callbacks. */
yang_all_snodes_iterate(nb_node_validate, 0, &errors, NULL);
yang_snodes_iterate_all(nb_node_validate, 0, &errors);
if (errors > 0) {
flog_err(
EC_LIB_NB_CBS_VALIDATION,
@ -1197,7 +1583,7 @@ void nb_terminate(void)
nb_cli_terminate();
/* Delete all nb_node's from all YANG modules. */
yang_all_snodes_iterate(nb_node_del_cb, 0, NULL, NULL);
nb_nodes_delete();
/* Delete the running configuration. */
nb_config_free(running_config);

View file

@ -21,9 +21,10 @@
#define _FRR_NORTHBOUND_H_
#include "hook.h"
#include "yang.h"
#include "linklist.h"
#include "openbsd-tree.h"
#include "yang.h"
#include "yang_translator.h"
/* Forward declaration(s). */
struct vty;
@ -211,15 +212,15 @@ struct nb_callbacks {
/*
* Operational data callback.
*
* The callback function should return the value of a specific leaf or
* inform if a typeless value (presence containers or leafs of type
* empty) exists or not.
* The callback function should return the value of a specific leaf,
* leaf-list entry or inform if a typeless value (presence containers or
* leafs of type empty) exists or not.
*
* xpath
* YANG data path of the data we want to get.
*
* list_entry
* Pointer to list entry.
* Pointer to list entry (might be NULL).
*
* Returns:
* Pointer to newly created yang_data structure, or NULL to indicate
@ -229,22 +230,24 @@ struct nb_callbacks {
const void *list_entry);
/*
* Operational data callback for YANG lists.
* Operational data callback for YANG lists and leaf-lists.
*
* The callback function should return the next entry in the list. The
* 'list_entry' parameter will be NULL on the first invocation.
* The callback function should return the next entry in the list or
* leaf-list. The 'list_entry' parameter will be NULL on the first
* invocation.
*
* xpath
* Data path of the YANG list.
* parent_list_entry
* Pointer to parent list entry.
*
* list_entry
* Pointer to list entry.
* Pointer to (leaf-)list entry.
*
* Returns:
* Pointer to the next entry in the list, or NULL to signal that the
* end of the list was reached.
* Pointer to the next entry in the (leaf-)list, or NULL to signal
* that the end of the (leaf-)list was reached.
*/
const void *(*get_next)(const char *xpath, const void *list_entry);
const void *(*get_next)(const void *parent_list_entry,
const void *list_entry);
/*
* Operational data callback for YANG lists.
@ -270,13 +273,17 @@ struct nb_callbacks {
* The callback function should return a list entry based on the list
* keys given as a parameter.
*
* parent_list_entry
* Pointer to parent list entry.
*
* keys
* Structure containing the keys of the list entry.
*
* Returns:
* Pointer to the list entry if found, or NULL if not found.
*/
const void *(*lookup_entry)(const struct yang_list_keys *keys);
const void *(*lookup_entry)(const void *parent_list_entry,
const struct yang_list_keys *keys);
/*
* RPC and action callback.
@ -349,11 +356,16 @@ struct nb_node {
/* Pointer to the nearest parent list, if any. */
struct nb_node *parent_list;
/* Flags. */
uint8_t flags;
#ifdef HAVE_CONFD
/* ConfD hash value corresponding to this YANG path. */
int confd_hash;
#endif
};
/* The YANG container or list contains only config data. */
#define F_NB_NODE_CONFIG_ONLY 0x01
struct frr_yang_module_info {
/* YANG module name. */
@ -429,12 +441,30 @@ struct nb_transaction {
struct nb_config_cbs changes;
};
/* Callback function used by nb_oper_data_iterate(). */
typedef int (*nb_oper_data_cb)(const struct lys_node *snode,
struct yang_translator *translator,
struct yang_data *data, void *arg);
/* Iterate over direct child nodes only. */
#define NB_OPER_DATA_ITER_NORECURSE 0x0001
DECLARE_HOOK(nb_notification_send, (const char *xpath, struct list *arguments),
(xpath, arguments))
extern int debug_northbound;
extern struct nb_config *running_config;
/*
* Create a northbound node for all YANG schema nodes.
*/
void nb_nodes_create(void);
/*
* Delete all northbound nodes from all YANG schema nodes.
*/
void nb_nodes_delete(void);
/*
* Find the northbound node corresponding to a YANG data path.
*
@ -685,6 +715,31 @@ extern int nb_candidate_commit(struct nb_config *candidate,
enum nb_client client, bool save_transaction,
const char *comment, uint32_t *transaction_id);
/*
* Iterate over operetional data.
*
* xpath
* Data path of the YANG data we want to iterate over.
*
* translator
* YANG module translator (might be NULL).
*
* flags
* NB_OPER_DATA_ITER_ flags to control how the iteration is performed.
*
* cb
* Function to call with each data node.
*
* arg
* Arbitrary argument passed as the fourth parameter in each call to 'cb'.
*
* Returns:
* NB_OK on success, NB_ERR otherwise.
*/
extern int nb_oper_data_iterate(const char *xpath,
struct yang_translator *translator,
uint32_t flags, nb_oper_data_cb cb, void *arg);
/*
* Validate if the northbound operation is valid for the given node.
*

View file

@ -386,7 +386,7 @@ static int nb_cli_show_config_libyang(struct vty *vty, LYD_FORMAT format,
{
struct lyd_node *dnode;
char *strp;
int options;
int options = 0;
dnode = yang_dnode_dup(config->dnode);
if (translator
@ -398,11 +398,11 @@ static int nb_cli_show_config_libyang(struct vty *vty, LYD_FORMAT format,
return CMD_WARNING;
}
options = LYP_FORMAT | LYP_WITHSIBLINGS;
SET_FLAG(options, LYP_FORMAT | LYP_WITHSIBLINGS);
if (with_defaults)
options |= LYP_WD_ALL;
SET_FLAG(options, LYP_WD_ALL);
else
options |= LYP_WD_TRIM;
SET_FLAG(options, LYP_WD_TRIM);
if (lyd_print_mem(&strp, dnode, format, options) == 0 && strp) {
vty_out(vty, "%s", strp);
@ -1124,6 +1124,117 @@ DEFPY (show_config_transaction,
#endif /* HAVE_CONFIG_ROLLBACKS */
}
static int nb_cli_oper_data_cb(const struct lys_node *snode,
struct yang_translator *translator,
struct yang_data *data, void *arg)
{
struct lyd_node *dnode = arg;
struct ly_ctx *ly_ctx;
if (translator) {
int ret;
ret = yang_translate_xpath(translator,
YANG_TRANSLATE_FROM_NATIVE,
data->xpath, sizeof(data->xpath));
switch (ret) {
case YANG_TRANSLATE_SUCCESS:
break;
case YANG_TRANSLATE_NOTFOUND:
goto exit;
case YANG_TRANSLATE_FAILURE:
goto error;
}
ly_ctx = translator->ly_ctx;
} else
ly_ctx = ly_native_ctx;
ly_errno = 0;
dnode = lyd_new_path(dnode, ly_ctx, data->xpath, (void *)data->value, 0,
LYD_PATH_OPT_UPDATE);
if (!dnode && ly_errno) {
flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path() failed",
__func__);
goto error;
}
exit:
yang_data_free(data);
return NB_OK;
error:
yang_data_free(data);
return NB_ERR;
}
DEFPY (show_yang_operational_data,
show_yang_operational_data_cmd,
"show yang operational-data XPATH$xpath\
[{\
format <json$json|xml$xml>\
|translate WORD$translator_family\
}]",
SHOW_STR
"YANG information\n"
"Show YANG operational data\n"
"XPath expression specifying the YANG data path\n"
"Set the output format\n"
"JavaScript Object Notation\n"
"Extensible Markup Language\n"
"Translate operational data\n"
"YANG module translator\n")
{
LYD_FORMAT format;
struct yang_translator *translator = NULL;
struct ly_ctx *ly_ctx;
struct lyd_node *dnode;
char *strp;
if (xml)
format = LYD_XML;
else
format = LYD_JSON;
if (translator_family) {
translator = yang_translator_find(translator_family);
if (!translator) {
vty_out(vty, "%% Module translator \"%s\" not found\n",
translator_family);
return CMD_WARNING;
}
ly_ctx = translator->ly_ctx;
} else
ly_ctx = ly_native_ctx;
/* Obtain data. */
dnode = yang_dnode_new(ly_ctx, false);
if (nb_oper_data_iterate(xpath, translator, 0, nb_cli_oper_data_cb,
dnode)
!= NB_OK) {
vty_out(vty, "%% Failed to fetch operational data.\n");
yang_dnode_free(dnode);
return CMD_WARNING;
}
lyd_validate(&dnode, LYD_OPT_DATA | LYD_OPT_DATA_NO_YANGLIB, ly_ctx);
/* Display the data. */
if (lyd_print_mem(&strp, dnode, format,
LYP_FORMAT | LYP_WITHSIBLINGS | LYP_WD_ALL)
!= 0
|| !strp) {
vty_out(vty, "%% Failed to display operational data.\n");
yang_dnode_free(dnode);
return CMD_WARNING;
}
vty_out(vty, "%s", strp);
free(strp);
yang_dnode_free(dnode);
return CMD_SUCCESS;
}
DEFPY (show_yang_module,
show_yang_module_cmd,
"show yang module [module-translator WORD$translator_family]",
@ -1463,6 +1574,7 @@ void nb_cli_init(void)
/* Other commands. */
install_element(CONFIG_NODE, &yang_module_translator_load_cmd);
install_element(CONFIG_NODE, &yang_module_translator_unload_cmd);
install_element(ENABLE_NODE, &show_yang_operational_data_cmd);
install_element(ENABLE_NODE, &show_yang_module_cmd);
install_element(ENABLE_NODE, &show_yang_module_detail_cmd);
install_element(ENABLE_NODE, &show_yang_module_translator_cmd);

View file

@ -91,22 +91,61 @@ static int frr_confd_val2str(const char *xpath, const confd_value_t *value,
return 0;
}
/* Obtain list keys from ConfD hashed keypath. */
static void frr_confd_hkeypath_get_keys(const confd_hkeypath_t *kp,
struct yang_list_keys *keys)
/* Obtain list entry from ConfD hashed keypath. */
static int frr_confd_hkeypath_get_list_entry(const confd_hkeypath_t *kp,
struct nb_node *nb_node,
const void **list_entry)
{
memset(keys, 0, sizeof(*keys));
for (int i = 0; i < kp->len; i++) {
struct nb_node *nb_node_list;
int parent_lists = 0;
int curr_list = 0;
*list_entry = NULL;
/*
* Count the number of YANG lists in the path, disconsidering the
* last element.
*/
nb_node_list = nb_node;
while (nb_node_list->parent_list) {
nb_node_list = nb_node_list->parent_list;
parent_lists++;
}
if (nb_node->snode->nodetype != LYS_LIST && parent_lists == 0)
return 0;
/* Start from the beginning and move down the tree. */
for (int i = kp->len; i >= 0; i--) {
struct yang_list_keys keys;
/* Not a YANG list. */
if (kp->v[i][0].type != C_BUF)
continue;
/* Obtain list keys. */
memset(&keys, 0, sizeof(keys));
for (int j = 0; kp->v[i][j].type != C_NOEXISTS; j++) {
strlcpy(keys->key[keys->num].value,
strlcpy(keys.key[keys.num],
(char *)kp->v[i][j].val.buf.ptr,
sizeof(keys->key[keys->num].value));
keys->num++;
sizeof(keys.key[keys.num]));
keys.num++;
}
/* Obtain northbound node associated to the YANG list. */
nb_node_list = nb_node;
for (int j = curr_list; j < parent_lists; j++)
nb_node_list = nb_node_list->parent_list;
/* Obtain list entry. */
*list_entry =
nb_node_list->cbs.lookup_entry(*list_entry, &keys);
if (*list_entry == NULL)
return -1;
curr_list++;
}
return 0;
}
/* Fill the current date and time into a confd_datetime structure. */
@ -430,6 +469,9 @@ static int frr_confd_init_cdb(void)
continue;
}
if (CHECK_FLAG(snode->flags, LYS_CONFIG_R))
continue;
nb_node = snode->priv;
if (debug_northbound)
zlog_debug("%s: subscribing to '%s'", __func__,
@ -490,12 +532,13 @@ static int frr_confd_transaction_init(struct confd_trans_ctx *tctx)
return CONFD_OK;
}
#define CONFD_MAX_CHILD_NODES 32
static int frr_confd_data_get_elem(struct confd_trans_ctx *tctx,
confd_hkeypath_t *kp)
{
struct nb_node *nb_node, *parent_list;
struct nb_node *nb_node;
char xpath[BUFSIZ];
struct yang_list_keys keys;
struct yang_data *data;
confd_value_t v;
const void *list_entry = NULL;
@ -510,17 +553,9 @@ static int frr_confd_data_get_elem(struct confd_trans_ctx *tctx,
return CONFD_OK;
}
parent_list = nb_node->parent_list;
if (parent_list) {
frr_confd_hkeypath_get_keys(kp, &keys);
list_entry = parent_list->cbs.lookup_entry(&keys);
if (!list_entry) {
flog_warn(EC_LIB_NB_CB_STATE,
"%s: list entry not found: %s", __func__,
xpath);
confd_data_reply_not_found(tctx);
return CONFD_OK;
}
if (frr_confd_hkeypath_get_list_entry(kp, nb_node, &list_entry) != 0) {
confd_data_reply_not_found(tctx);
return CONFD_OK;
}
data = nb_node->cbs.get_elem(xpath, list_entry);
@ -543,7 +578,8 @@ static int frr_confd_data_get_next(struct confd_trans_ctx *tctx,
struct nb_node *nb_node;
char xpath[BUFSIZ];
struct yang_list_keys keys;
const void *nb_next;
struct yang_data *data;
const void *parent_list_entry, *nb_next;
confd_value_t v[LIST_MAXKEYS];
frr_confd_get_xpath(kp, xpath, sizeof(xpath));
@ -556,24 +592,51 @@ static int frr_confd_data_get_next(struct confd_trans_ctx *tctx,
return CONFD_OK;
}
nb_next = nb_node->cbs.get_next(xpath,
(next == -1) ? NULL : (void *)next);
if (!nb_next) {
/* End of the list. */
confd_data_reply_next_key(tctx, NULL, -1, -1);
return CONFD_OK;
}
if (nb_node->cbs.get_keys(nb_next, &keys) != NB_OK) {
flog_warn(EC_LIB_NB_CB_STATE, "%s: failed to get list keys",
__func__);
if (frr_confd_hkeypath_get_list_entry(kp, nb_node, &parent_list_entry)
!= 0) {
/* List entry doesn't exist anymore. */
confd_data_reply_next_key(tctx, NULL, -1, -1);
return CONFD_OK;
}
/* Feed keys to ConfD. */
for (size_t i = 0; i < keys.num; i++)
CONFD_SET_STR(&v[i], keys.key[i].value);
confd_data_reply_next_key(tctx, v, keys.num, (long)nb_next);
nb_next = nb_node->cbs.get_next(parent_list_entry,
(next == -1) ? NULL : (void *)next);
if (!nb_next) {
/* End of the list or leaf-list. */
confd_data_reply_next_key(tctx, NULL, -1, -1);
return CONFD_OK;
}
switch (nb_node->snode->nodetype) {
case LYS_LIST:
memset(&keys, 0, sizeof(keys));
if (nb_node->cbs.get_keys(nb_next, &keys) != NB_OK) {
flog_warn(EC_LIB_NB_CB_STATE,
"%s: failed to get list keys", __func__);
confd_data_reply_next_key(tctx, NULL, -1, -1);
return CONFD_OK;
}
/* Feed keys to ConfD. */
for (size_t i = 0; i < keys.num; i++)
CONFD_SET_STR(&v[i], keys.key[i]);
confd_data_reply_next_key(tctx, v, keys.num, (long)nb_next);
break;
case LYS_LEAFLIST:
data = nb_node->cbs.get_elem(xpath, nb_next);
if (data) {
if (data->value) {
CONFD_SET_STR(&v[0], data->value);
confd_data_reply_next_key(tctx, v, 1,
(long)nb_next);
}
yang_data_free(data);
} else
confd_data_reply_next_key(tctx, NULL, -1, -1);
break;
default:
break;
}
return CONFD_OK;
}
@ -585,15 +648,14 @@ static int frr_confd_data_get_object(struct confd_trans_ctx *tctx,
confd_hkeypath_t *kp)
{
struct nb_node *nb_node;
const struct lys_node *child;
char xpath[BUFSIZ];
char xpath_children[XPATH_MAXLEN];
char xpath_child[XPATH_MAXLEN];
struct yang_list_keys keys;
struct list *elements;
struct yang_data *data;
const void *list_entry;
struct ly_set *set;
confd_value_t *values;
confd_value_t values[CONFD_MAX_CHILD_NODES];
size_t nvalues = 0;
frr_confd_get_xpath(kp, xpath, sizeof(xpath));
@ -602,57 +664,53 @@ static int frr_confd_data_get_object(struct confd_trans_ctx *tctx,
flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
"%s: unknown data path: %s", __func__, xpath);
confd_data_reply_not_found(tctx);
return CONFD_OK;
return CONFD_ERR;
}
frr_confd_hkeypath_get_keys(kp, &keys);
list_entry = nb_node->cbs.lookup_entry(&keys);
if (!list_entry) {
flog_warn(EC_LIB_NB_CB_STATE, "%s: list entry not found: %s",
__func__, xpath);
if (frr_confd_hkeypath_get_list_entry(kp, nb_node, &list_entry) != 0) {
confd_data_reply_not_found(tctx);
return CONFD_OK;
}
/* Find list child nodes. */
snprintf(xpath_children, sizeof(xpath_children), "%s/*", xpath);
set = lys_find_path(nb_node->snode->module, NULL, xpath_children);
if (!set) {
flog_warn(EC_LIB_LIBYANG, "%s: lys_find_path() failed",
__func__);
return CONFD_ERR;
}
elements = yang_data_list_new();
values = XMALLOC(MTYPE_CONFD, set->number * sizeof(*values));
/* Loop through list child nodes. */
for (size_t i = 0; i < set->number; i++) {
struct lys_node *child;
struct nb_node *nb_node_child;
LY_TREE_FOR (nb_node->snode->child, child) {
struct nb_node *nb_node_child = child->priv;
confd_value_t *v;
child = set->set.s[i];
nb_node_child = child->priv;
if (nvalues > CONFD_MAX_CHILD_NODES)
break;
v = &values[nvalues++];
/* Non-presence containers, lists and leaf-lists. */
if (!nb_node_child->cbs.get_elem) {
CONFD_SET_NOEXISTS(v);
continue;
}
snprintf(xpath_child, sizeof(xpath_child), "%s/%s", xpath,
child->name);
data = nb_node_child->cbs.get_elem(xpath_child, list_entry);
if (data) {
if (data->value)
CONFD_SET_STR(&values[i], data->value);
else
CONFD_SET_NOEXISTS(&values[i]);
CONFD_SET_STR(v, data->value);
else {
/* Presence containers and empty leafs. */
CONFD_SET_XMLTAG(
v, nb_node_child->confd_hash,
confd_str2hash(nb_node_child->snode
->module->ns));
}
listnode_add(elements, data);
} else
CONFD_SET_NOEXISTS(&values[i]);
CONFD_SET_NOEXISTS(v);
}
confd_data_reply_value_array(tctx, values, set->number);
confd_data_reply_value_array(tctx, values, nvalues);
/* Release memory. */
ly_set_free(set);
XFREE(MTYPE_CONFD, values);
list_delete(&elements);
return CONFD_OK;
@ -665,10 +723,9 @@ static int frr_confd_data_get_next_object(struct confd_trans_ctx *tctx,
confd_hkeypath_t *kp, long next)
{
char xpath[BUFSIZ];
char xpath_children[XPATH_MAXLEN];
struct nb_node *nb_node;
struct ly_set *set;
struct list *elements;
const void *parent_list_entry;
const void *nb_next;
#define CONFD_OBJECTS_PER_TIME 100
struct confd_next_object objects[CONFD_OBJECTS_PER_TIME + 1];
@ -684,13 +741,10 @@ static int frr_confd_data_get_next_object(struct confd_trans_ctx *tctx,
return CONFD_OK;
}
/* Find list child nodes. */
snprintf(xpath_children, sizeof(xpath_children), "%s/*", xpath);
set = lys_find_path(nb_node->snode->module, NULL, xpath_children);
if (!set) {
flog_warn(EC_LIB_LIBYANG, "%s: lys_find_path() failed",
__func__);
return CONFD_ERR;
if (frr_confd_hkeypath_get_list_entry(kp, nb_node, &parent_list_entry)
!= 0) {
confd_data_reply_next_object_array(tctx, NULL, 0, 0);
return CONFD_OK;
}
elements = yang_data_list_new();
@ -699,62 +753,76 @@ static int frr_confd_data_get_next_object(struct confd_trans_ctx *tctx,
memset(objects, 0, sizeof(objects));
for (int j = 0; j < CONFD_OBJECTS_PER_TIME; j++) {
struct confd_next_object *object;
struct yang_list_keys keys;
struct lys_node *child;
struct yang_data *data;
const void *list_entry;
size_t nvalues = 0;
object = &objects[j];
nb_next = nb_node->cbs.get_next(xpath, nb_next);
nb_next = nb_node->cbs.get_next(parent_list_entry, nb_next);
if (!nb_next)
/* End of the list. */
break;
if (nb_node->cbs.get_keys(nb_next, &keys) != NB_OK) {
flog_warn(EC_LIB_NB_CB_STATE,
"%s: failed to get list keys", __func__);
continue;
}
object->next = (long)nb_next;
list_entry = nb_node->cbs.lookup_entry(&keys);
if (!list_entry) {
flog_warn(EC_LIB_NB_CB_STATE,
"%s: failed to lookup list entry", __func__);
continue;
/* Leaf-lists require special handling. */
if (nb_node->snode->nodetype == LYS_LEAFLIST) {
object->v = XMALLOC(MTYPE_CONFD, sizeof(confd_value_t));
data = nb_node->cbs.get_elem(xpath, nb_next);
assert(data && data->value);
CONFD_SET_STR(object->v, data->value);
nvalues++;
listnode_add(elements, data);
goto next;
}
object->v = XMALLOC(MTYPE_CONFD,
set->number * sizeof(confd_value_t));
object->v =
XMALLOC(MTYPE_CONFD,
CONFD_MAX_CHILD_NODES * sizeof(confd_value_t));
/* Loop through list child nodes. */
for (unsigned int i = 0; i < set->number; i++) {
struct lys_node *child;
struct nb_node *nb_node_child;
LY_TREE_FOR (nb_node->snode->child, child) {
struct nb_node *nb_node_child = child->priv;
char xpath_child[XPATH_MAXLEN];
confd_value_t *v = &object->v[i];
confd_value_t *v;
child = set->set.s[i];
nb_node_child = child->priv;
if (nvalues > CONFD_MAX_CHILD_NODES)
break;
v = &object->v[nvalues++];
/* Non-presence containers, lists and leaf-lists. */
if (!nb_node_child->cbs.get_elem) {
CONFD_SET_NOEXISTS(v);
continue;
}
snprintf(xpath_child, sizeof(xpath_child), "%s/%s",
xpath, child->name);
data = nb_node_child->cbs.get_elem(xpath_child,
list_entry);
nb_next);
if (data) {
if (data->value)
CONFD_SET_STR(v, data->value);
else
CONFD_SET_NOEXISTS(v);
else {
/*
* Presence containers and empty leafs.
*/
CONFD_SET_XMLTAG(
v, nb_node_child->confd_hash,
confd_str2hash(
nb_node_child->snode
->module->ns));
}
listnode_add(elements, data);
} else
CONFD_SET_NOEXISTS(v);
}
object->n = set->number;
next:
object->n = nvalues;
nobjects++;
}
ly_set_free(set);
if (nobjects == 0) {
confd_data_reply_next_object_array(tctx, NULL, 0, 0);
@ -816,10 +884,18 @@ static int frr_confd_notification_send(const char *xpath,
CONFD_SET_TAG_XMLBEGIN(&values[i++], nb_node->confd_hash,
module->confd_hash);
for (ALL_LIST_ELEMENTS_RO(arguments, node, data)) {
struct nb_node *option_arg;
struct nb_node *nb_node_arg;
option_arg = data->snode->priv;
CONFD_SET_TAG_STR(&values[i++], option_arg->confd_hash,
nb_node_arg = nb_node_find(data->xpath);
if (!nb_node_arg) {
flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
"%s: unknown data path: %s", __func__,
data->xpath);
XFREE(MTYPE_CONFD, values);
return NB_ERR;
}
CONFD_SET_TAG_STR(&values[i++], nb_node_arg->confd_hash,
data->value);
}
CONFD_SET_TAG_XMLEND(&values[i++], nb_node->confd_hash,
@ -921,9 +997,18 @@ static int frr_confd_action_execute(struct confd_user_info *uinfo,
listcount(output) * sizeof(*reply));
for (ALL_LIST_ELEMENTS_RO(output, node, data)) {
struct nb_node *nb_node_output;
int hash;
hash = confd_str2hash(data->snode->name);
nb_node_output = nb_node_find(data->xpath);
if (!nb_node_output) {
flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
"%s: unknown data path: %s", __func__,
data->xpath);
goto exit;
}
hash = confd_str2hash(nb_node_output->snode->name);
CONFD_SET_TAG_STR(&reply[i++], hash, data->value);
}
confd_action_reply_values(uinfo, reply, listcount(output));
@ -962,17 +1047,16 @@ static int frr_confd_dp_read(struct thread *thread)
return 0;
}
static void frr_confd_subscribe_state(const struct lys_node *snode, void *arg1,
void *arg2)
static int frr_confd_subscribe_state(const struct lys_node *snode, void *arg)
{
struct nb_node *nb_node = snode->priv;
struct confd_data_cbs *data_cbs = arg1;
struct confd_data_cbs *data_cbs = arg;
if (!(snode->flags & LYS_CONFIG_R))
return;
if (!CHECK_FLAG(snode->flags, LYS_CONFIG_R))
return YANG_ITER_CONTINUE;
/* We only need to subscribe to the root of the state subtrees. */
if (snode->parent && (snode->parent->flags & LYS_CONFIG_R))
return;
if (snode->parent && CHECK_FLAG(snode->parent->flags, LYS_CONFIG_R))
return YANG_ITER_CONTINUE;
if (debug_northbound)
zlog_debug("%s: providing data to '%s' (callpoint %s)",
@ -981,6 +1065,8 @@ static void frr_confd_subscribe_state(const struct lys_node *snode, void *arg1,
strlcpy(data_cbs->callpoint, snode->name, sizeof(data_cbs->callpoint));
if (confd_register_data_cb(dctx, data_cbs) != CONFD_OK)
flog_err_confd("confd_register_data_cb");
return YANG_ITER_CONTINUE;
}
static int frr_confd_init_dp(const char *program_name)
@ -1053,7 +1139,7 @@ static int frr_confd_init_dp(const char *program_name)
* Iterate over all loaded YANG modules and subscribe to the paths
* referent to state data.
*/
yang_all_snodes_iterate(frr_confd_subscribe_state, 0, &data_cbs, NULL);
yang_snodes_iterate_all(frr_confd_subscribe_state, 0, &data_cbs);
/* Register notification stream. */
memset(&ncbs, 0, sizeof(ncbs));
@ -1117,12 +1203,14 @@ static void frr_confd_finish_dp(void)
/* ------------ Main ------------ */
static void frr_confd_calculate_snode_hash(const struct lys_node *snode,
void *arg1, void *arg2)
static int frr_confd_calculate_snode_hash(const struct lys_node *snode,
void *arg)
{
struct nb_node *nb_node = snode->priv;
nb_node->confd_hash = confd_str2hash(snode->name);
return YANG_ITER_CONTINUE;
}
static int frr_confd_init(const char *program_name)
@ -1153,7 +1241,7 @@ static int frr_confd_init(const char *program_name)
goto error;
}
yang_all_snodes_iterate(frr_confd_calculate_snode_hash, 0, NULL, NULL);
yang_snodes_iterate_all(frr_confd_calculate_snode_hash, 0, NULL);
hook_register(nb_notification_send, frr_confd_notification_send);

View file

@ -45,6 +45,7 @@ static int frr_sr_finish(void);
/* Convert FRR YANG data value to sysrepo YANG data value. */
static int yang_data_frr2sr(struct yang_data *frr_data, sr_val_t *sr_data)
{
struct nb_node *nb_node;
const struct lys_node *snode;
struct lys_node_container *scontainer;
struct lys_node_leaf *sleaf;
@ -53,7 +54,15 @@ static int yang_data_frr2sr(struct yang_data *frr_data, sr_val_t *sr_data)
sr_val_set_xpath(sr_data, frr_data->xpath);
snode = frr_data->snode;
nb_node = nb_node_find(frr_data->xpath);
if (!nb_node) {
flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
"%s: unknown data path: %s", __func__,
frr_data->xpath);
return -1;
}
snode = nb_node->snode;
switch (snode->nodetype) {
case LYS_CONTAINER:
scontainer = (struct lys_node_container *)snode;
@ -290,111 +299,15 @@ static int frr_sr_config_change_cb(sr_session_ctx_t *session,
}
}
static void frr_sr_state_get_elem(struct list *elements,
struct nb_node *nb_node,
const void *list_entry, const char *xpath)
static int frr_sr_state_data_iter_cb(const struct lys_node *snode,
struct yang_translator *translator,
struct yang_data *data, void *arg)
{
struct yang_data *data;
struct list *elements = arg;
data = nb_node->cbs.get_elem(xpath, list_entry);
if (data)
listnode_add(elements, data);
}
listnode_add(elements, data);
static void frr_sr_state_cb_container(struct list *elements, const char *xpath,
const struct lys_node *snode)
{
struct lys_node *child;
LY_TREE_FOR (snode->child, child) {
struct nb_node *nb_node = child->priv;
char xpath_child[XPATH_MAXLEN];
if (!nb_operation_is_valid(NB_OP_GET_ELEM, child))
continue;
snprintf(xpath_child, sizeof(xpath_child), "%s/%s", xpath,
child->name);
frr_sr_state_get_elem(elements, nb_node, NULL, xpath_child);
}
}
static void frr_sr_state_cb_list_entry(struct list *elements,
const char *xpath_list,
const void *list_entry,
struct lys_node *child)
{
struct nb_node *nb_node = child->priv;
struct lys_node_leaf *sleaf;
char xpath_child[XPATH_MAXLEN];
/* Sysrepo doesn't want to know about list keys. */
switch (child->nodetype) {
case LYS_LEAF:
sleaf = (struct lys_node_leaf *)child;
if (lys_is_key(sleaf, NULL))
return;
break;
case LYS_LEAFLIST:
break;
default:
return;
}
if (!nb_operation_is_valid(NB_OP_GET_ELEM, child))
return;
snprintf(xpath_child, sizeof(xpath_child), "%s/%s", xpath_list,
child->name);
frr_sr_state_get_elem(elements, nb_node, list_entry, xpath_child);
}
static void frr_sr_state_cb_list(struct list *elements, const char *xpath,
const struct lys_node *snode)
{
struct nb_node *nb_node = snode->priv;
struct lys_node_list *slist = (struct lys_node_list *)snode;
const void *next;
for (next = nb_node->cbs.get_next(xpath, NULL); next;
next = nb_node->cbs.get_next(xpath, next)) {
struct yang_list_keys keys;
const void *list_entry;
char xpath_list[XPATH_MAXLEN];
struct lys_node *child;
/* Get the list keys. */
if (nb_node->cbs.get_keys(next, &keys) != NB_OK) {
flog_warn(EC_LIB_NB_CB_STATE,
"%s: failed to get list keys", __func__);
continue;
}
/* Get list item. */
list_entry = nb_node->cbs.lookup_entry(&keys);
if (!list_entry) {
flog_warn(EC_LIB_NB_CB_STATE,
"%s: failed to lookup list entry", __func__);
continue;
}
/* Append list keys to the XPath. */
strlcpy(xpath_list, xpath, sizeof(xpath_list));
for (unsigned int i = 0; i < keys.num; i++) {
snprintf(xpath_list + strlen(xpath_list),
sizeof(xpath_list) - strlen(xpath_list),
"[%s='%s']", slist->keys[i]->name,
keys.key[i].value);
}
/* Loop through list entries. */
LY_TREE_FOR (snode->child, child) {
frr_sr_state_cb_list_entry(elements, xpath_list,
list_entry, child);
}
}
return NB_OK;
}
/* Callback for state retrieval. */
@ -404,26 +317,20 @@ static int frr_sr_state_cb(const char *xpath, sr_val_t **values,
{
struct list *elements;
struct yang_data *data;
const struct lys_node *snode;
struct listnode *node;
sr_val_t *v;
int ret, count, i = 0;
/* Find schema node. */
snode = ly_ctx_get_node(ly_native_ctx, NULL, xpath, 0);
elements = yang_data_list_new();
switch (snode->nodetype) {
case LYS_CONTAINER:
frr_sr_state_cb_container(elements, xpath, snode);
break;
case LYS_LIST:
frr_sr_state_cb_list(elements, xpath, snode);
break;
default:
break;
if (nb_oper_data_iterate(xpath, NULL, NB_OPER_DATA_ITER_NORECURSE,
frr_sr_state_data_iter_cb, elements)
!= NB_OK) {
flog_warn(EC_LIB_NB_OPERATIONAL_DATA,
"%s: failed to obtain operational data [xpath %s]",
__func__, xpath);
goto exit;
}
if (list_isempty(elements))
goto exit;
@ -723,18 +630,17 @@ static void frr_sr_subscribe_config(struct yang_module *module)
sr_strerror(ret));
}
static void frr_sr_subscribe_state(const struct lys_node *snode, void *arg1,
void *arg2)
static int frr_sr_subscribe_state(const struct lys_node *snode, void *arg)
{
struct yang_module *module = arg1;
struct yang_module *module = arg;
struct nb_node *nb_node;
int ret;
if (!(snode->flags & LYS_CONFIG_R))
return;
if (!CHECK_FLAG(snode->flags, LYS_CONFIG_R))
return YANG_ITER_CONTINUE;
/* We only need to subscribe to the root of the state subtrees. */
if (snode->parent && (snode->parent->flags & LYS_CONFIG_R))
return;
if (snode->parent && CHECK_FLAG(snode->parent->flags, LYS_CONFIG_R))
return YANG_ITER_CONTINUE;
nb_node = snode->priv;
if (debug_northbound)
@ -747,17 +653,18 @@ static void frr_sr_subscribe_state(const struct lys_node *snode, void *arg1,
if (ret != SR_ERR_OK)
flog_err(EC_LIB_LIBSYSREPO, "sr_dp_get_items_subscribe(): %s",
sr_strerror(ret));
return YANG_ITER_CONTINUE;
}
static void frr_sr_subscribe_rpc(const struct lys_node *snode, void *arg1,
void *arg2)
static int frr_sr_subscribe_rpc(const struct lys_node *snode, void *arg)
{
struct yang_module *module = arg1;
struct yang_module *module = arg;
struct nb_node *nb_node;
int ret;
if (snode->nodetype != LYS_RPC)
return;
return YANG_ITER_CONTINUE;
nb_node = snode->priv;
if (debug_northbound)
@ -770,17 +677,18 @@ static void frr_sr_subscribe_rpc(const struct lys_node *snode, void *arg1,
if (ret != SR_ERR_OK)
flog_err(EC_LIB_LIBSYSREPO, "sr_rpc_subscribe(): %s",
sr_strerror(ret));
return YANG_ITER_CONTINUE;
}
static void frr_sr_subscribe_action(const struct lys_node *snode, void *arg1,
void *arg2)
static int frr_sr_subscribe_action(const struct lys_node *snode, void *arg)
{
struct yang_module *module = arg1;
struct yang_module *module = arg;
struct nb_node *nb_node;
int ret;
if (snode->nodetype != LYS_ACTION)
return;
return YANG_ITER_CONTINUE;
nb_node = snode->priv;
if (debug_northbound)
@ -793,6 +701,8 @@ static void frr_sr_subscribe_action(const struct lys_node *snode, void *arg1,
if (ret != SR_ERR_OK)
flog_err(EC_LIB_LIBSYSREPO, "sr_action_subscribe(): %s",
sr_strerror(ret));
return YANG_ITER_CONTINUE;
}
/* FRR's Sysrepo initialization. */
@ -830,12 +740,12 @@ static int frr_sr_init(const char *program_name)
/* Perform subscriptions. */
RB_FOREACH (module, yang_modules, &yang_modules) {
frr_sr_subscribe_config(module);
yang_module_snodes_iterate(module->info, frr_sr_subscribe_state,
0, module, NULL);
yang_module_snodes_iterate(module->info, frr_sr_subscribe_rpc,
0, module, NULL);
yang_module_snodes_iterate(
module->info, frr_sr_subscribe_action, 0, module, NULL);
yang_snodes_iterate_module(module->info, frr_sr_subscribe_state,
0, module);
yang_snodes_iterate_module(module->info, frr_sr_subscribe_rpc,
0, module);
yang_snodes_iterate_module(module->info,
frr_sr_subscribe_action, 0, module);
}
hook_register(nb_notification_send, frr_sr_notification_send);

View file

@ -71,6 +71,11 @@ static const char *yang_module_imp_clb(const char *mod_name,
return NULL;
}
static const char * const frr_native_modules[] = {
"frr-interface",
"frr-ripd",
};
/* Generate the yang_modules tree. */
static inline int yang_module_compare(const struct yang_module *a,
const struct yang_module *b)
@ -108,6 +113,12 @@ struct yang_module *yang_module_load(const char *module_name)
return module;
}
void yang_module_load_all(void)
{
for (size_t i = 0; i < array_size(frr_native_modules); i++)
yang_module_load(frr_native_modules[i]);
}
struct yang_module *yang_module_find(const char *module_name)
{
struct yang_module s;
@ -116,23 +127,18 @@ struct yang_module *yang_module_find(const char *module_name)
return RB_FIND(yang_modules, &yang_modules, &s);
}
/*
* Helper function for yang_module_snodes_iterate() and
* yang_all_snodes_iterate(). This is a recursive function.
*/
static void yang_snodes_iterate(const struct lys_node *snode,
void (*func)(const struct lys_node *, void *,
void *),
uint16_t flags, void *arg1, void *arg2)
int yang_snodes_iterate_subtree(const struct lys_node *snode,
yang_iterate_cb cb, uint16_t flags, void *arg)
{
struct lys_node *child;
int ret = YANG_ITER_CONTINUE;
if (CHECK_FLAG(flags, YANG_ITER_FILTER_IMPLICIT)) {
switch (snode->nodetype) {
case LYS_CASE:
case LYS_INPUT:
case LYS_OUTPUT:
if (snode->flags & LYS_IMPLICIT)
if (CHECK_FLAG(snode->flags, LYS_IMPLICIT))
goto next;
break;
default:
@ -162,7 +168,7 @@ static void yang_snodes_iterate(const struct lys_node *snode,
break;
case LYS_GROUPING:
/* Return since we're not interested in the grouping subtree. */
return;
return YANG_ITER_CONTINUE;
case LYS_USES:
case LYS_AUGMENT:
/* Always ignore nodes of these types. */
@ -176,50 +182,66 @@ static void yang_snodes_iterate(const struct lys_node *snode,
break;
}
(*func)(snode, arg1, arg2);
ret = (*cb)(snode, arg);
if (ret == YANG_ITER_STOP)
return ret;
next:
/*
* YANG leafs and leaf-lists can't have child nodes, and trying to
* access snode->child is undefined behavior.
*/
if (snode->nodetype & (LYS_LEAF | LYS_LEAFLIST))
return;
if (CHECK_FLAG(snode->nodetype, LYS_LEAF | LYS_LEAFLIST))
return YANG_ITER_CONTINUE;
LY_TREE_FOR (snode->child, child) {
if (child->parent != snode)
if (!CHECK_FLAG(flags, YANG_ITER_ALLOW_AUGMENTATIONS)
&& child->parent != snode)
continue;
yang_snodes_iterate(child, func, flags, arg1, arg2);
ret = yang_snodes_iterate_subtree(child, cb, flags, arg);
if (ret == YANG_ITER_STOP)
return ret;
}
return ret;
}
void yang_module_snodes_iterate(const struct lys_module *module,
void (*func)(const struct lys_node *, void *,
void *),
uint16_t flags, void *arg1, void *arg2)
int yang_snodes_iterate_module(const struct lys_module *module,
yang_iterate_cb cb, uint16_t flags, void *arg)
{
struct lys_node *snode;
int ret = YANG_ITER_CONTINUE;
LY_TREE_FOR (module->data, snode) {
yang_snodes_iterate(snode, func, flags, arg1, arg2);
ret = yang_snodes_iterate_subtree(snode, cb, flags, arg);
if (ret == YANG_ITER_STOP)
return ret;
}
for (uint8_t i = 0; i < module->augment_size; i++) {
yang_snodes_iterate(
(const struct lys_node *)&module->augment[i], func,
flags, arg1, arg2);
ret = yang_snodes_iterate_subtree(
(const struct lys_node *)&module->augment[i], cb, flags,
arg);
if (ret == YANG_ITER_STOP)
return ret;
}
return ret;
}
void yang_all_snodes_iterate(void (*func)(const struct lys_node *, void *,
void *),
uint16_t flags, void *arg1, void *arg2)
int yang_snodes_iterate_all(yang_iterate_cb cb, uint16_t flags, void *arg)
{
struct yang_module *module;
int ret = YANG_ITER_CONTINUE;
RB_FOREACH (module, yang_modules, &yang_modules)
yang_module_snodes_iterate(module->info, func, flags, arg1,
arg2);
RB_FOREACH (module, yang_modules, &yang_modules) {
ret = yang_snodes_iterate_module(module->info, cb, flags, arg);
if (ret == YANG_ITER_STOP)
return ret;
}
return ret;
}
void yang_snode_get_path(const struct lys_node *snode, enum yang_path_type type,
@ -324,7 +346,7 @@ const struct lys_type *yang_snode_get_type(const struct lys_node *snode)
struct lys_node_leaf *sleaf = (struct lys_node_leaf *)snode;
struct lys_type *type;
if (!(sleaf->nodetype & (LYS_LEAF | LYS_LEAFLIST)))
if (!CHECK_FLAG(sleaf->nodetype, LYS_LEAF | LYS_LEAFLIST))
return NULL;
type = &sleaf->type;
@ -463,7 +485,7 @@ bool yang_dnode_is_default_recursive(const struct lyd_node *dnode)
struct lyd_node *root, *next, *dnode_iter;
snode = dnode->schema;
if (snode->nodetype & (LYS_LEAF | LYS_LEAFLIST))
if (CHECK_FLAG(snode->nodetype, LYS_LEAF | LYS_LEAFLIST))
return yang_dnode_is_default(dnode, NULL);
if (!yang_dnode_is_default(dnode, NULL))
@ -489,7 +511,7 @@ void yang_dnode_change_leaf(struct lyd_node *dnode, const char *value)
void yang_dnode_set_entry(const struct lyd_node *dnode, void *entry)
{
assert(dnode->schema->nodetype & (LYS_LIST | LYS_CONTAINER));
assert(CHECK_FLAG(dnode->schema->nodetype, LYS_LIST | LYS_CONTAINER));
lyd_set_private(dnode, entry);
}
@ -523,12 +545,18 @@ void *yang_dnode_get_entry(const struct lyd_node *dnode,
abort();
}
struct lyd_node *yang_dnode_new(struct ly_ctx *ly_ctx)
struct lyd_node *yang_dnode_new(struct ly_ctx *ly_ctx, bool config_only)
{
struct lyd_node *dnode;
int options;
if (config_only)
options = LYD_OPT_CONFIG;
else
options = LYD_OPT_DATA | LYD_OPT_DATA_NO_YANGLIB;
dnode = NULL;
if (lyd_validate(&dnode, LYD_OPT_CONFIG, ly_ctx) != 0) {
if (lyd_validate(&dnode, options, ly_ctx) != 0) {
/* Should never happen. */
flog_err(EC_LIB_LIBYANG, "%s: lyd_validate() failed", __func__);
exit(1);
@ -544,27 +572,17 @@ struct lyd_node *yang_dnode_dup(const struct lyd_node *dnode)
void yang_dnode_free(struct lyd_node *dnode)
{
while (dnode->parent)
dnode = dnode->parent;
lyd_free_withsiblings(dnode);
}
struct yang_data *yang_data_new(const char *xpath, const char *value)
{
const struct lys_node *snode;
struct yang_data *data;
snode = ly_ctx_get_node(ly_native_ctx, NULL, xpath, 0);
if (!snode)
snode = ly_ctx_get_node(ly_native_ctx, NULL, xpath, 1);
if (!snode) {
flog_err(EC_LIB_YANG_UNKNOWN_DATA_PATH,
"%s: unknown data path: %s", __func__, xpath);
zlog_backtrace(LOG_ERR);
abort();
}
data = XCALLOC(MTYPE_YANG_DATA, sizeof(*data));
strlcpy(data->xpath, xpath, sizeof(data->xpath));
data->snode = snode;
if (value)
data->value = strdup(value);

View file

@ -69,12 +69,6 @@ struct yang_data {
/* XPath identifier of the data element. */
char xpath[XPATH_MAXLEN];
/*
* Schema information (necessary to interpret certain values like
* enums).
*/
const struct lys_node *snode;
/* Value encoded as a raw string. */
char *value;
};
@ -83,16 +77,8 @@ struct yang_list_keys {
/* Number os keys (max: LIST_MAXKEYS). */
uint8_t num;
struct {
/*
* Schema information (necessary to interpret certain values
* like enums).
*/
struct lys_node *snode;
/* Value encoded as a raw string. */
char value[LIST_MAXKEYLEN];
} key[LIST_MAXKEYS];
/* Value encoded as a raw string. */
char key[LIST_MAXKEYS][LIST_MAXKEYLEN];
};
enum yang_path_type {
@ -100,14 +86,29 @@ enum yang_path_type {
YANG_PATH_DATA,
};
/* Filter non-presence containers. */
#define YANG_ITER_FILTER_NPCONTAINERS 0x0001
/* Filter list keys (leafs). */
#define YANG_ITER_FILTER_LIST_KEYS 0x0002
/* Filter RPC input/output nodes. */
#define YANG_ITER_FILTER_INPUT_OUTPUT 0x0004
/* Filter implicitely created nodes. */
#define YANG_ITER_FILTER_IMPLICIT 0x0008
enum yang_iter_flags {
/* Filter non-presence containers. */
YANG_ITER_FILTER_NPCONTAINERS = (1<<0),
/* Filter list keys (leafs). */
YANG_ITER_FILTER_LIST_KEYS = (1<<1),
/* Filter RPC input/output nodes. */
YANG_ITER_FILTER_INPUT_OUTPUT = (1<<2),
/* Filter implicitely created nodes. */
YANG_ITER_FILTER_IMPLICIT = (1<<3),
/* Allow iteration over augmentations. */
YANG_ITER_ALLOW_AUGMENTATIONS = (1<<4),
};
/* Callback used by the yang_snodes_iterate_*() family of functions. */
typedef int (*yang_iterate_cb)(const struct lys_node *snode, void *arg);
/* Return values of the 'yang_iterate_cb' callback. */
#define YANG_ITER_CONTINUE 0
#define YANG_ITER_STOP -1
/* Global libyang context for native FRR models. */
extern struct ly_ctx *ly_native_ctx;
@ -128,6 +129,11 @@ extern struct yang_modules yang_modules;
*/
extern struct yang_module *yang_module_load(const char *module_name);
/*
* Load all FRR native YANG models.
*/
extern void yang_module_load_all(void);
/*
* Find a YANG module by its name.
*
@ -149,47 +155,67 @@ extern struct yang_module *yang_module_find(const char *module_name);
*/
extern void yang_module_embed(struct yang_module_embed *embed);
/*
* Iterate recursively over all children of a schema node.
*
* snode
* YANG schema node to operate on.
*
* cb
* Function to call with each schema node.
*
* flags
* YANG_ITER_* flags to control how the iteration is performed.
*
* arg
* Arbitrary argument passed as the second parameter in each call to 'cb'.
*
* Returns:
* The return value of the last called callback.
*/
extern int yang_snodes_iterate_subtree(const struct lys_node *snode,
yang_iterate_cb cb, uint16_t flags,
void *arg);
/*
* Iterate over all libyang schema nodes from the given YANG module.
*
* module
* YANG module to operate on.
*
* func
* cb
* Function to call with each schema node.
*
* flags
* YANG_ITER_FILTER_* flags to specify node types that should be filtered.
* YANG_ITER_* flags to control how the iteration is performed.
*
* arg1
* Arbitrary argument passed as the second parameter in each call to 'func'.
* arg
* Arbitrary argument passed as the second parameter in each call to 'cb'.
*
* arg2
* Arbitrary argument passed as the third parameter in each call to 'func'.
* Returns:
* The return value of the last called callback.
*/
extern void yang_module_snodes_iterate(const struct lys_module *module,
void (*func)(const struct lys_node *,
void *, void *),
uint16_t flags, void *arg1, void *arg2);
extern int yang_snodes_iterate_module(const struct lys_module *module,
yang_iterate_cb cb, uint16_t flags,
void *arg);
/*
* Iterate over all libyang schema nodes from all loaded YANG modules.
*
* func
* cb
* Function to call with each schema node.
*
* flags
* YANG_ITER_FILTER_* flags to specify node types that should be filtered.
* YANG_ITER_* flags to control how the iteration is performed.
*
* arg1
* Arbitrary argument passed as the second parameter in each call to 'func'.
* arg
* Arbitrary argument passed as the second parameter in each call to 'cb'.
*
* arg2
* Arbitrary argument passed as the third parameter in each call to 'func'.
* Returns:
* The return value of the last called callback.
*/
extern void yang_all_snodes_iterate(void (*func)(const struct lys_node *,
void *, void *),
uint16_t flags, void *arg1, void *arg2);
extern int yang_snodes_iterate_all(yang_iterate_cb cb, uint16_t flags,
void *arg);
/*
* Build schema path or data path of the schema node.
@ -423,10 +449,14 @@ extern void *yang_dnode_get_entry(const struct lyd_node *dnode,
* ly_ctx
* libyang context to operate on.
*
* config
* Specify whether the data node will contain only configuration data (true)
* or both configuration data and state data (false).
*
* Returns:
* Pointer to newly created libyang data node.
*/
extern struct lyd_node *yang_dnode_new(struct ly_ctx *ly_ctx);
extern struct lyd_node *yang_dnode_new(struct ly_ctx *ly_ctx, bool config_only);
/*
* Duplicate a libyang data node.

View file

@ -351,7 +351,7 @@ int yang_translate_dnode(const struct yang_translator *translator, int dir,
ly_ctx = ly_native_ctx;
else
ly_ctx = translator->ly_ctx;
new = yang_dnode_new(ly_ctx);
new = yang_dnode_new(ly_ctx, false);
/* Iterate over all nodes from the data tree. */
LY_TREE_FOR (*dnode, root) {
@ -400,24 +400,28 @@ error:
return YANG_TRANSLATE_FAILURE;
}
static void yang_translator_validate_cb(const struct lys_node *snode_custom,
void *arg1, void *arg2)
struct translator_validate_args {
struct yang_translator *translator;
unsigned int errors;
};
static int yang_translator_validate_cb(const struct lys_node *snode_custom,
void *arg)
{
struct yang_translator *translator = arg1;
unsigned int *errors = arg2;
struct translator_validate_args *args = arg;
struct yang_mapping_node *mapping;
const struct lys_node *snode_native;
const struct lys_type *stype_custom, *stype_native;
char xpath[XPATH_MAXLEN];
yang_snode_get_path(snode_custom, YANG_PATH_DATA, xpath, sizeof(xpath));
mapping = yang_mapping_lookup(translator, YANG_TRANSLATE_TO_NATIVE,
xpath);
mapping = yang_mapping_lookup(args->translator,
YANG_TRANSLATE_TO_NATIVE, xpath);
if (!mapping) {
flog_warn(EC_LIB_YANG_TRANSLATOR_LOAD,
"%s: missing mapping for \"%s\"", __func__, xpath);
*errors += 1;
return;
args->errors += 1;
return YANG_ITER_CONTINUE;
}
snode_native =
@ -433,12 +437,14 @@ static void yang_translator_validate_cb(const struct lys_node *snode_custom,
EC_LIB_YANG_TRANSLATOR_LOAD,
"%s: YANG types are incompatible (xpath: \"%s\")",
__func__, xpath);
*errors += 1;
return;
args->errors += 1;
return YANG_ITER_CONTINUE;
}
/* TODO: check if the value spaces are identical. */
}
return YANG_ITER_CONTINUE;
}
/*
@ -449,32 +455,36 @@ static unsigned int yang_translator_validate(struct yang_translator *translator)
{
struct yang_tmodule *tmodule;
struct listnode *ln;
unsigned int errors = 0;
struct translator_validate_args args;
args.translator = translator;
args.errors = 0;
for (ALL_LIST_ELEMENTS_RO(translator->modules, ln, tmodule)) {
yang_module_snodes_iterate(
yang_snodes_iterate_module(
tmodule->module, yang_translator_validate_cb,
YANG_ITER_FILTER_NPCONTAINERS
| YANG_ITER_FILTER_LIST_KEYS
| YANG_ITER_FILTER_INPUT_OUTPUT,
translator, &errors);
&args);
}
if (errors)
if (args.errors)
flog_warn(
EC_LIB_YANG_TRANSLATOR_LOAD,
"%s: failed to validate \"%s\" module translator: %u error(s)",
__func__, translator->family, errors);
__func__, translator->family, args.errors);
return errors;
return args.errors;
}
static void yang_module_nodes_count_cb(const struct lys_node *snode, void *arg1,
void *arg2)
static int yang_module_nodes_count_cb(const struct lys_node *snode, void *arg)
{
unsigned int *total = arg1;
unsigned int *total = arg;
*total += 1;
return YANG_ITER_CONTINUE;
}
/* Calculate the number of nodes for the given module. */
@ -482,11 +492,11 @@ static unsigned int yang_module_nodes_count(const struct lys_module *module)
{
unsigned int total = 0;
yang_module_snodes_iterate(module, yang_module_nodes_count_cb,
yang_snodes_iterate_module(module, yang_module_nodes_count_cb,
YANG_ITER_FILTER_NPCONTAINERS
| YANG_ITER_FILTER_LIST_KEYS
| YANG_ITER_FILTER_INPUT_OUTPUT,
&total, NULL);
&total);
return total;
}

View file

@ -840,7 +840,7 @@ void yang_str2ipv4p(const char *value, union prefixptr prefix)
}
struct yang_data *yang_data_new_ipv4p(const char *xpath,
const union prefixptr prefix)
union prefixconstptr prefix)
{
char value_str[PREFIX2STR_BUFFER];
@ -950,7 +950,7 @@ void yang_str2ipv6p(const char *value, union prefixptr prefix)
}
struct yang_data *yang_data_new_ipv6p(const char *xpath,
const union prefixptr prefix)
union prefixconstptr prefix)
{
char value_str[PREFIX2STR_BUFFER];

View file

@ -127,7 +127,7 @@ extern void yang_get_default_ipv4(struct in_addr *var, const char *xpath_fmt,
/* ipv4p */
extern void yang_str2ipv4p(const char *value, union prefixptr prefix);
extern struct yang_data *yang_data_new_ipv4p(const char *xpath,
const union prefixptr prefix);
union prefixconstptr prefix);
extern void yang_dnode_get_ipv4p(union prefixptr prefix,
const struct lyd_node *dnode,
const char *xpath_fmt, ...);
@ -147,7 +147,7 @@ extern void yang_get_default_ipv6(struct in6_addr *var, const char *xpath_fmt,
/* ipv6p */
extern void yang_str2ipv6p(const char *value, union prefixptr prefix);
extern struct yang_data *yang_data_new_ipv6p(const char *xpath,
const union prefixptr prefix);
union prefixconstptr prefix);
extern void yang_dnode_get_ipv6p(union prefixptr prefix,
const struct lyd_node *dnode,
const char *xpath_fmt, ...);

View file

@ -1003,7 +1003,7 @@ lib_interface_rip_authentication_key_chain_delete(enum nb_event event,
* XPath: /frr-ripd:ripd/state/neighbors/neighbor
*/
static const void *
ripd_state_neighbors_neighbor_get_next(const char *xpath,
ripd_state_neighbors_neighbor_get_next(const void *parent_list_entry,
const void *list_entry)
{
struct listnode *node;
@ -1023,20 +1023,28 @@ static int ripd_state_neighbors_neighbor_get_keys(const void *list_entry,
const struct rip_peer *peer = listgetdata(node);
keys->num = 1;
(void)inet_ntop(AF_INET, &peer->addr, keys->key[0].value,
sizeof(keys->key[0].value));
(void)inet_ntop(AF_INET, &peer->addr, keys->key[0],
sizeof(keys->key[0]));
return NB_OK;
}
static const void *
ripd_state_neighbors_neighbor_lookup_entry(const struct yang_list_keys *keys)
ripd_state_neighbors_neighbor_lookup_entry(const void *parent_list_entry,
const struct yang_list_keys *keys)
{
struct in_addr address;
struct rip_peer *peer;
struct listnode *node;
yang_str2ipv4(keys->key[0].value, &address);
yang_str2ipv4(keys->key[0], &address);
return rip_peer_lookup(&address);
for (ALL_LIST_ELEMENTS_RO(peer_list, node, peer)) {
if (IPV4_ADDR_SAME(&peer->addr, &address))
return node;
}
return NULL;
}
/*
@ -1046,7 +1054,8 @@ static struct yang_data *
ripd_state_neighbors_neighbor_address_get_elem(const char *xpath,
const void *list_entry)
{
const struct rip_peer *peer = list_entry;
const struct listnode *node = list_entry;
const struct rip_peer *peer = listgetdata(node);
return yang_data_new_ipv4(xpath, &peer->addr);
}
@ -1069,7 +1078,8 @@ static struct yang_data *
ripd_state_neighbors_neighbor_bad_packets_rcvd_get_elem(const char *xpath,
const void *list_entry)
{
const struct rip_peer *peer = list_entry;
const struct listnode *node = list_entry;
const struct rip_peer *peer = listgetdata(node);
return yang_data_new_uint32(xpath, peer->recv_badpackets);
}
@ -1081,7 +1091,8 @@ static struct yang_data *
ripd_state_neighbors_neighbor_bad_routes_rcvd_get_elem(const char *xpath,
const void *list_entry)
{
const struct rip_peer *peer = list_entry;
const struct listnode *node = list_entry;
const struct rip_peer *peer = listgetdata(node);
return yang_data_new_uint32(xpath, peer->recv_badroutes);
}
@ -1089,8 +1100,9 @@ ripd_state_neighbors_neighbor_bad_routes_rcvd_get_elem(const char *xpath,
/*
* XPath: /frr-ripd:ripd/state/routes/route
*/
static const void *ripd_state_routes_route_get_next(const char *xpath,
const void *list_entry)
static const void *
ripd_state_routes_route_get_next(const void *parent_list_entry,
const void *list_entry)
{
struct route_node *rn;
@ -1113,19 +1125,19 @@ static int ripd_state_routes_route_get_keys(const void *list_entry,
const struct route_node *rn = list_entry;
keys->num = 1;
(void)prefix2str(&rn->p, keys->key[0].value,
sizeof(keys->key[0].value));
(void)prefix2str(&rn->p, keys->key[0], sizeof(keys->key[0]));
return NB_OK;
}
static const void *
ripd_state_routes_route_lookup_entry(const struct yang_list_keys *keys)
ripd_state_routes_route_lookup_entry(const void *parent_list_entry,
const struct yang_list_keys *keys)
{
struct prefix prefix;
struct route_node *rn;
yang_str2ipv4p(keys->key[0].value, &prefix);
yang_str2ipv4p(keys->key[0], &prefix);
rn = route_node_lookup(rip->table, &prefix);
if (!rn || !rn->info)
@ -1133,10 +1145,7 @@ ripd_state_routes_route_lookup_entry(const struct yang_list_keys *keys)
route_unlock_node(rn);
/*
* TODO: we need to handle ECMP properly.
*/
return listnode_head(rn->info);
return rn;
}
/*
@ -1146,7 +1155,8 @@ static struct yang_data *
ripd_state_routes_route_prefix_get_elem(const char *xpath,
const void *list_entry)
{
const struct rip_info *rinfo = list_entry;
const struct route_node *rn = list_entry;
const struct rip_info *rinfo = listnode_head(rn->info);
return yang_data_new_ipv4p(xpath, &rinfo->rp->p);
}
@ -1158,7 +1168,8 @@ static struct yang_data *
ripd_state_routes_route_next_hop_get_elem(const char *xpath,
const void *list_entry)
{
const struct rip_info *rinfo = list_entry;
const struct route_node *rn = list_entry;
const struct rip_info *rinfo = listnode_head(rn->info);
switch (rinfo->nh.type) {
case NEXTHOP_TYPE_IPV4:
@ -1176,7 +1187,8 @@ static struct yang_data *
ripd_state_routes_route_interface_get_elem(const char *xpath,
const void *list_entry)
{
const struct rip_info *rinfo = list_entry;
const struct route_node *rn = list_entry;
const struct rip_info *rinfo = listnode_head(rn->info);
switch (rinfo->nh.type) {
case NEXTHOP_TYPE_IFINDEX:
@ -1195,7 +1207,8 @@ static struct yang_data *
ripd_state_routes_route_metric_get_elem(const char *xpath,
const void *list_entry)
{
const struct rip_info *rinfo = list_entry;
const struct route_node *rn = list_entry;
const struct rip_info *rinfo = listnode_head(rn->info);
return yang_data_new_uint8(xpath, rinfo->metric);
}

1
tests/.gitignore vendored
View file

@ -18,6 +18,7 @@
/lib/cli/test_cli_clippy.c
/lib/cli/test_commands
/lib/cli/test_commands_defun.c
/lib/northbound/test_oper_data
/lib/test_buffer
/lib/test_checksum
/lib/test_graph

View file

@ -0,0 +1,466 @@
/*
* Copyright (C) 2018 NetDEF, Inc.
* Renato Westphal
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <zebra.h>
#include "thread.h"
#include "vty.h"
#include "command.h"
#include "memory.h"
#include "memory_vty.h"
#include "log.h"
#include "northbound.h"
static struct thread_master *master;
struct troute {
struct prefix_ipv4 prefix;
struct in_addr nexthop;
char ifname[IFNAMSIZ];
uint8_t metric;
bool active;
};
struct tvrf {
char name[32];
struct list *interfaces;
struct list *routes;
};
static struct list *vrfs;
/*
* XPath: /frr-test-module:frr-test-module/vrfs/vrf
*/
static const void *
frr_test_module_vrfs_vrf_get_next(const void *parent_list_entry,
const void *list_entry)
{
struct listnode *node;
if (list_entry == NULL)
node = listhead(vrfs);
else
node = listnextnode((struct listnode *)list_entry);
return node;
}
static int frr_test_module_vrfs_vrf_get_keys(const void *list_entry,
struct yang_list_keys *keys)
{
const struct tvrf *vrf;
vrf = listgetdata((struct listnode *)list_entry);
keys->num = 1;
strlcpy(keys->key[0], vrf->name, sizeof(keys->key[0]));
return NB_OK;
}
static const void *
frr_test_module_vrfs_vrf_lookup_entry(const void *parent_list_entry,
const struct yang_list_keys *keys)
{
struct listnode *node;
struct tvrf *vrf;
const char *vrfname;
vrfname = keys->key[0];
for (ALL_LIST_ELEMENTS_RO(vrfs, node, vrf)) {
if (strmatch(vrf->name, vrfname))
return node;
}
return NULL;
}
/*
* XPath: /frr-test-module:frr-test-module/vrfs/vrf/name
*/
static struct yang_data *
frr_test_module_vrfs_vrf_name_get_elem(const char *xpath,
const void *list_entry)
{
const struct tvrf *vrf;
vrf = listgetdata((struct listnode *)list_entry);
return yang_data_new_string(xpath, vrf->name);
}
/*
* XPath: /frr-test-module:frr-test-module/vrfs/vrf/interfaces/interface
*/
static struct yang_data *
frr_test_module_vrfs_vrf_interfaces_interface_get_elem(const char *xpath,
const void *list_entry)
{
const char *interface;
interface = listgetdata((struct listnode *)list_entry);
return yang_data_new_string(xpath, interface);
}
static const void *frr_test_module_vrfs_vrf_interfaces_interface_get_next(
const void *parent_list_entry, const void *list_entry)
{
const struct tvrf *vrf;
struct listnode *node;
vrf = listgetdata((struct listnode *)parent_list_entry);
if (list_entry == NULL)
node = listhead(vrf->interfaces);
else
node = listnextnode((struct listnode *)list_entry);
return node;
}
/*
* XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route
*/
static const void *
frr_test_module_vrfs_vrf_routes_route_get_next(const void *parent_list_entry,
const void *list_entry)
{
const struct tvrf *vrf;
struct listnode *node;
vrf = listgetdata((struct listnode *)parent_list_entry);
if (list_entry == NULL)
node = listhead(vrf->routes);
else
node = listnextnode((struct listnode *)list_entry);
return node;
}
static int
frr_test_module_vrfs_vrf_routes_route_get_keys(const void *list_entry,
struct yang_list_keys *keys)
{
const struct troute *route;
route = listgetdata((struct listnode *)list_entry);
keys->num = 1;
(void)prefix2str(&route->prefix, keys->key[0], sizeof(keys->key[0]));
return NB_OK;
}
static const void *frr_test_module_vrfs_vrf_routes_route_lookup_entry(
const void *parent_list_entry, const struct yang_list_keys *keys)
{
const struct tvrf *vrf;
const struct troute *route;
struct listnode *node;
struct prefix prefix;
yang_str2ipv4p(keys->key[0], &prefix);
vrf = listgetdata((struct listnode *)parent_list_entry);
for (ALL_LIST_ELEMENTS_RO(vrf->routes, node, route)) {
if (prefix_same((struct prefix *)&route->prefix, &prefix))
return node;
}
return NULL;
}
/*
* XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route/prefix
*/
static struct yang_data *
frr_test_module_vrfs_vrf_routes_route_prefix_get_elem(const char *xpath,
const void *list_entry)
{
const struct troute *route;
route = listgetdata((struct listnode *)list_entry);
return yang_data_new_ipv4p(xpath, &route->prefix);
}
/*
* XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route/next-hop
*/
static struct yang_data *
frr_test_module_vrfs_vrf_routes_route_next_hop_get_elem(const char *xpath,
const void *list_entry)
{
const struct troute *route;
route = listgetdata((struct listnode *)list_entry);
return yang_data_new_ipv4(xpath, &route->nexthop);
}
/*
* XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route/interface
*/
static struct yang_data *
frr_test_module_vrfs_vrf_routes_route_interface_get_elem(const char *xpath,
const void *list_entry)
{
const struct troute *route;
route = listgetdata((struct listnode *)list_entry);
return yang_data_new_string(xpath, route->ifname);
}
/*
* XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route/metric
*/
static struct yang_data *
frr_test_module_vrfs_vrf_routes_route_metric_get_elem(const char *xpath,
const void *list_entry)
{
const struct troute *route;
route = listgetdata((struct listnode *)list_entry);
return yang_data_new_uint8(xpath, route->metric);
}
/*
* XPath: /frr-test-module:frr-test-module/vrfs/vrf/routes/route/active
*/
static struct yang_data *
frr_test_module_vrfs_vrf_routes_route_active_get_elem(const char *xpath,
const void *list_entry)
{
const struct troute *route;
route = listgetdata((struct listnode *)list_entry);
if (route->active)
return yang_data_new(xpath, NULL);
return NULL;
}
/* clang-format off */
const struct frr_yang_module_info frr_test_module_info = {
.name = "frr-test-module",
.nodes = {
{
.xpath = "/frr-test-module:frr-test-module/vrfs/vrf",
.cbs.get_next = frr_test_module_vrfs_vrf_get_next,
.cbs.get_keys = frr_test_module_vrfs_vrf_get_keys,
.cbs.lookup_entry = frr_test_module_vrfs_vrf_lookup_entry,
},
{
.xpath = "/frr-test-module:frr-test-module/vrfs/vrf/name",
.cbs.get_elem = frr_test_module_vrfs_vrf_name_get_elem,
},
{
.xpath = "/frr-test-module:frr-test-module/vrfs/vrf/interfaces/interface",
.cbs.get_elem = frr_test_module_vrfs_vrf_interfaces_interface_get_elem,
.cbs.get_next = frr_test_module_vrfs_vrf_interfaces_interface_get_next,
},
{
.xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route",
.cbs.get_next = frr_test_module_vrfs_vrf_routes_route_get_next,
.cbs.get_keys = frr_test_module_vrfs_vrf_routes_route_get_keys,
.cbs.lookup_entry = frr_test_module_vrfs_vrf_routes_route_lookup_entry,
},
{
.xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route/prefix",
.cbs.get_elem = frr_test_module_vrfs_vrf_routes_route_prefix_get_elem,
},
{
.xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route/next-hop",
.cbs.get_elem = frr_test_module_vrfs_vrf_routes_route_next_hop_get_elem,
},
{
.xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route/interface",
.cbs.get_elem = frr_test_module_vrfs_vrf_routes_route_interface_get_elem,
},
{
.xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route/metric",
.cbs.get_elem = frr_test_module_vrfs_vrf_routes_route_metric_get_elem,
},
{
.xpath = "/frr-test-module:frr-test-module/vrfs/vrf/routes/route/active",
.cbs.get_elem = frr_test_module_vrfs_vrf_routes_route_active_get_elem,
},
{
.xpath = NULL,
},
}
};
/* clang-format on */
static const struct frr_yang_module_info *modules[] = {
&frr_test_module_info,
};
static void create_data(unsigned int num_vrfs, unsigned int num_interfaces,
unsigned int num_routes)
{
struct prefix_ipv4 base_prefix;
struct in_addr base_nexthop;
(void)str2prefix_ipv4("10.0.0.0/32", &base_prefix);
(void)inet_pton(AF_INET, "172.16.0.0", &base_nexthop);
vrfs = list_new();
/* Create VRFs. */
for (unsigned int i = 0; i < num_vrfs; i++) {
struct tvrf *vrf;
vrf = XCALLOC(MTYPE_TMP, sizeof(*vrf));
snprintf(vrf->name, sizeof(vrf->name), "vrf%u", i);
vrf->interfaces = list_new();
vrf->routes = list_new();
/* Create interfaces. */
for (unsigned int j = 0; j < num_interfaces; j++) {
char ifname[32];
char *interface;
snprintf(ifname, sizeof(ifname), "eth%u", j);
interface = XSTRDUP(MTYPE_TMP, ifname);
listnode_add(vrf->interfaces, interface);
}
/* Create routes. */
for (unsigned int j = 0; j < num_routes; j++) {
struct troute *route;
route = XCALLOC(MTYPE_TMP, sizeof(*route));
memcpy(&route->prefix, &base_prefix,
sizeof(route->prefix));
route->prefix.prefix.s_addr =
htonl(ntohl(route->prefix.prefix.s_addr) + j);
memcpy(&route->nexthop, &base_nexthop,
sizeof(route->nexthop));
route->nexthop.s_addr =
htonl(ntohl(route->nexthop.s_addr) + j);
snprintf(route->ifname, sizeof(route->ifname), "eth%u",
j);
route->metric = j % 256;
route->active = (j % 2 == 0);
listnode_add(vrf->routes, route);
}
listnode_add(vrfs, vrf);
}
}
static void interface_delete(void *ptr)
{
char *interface = ptr;
XFREE(MTYPE_TMP, interface);
}
static void route_delete(void *ptr)
{
struct troute *route = ptr;
XFREE(MTYPE_TMP, route);
}
static void vrf_delete(void *ptr)
{
struct tvrf *vrf = ptr;
vrf->interfaces->del = interface_delete;
list_delete(&vrf->interfaces);
vrf->routes->del = route_delete;
list_delete(&vrf->routes);
XFREE(MTYPE_TMP, vrf);
}
static void delete_data(void)
{
vrfs->del = vrf_delete;
list_delete(&vrfs);
}
static void vty_do_exit(int isexit)
{
printf("\nend.\n");
delete_data();
cmd_terminate();
vty_terminate();
nb_terminate();
yang_terminate();
thread_master_free(master);
closezlog();
log_memstats(stderr, "test-nb-oper-data");
if (!isexit)
exit(0);
}
/* main routine. */
int main(int argc, char **argv)
{
struct thread thread;
unsigned int num_vrfs = 2;
unsigned int num_interfaces = 4;
unsigned int num_routes = 6;
if (argc > 1)
num_vrfs = atoi(argv[1]);
if (argc > 2)
num_interfaces = atoi(argv[2]);
if (argc > 3)
num_routes = atoi(argv[3]);
/* Set umask before anything for security */
umask(0027);
/* master init. */
master = thread_master_create(NULL);
openzlog("test-nb-oper-data", "NONE", 0,
LOG_CONS | LOG_NDELAY | LOG_PID, LOG_DAEMON);
zlog_set_level(ZLOG_DEST_SYSLOG, ZLOG_DISABLED);
zlog_set_level(ZLOG_DEST_STDOUT, ZLOG_DISABLED);
zlog_set_level(ZLOG_DEST_MONITOR, LOG_DEBUG);
/* Library inits. */
cmd_init(1);
cmd_hostname_set("test");
vty_init(master);
memory_init();
yang_init();
nb_init(modules, array_size(modules));
/* Create artificial data. */
create_data(num_vrfs, num_interfaces, num_routes);
/* Read input from .in file. */
vty_stdio(vty_do_exit);
/* Fetch next active thread. */
while (thread_fetch(master, &thread))
thread_call(&thread);
/* Not reached. */
exit(0);
}

View file

@ -0,0 +1 @@
show yang operational-data /frr-test-module:frr-test-module

View file

@ -0,0 +1,4 @@
import frrtest
class TestNbOperData(frrtest.TestRefOut):
program = './test_oper_data'

View file

@ -0,0 +1,119 @@
test# show yang operational-data /frr-test-module:frr-test-module
{
"frr-test-module:frr-test-module": {
"vrfs": {
"vrf": [
{
"name": "vrf0",
"interfaces": {
"interface": [
"eth0",
"eth1",
"eth2",
"eth3"
]
},
"routes": {
"route": [
{
"prefix": "10.0.0.0/32",
"next-hop": "172.16.0.0",
"interface": "eth0",
"metric": 0,
"active": [null]
},
{
"prefix": "10.0.0.1/32",
"next-hop": "172.16.0.1",
"interface": "eth1",
"metric": 1
},
{
"prefix": "10.0.0.2/32",
"next-hop": "172.16.0.2",
"interface": "eth2",
"metric": 2,
"active": [null]
},
{
"prefix": "10.0.0.3/32",
"next-hop": "172.16.0.3",
"interface": "eth3",
"metric": 3
},
{
"prefix": "10.0.0.4/32",
"next-hop": "172.16.0.4",
"interface": "eth4",
"metric": 4,
"active": [null]
},
{
"prefix": "10.0.0.5/32",
"next-hop": "172.16.0.5",
"interface": "eth5",
"metric": 5
}
]
}
},
{
"name": "vrf1",
"interfaces": {
"interface": [
"eth0",
"eth1",
"eth2",
"eth3"
]
},
"routes": {
"route": [
{
"prefix": "10.0.0.0/32",
"next-hop": "172.16.0.0",
"interface": "eth0",
"metric": 0,
"active": [null]
},
{
"prefix": "10.0.0.1/32",
"next-hop": "172.16.0.1",
"interface": "eth1",
"metric": 1
},
{
"prefix": "10.0.0.2/32",
"next-hop": "172.16.0.2",
"interface": "eth2",
"metric": 2,
"active": [null]
},
{
"prefix": "10.0.0.3/32",
"next-hop": "172.16.0.3",
"interface": "eth3",
"metric": 3
},
{
"prefix": "10.0.0.4/32",
"next-hop": "172.16.0.4",
"interface": "eth4",
"metric": 4,
"active": [null]
},
{
"prefix": "10.0.0.5/32",
"next-hop": "172.16.0.5",
"interface": "eth5",
"metric": 5
}
]
}
}
]
}
}
}
test#
end.

View file

@ -102,7 +102,7 @@ static unsigned int log_key(void *data)
return hash;
}
static int log_cmp(const void *a, const void *b)
static bool log_cmp(const void *a, const void *b)
{
if (a == NULL || b == NULL)
return 0;

View file

@ -68,6 +68,7 @@ check_PROGRAMS = \
tests/lib/test_graph \
tests/lib/cli/test_cli \
tests/lib/cli/test_commands \
tests/lib/northbound/test_oper_data \
$(TESTS_BGPD) \
$(TESTS_ISISD) \
$(TESTS_OSPF6D) \
@ -175,6 +176,11 @@ tests_lib_cli_test_commands_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_cli_test_commands_LDADD = $(ALL_TESTS_LDADD)
nodist_tests_lib_cli_test_commands_SOURCES = tests/lib/cli/test_commands_defun.c
tests_lib_cli_test_commands_SOURCES = tests/lib/cli/test_commands.c tests/helpers/c/prng.c
tests_lib_northbound_test_oper_data_CFLAGS = $(TESTS_CFLAGS)
tests_lib_northbound_test_oper_data_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_northbound_test_oper_data_LDADD = $(ALL_TESTS_LDADD)
tests_lib_northbound_test_oper_data_SOURCES = tests/lib/northbound/test_oper_data.c
nodist_tests_lib_northbound_test_oper_data_SOURCES = yang/frr-test-module.yang.c
tests_lib_test_buffer_CFLAGS = $(TESTS_CFLAGS)
tests_lib_test_buffer_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_test_buffer_LDADD = $(ALL_TESTS_LDADD)
@ -284,6 +290,9 @@ EXTRA_DIST += \
tests/lib/cli/test_cli.in \
tests/lib/cli/test_cli.py \
tests/lib/cli/test_cli.refout \
tests/lib/northbound/test_oper_data.in \
tests/lib/northbound/test_oper_data.py \
tests/lib/northbound/test_oper_data.refout \
tests/lib/test_nexthop_iter.py \
tests/lib/test_ringbuf.py \
tests/lib/test_srcdest_table.py \

View file

@ -84,19 +84,22 @@ static struct nb_callback_info {
.operation = NB_OP_GET_NEXT,
.return_type = "const void *",
.return_value = "NULL",
.arguments = "const char *xpath, const void *list_entry",
.arguments =
"const void *parent_list_entry, const void *list_entry",
},
{
.operation = NB_OP_GET_KEYS,
.return_type = "int ",
.return_value = "NB_OK",
.arguments = "const void *list_entry, struct yang_list_keys *keys",
.arguments =
"const void *list_entry, struct yang_list_keys *keys",
},
{
.operation = NB_OP_LOOKUP_ENTRY,
.return_type = "const void *",
.return_value = "NULL",
.arguments = "const struct yang_list_keys *keys",
.arguments =
"const void *parent_list_entry, const struct yang_list_keys *keys",
},
{
.operation = NB_OP_RPC,
@ -130,9 +133,9 @@ static void generate_callback_name(struct lys_node *snode,
snodes = list_new();
for (; snode; snode = lys_parent(snode)) {
/* Skip schema-only snodes. */
if (snode->nodetype
& (LYS_USES | LYS_CHOICE | LYS_CASE | LYS_INPUT
| LYS_OUTPUT))
if (CHECK_FLAG(snode->nodetype, LYS_USES | LYS_CHOICE | LYS_CASE
| LYS_INPUT
| LYS_OUTPUT))
continue;
listnode_add_head(snodes, snode);
@ -149,8 +152,7 @@ static void generate_callback_name(struct lys_node *snode,
replace_hyphens_by_underscores(buffer);
}
static void generate_callbacks(const struct lys_node *snode, void *arg1,
void *arg2)
static int generate_callbacks(const struct lys_node *snode, void *arg)
{
bool first = true;
@ -163,7 +165,7 @@ static void generate_callbacks(const struct lys_node *snode, void *arg1,
case LYS_RPC:
break;
default:
return;
return YANG_ITER_CONTINUE;
}
for (struct nb_callback_info *cb = &nb_callbacks[0];
@ -198,10 +200,11 @@ static void generate_callbacks(const struct lys_node *snode, void *arg1,
nb_callbacks[cb->operation].arguments,
nb_callbacks[cb->operation].return_value);
}
return YANG_ITER_CONTINUE;
}
static void generate_nb_nodes(const struct lys_node *snode, void *arg1,
void *arg2)
static int generate_nb_nodes(const struct lys_node *snode, void *arg)
{
bool first = true;
@ -214,7 +217,7 @@ static void generate_nb_nodes(const struct lys_node *snode, void *arg1,
case LYS_RPC:
break;
default:
return;
return YANG_ITER_CONTINUE;
}
for (struct nb_callback_info *cb = &nb_callbacks[0];
@ -245,6 +248,8 @@ static void generate_nb_nodes(const struct lys_node *snode, void *arg1,
if (!first)
printf("\t\t},\n");
return YANG_ITER_CONTINUE;
}
int main(int argc, char *argv[])
@ -270,12 +275,18 @@ int main(int argc, char *argv[])
yang_init();
/* Load YANG module. */
module = yang_module_load(argv[0]);
/* Load all FRR native models to ensure all augmentations are loaded. */
yang_module_load_all();
module = yang_module_find(argv[0]);
if (!module)
/* Non-native FRR module (e.g. modules from unit tests). */
module = yang_module_load(argv[0]);
/* Create a nb_node for all YANG schema nodes. */
nb_nodes_create();
/* Generate callback functions. */
yang_module_snodes_iterate(module->info, generate_callbacks, 0, NULL,
NULL);
yang_snodes_iterate_module(module->info, generate_callbacks, 0, NULL);
strlcpy(module_name_underscores, module->name,
sizeof(module_name_underscores));
@ -287,8 +298,7 @@ int main(int argc, char *argv[])
"\t.name = \"%s\",\n"
"\t.nodes = {\n",
module_name_underscores, module->name);
yang_module_snodes_iterate(module->info, generate_nb_nodes, 0, NULL,
NULL);
yang_snodes_iterate_module(module->info, generate_nb_nodes, 0, NULL);
printf("\t\t{\n"
"\t\t\t.xpath = NULL,\n"
"\t\t},\n");
@ -296,6 +306,7 @@ int main(int argc, char *argv[])
"};\n");
/* Cleanup and exit. */
nb_nodes_delete();
yang_terminate();
return 0;

View file

@ -32,8 +32,7 @@ static void __attribute__((noreturn)) usage(int status)
exit(status);
}
static void generate_yang_deviation(const struct lys_node *snode, void *arg1,
void *arg2)
static int generate_yang_deviation(const struct lys_node *snode, void *arg)
{
char xpath[XPATH_MAXLEN];
@ -42,6 +41,8 @@ static void generate_yang_deviation(const struct lys_node *snode, void *arg1,
printf(" deviation \"%s\" {\n", xpath);
printf(" deviate not-supported;\n");
printf(" }\n\n");
return YANG_ITER_CONTINUE;
}
int main(int argc, char *argv[])
@ -70,8 +71,8 @@ int main(int argc, char *argv[])
module = yang_module_load(argv[0]);
/* Generate deviations. */
yang_module_snodes_iterate(module->info, generate_yang_deviation,
YANG_ITER_FILTER_IMPLICIT, NULL, NULL);
yang_snodes_iterate_module(module->info, generate_yang_deviation,
YANG_ITER_FILTER_IMPLICIT, NULL);
/* Cleanup and exit. */
yang_terminate();

59
yang/frr-test-module.yang Normal file
View file

@ -0,0 +1,59 @@
module frr-test-module {
yang-version 1.1;
namespace "urn:frr-test-module";
prefix frr-test-module;
import ietf-inet-types {
prefix inet;
}
import ietf-yang-types {
prefix yang;
}
import frr-interface {
prefix frr-interface;
}
revision 2018-11-26 {
description
"Initial revision.";
}
container frr-test-module {
config false;
container vrfs {
list vrf {
key "name";
leaf name {
type string;
}
container interfaces {
leaf-list interface {
type string;
}
}
container routes {
list route {
key "prefix";
leaf prefix {
type inet:ipv4-prefix;
}
leaf next-hop {
type inet:ipv4-address;
}
leaf interface {
type string;
}
leaf metric {
type uint8;
}
leaf active {
type empty;
}
}
}
}
}
}
}

View file

@ -20,6 +20,7 @@ EXTRA_DIST += yang/embedmodel.py
# without problems, as seen in libfrr.
dist_yangmodels_DATA += yang/frr-module-translator.yang
dist_yangmodels_DATA += yang/frr-test-module.yang
dist_yangmodels_DATA += yang/frr-interface.yang
dist_yangmodels_DATA += yang/frr-route-types.yang