forked from Mirror/frr
Merge pull request #3342 from opensourcerouting/nb-operational-data
Northbound: improved support for YANG-modeled operational data
This commit is contained in:
commit
9f7b49e105
|
@ -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",
|
||||
|
|
|
@ -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,
|
||||
|
|
436
lib/northbound.c
436
lib/northbound.c
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
110
lib/yang.c
110
lib/yang.c
|
@ -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);
|
||||
|
||||
|
|
118
lib/yang.h
118
lib/yang.h
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
|
|
@ -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, ...);
|
||||
|
|
|
@ -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
1
tests/.gitignore
vendored
|
@ -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
|
||||
|
|
466
tests/lib/northbound/test_oper_data.c
Normal file
466
tests/lib/northbound/test_oper_data.c
Normal 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);
|
||||
}
|
1
tests/lib/northbound/test_oper_data.in
Normal file
1
tests/lib/northbound/test_oper_data.in
Normal file
|
@ -0,0 +1 @@
|
|||
show yang operational-data /frr-test-module:frr-test-module
|
4
tests/lib/northbound/test_oper_data.py
Normal file
4
tests/lib/northbound/test_oper_data.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
import frrtest
|
||||
|
||||
class TestNbOperData(frrtest.TestRefOut):
|
||||
program = './test_oper_data'
|
119
tests/lib/northbound/test_oper_data.refout
Normal file
119
tests/lib/northbound/test_oper_data.refout
Normal 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.
|
|
@ -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;
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
59
yang/frr-test-module.yang
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue