2023-02-08 13:17:09 +01:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2017-12-07 20:31:48 +01:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2018 NetDEF, Inc.
|
|
|
|
* Renato Westphal
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <zebra.h>
|
|
|
|
|
2024-01-06 10:45:29 +01:00
|
|
|
#include "darr.h"
|
2017-12-07 20:31:48 +01:00
|
|
|
#include "libfrr.h"
|
|
|
|
#include "log.h"
|
|
|
|
#include "lib_errors.h"
|
2019-04-18 16:55:52 +02:00
|
|
|
#include "hash.h"
|
2017-12-07 20:31:48 +01:00
|
|
|
#include "command.h"
|
2019-04-12 23:00:26 +02:00
|
|
|
#include "debug.h"
|
2017-12-07 20:31:48 +01:00
|
|
|
#include "db.h"
|
2019-06-21 10:58:02 +02:00
|
|
|
#include "frr_pthread.h"
|
2017-12-07 20:31:48 +01:00
|
|
|
#include "northbound.h"
|
|
|
|
#include "northbound_cli.h"
|
|
|
|
#include "northbound_db.h"
|
2020-06-05 01:26:42 +02:00
|
|
|
#include "frrstr.h"
|
2017-12-07 20:31:48 +01:00
|
|
|
|
|
|
|
DEFINE_MTYPE_STATIC(LIB, NB_NODE, "Northbound Node");
|
|
|
|
DEFINE_MTYPE_STATIC(LIB, NB_CONFIG, "Northbound Configuration");
|
2019-04-18 16:55:52 +02:00
|
|
|
DEFINE_MTYPE_STATIC(LIB, NB_CONFIG_ENTRY, "Northbound Configuration Entry");
|
2017-12-07 20:31:48 +01:00
|
|
|
|
|
|
|
/* Running configuration - shouldn't be modified directly. */
|
|
|
|
struct nb_config *running_config;
|
|
|
|
|
2019-04-18 16:55:52 +02:00
|
|
|
/* Hash table of user pointers associated with configuration entries. */
|
|
|
|
static struct hash *running_config_entries;
|
|
|
|
|
2019-02-02 21:03:15 +01:00
|
|
|
/* Management lock for the running configuration. */
|
|
|
|
static struct {
|
|
|
|
/* Mutex protecting this structure. */
|
|
|
|
pthread_mutex_t mtx;
|
|
|
|
|
|
|
|
/* Actual lock. */
|
|
|
|
bool locked;
|
|
|
|
|
|
|
|
/* Northbound client who owns this lock. */
|
|
|
|
enum nb_client owner_client;
|
|
|
|
|
|
|
|
/* Northbound user who owns this lock. */
|
|
|
|
const void *owner_user;
|
|
|
|
} running_config_mgmt_lock;
|
|
|
|
|
2020-08-20 21:09:53 +02:00
|
|
|
/* Knob to record config transaction */
|
|
|
|
static bool nb_db_enabled;
|
2017-12-07 20:31:48 +01:00
|
|
|
/*
|
|
|
|
* Global lock used to prevent multiple configuration transactions from
|
|
|
|
* happening concurrently.
|
|
|
|
*/
|
|
|
|
static bool transaction_in_progress;
|
|
|
|
|
2020-04-27 18:13:57 +02:00
|
|
|
static int nb_callback_pre_validate(struct nb_context *context,
|
|
|
|
const struct nb_node *nb_node,
|
2020-05-14 17:30:34 +02:00
|
|
|
const struct lyd_node *dnode, char *errmsg,
|
|
|
|
size_t errmsg_len);
|
2020-04-27 18:13:57 +02:00
|
|
|
static int nb_callback_configuration(struct nb_context *context,
|
|
|
|
const enum nb_event event,
|
2020-05-14 17:30:34 +02:00
|
|
|
struct nb_config_change *change,
|
|
|
|
char *errmsg, size_t errmsg_len);
|
|
|
|
static struct nb_transaction *
|
2023-02-24 02:23:51 +01:00
|
|
|
nb_transaction_new(struct nb_context context, struct nb_config *config,
|
2020-05-14 17:30:34 +02:00
|
|
|
struct nb_config_cbs *changes, const char *comment,
|
|
|
|
char *errmsg, size_t errmsg_len);
|
2017-12-07 20:31:48 +01:00
|
|
|
static void nb_transaction_free(struct nb_transaction *transaction);
|
|
|
|
static int nb_transaction_process(enum nb_event event,
|
2020-05-14 17:30:34 +02:00
|
|
|
struct nb_transaction *transaction,
|
|
|
|
char *errmsg, size_t errmsg_len);
|
|
|
|
static void nb_transaction_apply_finish(struct nb_transaction *transaction,
|
|
|
|
char *errmsg, size_t errmsg_len);
|
2017-12-07 20:31:48 +01:00
|
|
|
|
2021-05-04 16:41:58 +02:00
|
|
|
static int nb_node_check_config_only(const struct lysc_node *snode, void *arg)
|
2018-11-13 23:36:48 +01:00
|
|
|
{
|
|
|
|
bool *config_only = arg;
|
|
|
|
|
|
|
|
if (CHECK_FLAG(snode->flags, LYS_CONFIG_R)) {
|
|
|
|
*config_only = false;
|
|
|
|
return YANG_ITER_STOP;
|
|
|
|
}
|
|
|
|
|
|
|
|
return YANG_ITER_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2021-05-04 16:41:58 +02:00
|
|
|
static int nb_node_new_cb(const struct lysc_node *snode, void *arg)
|
2017-12-07 20:31:48 +01:00
|
|
|
{
|
|
|
|
struct nb_node *nb_node;
|
2021-05-04 16:41:58 +02:00
|
|
|
struct lysc_node *sparent, *sparent_list;
|
2023-02-28 16:00:19 +01:00
|
|
|
struct frr_yang_module_info *module;
|
2017-12-07 20:31:48 +01:00
|
|
|
|
2023-02-28 16:00:19 +01:00
|
|
|
module = (struct frr_yang_module_info *)arg;
|
2017-12-07 20:31:48 +01:00
|
|
|
nb_node = XCALLOC(MTYPE_NB_NODE, sizeof(*nb_node));
|
|
|
|
yang_snode_get_path(snode, YANG_PATH_DATA, nb_node->xpath,
|
|
|
|
sizeof(nb_node->xpath));
|
|
|
|
nb_node->priority = NB_DFLT_PRIORITY;
|
|
|
|
sparent = yang_snode_real_parent(snode);
|
|
|
|
if (sparent)
|
|
|
|
nb_node->parent = sparent->priv;
|
|
|
|
sparent_list = yang_snode_parent_list(snode);
|
|
|
|
if (sparent_list)
|
|
|
|
nb_node->parent_list = sparent_list->priv;
|
|
|
|
|
2018-11-13 23:36:48 +01:00
|
|
|
/* Set flags. */
|
|
|
|
if (CHECK_FLAG(snode->nodetype, LYS_CONTAINER | LYS_LIST)) {
|
|
|
|
bool config_only = true;
|
|
|
|
|
2020-09-24 13:52:20 +02:00
|
|
|
(void)yang_snodes_iterate_subtree(snode, NULL,
|
|
|
|
nb_node_check_config_only, 0,
|
|
|
|
&config_only);
|
2018-11-13 23:36:48 +01:00
|
|
|
if (config_only)
|
|
|
|
SET_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY);
|
|
|
|
}
|
2018-12-08 20:31:16 +01:00
|
|
|
if (CHECK_FLAG(snode->nodetype, LYS_LIST)) {
|
2021-05-04 16:41:58 +02:00
|
|
|
if (yang_snode_num_keys(snode) == 0)
|
2018-12-08 20:31:16 +01:00
|
|
|
SET_FLAG(nb_node->flags, F_NB_NODE_KEYLESS_LIST);
|
|
|
|
}
|
2018-11-13 23:36:48 +01:00
|
|
|
|
2017-12-07 20:31:48 +01:00
|
|
|
/*
|
|
|
|
* Link the northbound node and the libyang schema node with one
|
|
|
|
* another.
|
|
|
|
*/
|
|
|
|
nb_node->snode = snode;
|
2020-09-10 22:12:09 +02:00
|
|
|
assert(snode->priv == NULL);
|
2021-05-04 16:41:58 +02:00
|
|
|
((struct lysc_node *)snode)->priv = nb_node;
|
2018-11-03 00:56:26 +01:00
|
|
|
|
2024-01-11 22:47:48 +01:00
|
|
|
if (module && module->ignore_cfg_cbs)
|
|
|
|
SET_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS);
|
2023-02-28 16:00:19 +01:00
|
|
|
|
2018-11-03 00:56:26 +01:00
|
|
|
return YANG_ITER_CONTINUE;
|
2017-12-07 20:31:48 +01:00
|
|
|
}
|
|
|
|
|
2021-05-04 16:41:58 +02:00
|
|
|
static int nb_node_del_cb(const struct lysc_node *snode, void *arg)
|
2017-12-07 20:31:48 +01:00
|
|
|
{
|
|
|
|
struct nb_node *nb_node;
|
|
|
|
|
|
|
|
nb_node = snode->priv;
|
2020-10-20 04:56:54 +02:00
|
|
|
if (nb_node) {
|
2021-05-04 16:41:58 +02:00
|
|
|
((struct lysc_node *)snode)->priv = NULL;
|
2020-10-20 04:56:54 +02:00
|
|
|
XFREE(MTYPE_NB_NODE, nb_node);
|
|
|
|
}
|
2018-11-03 00:56:26 +01:00
|
|
|
|
|
|
|
return YANG_ITER_CONTINUE;
|
2017-12-07 20:31:48 +01:00
|
|
|
}
|
|
|
|
|
2018-11-13 23:36:48 +01:00
|
|
|
void nb_nodes_create(void)
|
|
|
|
{
|
2020-10-23 03:19:10 +02:00
|
|
|
yang_snodes_iterate(NULL, nb_node_new_cb, 0, NULL);
|
2018-11-13 23:36:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void nb_nodes_delete(void)
|
|
|
|
{
|
2020-10-23 03:19:10 +02:00
|
|
|
yang_snodes_iterate(NULL, nb_node_del_cb, 0, NULL);
|
2018-11-13 23:36:48 +01:00
|
|
|
}
|
|
|
|
|
2021-05-04 16:41:58 +02:00
|
|
|
struct nb_node *nb_node_find(const char *path)
|
2017-12-07 20:31:48 +01:00
|
|
|
{
|
2021-05-04 16:41:58 +02:00
|
|
|
const struct lysc_node *snode;
|
2024-01-19 16:25:57 +01:00
|
|
|
uint32_t llopts;
|
2017-12-07 20:31:48 +01:00
|
|
|
|
|
|
|
/*
|
2021-05-04 16:41:58 +02:00
|
|
|
* Use libyang to find the schema node associated to the path and get
|
2024-01-19 16:25:57 +01:00
|
|
|
* the northbound node from there (snode private pointer). We need to
|
|
|
|
* disable logging temporarily to avoid libyang from logging an error
|
|
|
|
* message when the node is not found.
|
2017-12-07 20:31:48 +01:00
|
|
|
*/
|
2024-01-19 16:25:57 +01:00
|
|
|
llopts = ly_log_options(LY_LOSTORE);
|
|
|
|
llopts &= ~LY_LOLOG;
|
|
|
|
ly_temp_log_options(&llopts);
|
|
|
|
|
2023-04-17 07:09:05 +02:00
|
|
|
snode = yang_find_snode(ly_native_ctx, path, 0);
|
2024-01-19 16:25:57 +01:00
|
|
|
|
|
|
|
ly_temp_log_options(NULL);
|
2017-12-07 20:31:48 +01:00
|
|
|
if (!snode)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return snode->priv;
|
|
|
|
}
|
|
|
|
|
2024-01-06 10:45:29 +01:00
|
|
|
struct nb_node **nb_nodes_find(const char *xpath)
|
|
|
|
{
|
|
|
|
struct lysc_node **snodes = NULL;
|
|
|
|
struct nb_node **nb_nodes = NULL;
|
|
|
|
bool simple;
|
|
|
|
LY_ERR err;
|
|
|
|
uint i;
|
|
|
|
|
|
|
|
err = yang_resolve_snode_xpath(ly_native_ctx, xpath, &snodes, &simple);
|
|
|
|
if (err)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
darr_ensure_i(nb_nodes, darr_lasti(snodes));
|
|
|
|
darr_foreach_i (snodes, i)
|
|
|
|
nb_nodes[i] = snodes[i]->priv;
|
|
|
|
darr_free(snodes);
|
|
|
|
return nb_nodes;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-02-16 10:49:25 +01:00
|
|
|
void nb_node_set_dependency_cbs(const char *dependency_xpath,
|
|
|
|
const char *dependant_xpath,
|
|
|
|
struct nb_dependency_callbacks *cbs)
|
|
|
|
{
|
|
|
|
struct nb_node *dependency = nb_node_find(dependency_xpath);
|
|
|
|
struct nb_node *dependant = nb_node_find(dependant_xpath);
|
|
|
|
|
|
|
|
if (!dependency || !dependant)
|
|
|
|
return;
|
|
|
|
|
|
|
|
dependency->dep_cbs.get_dependant_xpath = cbs->get_dependant_xpath;
|
|
|
|
dependant->dep_cbs.get_dependency_xpath = cbs->get_dependency_xpath;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool nb_node_has_dependency(struct nb_node *node)
|
|
|
|
{
|
|
|
|
return node->dep_cbs.get_dependency_xpath != NULL;
|
|
|
|
}
|
|
|
|
|
2017-12-07 20:31:48 +01:00
|
|
|
static int nb_node_validate_cb(const struct nb_node *nb_node,
|
2023-10-05 23:13:16 +02:00
|
|
|
enum nb_cb_operation operation,
|
2017-12-07 20:31:48 +01:00
|
|
|
int callback_implemented, bool optional)
|
|
|
|
{
|
|
|
|
bool valid;
|
|
|
|
|
2023-10-05 23:13:16 +02:00
|
|
|
valid = nb_cb_operation_is_valid(operation, nb_node->snode);
|
2017-12-07 20:31:48 +01:00
|
|
|
|
2019-09-18 15:55:55 +02:00
|
|
|
/*
|
|
|
|
* Add an exception for operational data callbacks. A rw list usually
|
|
|
|
* doesn't need any associated operational data callbacks. But if this
|
|
|
|
* rw list is augmented by another module which adds state nodes under
|
|
|
|
* it, then this list will need to have the 'get_next()', 'get_keys()'
|
|
|
|
* and 'lookup_entry()' callbacks. As such, never log a warning when
|
|
|
|
* these callbacks are implemented when they are not needed, since this
|
|
|
|
* depends on context (e.g. some daemons might augment "frr-interface"
|
|
|
|
* while others don't).
|
|
|
|
*/
|
2023-10-05 23:13:16 +02:00
|
|
|
if (!valid && callback_implemented && operation != NB_CB_GET_NEXT
|
|
|
|
&& operation != NB_CB_GET_KEYS && operation != NB_CB_LOOKUP_ENTRY)
|
2017-12-07 20:31:48 +01:00
|
|
|
flog_warn(EC_LIB_NB_CB_UNNEEDED,
|
|
|
|
"unneeded '%s' callback for '%s'",
|
2023-10-05 23:13:16 +02:00
|
|
|
nb_cb_operation_name(operation), nb_node->xpath);
|
2017-12-07 20:31:48 +01:00
|
|
|
|
|
|
|
if (!optional && valid && !callback_implemented) {
|
|
|
|
flog_err(EC_LIB_NB_CB_MISSING, "missing '%s' callback for '%s'",
|
2023-10-05 23:13:16 +02:00
|
|
|
nb_cb_operation_name(operation), nb_node->xpath);
|
2017-12-07 20:31:48 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if the required callbacks were implemented for the given northbound
|
|
|
|
* node.
|
|
|
|
*/
|
|
|
|
static unsigned int nb_node_validate_cbs(const struct nb_node *nb_node)
|
|
|
|
|
|
|
|
{
|
|
|
|
unsigned int error = 0;
|
|
|
|
|
2024-01-11 22:47:48 +01:00
|
|
|
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
|
2023-02-28 16:00:19 +01:00
|
|
|
return error;
|
|
|
|
|
2023-10-05 23:13:16 +02:00
|
|
|
error += nb_node_validate_cb(nb_node, NB_CB_CREATE,
|
2017-12-07 20:31:48 +01:00
|
|
|
!!nb_node->cbs.create, false);
|
2023-10-05 23:13:16 +02:00
|
|
|
error += nb_node_validate_cb(nb_node, NB_CB_MODIFY,
|
2017-12-07 20:31:48 +01:00
|
|
|
!!nb_node->cbs.modify, false);
|
2023-10-05 23:13:16 +02:00
|
|
|
error += nb_node_validate_cb(nb_node, NB_CB_DESTROY,
|
2019-01-15 19:34:23 +01:00
|
|
|
!!nb_node->cbs.destroy, false);
|
2023-10-05 23:13:16 +02:00
|
|
|
error += nb_node_validate_cb(nb_node, NB_CB_MOVE, !!nb_node->cbs.move,
|
2017-12-07 20:31:48 +01:00
|
|
|
false);
|
2023-10-05 23:13:16 +02:00
|
|
|
error += nb_node_validate_cb(nb_node, NB_CB_PRE_VALIDATE,
|
2019-09-13 00:07:54 +02:00
|
|
|
!!nb_node->cbs.pre_validate, true);
|
2023-10-05 23:13:16 +02:00
|
|
|
error += nb_node_validate_cb(nb_node, NB_CB_APPLY_FINISH,
|
2017-12-07 20:31:48 +01:00
|
|
|
!!nb_node->cbs.apply_finish, true);
|
2023-10-05 23:13:16 +02:00
|
|
|
error += nb_node_validate_cb(nb_node, NB_CB_GET_ELEM,
|
2017-12-07 20:31:48 +01:00
|
|
|
!!nb_node->cbs.get_elem, false);
|
2023-10-05 23:13:16 +02:00
|
|
|
error += nb_node_validate_cb(nb_node, NB_CB_GET_NEXT,
|
2017-12-07 20:31:48 +01:00
|
|
|
!!nb_node->cbs.get_next, false);
|
2023-10-05 23:13:16 +02:00
|
|
|
error += nb_node_validate_cb(nb_node, NB_CB_GET_KEYS,
|
2017-12-07 20:31:48 +01:00
|
|
|
!!nb_node->cbs.get_keys, false);
|
2023-10-05 23:13:16 +02:00
|
|
|
error += nb_node_validate_cb(nb_node, NB_CB_LOOKUP_ENTRY,
|
2017-12-07 20:31:48 +01:00
|
|
|
!!nb_node->cbs.lookup_entry, false);
|
2023-10-05 23:13:16 +02:00
|
|
|
error += nb_node_validate_cb(nb_node, NB_CB_RPC, !!nb_node->cbs.rpc,
|
2017-12-07 20:31:48 +01:00
|
|
|
false);
|
2024-02-10 01:11:13 +01:00
|
|
|
error += nb_node_validate_cb(nb_node, NB_CB_NOTIFY,
|
|
|
|
!!nb_node->cbs.notify, true);
|
2017-12-07 20:31:48 +01:00
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int nb_node_validate_priority(const struct nb_node *nb_node)
|
|
|
|
{
|
|
|
|
/* Top-level nodes can have any priority. */
|
|
|
|
if (!nb_node->parent)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (nb_node->priority < nb_node->parent->priority) {
|
|
|
|
flog_err(EC_LIB_NB_CB_INVALID_PRIO,
|
|
|
|
"node has higher priority than its parent [xpath %s]",
|
|
|
|
nb_node->xpath);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-05-04 16:41:58 +02:00
|
|
|
static int nb_node_validate(const struct lysc_node *snode, void *arg)
|
2017-12-07 20:31:48 +01:00
|
|
|
{
|
|
|
|
struct nb_node *nb_node = snode->priv;
|
2018-11-03 00:56:26 +01:00
|
|
|
unsigned int *errors = arg;
|
2017-12-07 20:31:48 +01:00
|
|
|
|
|
|
|
/* Validate callbacks and priority. */
|
2020-10-20 04:56:54 +02:00
|
|
|
if (nb_node) {
|
|
|
|
*errors += nb_node_validate_cbs(nb_node);
|
|
|
|
*errors += nb_node_validate_priority(nb_node);
|
|
|
|
}
|
2018-11-03 00:56:26 +01:00
|
|
|
|
|
|
|
return YANG_ITER_CONTINUE;
|
2017-12-07 20:31:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
struct nb_config *nb_config_new(struct lyd_node *dnode)
|
|
|
|
{
|
|
|
|
struct nb_config *config;
|
|
|
|
|
|
|
|
config = XCALLOC(MTYPE_NB_CONFIG, sizeof(*config));
|
|
|
|
if (dnode)
|
|
|
|
config->dnode = dnode;
|
|
|
|
else
|
2018-11-03 00:57:20 +01:00
|
|
|
config->dnode = yang_dnode_new(ly_native_ctx, true);
|
2017-12-07 20:31:48 +01:00
|
|
|
config->version = 0;
|
|
|
|
|
|
|
|
return config;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nb_config_free(struct nb_config *config)
|
|
|
|
{
|
|
|
|
if (config->dnode)
|
|
|
|
yang_dnode_free(config->dnode);
|
2023-10-06 20:18:33 +02:00
|
|
|
|
2017-12-07 20:31:48 +01:00
|
|
|
XFREE(MTYPE_NB_CONFIG, config);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct nb_config *nb_config_dup(const struct nb_config *config)
|
|
|
|
{
|
|
|
|
struct nb_config *dup;
|
|
|
|
|
|
|
|
dup = XCALLOC(MTYPE_NB_CONFIG, sizeof(*dup));
|
|
|
|
dup->dnode = yang_dnode_dup(config->dnode);
|
|
|
|
dup->version = config->version;
|
|
|
|
|
|
|
|
return dup;
|
|
|
|
}
|
|
|
|
|
|
|
|
int nb_config_merge(struct nb_config *config_dst, struct nb_config *config_src,
|
|
|
|
bool preserve_source)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2021-05-20 08:49:32 +02:00
|
|
|
ret = lyd_merge_siblings(&config_dst->dnode, config_src->dnode, 0);
|
2017-12-07 20:31:48 +01:00
|
|
|
if (ret != 0)
|
|
|
|
flog_warn(EC_LIB_LIBYANG, "%s: lyd_merge() failed", __func__);
|
|
|
|
|
|
|
|
if (!preserve_source)
|
|
|
|
nb_config_free(config_src);
|
|
|
|
|
|
|
|
return (ret == 0) ? NB_OK : NB_ERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nb_config_replace(struct nb_config *config_dst,
|
|
|
|
struct nb_config *config_src, bool preserve_source)
|
|
|
|
{
|
|
|
|
/* Update version. */
|
|
|
|
if (config_src->version != 0)
|
|
|
|
config_dst->version = config_src->version;
|
|
|
|
|
|
|
|
/* Update dnode. */
|
2018-11-08 05:18:38 +01:00
|
|
|
if (config_dst->dnode)
|
|
|
|
yang_dnode_free(config_dst->dnode);
|
2017-12-07 20:31:48 +01:00
|
|
|
if (preserve_source) {
|
|
|
|
config_dst->dnode = yang_dnode_dup(config_src->dnode);
|
|
|
|
} else {
|
|
|
|
config_dst->dnode = config_src->dnode;
|
|
|
|
config_src->dnode = NULL;
|
|
|
|
nb_config_free(config_src);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Generate the nb_config_cbs tree. */
|
|
|
|
static inline int nb_config_cb_compare(const struct nb_config_cb *a,
|
|
|
|
const struct nb_config_cb *b)
|
|
|
|
{
|
2024-02-20 21:32:52 +01:00
|
|
|
/*
|
|
|
|
* Sort by priority first. If the operation is "destroy", reverse the
|
|
|
|
* order, so that the dependencies are destroyed before the dependants.
|
|
|
|
*/
|
2017-12-07 20:31:48 +01:00
|
|
|
if (a->nb_node->priority < b->nb_node->priority)
|
2024-02-20 21:32:52 +01:00
|
|
|
return a->operation != NB_CB_DESTROY ? -1 : 1;
|
2017-12-07 20:31:48 +01:00
|
|
|
if (a->nb_node->priority > b->nb_node->priority)
|
2024-02-20 21:32:52 +01:00
|
|
|
return a->operation != NB_CB_DESTROY ? 1 : -1;
|
2017-12-07 20:31:48 +01:00
|
|
|
|
|
|
|
/*
|
2019-08-27 03:31:21 +02:00
|
|
|
* Preserve the order of the configuration changes as told by libyang.
|
2017-12-07 20:31:48 +01:00
|
|
|
*/
|
2019-10-10 04:24:04 +02:00
|
|
|
if (a->seq < b->seq)
|
|
|
|
return -1;
|
|
|
|
if (a->seq > b->seq)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* All 'apply_finish' callbacks have their sequence number set to zero.
|
|
|
|
* In this case, compare them using their dnode pointers (the order
|
|
|
|
* doesn't matter for callbacks that have the same priority).
|
|
|
|
*/
|
|
|
|
if (a->dnode < b->dnode)
|
|
|
|
return -1;
|
|
|
|
if (a->dnode > b->dnode)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
2017-12-07 20:31:48 +01:00
|
|
|
}
|
|
|
|
RB_GENERATE(nb_config_cbs, nb_config_cb, entry, nb_config_cb_compare);
|
|
|
|
|
2024-01-18 18:06:45 +01:00
|
|
|
void nb_config_diff_add_change(struct nb_config_cbs *changes,
|
|
|
|
enum nb_cb_operation operation, uint32_t *seq,
|
|
|
|
const struct lyd_node *dnode)
|
2017-12-07 20:31:48 +01:00
|
|
|
{
|
|
|
|
struct nb_config_change *change;
|
|
|
|
|
2020-09-11 15:43:49 +02:00
|
|
|
/* Ignore unimplemented nodes. */
|
|
|
|
if (!dnode->schema->priv)
|
|
|
|
return;
|
|
|
|
|
2017-12-07 20:31:48 +01:00
|
|
|
change = XCALLOC(MTYPE_TMP, sizeof(*change));
|
|
|
|
change->cb.operation = operation;
|
2019-08-27 03:31:21 +02:00
|
|
|
change->cb.seq = *seq;
|
|
|
|
*seq = *seq + 1;
|
2017-12-07 20:31:48 +01:00
|
|
|
change->cb.nb_node = dnode->schema->priv;
|
|
|
|
change->cb.dnode = dnode;
|
|
|
|
|
|
|
|
RB_INSERT(nb_config_cbs, changes, &change->cb);
|
|
|
|
}
|
|
|
|
|
2023-02-28 16:00:19 +01:00
|
|
|
void nb_config_diff_del_changes(struct nb_config_cbs *changes)
|
2017-12-07 20:31:48 +01:00
|
|
|
{
|
|
|
|
while (!RB_EMPTY(nb_config_cbs, changes)) {
|
|
|
|
struct nb_config_change *change;
|
|
|
|
|
|
|
|
change = (struct nb_config_change *)RB_ROOT(nb_config_cbs,
|
|
|
|
changes);
|
|
|
|
RB_REMOVE(nb_config_cbs, changes, &change->cb);
|
|
|
|
XFREE(MTYPE_TMP, change);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Helper function used when calculating the delta between two different
|
|
|
|
* configurations. Given a new subtree, calculate all new YANG data nodes,
|
|
|
|
* excluding default leafs and leaf-lists. This is a recursive function.
|
|
|
|
*/
|
2023-02-28 16:00:19 +01:00
|
|
|
void nb_config_diff_created(const struct lyd_node *dnode, uint32_t *seq,
|
|
|
|
struct nb_config_cbs *changes)
|
2017-12-07 20:31:48 +01:00
|
|
|
{
|
2023-10-05 23:13:16 +02:00
|
|
|
enum nb_cb_operation operation;
|
2017-12-07 20:31:48 +01:00
|
|
|
struct lyd_node *child;
|
|
|
|
|
2020-09-11 15:43:49 +02:00
|
|
|
/* Ignore unimplemented nodes. */
|
|
|
|
if (!dnode->schema->priv)
|
|
|
|
return;
|
|
|
|
|
2019-02-28 23:49:28 +01:00
|
|
|
switch (dnode->schema->nodetype) {
|
|
|
|
case LYS_LEAF:
|
|
|
|
case LYS_LEAFLIST:
|
2021-05-04 16:41:58 +02:00
|
|
|
if (lyd_is_default(dnode))
|
2019-02-28 23:49:28 +01:00
|
|
|
break;
|
2017-12-07 20:31:48 +01:00
|
|
|
|
2023-10-05 23:13:16 +02:00
|
|
|
if (nb_cb_operation_is_valid(NB_CB_CREATE, dnode->schema))
|
|
|
|
operation = NB_CB_CREATE;
|
|
|
|
else if (nb_cb_operation_is_valid(NB_CB_MODIFY, dnode->schema))
|
|
|
|
operation = NB_CB_MODIFY;
|
2019-02-28 23:49:28 +01:00
|
|
|
else
|
|
|
|
return;
|
2017-12-07 20:31:48 +01:00
|
|
|
|
2019-08-27 03:31:21 +02:00
|
|
|
nb_config_diff_add_change(changes, operation, seq, dnode);
|
2019-02-28 23:49:28 +01:00
|
|
|
break;
|
|
|
|
case LYS_CONTAINER:
|
|
|
|
case LYS_LIST:
|
2023-10-05 23:13:16 +02:00
|
|
|
if (nb_cb_operation_is_valid(NB_CB_CREATE, dnode->schema))
|
|
|
|
nb_config_diff_add_change(changes, NB_CB_CREATE, seq,
|
2019-08-27 03:31:21 +02:00
|
|
|
dnode);
|
2019-02-28 23:49:28 +01:00
|
|
|
|
|
|
|
/* Process child nodes recursively. */
|
2021-05-04 16:41:58 +02:00
|
|
|
LY_LIST_FOR (lyd_child(dnode), child) {
|
2019-08-27 03:31:21 +02:00
|
|
|
nb_config_diff_created(child, seq, changes);
|
2017-12-07 20:31:48 +01:00
|
|
|
}
|
2019-02-28 23:49:28 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2017-12-07 20:31:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-27 03:31:21 +02:00
|
|
|
static void nb_config_diff_deleted(const struct lyd_node *dnode, uint32_t *seq,
|
2019-02-28 23:54:47 +01:00
|
|
|
struct nb_config_cbs *changes)
|
|
|
|
{
|
2020-09-11 15:43:49 +02:00
|
|
|
/* Ignore unimplemented nodes. */
|
|
|
|
if (!dnode->schema->priv)
|
|
|
|
return;
|
|
|
|
|
2023-10-05 23:13:16 +02:00
|
|
|
if (nb_cb_operation_is_valid(NB_CB_DESTROY, dnode->schema))
|
|
|
|
nb_config_diff_add_change(changes, NB_CB_DESTROY, seq, dnode);
|
2019-02-28 23:54:47 +01:00
|
|
|
else if (CHECK_FLAG(dnode->schema->nodetype, LYS_CONTAINER)) {
|
|
|
|
struct lyd_node *child;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Non-presence containers need special handling since they
|
|
|
|
* don't have "destroy" callbacks. In this case, what we need to
|
|
|
|
* do is to call the "destroy" callbacks of their child nodes
|
|
|
|
* when applicable (i.e. optional nodes).
|
|
|
|
*/
|
2021-05-04 16:41:58 +02:00
|
|
|
LY_LIST_FOR (lyd_child(dnode), child) {
|
2019-08-27 03:31:21 +02:00
|
|
|
nb_config_diff_deleted(child, seq, changes);
|
2019-02-28 23:54:47 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-04 16:41:58 +02:00
|
|
|
static int nb_lyd_diff_get_op(const struct lyd_node *dnode)
|
|
|
|
{
|
|
|
|
const struct lyd_meta *meta;
|
|
|
|
LY_LIST_FOR (dnode->meta, meta) {
|
|
|
|
if (strcmp(meta->name, "operation")
|
|
|
|
|| strcmp(meta->annotation->module->name, "yang"))
|
|
|
|
continue;
|
|
|
|
return lyd_get_meta_value(meta)[0];
|
|
|
|
}
|
|
|
|
return 'n';
|
|
|
|
}
|
|
|
|
|
2021-05-28 21:16:18 +02:00
|
|
|
#if 0 /* Used below in nb_config_diff inside normally disabled code */
|
2021-05-04 16:41:58 +02:00
|
|
|
static inline void nb_config_diff_dnode_log_path(const char *context,
|
|
|
|
const char *path,
|
|
|
|
const struct lyd_node *dnode)
|
|
|
|
{
|
|
|
|
if (dnode->schema->nodetype & LYD_NODE_TERM)
|
|
|
|
zlog_debug("nb_config_diff: %s: %s: %s", context, path,
|
|
|
|
lyd_get_value(dnode));
|
|
|
|
else
|
|
|
|
zlog_debug("nb_config_diff: %s: %s", context, path);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void nb_config_diff_dnode_log(const char *context,
|
|
|
|
const struct lyd_node *dnode)
|
|
|
|
{
|
|
|
|
if (!dnode) {
|
|
|
|
zlog_debug("nb_config_diff: %s: NULL", context);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
char *path = lyd_path(dnode, LYD_PATH_STD, NULL, 0);
|
|
|
|
nb_config_diff_dnode_log_path(context, path, dnode);
|
|
|
|
free(path);
|
|
|
|
}
|
2021-05-28 21:16:18 +02:00
|
|
|
#endif
|
2021-05-04 16:41:58 +02:00
|
|
|
|
2023-02-28 16:00:19 +01:00
|
|
|
/*
|
|
|
|
* Calculate the delta between two different configurations.
|
|
|
|
*
|
|
|
|
* NOTE: 'config1' is the reference DB, while 'config2' is
|
|
|
|
* the DB being compared against 'config1'. Typically 'config1'
|
|
|
|
* should be the Running DB and 'config2' is the Candidate DB.
|
|
|
|
*/
|
|
|
|
void nb_config_diff(const struct nb_config *config1,
|
|
|
|
const struct nb_config *config2,
|
|
|
|
struct nb_config_cbs *changes)
|
2017-12-07 20:31:48 +01:00
|
|
|
{
|
2021-05-04 16:41:58 +02:00
|
|
|
struct lyd_node *diff = NULL;
|
|
|
|
const struct lyd_node *root, *dnode;
|
|
|
|
struct lyd_node *target;
|
|
|
|
int op;
|
|
|
|
LY_ERR err;
|
|
|
|
char *path;
|
|
|
|
|
2021-05-28 21:16:18 +02:00
|
|
|
#if 0 /* Useful (noisy) when debugging diff code, and for improving later */
|
2021-05-04 16:41:58 +02:00
|
|
|
if (DEBUG_MODE_CHECK(&nb_dbg_cbs_config, DEBUG_MODE_ALL)) {
|
|
|
|
LY_LIST_FOR(config1->dnode, root) {
|
|
|
|
LYD_TREE_DFS_BEGIN(root, dnode) {
|
|
|
|
nb_config_diff_dnode_log("from", dnode);
|
|
|
|
LYD_TREE_DFS_END(root, dnode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
LY_LIST_FOR(config2->dnode, root) {
|
|
|
|
LYD_TREE_DFS_BEGIN(root, dnode) {
|
|
|
|
nb_config_diff_dnode_log("to", dnode);
|
|
|
|
LYD_TREE_DFS_END(root, dnode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2017-12-07 20:31:48 +01:00
|
|
|
|
2021-05-04 16:41:58 +02:00
|
|
|
err = lyd_diff_siblings(config1->dnode, config2->dnode,
|
|
|
|
LYD_DIFF_DEFAULTS, &diff);
|
|
|
|
assert(!err);
|
2017-12-07 20:31:48 +01:00
|
|
|
|
2021-05-28 21:16:18 +02:00
|
|
|
if (diff && DEBUG_MODE_CHECK(&nb_dbg_cbs_config, DEBUG_MODE_ALL)) {
|
|
|
|
char *s;
|
|
|
|
|
|
|
|
if (!lyd_print_mem(&s, diff, LYD_JSON,
|
|
|
|
LYD_PRINT_WITHSIBLINGS | LYD_PRINT_WD_ALL)) {
|
|
|
|
zlog_debug("%s: %s", __func__, s);
|
|
|
|
free(s);
|
|
|
|
}
|
|
|
|
}
|
2017-12-07 20:31:48 +01:00
|
|
|
|
2021-05-04 16:41:58 +02:00
|
|
|
uint32_t seq = 0;
|
2021-05-28 21:16:18 +02:00
|
|
|
|
2021-05-04 16:41:58 +02:00
|
|
|
LY_LIST_FOR (diff, root) {
|
|
|
|
LYD_TREE_DFS_BEGIN (root, dnode) {
|
|
|
|
op = nb_lyd_diff_get_op(dnode);
|
|
|
|
|
|
|
|
path = lyd_path(dnode, LYD_PATH_STD, NULL, 0);
|
|
|
|
|
2021-05-28 21:16:18 +02:00
|
|
|
#if 0 /* Useful (noisy) when debugging diff code, and for improving later */
|
2021-05-04 16:41:58 +02:00
|
|
|
if (DEBUG_MODE_CHECK(&nb_dbg_cbs_config, DEBUG_MODE_ALL)) {
|
|
|
|
char context[80];
|
|
|
|
snprintf(context, sizeof(context),
|
|
|
|
"iterating diff: oper: %c seq: %u", op, seq);
|
|
|
|
nb_config_diff_dnode_log_path(context, path, dnode);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
switch (op) {
|
|
|
|
case 'c': /* create */
|
|
|
|
/*
|
|
|
|
* This is rather inefficient, but when we use
|
|
|
|
* dnode from the diff instead of the
|
|
|
|
* candidate config node we get failures when
|
|
|
|
* looking up default values, etc, based on
|
|
|
|
* the diff tree.
|
|
|
|
*/
|
|
|
|
target = yang_dnode_get(config2->dnode, path);
|
2021-05-19 14:53:16 +02:00
|
|
|
assert(target);
|
2021-05-04 16:41:58 +02:00
|
|
|
nb_config_diff_created(target, &seq, changes);
|
|
|
|
|
|
|
|
/* Skip rest of sub-tree, move to next sibling
|
|
|
|
*/
|
|
|
|
LYD_TREE_DFS_continue = 1;
|
|
|
|
break;
|
|
|
|
case 'd': /* delete */
|
|
|
|
target = yang_dnode_get(config1->dnode, path);
|
2021-05-19 14:53:16 +02:00
|
|
|
assert(target);
|
2021-05-04 16:41:58 +02:00
|
|
|
nb_config_diff_deleted(target, &seq, changes);
|
2017-12-07 20:31:48 +01:00
|
|
|
|
2021-05-04 16:41:58 +02:00
|
|
|
/* Skip rest of sub-tree, move to next sibling
|
|
|
|
*/
|
|
|
|
LYD_TREE_DFS_continue = 1;
|
|
|
|
break;
|
|
|
|
case 'r': /* replace */
|
|
|
|
/* either moving an entry or changing a value */
|
|
|
|
target = yang_dnode_get(config2->dnode, path);
|
|
|
|
assert(target);
|
2023-10-05 23:13:16 +02:00
|
|
|
nb_config_diff_add_change(changes, NB_CB_MODIFY,
|
2021-05-04 16:41:58 +02:00
|
|
|
&seq, target);
|
|
|
|
break;
|
|
|
|
case 'n': /* none */
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
free(path);
|
|
|
|
LYD_TREE_DFS_END(root, dnode);
|
2017-12-07 20:31:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-28 21:16:18 +02:00
|
|
|
lyd_free_all(diff);
|
2017-12-07 20:31:48 +01:00
|
|
|
}
|
|
|
|
|
2023-10-09 02:10:18 +02:00
|
|
|
static int dnode_create(struct nb_config *candidate, const char *xpath,
|
|
|
|
const char *value, uint32_t options,
|
|
|
|
struct lyd_node **new_dnode)
|
|
|
|
{
|
|
|
|
struct lyd_node *dnode;
|
|
|
|
LY_ERR err;
|
|
|
|
|
|
|
|
err = lyd_new_path(candidate->dnode, ly_native_ctx, xpath, value,
|
|
|
|
options, &dnode);
|
|
|
|
if (err) {
|
|
|
|
flog_warn(EC_LIB_LIBYANG, "%s: lyd_new_path(%s) failed: %d",
|
|
|
|
__func__, xpath, err);
|
|
|
|
return NB_ERR;
|
|
|
|
} else if (dnode) {
|
|
|
|
err = lyd_new_implicit_tree(dnode, LYD_IMPLICIT_NO_STATE, NULL);
|
|
|
|
if (err) {
|
|
|
|
flog_warn(EC_LIB_LIBYANG,
|
|
|
|
"%s: lyd_new_implicit_all failed: %d",
|
|
|
|
__func__, err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (new_dnode)
|
|
|
|
*new_dnode = dnode;
|
|
|
|
return NB_OK;
|
|
|
|
}
|
|
|
|
|
2024-01-18 05:17:35 +01:00
|
|
|
int nb_candidate_edit(struct nb_config *candidate, const struct nb_node *nb_node,
|
2017-12-07 20:31:48 +01:00
|
|
|
enum nb_operation operation, const char *xpath,
|
2024-01-23 01:09:25 +01:00
|
|
|
const struct yang_data *previous,
|
2017-12-07 20:31:48 +01:00
|
|
|
const struct yang_data *data)
|
|
|
|
{
|
2024-01-18 18:06:45 +01:00
|
|
|
struct lyd_node *dnode, *dep_dnode, *old_dnode;
|
2021-02-16 10:49:25 +01:00
|
|
|
char dep_xpath[XPATH_MAXLEN];
|
2024-01-18 18:06:45 +01:00
|
|
|
struct lyd_node *parent = NULL;
|
2023-10-06 01:58:58 +02:00
|
|
|
uint32_t options = 0;
|
2021-05-04 16:41:58 +02:00
|
|
|
LY_ERR err;
|
2017-12-07 20:31:48 +01:00
|
|
|
|
|
|
|
switch (operation) {
|
|
|
|
case NB_OP_CREATE:
|
|
|
|
case NB_OP_MODIFY:
|
2023-10-06 01:58:58 +02:00
|
|
|
options = LYD_NEW_PATH_UPDATE;
|
|
|
|
fallthrough;
|
|
|
|
case NB_OP_CREATE_EXCL:
|
2024-01-23 01:09:25 +01:00
|
|
|
err = dnode_create(candidate, xpath, data->value, options,
|
2021-05-04 16:41:58 +02:00
|
|
|
&dnode);
|
|
|
|
if (err) {
|
2023-10-09 02:10:18 +02:00
|
|
|
return err;
|
2021-05-04 16:41:58 +02:00
|
|
|
} else if (dnode) {
|
2021-02-16 10:49:25 +01:00
|
|
|
/*
|
|
|
|
* create dependency
|
|
|
|
*
|
|
|
|
* dnode returned by the lyd_new_path may be from a
|
|
|
|
* different schema, so we need to update the nb_node
|
|
|
|
*/
|
|
|
|
nb_node = dnode->schema->priv;
|
|
|
|
if (nb_node->dep_cbs.get_dependency_xpath) {
|
|
|
|
nb_node->dep_cbs.get_dependency_xpath(
|
|
|
|
dnode, dep_xpath);
|
|
|
|
|
2023-10-09 02:10:18 +02:00
|
|
|
err = dnode_create(candidate, dep_xpath, NULL,
|
|
|
|
LYD_NEW_PATH_UPDATE, NULL);
|
2021-05-04 16:41:58 +02:00
|
|
|
if (err) {
|
2023-10-09 02:10:18 +02:00
|
|
|
lyd_free_tree(dnode);
|
|
|
|
return err;
|
2021-02-16 10:49:25 +01:00
|
|
|
}
|
|
|
|
}
|
2017-12-07 20:31:48 +01:00
|
|
|
}
|
|
|
|
break;
|
2019-01-30 10:54:25 +01:00
|
|
|
case NB_OP_DESTROY:
|
2023-10-06 14:01:16 +02:00
|
|
|
case NB_OP_DELETE:
|
2024-01-23 01:09:25 +01:00
|
|
|
dnode = yang_dnode_get(candidate->dnode, xpath);
|
2023-10-06 14:01:16 +02:00
|
|
|
if (!dnode) {
|
|
|
|
if (operation == NB_OP_DELETE)
|
|
|
|
return NB_ERR;
|
|
|
|
else
|
|
|
|
return NB_OK;
|
|
|
|
}
|
2021-02-16 10:49:25 +01:00
|
|
|
/* destroy dependant */
|
|
|
|
if (nb_node->dep_cbs.get_dependant_xpath) {
|
|
|
|
nb_node->dep_cbs.get_dependant_xpath(dnode, dep_xpath);
|
|
|
|
|
|
|
|
dep_dnode = yang_dnode_get(candidate->dnode, dep_xpath);
|
|
|
|
if (dep_dnode)
|
2021-05-04 16:41:58 +02:00
|
|
|
lyd_free_tree(dep_dnode);
|
2021-02-16 10:49:25 +01:00
|
|
|
}
|
2021-05-04 16:41:58 +02:00
|
|
|
lyd_free_tree(dnode);
|
2017-12-07 20:31:48 +01:00
|
|
|
break;
|
2023-10-09 02:21:16 +02:00
|
|
|
case NB_OP_REPLACE:
|
2024-01-23 01:09:25 +01:00
|
|
|
old_dnode = yang_dnode_get(candidate->dnode, xpath);
|
2023-10-09 02:21:16 +02:00
|
|
|
if (old_dnode) {
|
|
|
|
parent = lyd_parent(old_dnode);
|
|
|
|
lyd_unlink_tree(old_dnode);
|
|
|
|
}
|
2024-01-23 01:09:25 +01:00
|
|
|
err = dnode_create(candidate, xpath, data->value, options,
|
2023-10-09 02:21:16 +02:00
|
|
|
&dnode);
|
|
|
|
if (!err && dnode && !old_dnode) {
|
|
|
|
/* create dependency if the node didn't exist */
|
|
|
|
nb_node = dnode->schema->priv;
|
|
|
|
if (nb_node->dep_cbs.get_dependency_xpath) {
|
|
|
|
nb_node->dep_cbs.get_dependency_xpath(
|
|
|
|
dnode, dep_xpath);
|
|
|
|
|
|
|
|
err = dnode_create(candidate, dep_xpath, NULL,
|
|
|
|
LYD_NEW_PATH_UPDATE, NULL);
|
|
|
|
if (err)
|
|
|
|
lyd_free_tree(dnode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (old_dnode) {
|
|
|
|
/* restore original node on error, free it otherwise */
|
|
|
|
if (err) {
|
|
|
|
if (parent)
|
|
|
|
lyd_insert_child(parent, old_dnode);
|
|
|
|
else
|
|
|
|
lyd_insert_sibling(candidate->dnode,
|
|
|
|
old_dnode, NULL);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
lyd_free_tree(old_dnode);
|
|
|
|
}
|
|
|
|
break;
|
2017-12-07 20:31:48 +01:00
|
|
|
case NB_OP_MOVE:
|
|
|
|
/* TODO: update configuration. */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NB_OK;
|
|
|
|
}
|
|
|
|
|
2023-10-05 23:13:16 +02:00
|
|
|
const char *nb_operation_name(enum nb_operation operation)
|
2023-02-28 16:00:19 +01:00
|
|
|
{
|
2023-10-05 23:13:16 +02:00
|
|
|
switch (operation) {
|
2023-10-06 01:58:58 +02:00
|
|
|
case NB_OP_CREATE_EXCL:
|
|
|
|
return "create exclusive";
|
2023-10-05 23:13:16 +02:00
|
|
|
case NB_OP_CREATE:
|
|
|
|
return "create";
|
|
|
|
case NB_OP_MODIFY:
|
|
|
|
return "modify";
|
|
|
|
case NB_OP_DESTROY:
|
|
|
|
return "destroy";
|
2023-10-06 14:01:16 +02:00
|
|
|
case NB_OP_DELETE:
|
|
|
|
return "delete";
|
2023-10-09 02:21:16 +02:00
|
|
|
case NB_OP_REPLACE:
|
|
|
|
return "replace";
|
2023-10-05 23:13:16 +02:00
|
|
|
case NB_OP_MOVE:
|
|
|
|
return "move";
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(!"Reached end of function we should never hit");
|
|
|
|
}
|
2023-02-28 16:00:19 +01:00
|
|
|
|
2023-10-05 23:13:16 +02:00
|
|
|
bool nb_is_operation_allowed(struct nb_node *nb_node, enum nb_operation oper)
|
|
|
|
{
|
2023-02-28 16:00:19 +01:00
|
|
|
if (lysc_is_key(nb_node->snode)) {
|
2023-10-06 14:01:16 +02:00
|
|
|
if (oper == NB_OP_MODIFY || oper == NB_OP_DESTROY
|
2023-10-09 02:21:16 +02:00
|
|
|
|| oper == NB_OP_DELETE || oper == NB_OP_REPLACE)
|
2023-02-28 16:00:19 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-01-18 05:17:35 +01:00
|
|
|
void nb_candidate_edit_config_changes(struct nb_config *candidate_config,
|
|
|
|
struct nb_cfg_change cfg_changes[],
|
|
|
|
size_t num_cfg_changes,
|
|
|
|
const char *xpath_base, bool in_backend,
|
|
|
|
char *err_buf, int err_bufsize,
|
|
|
|
bool *error)
|
2023-02-28 16:00:19 +01:00
|
|
|
{
|
|
|
|
if (error)
|
|
|
|
*error = false;
|
|
|
|
|
|
|
|
if (xpath_base == NULL)
|
|
|
|
xpath_base = "";
|
|
|
|
|
|
|
|
/* Edit candidate configuration. */
|
|
|
|
for (size_t i = 0; i < num_cfg_changes; i++) {
|
|
|
|
struct nb_cfg_change *change = &cfg_changes[i];
|
|
|
|
struct nb_node *nb_node;
|
2023-11-11 01:13:17 +01:00
|
|
|
char *change_xpath = change->xpath;
|
2023-02-28 16:00:19 +01:00
|
|
|
char xpath[XPATH_MAXLEN];
|
2023-11-11 01:06:11 +01:00
|
|
|
const char *value;
|
2023-02-28 16:00:19 +01:00
|
|
|
struct yang_data *data;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
memset(xpath, 0, sizeof(xpath));
|
2023-11-11 01:13:17 +01:00
|
|
|
/* If change xpath is relative, prepend base xpath. */
|
|
|
|
if (change_xpath[0] == '.') {
|
|
|
|
strlcpy(xpath, xpath_base, sizeof(xpath));
|
|
|
|
change_xpath++; /* skip '.' */
|
2023-02-28 16:00:19 +01:00
|
|
|
}
|
2023-11-11 01:13:17 +01:00
|
|
|
strlcat(xpath, change_xpath, sizeof(xpath));
|
2023-02-28 16:00:19 +01:00
|
|
|
|
|
|
|
/* Find the northbound node associated to the data path. */
|
|
|
|
nb_node = nb_node_find(xpath);
|
|
|
|
if (!nb_node) {
|
2024-01-18 18:06:45 +01:00
|
|
|
if (in_backend)
|
|
|
|
DEBUGD(&nb_dbg_cbs_config,
|
|
|
|
"%s: ignoring non-handled path: %s",
|
|
|
|
__func__, xpath);
|
|
|
|
else {
|
|
|
|
flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
|
|
|
|
"%s: unknown data path: %s", __func__,
|
|
|
|
xpath);
|
|
|
|
if (error)
|
|
|
|
*error = true;
|
|
|
|
}
|
2023-02-28 16:00:19 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
/* Find if the node to be edited is not a key node */
|
2023-10-05 23:13:16 +02:00
|
|
|
if (!nb_is_operation_allowed(nb_node, change->operation)) {
|
2023-02-28 16:00:19 +01:00
|
|
|
zlog_err(" Xpath %s points to key node", xpath);
|
|
|
|
if (error)
|
|
|
|
*error = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If the value is not set, get the default if it exists. */
|
2023-11-11 01:06:11 +01:00
|
|
|
value = change->value;
|
|
|
|
if (value == NULL)
|
|
|
|
value = yang_snode_get_default(nb_node->snode);
|
|
|
|
data = yang_data_new(xpath, value);
|
2023-02-28 16:00:19 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Ignore "not found" errors when editing the candidate
|
|
|
|
* configuration.
|
|
|
|
*/
|
|
|
|
ret = nb_candidate_edit(candidate_config, nb_node,
|
2024-01-23 01:09:25 +01:00
|
|
|
change->operation, xpath, NULL, data);
|
2023-02-28 16:00:19 +01:00
|
|
|
yang_data_free(data);
|
2023-10-06 14:01:16 +02:00
|
|
|
if (ret != NB_OK) {
|
2023-02-28 16:00:19 +01:00
|
|
|
flog_warn(
|
|
|
|
EC_LIB_NB_CANDIDATE_EDIT_ERROR,
|
|
|
|
"%s: failed to edit candidate configuration: operation [%s] xpath [%s]",
|
|
|
|
__func__, nb_operation_name(change->operation),
|
|
|
|
xpath);
|
|
|
|
if (error)
|
|
|
|
*error = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error && *error) {
|
|
|
|
char buf[BUFSIZ];
|
|
|
|
|
|
|
|
snprintf(err_buf, err_bufsize,
|
|
|
|
"%% Failed to edit configuration.\n\n%s",
|
|
|
|
yang_print_errors(ly_native_ctx, buf, sizeof(buf)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-07 20:31:48 +01:00
|
|
|
bool nb_candidate_needs_update(const struct nb_config *candidate)
|
|
|
|
{
|
2019-09-17 02:51:11 +02:00
|
|
|
if (candidate->version < running_config->version)
|
|
|
|
return true;
|
2017-12-07 20:31:48 +01:00
|
|
|
|
2019-09-17 02:51:11 +02:00
|
|
|
return false;
|
2017-12-07 20:31:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int nb_candidate_update(struct nb_config *candidate)
|
|
|
|
{
|
|
|
|
struct nb_config *updated_config;
|
|
|
|
|
2019-09-17 02:51:11 +02:00
|
|
|
updated_config = nb_config_dup(running_config);
|
2017-12-07 20:31:48 +01:00
|
|
|
if (nb_config_merge(updated_config, candidate, true) != NB_OK)
|
|
|
|
return NB_ERR;
|
|
|
|
|
|
|
|
nb_config_replace(candidate, updated_config, false);
|
|
|
|
|
|
|
|
return NB_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Perform YANG syntactic and semantic validation.
|
|
|
|
*
|
|
|
|
* WARNING: lyd_validate() can change the configuration as part of the
|
|
|
|
* validation process.
|
|
|
|
*/
|
2021-10-28 09:07:11 +02:00
|
|
|
int nb_candidate_validate_yang(struct nb_config *candidate, bool no_state,
|
|
|
|
char *errmsg, size_t errmsg_len)
|
2017-12-07 20:31:48 +01:00
|
|
|
{
|
2023-11-20 10:29:57 +01:00
|
|
|
uint32_t options = 0;
|
|
|
|
|
|
|
|
#ifdef LYD_VALIDATE_MULTI_ERROR
|
|
|
|
/* libyang 2.1.36+ */
|
|
|
|
options |= LYD_VALIDATE_MULTI_ERROR;
|
|
|
|
#endif
|
2023-11-14 19:57:05 +01:00
|
|
|
|
|
|
|
if (no_state)
|
|
|
|
SET_FLAG(options, LYD_VALIDATE_NO_STATE);
|
|
|
|
else
|
|
|
|
SET_FLAG(options, LYD_VALIDATE_PRESENT);
|
|
|
|
|
|
|
|
if (lyd_validate_all(&candidate->dnode, ly_native_ctx, options,
|
2023-03-20 20:07:44 +01:00
|
|
|
NULL) != 0) {
|
2020-05-14 17:30:34 +02:00
|
|
|
yang_print_errors(ly_native_ctx, errmsg, errmsg_len);
|
2017-12-07 20:31:48 +01:00
|
|
|
return NB_ERR_VALIDATION;
|
2020-05-14 17:30:34 +02:00
|
|
|
}
|
2017-12-07 20:31:48 +01:00
|
|
|
|
|
|
|
return NB_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Perform code-level validation using the northbound callbacks. */
|
2023-02-28 16:00:19 +01:00
|
|
|
int nb_candidate_validate_code(struct nb_context *context,
|
|
|
|
struct nb_config *candidate,
|
|
|
|
struct nb_config_cbs *changes, char *errmsg,
|
|
|
|
size_t errmsg_len)
|
2017-12-07 20:31:48 +01:00
|
|
|
{
|
|
|
|
struct nb_config_cb *cb;
|
2021-05-04 16:41:58 +02:00
|
|
|
struct lyd_node *root, *child;
|
2019-09-13 00:07:54 +02:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* First validate the candidate as a whole. */
|
2021-05-04 16:41:58 +02:00
|
|
|
LY_LIST_FOR (candidate->dnode, root) {
|
|
|
|
LYD_TREE_DFS_BEGIN (root, child) {
|
2019-09-13 00:07:54 +02:00
|
|
|
struct nb_node *nb_node;
|
|
|
|
|
|
|
|
nb_node = child->schema->priv;
|
2020-09-11 15:43:49 +02:00
|
|
|
if (!nb_node || !nb_node->cbs.pre_validate)
|
2019-09-13 00:07:54 +02:00
|
|
|
goto next;
|
|
|
|
|
2020-05-14 17:30:34 +02:00
|
|
|
ret = nb_callback_pre_validate(context, nb_node, child,
|
|
|
|
errmsg, errmsg_len);
|
2019-09-13 00:07:54 +02:00
|
|
|
if (ret != NB_OK)
|
|
|
|
return NB_ERR_VALIDATION;
|
|
|
|
|
|
|
|
next:
|
2021-05-04 16:41:58 +02:00
|
|
|
LYD_TREE_DFS_END(root, child);
|
2019-09-13 00:07:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now validate the configuration changes. */
|
2017-12-07 20:31:48 +01:00
|
|
|
RB_FOREACH (cb, nb_config_cbs, changes) {
|
|
|
|
struct nb_config_change *change = (struct nb_config_change *)cb;
|
|
|
|
|
2020-05-14 17:30:34 +02:00
|
|
|
ret = nb_callback_configuration(context, NB_EV_VALIDATE, change,
|
|
|
|
errmsg, errmsg_len);
|
2017-12-07 20:31:48 +01:00
|
|
|
if (ret != NB_OK)
|
|
|
|
return NB_ERR_VALIDATION;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NB_OK;
|
|
|
|
}
|
|
|
|
|
2023-02-28 16:00:19 +01:00
|
|
|
int nb_candidate_diff_and_validate_yang(struct nb_context *context,
|
|
|
|
struct nb_config *candidate,
|
|
|
|
struct nb_config_cbs *changes,
|
|
|
|
char *errmsg, size_t errmsg_len)
|
|
|
|
{
|
2021-10-28 09:07:11 +02:00
|
|
|
if (nb_candidate_validate_yang(candidate, true, errmsg,
|
2023-03-20 20:07:44 +01:00
|
|
|
sizeof(errmsg_len)) != NB_OK)
|
2023-02-28 16:00:19 +01:00
|
|
|
return NB_ERR_VALIDATION;
|
|
|
|
|
|
|
|
RB_INIT(nb_config_cbs, changes);
|
|
|
|
nb_config_diff(running_config, candidate, changes);
|
|
|
|
|
|
|
|
return NB_OK;
|
|
|
|
}
|
|
|
|
|
2020-04-27 18:13:57 +02:00
|
|
|
int nb_candidate_validate(struct nb_context *context,
|
2020-05-14 17:30:34 +02:00
|
|
|
struct nb_config *candidate, char *errmsg,
|
|
|
|
size_t errmsg_len)
|
2017-12-07 20:31:48 +01:00
|
|
|
{
|
|
|
|
struct nb_config_cbs changes;
|
|
|
|
int ret;
|
|
|
|
|
2023-02-28 16:00:19 +01:00
|
|
|
ret = nb_candidate_diff_and_validate_yang(context, candidate, &changes,
|
|
|
|
errmsg, errmsg_len);
|
|
|
|
if (ret != NB_OK)
|
|
|
|
return ret;
|
2017-12-07 20:31:48 +01:00
|
|
|
|
2020-05-14 17:30:34 +02:00
|
|
|
ret = nb_candidate_validate_code(context, candidate, &changes, errmsg,
|
|
|
|
errmsg_len);
|
2019-09-17 02:51:11 +02:00
|
|
|
nb_config_diff_del_changes(&changes);
|
2017-12-07 20:31:48 +01:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-02-24 02:23:51 +01:00
|
|
|
int nb_candidate_commit_prepare(struct nb_context context,
|
2020-04-27 18:13:57 +02:00
|
|
|
struct nb_config *candidate,
|
2019-02-02 21:03:15 +01:00
|
|
|
const char *comment,
|
2020-05-14 17:30:34 +02:00
|
|
|
struct nb_transaction **transaction,
|
2023-02-28 16:00:19 +01:00
|
|
|
bool skip_validate, bool ignore_zero_change,
|
2020-05-14 17:30:34 +02:00
|
|
|
char *errmsg, size_t errmsg_len)
|
2017-12-07 20:31:48 +01:00
|
|
|
{
|
|
|
|
struct nb_config_cbs changes;
|
|
|
|
|
2023-03-20 20:07:44 +01:00
|
|
|
if (!skip_validate &&
|
|
|
|
nb_candidate_validate_yang(candidate, true, errmsg, errmsg_len) !=
|
|
|
|
NB_OK) {
|
2017-12-07 20:31:48 +01:00
|
|
|
flog_warn(EC_LIB_NB_CANDIDATE_INVALID,
|
|
|
|
"%s: failed to validate candidate configuration",
|
|
|
|
__func__);
|
|
|
|
return NB_ERR_VALIDATION;
|
|
|
|
}
|
|
|
|
|
|
|
|
RB_INIT(nb_config_cbs, &changes);
|
2019-09-17 02:51:11 +02:00
|
|
|
nb_config_diff(running_config, candidate, &changes);
|
2023-02-28 16:00:19 +01:00
|
|
|
if (!ignore_zero_change && RB_EMPTY(nb_config_cbs, &changes)) {
|
2020-08-18 21:59:41 +02:00
|
|
|
snprintf(
|
|
|
|
errmsg, errmsg_len,
|
|
|
|
"No changes to apply were found during preparation phase");
|
2019-09-17 02:51:11 +02:00
|
|
|
return NB_ERR_NO_CHANGES;
|
2020-08-18 21:59:41 +02:00
|
|
|
}
|
2017-12-07 20:31:48 +01:00
|
|
|
|
2023-03-20 20:07:44 +01:00
|
|
|
if (!skip_validate &&
|
|
|
|
nb_candidate_validate_code(&context, candidate, &changes, errmsg,
|
|
|
|
errmsg_len) != NB_OK) {
|
2019-09-17 02:51:11 +02:00
|
|
|
flog_warn(EC_LIB_NB_CANDIDATE_INVALID,
|
|
|
|
"%s: failed to validate candidate configuration",
|
|
|
|
__func__);
|
|
|
|
nb_config_diff_del_changes(&changes);
|
|
|
|
return NB_ERR_VALIDATION;
|
|
|
|
}
|
2017-12-07 20:31:48 +01:00
|
|
|
|
2023-02-28 16:00:19 +01:00
|
|
|
/*
|
|
|
|
* Re-use an existing transaction if provided. Else allocate a new one.
|
|
|
|
*/
|
|
|
|
if (!*transaction)
|
|
|
|
*transaction = nb_transaction_new(context, candidate, &changes,
|
|
|
|
comment, errmsg, errmsg_len);
|
2019-09-17 02:51:11 +02:00
|
|
|
if (*transaction == NULL) {
|
|
|
|
flog_warn(EC_LIB_NB_TRANSACTION_CREATION_FAILED,
|
2020-05-14 17:30:34 +02:00
|
|
|
"%s: failed to create transaction: %s", __func__,
|
|
|
|
errmsg);
|
2019-09-17 02:51:11 +02:00
|
|
|
nb_config_diff_del_changes(&changes);
|
|
|
|
return NB_ERR_LOCKED;
|
2017-12-07 20:31:48 +01:00
|
|
|
}
|
|
|
|
|
2020-05-14 17:30:34 +02:00
|
|
|
return nb_transaction_process(NB_EV_PREPARE, *transaction, errmsg,
|
|
|
|
errmsg_len);
|
2017-12-07 20:31:48 +01:00
|
|
|
}
|
|
|
|
|
2020-08-15 00:49:41 +02:00
|
|
|
void nb_candidate_commit_abort(struct nb_transaction *transaction, char *errmsg,
|
|
|
|
size_t errmsg_len)
|
2017-12-07 20:31:48 +01:00
|
|
|
{
|
2020-05-14 17:30:34 +02:00
|
|
|
(void)nb_transaction_process(NB_EV_ABORT, transaction, errmsg,
|
2020-08-15 00:49:41 +02:00
|
|
|
errmsg_len);
|
2017-12-07 20:31:48 +01:00
|
|
|
nb_transaction_free(transaction);
|
|
|
|
}
|
|
|
|
|
|
|
|
void nb_candidate_commit_apply(struct nb_transaction *transaction,
|
2020-08-15 00:49:41 +02:00
|
|
|
bool save_transaction, uint32_t *transaction_id,
|
|
|
|
char *errmsg, size_t errmsg_len)
|
2017-12-07 20:31:48 +01:00
|
|
|
{
|
2020-05-14 17:30:34 +02:00
|
|
|
(void)nb_transaction_process(NB_EV_APPLY, transaction, errmsg,
|
2020-08-15 00:49:41 +02:00
|
|
|
errmsg_len);
|
|
|
|
nb_transaction_apply_finish(transaction, errmsg, errmsg_len);
|
2017-12-07 20:31:48 +01:00
|
|
|
|
|
|
|
/* Replace running by candidate. */
|
|
|
|
transaction->config->version++;
|
2019-09-17 02:51:11 +02:00
|
|
|
nb_config_replace(running_config, transaction->config, true);
|
2017-12-07 20:31:48 +01:00
|
|
|
|
|
|
|
/* Record transaction. */
|
2020-08-20 21:09:53 +02:00
|
|
|
if (save_transaction && nb_db_enabled
|
2017-12-07 20:31:48 +01:00
|
|
|
&& nb_db_transaction_save(transaction, transaction_id) != NB_OK)
|
|
|
|
flog_warn(EC_LIB_NB_TRANSACTION_RECORD_FAILED,
|
|
|
|
"%s: failed to record transaction", __func__);
|
|
|
|
|
|
|
|
nb_transaction_free(transaction);
|
|
|
|
}
|
|
|
|
|
2023-02-24 02:23:51 +01:00
|
|
|
int nb_candidate_commit(struct nb_context context, struct nb_config *candidate,
|
2020-04-27 18:13:57 +02:00
|
|
|
bool save_transaction, const char *comment,
|
2020-05-14 17:30:34 +02:00
|
|
|
uint32_t *transaction_id, char *errmsg,
|
|
|
|
size_t errmsg_len)
|
2017-12-07 20:31:48 +01:00
|
|
|
{
|
|
|
|
struct nb_transaction *transaction = NULL;
|
|
|
|
int ret;
|
|
|
|
|
2020-04-27 18:13:57 +02:00
|
|
|
ret = nb_candidate_commit_prepare(context, candidate, comment,
|
2023-02-28 16:00:19 +01:00
|
|
|
&transaction, false, false, errmsg,
|
|
|
|
errmsg_len);
|
2017-12-07 20:31:48 +01:00
|
|
|
/*
|
|
|
|
* Apply the changes if the preparation phase succeeded. Otherwise abort
|
|
|
|
* the transaction.
|
|
|
|
*/
|
|
|
|
if (ret == NB_OK)
|
|
|
|
nb_candidate_commit_apply(transaction, save_transaction,
|
2020-08-15 00:49:41 +02:00
|
|
|
transaction_id, errmsg, errmsg_len);
|
2017-12-07 20:31:48 +01:00
|
|
|
else if (transaction != NULL)
|
2020-08-15 00:49:41 +02:00
|
|
|
nb_candidate_commit_abort(transaction, errmsg, errmsg_len);
|
2017-12-07 20:31:48 +01:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-02-02 21:03:15 +01:00
|
|
|
int nb_running_lock(enum nb_client client, const void *user)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
|
2020-05-15 02:23:36 +02:00
|
|
|
frr_with_mutex (&running_config_mgmt_lock.mtx) {
|
2019-02-02 21:03:15 +01:00
|
|
|
if (!running_config_mgmt_lock.locked) {
|
|
|
|
running_config_mgmt_lock.locked = true;
|
|
|
|
running_config_mgmt_lock.owner_client = client;
|
|
|
|
running_config_mgmt_lock.owner_user = user;
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int nb_running_unlock(enum nb_client client, const void *user)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
|
2020-05-15 02:23:36 +02:00
|
|
|
frr_with_mutex (&running_config_mgmt_lock.mtx) {
|
2019-02-02 21:03:15 +01:00
|
|
|
if (running_config_mgmt_lock.locked
|
|
|
|
&& running_config_mgmt_lock.owner_client == client
|
|
|
|
&& running_config_mgmt_lock.owner_user == user) {
|
|
|
|
running_config_mgmt_lock.locked = false;
|
|
|
|
running_config_mgmt_lock.owner_client = NB_CLIENT_NONE;
|
|
|
|
running_config_mgmt_lock.owner_user = NULL;
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int nb_running_lock_check(enum nb_client client, const void *user)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
|
2020-05-15 02:23:36 +02:00
|
|
|
frr_with_mutex (&running_config_mgmt_lock.mtx) {
|
2019-02-02 21:03:15 +01:00
|
|
|
if (!running_config_mgmt_lock.locked
|
|
|
|
|| (running_config_mgmt_lock.owner_client == client
|
|
|
|
&& running_config_mgmt_lock.owner_user == user))
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-04-22 02:27:47 +02:00
|
|
|
static void nb_log_config_callback(const enum nb_event event,
|
2023-10-05 23:13:16 +02:00
|
|
|
enum nb_cb_operation operation,
|
2020-04-22 02:27:47 +02:00
|
|
|
const struct lyd_node *dnode)
|
2017-12-07 20:31:48 +01:00
|
|
|
{
|
2020-04-22 02:27:47 +02:00
|
|
|
const char *value;
|
|
|
|
char xpath[XPATH_MAXLEN];
|
|
|
|
|
|
|
|
if (!DEBUG_MODE_CHECK(&nb_dbg_cbs_config, DEBUG_MODE_ALL))
|
|
|
|
return;
|
|
|
|
|
|
|
|
yang_dnode_get_path(dnode, xpath, sizeof(xpath));
|
|
|
|
if (yang_snode_is_typeless_data(dnode->schema))
|
|
|
|
value = "(none)";
|
|
|
|
else
|
|
|
|
value = yang_dnode_get_string(dnode, NULL);
|
|
|
|
|
2017-12-07 20:31:48 +01:00
|
|
|
zlog_debug(
|
|
|
|
"northbound callback: event [%s] op [%s] xpath [%s] value [%s]",
|
2023-10-05 23:13:16 +02:00
|
|
|
nb_event_name(event), nb_cb_operation_name(operation), xpath,
|
2020-04-22 02:27:47 +02:00
|
|
|
value);
|
|
|
|
}
|
|
|
|
|
2020-04-27 18:13:57 +02:00
|
|
|
static int nb_callback_create(struct nb_context *context,
|
|
|
|
const struct nb_node *nb_node,
|
2020-04-22 02:27:47 +02:00
|
|
|
enum nb_event event, const struct lyd_node *dnode,
|
2020-05-14 17:30:34 +02:00
|
|
|
union nb_resource *resource, char *errmsg,
|
|
|
|
size_t errmsg_len)
|
2020-04-22 02:27:47 +02:00
|
|
|
{
|
2020-04-04 18:38:51 +02:00
|
|
|
struct nb_cb_create_args args = {};
|
2020-05-15 02:18:26 +02:00
|
|
|
bool unexpected_error = false;
|
|
|
|
int ret;
|
2020-04-04 18:38:51 +02:00
|
|
|
|
2024-01-11 22:47:48 +01:00
|
|
|
assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS));
|
2023-02-28 16:00:19 +01:00
|
|
|
|
2023-10-05 23:13:16 +02:00
|
|
|
nb_log_config_callback(event, NB_CB_CREATE, dnode);
|
2020-04-22 02:27:47 +02:00
|
|
|
|
2020-04-27 18:13:57 +02:00
|
|
|
args.context = context;
|
2020-04-04 18:38:51 +02:00
|
|
|
args.event = event;
|
|
|
|
args.dnode = dnode;
|
|
|
|
args.resource = resource;
|
2020-05-14 17:30:34 +02:00
|
|
|
args.errmsg = errmsg;
|
|
|
|
args.errmsg_len = errmsg_len;
|
2020-05-15 02:18:26 +02:00
|
|
|
ret = nb_node->cbs.create(&args);
|
|
|
|
|
|
|
|
/* Detect and log unexpected errors. */
|
|
|
|
switch (ret) {
|
|
|
|
case NB_OK:
|
|
|
|
case NB_ERR:
|
|
|
|
break;
|
|
|
|
case NB_ERR_VALIDATION:
|
|
|
|
if (event != NB_EV_VALIDATE)
|
|
|
|
unexpected_error = true;
|
|
|
|
break;
|
|
|
|
case NB_ERR_RESOURCE:
|
|
|
|
if (event != NB_EV_PREPARE)
|
|
|
|
unexpected_error = true;
|
|
|
|
break;
|
|
|
|
case NB_ERR_INCONSISTENCY:
|
|
|
|
if (event == NB_EV_VALIDATE)
|
|
|
|
unexpected_error = true;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
unexpected_error = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (unexpected_error)
|
|
|
|
DEBUGD(&nb_dbg_cbs_config,
|
|
|
|
"northbound callback: unexpected return value: %s",
|
|
|
|
nb_err_name(ret));
|
|
|
|
|
|
|
|
return ret;
|
2020-04-22 02:27:47 +02:00
|
|
|
}
|
|
|
|
|
2020-04-27 18:13:57 +02:00
|
|
|
static int nb_callback_modify(struct nb_context *context,
|
|
|
|
const struct nb_node *nb_node,
|
2020-04-22 02:27:47 +02:00
|
|
|
enum nb_event event, const struct lyd_node *dnode,
|
2020-05-14 17:30:34 +02:00
|
|
|
union nb_resource *resource, char *errmsg,
|
|
|
|
size_t errmsg_len)
|
2020-04-22 02:27:47 +02:00
|
|
|
{
|
2020-04-04 18:38:51 +02:00
|
|
|
struct nb_cb_modify_args args = {};
|
2020-05-15 02:18:26 +02:00
|
|
|
bool unexpected_error = false;
|
|
|
|
int ret;
|
2020-04-04 18:38:51 +02:00
|
|
|
|
2024-01-11 22:47:48 +01:00
|
|
|
assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS));
|
2023-02-28 16:00:19 +01:00
|
|
|
|
2023-10-05 23:13:16 +02:00
|
|
|
nb_log_config_callback(event, NB_CB_MODIFY, dnode);
|
2020-04-22 02:27:47 +02:00
|
|
|
|
2020-04-27 18:13:57 +02:00
|
|
|
args.context = context;
|
2020-04-04 18:38:51 +02:00
|
|
|
args.event = event;
|
|
|
|
args.dnode = dnode;
|
|
|
|
args.resource = resource;
|
2020-05-14 17:30:34 +02:00
|
|
|
args.errmsg = errmsg;
|
|
|
|
args.errmsg_len = errmsg_len;
|
2020-05-15 02:18:26 +02:00
|
|
|
ret = nb_node->cbs.modify(&args);
|
|
|
|
|
|
|
|
/* Detect and log unexpected errors. */
|
|
|
|
switch (ret) {
|
|
|
|
case NB_OK:
|
|
|
|
case NB_ERR:
|
|
|
|
break;
|
|
|
|
case NB_ERR_VALIDATION:
|
|
|
|
if (event != NB_EV_VALIDATE)
|
|
|
|
unexpected_error = true;
|
|
|
|
break;
|
|
|
|
case NB_ERR_RESOURCE:
|
|
|
|
if (event != NB_EV_PREPARE)
|
|
|
|
unexpected_error = true;
|
|
|
|
break;
|
|
|
|
case NB_ERR_INCONSISTENCY:
|
|
|
|
if (event == NB_EV_VALIDATE)
|
|
|
|
unexpected_error = true;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
unexpected_error = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (unexpected_error)
|
|
|
|
DEBUGD(&nb_dbg_cbs_config,
|
|
|
|
"northbound callback: unexpected return value: %s",
|
|
|
|
nb_err_name(ret));
|
|
|
|
|
|
|
|
return ret;
|
2020-04-22 02:27:47 +02:00
|
|
|
}
|
|
|
|
|
2020-04-27 18:13:57 +02:00
|
|
|
static int nb_callback_destroy(struct nb_context *context,
|
|
|
|
const struct nb_node *nb_node,
|
2020-04-22 02:27:47 +02:00
|
|
|
enum nb_event event,
|
2020-05-14 17:30:34 +02:00
|
|
|
const struct lyd_node *dnode, char *errmsg,
|
|
|
|
size_t errmsg_len)
|
2020-04-22 02:27:47 +02:00
|
|
|
{
|
2020-04-04 18:38:51 +02:00
|
|
|
struct nb_cb_destroy_args args = {};
|
2020-05-15 02:18:26 +02:00
|
|
|
bool unexpected_error = false;
|
|
|
|
int ret;
|
2020-04-04 18:38:51 +02:00
|
|
|
|
2024-01-11 22:47:48 +01:00
|
|
|
assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS));
|
2023-02-28 16:00:19 +01:00
|
|
|
|
2023-10-05 23:13:16 +02:00
|
|
|
nb_log_config_callback(event, NB_CB_DESTROY, dnode);
|
2020-04-22 02:27:47 +02:00
|
|
|
|
2020-04-27 18:13:57 +02:00
|
|
|
args.context = context;
|
2020-04-04 18:38:51 +02:00
|
|
|
args.event = event;
|
|
|
|
args.dnode = dnode;
|
2020-05-14 17:30:34 +02:00
|
|
|
args.errmsg = errmsg;
|
|
|
|
args.errmsg_len = errmsg_len;
|
2020-05-15 02:18:26 +02:00
|
|
|
ret = nb_node->cbs.destroy(&args);
|
|
|
|
|
|
|
|
/* Detect and log unexpected errors. */
|
|
|
|
switch (ret) {
|
|
|
|
case NB_OK:
|
|
|
|
case NB_ERR:
|
|
|
|
break;
|
|
|
|
case NB_ERR_VALIDATION:
|
|
|
|
if (event != NB_EV_VALIDATE)
|
|
|
|
unexpected_error = true;
|
|
|
|
break;
|
|
|
|
case NB_ERR_INCONSISTENCY:
|
|
|
|
if (event == NB_EV_VALIDATE)
|
|
|
|
unexpected_error = true;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
unexpected_error = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (unexpected_error)
|
|
|
|
DEBUGD(&nb_dbg_cbs_config,
|
|
|
|
"northbound callback: unexpected return value: %s",
|
|
|
|
nb_err_name(ret));
|
|
|
|
|
|
|
|
return ret;
|
2020-04-22 02:27:47 +02:00
|
|
|
}
|
|
|
|
|
2020-04-27 18:13:57 +02:00
|
|
|
static int nb_callback_move(struct nb_context *context,
|
|
|
|
const struct nb_node *nb_node, enum nb_event event,
|
2020-05-14 17:30:34 +02:00
|
|
|
const struct lyd_node *dnode, char *errmsg,
|
|
|
|
size_t errmsg_len)
|
2020-04-22 02:27:47 +02:00
|
|
|
{
|
2020-04-04 18:38:51 +02:00
|
|
|
struct nb_cb_move_args args = {};
|
2020-05-15 02:18:26 +02:00
|
|
|
bool unexpected_error = false;
|
|
|
|
int ret;
|
2020-04-04 18:38:51 +02:00
|
|
|
|
2024-01-11 22:47:48 +01:00
|
|
|
assert(!CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS));
|
2023-02-28 16:00:19 +01:00
|
|
|
|
2023-10-05 23:13:16 +02:00
|
|
|
nb_log_config_callback(event, NB_CB_MOVE, dnode);
|
2020-04-22 02:27:47 +02:00
|
|
|
|
2020-04-27 18:13:57 +02:00
|
|
|
args.context = context;
|
2020-04-04 18:38:51 +02:00
|
|
|
args.event = event;
|
|
|
|
args.dnode = dnode;
|
2020-05-14 17:30:34 +02:00
|
|
|
args.errmsg = errmsg;
|
|
|
|
args.errmsg_len = errmsg_len;
|
2020-05-15 02:18:26 +02:00
|
|
|
ret = nb_node->cbs.move(&args);
|
|
|
|
|
|
|
|
/* Detect and log unexpected errors. */
|
|
|
|
switch (ret) {
|
|
|
|
case NB_OK:
|
|
|
|
case NB_ERR:
|
|
|
|
break;
|
|
|
|
case NB_ERR_VALIDATION:
|
|
|
|
if (event != NB_EV_VALIDATE)
|
|
|
|
unexpected_error = true;
|
|
|
|
break;
|
|
|
|
case NB_ERR_INCONSISTENCY:
|
|
|
|
if (event == NB_EV_VALIDATE)
|
|
|
|
unexpected_error = true;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
unexpected_error = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (unexpected_error)
|
|
|
|
DEBUGD(&nb_dbg_cbs_config,
|
|
|
|
"northbound callback: unexpected return value: %s",
|
|
|
|
nb_err_name(ret));
|
|
|
|
|
|
|
|
return ret;
|
2020-04-22 02:27:47 +02:00
|
|
|
}
|
|
|
|
|
2020-04-27 18:13:57 +02:00
|
|
|
static int nb_callback_pre_validate(struct nb_context *context,
|
|
|
|
const struct nb_node *nb_node,
|
2020-05-14 17:30:34 +02:00
|
|
|
const struct lyd_node *dnode, char *errmsg,
|
|
|
|
size_t errmsg_len)
|
2020-04-22 02:27:47 +02:00
|
|
|
{
|
2020-04-04 18:38:51 +02:00
|
|
|
struct nb_cb_pre_validate_args args = {};
|
2020-05-15 02:18:26 +02:00
|
|
|
bool unexpected_error = false;
|
|
|
|
int ret;
|
2020-04-04 18:38:51 +02:00
|
|
|
|
2024-01-11 22:47:48 +01:00
|
|
|
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
|
2023-02-28 16:00:19 +01:00
|
|
|
return 0;
|
|
|
|
|
2023-10-05 23:13:16 +02:00
|
|
|
nb_log_config_callback(NB_EV_VALIDATE, NB_CB_PRE_VALIDATE, dnode);
|
2020-04-22 02:27:47 +02:00
|
|
|
|
2020-04-04 18:38:51 +02:00
|
|
|
args.dnode = dnode;
|
2020-05-14 17:30:34 +02:00
|
|
|
args.errmsg = errmsg;
|
|
|
|
args.errmsg_len = errmsg_len;
|
2020-05-15 02:18:26 +02:00
|
|
|
ret = nb_node->cbs.pre_validate(&args);
|
|
|
|
|
|
|
|
/* Detect and log unexpected errors. */
|
|
|
|
switch (ret) {
|
|
|
|
case NB_OK:
|
|
|
|
case NB_ERR_VALIDATION:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
unexpected_error = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (unexpected_error)
|
|
|
|
DEBUGD(&nb_dbg_cbs_config,
|
|
|
|
"northbound callback: unexpected return value: %s",
|
|
|
|
nb_err_name(ret));
|
|
|
|
|
|
|
|
return ret;
|
2020-04-22 02:27:47 +02:00
|
|
|
}
|
|
|
|
|
2020-04-27 18:13:57 +02:00
|
|
|
static void nb_callback_apply_finish(struct nb_context *context,
|
|
|
|
const struct nb_node *nb_node,
|
2020-05-14 17:30:34 +02:00
|
|
|
const struct lyd_node *dnode, char *errmsg,
|
|
|
|
size_t errmsg_len)
|
2020-04-22 02:27:47 +02:00
|
|
|
{
|
2020-04-04 18:38:51 +02:00
|
|
|
struct nb_cb_apply_finish_args args = {};
|
|
|
|
|
2024-01-11 22:47:48 +01:00
|
|
|
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
|
2023-02-28 16:00:19 +01:00
|
|
|
return;
|
|
|
|
|
2023-10-05 23:13:16 +02:00
|
|
|
nb_log_config_callback(NB_EV_APPLY, NB_CB_APPLY_FINISH, dnode);
|
2020-04-22 02:27:47 +02:00
|
|
|
|
2020-04-27 18:13:57 +02:00
|
|
|
args.context = context;
|
2020-04-04 18:38:51 +02:00
|
|
|
args.dnode = dnode;
|
2020-05-14 17:30:34 +02:00
|
|
|
args.errmsg = errmsg;
|
|
|
|
args.errmsg_len = errmsg_len;
|
2020-04-04 18:38:51 +02:00
|
|
|
nb_node->cbs.apply_finish(&args);
|
2020-04-22 02:27:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
struct yang_data *nb_callback_get_elem(const struct nb_node *nb_node,
|
|
|
|
const char *xpath,
|
|
|
|
const void *list_entry)
|
|
|
|
{
|
2020-04-04 18:38:51 +02:00
|
|
|
struct nb_cb_get_elem_args args = {};
|
|
|
|
|
2024-01-11 22:47:48 +01:00
|
|
|
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
|
2023-02-28 16:00:19 +01:00
|
|
|
return NULL;
|
|
|
|
|
2020-04-22 02:27:47 +02:00
|
|
|
DEBUGD(&nb_dbg_cbs_state,
|
|
|
|
"northbound callback (get_elem): xpath [%s] list_entry [%p]",
|
|
|
|
xpath, list_entry);
|
|
|
|
|
2020-04-04 18:38:51 +02:00
|
|
|
args.xpath = xpath;
|
|
|
|
args.list_entry = list_entry;
|
|
|
|
return nb_node->cbs.get_elem(&args);
|
2020-04-22 02:27:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const void *nb_callback_get_next(const struct nb_node *nb_node,
|
|
|
|
const void *parent_list_entry,
|
|
|
|
const void *list_entry)
|
|
|
|
{
|
2020-04-04 18:38:51 +02:00
|
|
|
struct nb_cb_get_next_args args = {};
|
|
|
|
|
2024-01-11 22:47:48 +01:00
|
|
|
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
|
2023-02-28 16:00:19 +01:00
|
|
|
return NULL;
|
|
|
|
|
2020-04-22 02:27:47 +02:00
|
|
|
DEBUGD(&nb_dbg_cbs_state,
|
|
|
|
"northbound callback (get_next): node [%s] parent_list_entry [%p] list_entry [%p]",
|
|
|
|
nb_node->xpath, parent_list_entry, list_entry);
|
|
|
|
|
2020-04-04 18:38:51 +02:00
|
|
|
args.parent_list_entry = parent_list_entry;
|
|
|
|
args.list_entry = list_entry;
|
|
|
|
return nb_node->cbs.get_next(&args);
|
2020-04-22 02:27:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int nb_callback_get_keys(const struct nb_node *nb_node, const void *list_entry,
|
|
|
|
struct yang_list_keys *keys)
|
|
|
|
{
|
2020-04-04 18:38:51 +02:00
|
|
|
struct nb_cb_get_keys_args args = {};
|
|
|
|
|
2024-01-11 22:47:48 +01:00
|
|
|
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
|
2023-02-28 16:00:19 +01:00
|
|
|
return 0;
|
|
|
|
|
2020-04-22 02:27:47 +02:00
|
|
|
DEBUGD(&nb_dbg_cbs_state,
|
|
|
|
"northbound callback (get_keys): node [%s] list_entry [%p]",
|
|
|
|
nb_node->xpath, list_entry);
|
|
|
|
|
2020-04-04 18:38:51 +02:00
|
|
|
args.list_entry = list_entry;
|
|
|
|
args.keys = keys;
|
|
|
|
return nb_node->cbs.get_keys(&args);
|
2020-04-22 02:27:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const void *nb_callback_lookup_entry(const struct nb_node *nb_node,
|
|
|
|
const void *parent_list_entry,
|
|
|
|
const struct yang_list_keys *keys)
|
|
|
|
{
|
2020-04-04 18:38:51 +02:00
|
|
|
struct nb_cb_lookup_entry_args args = {};
|
|
|
|
|
2024-01-11 22:47:48 +01:00
|
|
|
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
|
2023-02-28 16:00:19 +01:00
|
|
|
return NULL;
|
|
|
|
|
2020-04-22 02:27:47 +02:00
|
|
|
DEBUGD(&nb_dbg_cbs_state,
|
|
|
|
"northbound callback (lookup_entry): node [%s] parent_list_entry [%p]",
|
|
|
|
nb_node->xpath, parent_list_entry);
|
|
|
|
|
2020-04-04 18:38:51 +02:00
|
|
|
args.parent_list_entry = parent_list_entry;
|
|
|
|
args.keys = keys;
|
|
|
|
return nb_node->cbs.lookup_entry(&args);
|
2020-04-22 02:27:47 +02:00
|
|
|
}
|
|
|
|
|
2023-10-30 10:09:19 +01:00
|
|
|
const void *nb_callback_lookup_node_entry(struct lyd_node *node,
|
|
|
|
const void *parent_list_entry)
|
|
|
|
{
|
|
|
|
struct yang_list_keys keys;
|
|
|
|
struct nb_cb_lookup_entry_args args = {};
|
|
|
|
const struct nb_node *nb_node = node->schema->priv;
|
|
|
|
|
2024-01-11 22:47:48 +01:00
|
|
|
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
|
2023-10-30 10:09:19 +01:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (yang_get_node_keys(node, &keys)) {
|
|
|
|
flog_warn(EC_LIB_LIBYANG,
|
|
|
|
"%s: can't get keys for lookup from existing data node %s",
|
|
|
|
__func__, node->schema->name);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
DEBUGD(&nb_dbg_cbs_state,
|
|
|
|
"northbound callback (lookup_node_entry): node [%s] parent_list_entry [%p]",
|
|
|
|
nb_node->xpath, parent_list_entry);
|
|
|
|
|
|
|
|
args.parent_list_entry = parent_list_entry;
|
|
|
|
args.keys = &keys;
|
|
|
|
return nb_node->cbs.lookup_entry(&args);
|
|
|
|
}
|
|
|
|
|
|
|
|
const void *nb_callback_lookup_next(const struct nb_node *nb_node,
|
|
|
|
const void *parent_list_entry,
|
|
|
|
const struct yang_list_keys *keys)
|
|
|
|
{
|
|
|
|
struct nb_cb_lookup_entry_args args = {};
|
|
|
|
|
2024-01-11 22:47:48 +01:00
|
|
|
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
|
2023-10-30 10:09:19 +01:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
DEBUGD(&nb_dbg_cbs_state,
|
|
|
|
"northbound callback (lookup_entry): node [%s] parent_list_entry [%p]",
|
|
|
|
nb_node->xpath, parent_list_entry);
|
|
|
|
|
|
|
|
args.parent_list_entry = parent_list_entry;
|
|
|
|
args.keys = keys;
|
|
|
|
return nb_node->cbs.lookup_next(&args);
|
|
|
|
}
|
|
|
|
|
2020-04-22 02:27:47 +02:00
|
|
|
int nb_callback_rpc(const struct nb_node *nb_node, const char *xpath,
|
2020-10-04 00:34:33 +02:00
|
|
|
const struct list *input, struct list *output, char *errmsg,
|
|
|
|
size_t errmsg_len)
|
2020-04-22 02:27:47 +02:00
|
|
|
{
|
2020-04-04 18:38:51 +02:00
|
|
|
struct nb_cb_rpc_args args = {};
|
|
|
|
|
2024-01-11 22:47:48 +01:00
|
|
|
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
|
2023-02-28 16:00:19 +01:00
|
|
|
return 0;
|
|
|
|
|
2020-04-22 02:27:47 +02:00
|
|
|
DEBUGD(&nb_dbg_cbs_rpc, "northbound RPC: %s", xpath);
|
|
|
|
|
2020-04-04 18:38:51 +02:00
|
|
|
args.xpath = xpath;
|
|
|
|
args.input = input;
|
|
|
|
args.output = output;
|
2020-10-04 00:34:33 +02:00
|
|
|
args.errmsg = errmsg;
|
|
|
|
args.errmsg_len = errmsg_len;
|
2020-04-04 18:38:51 +02:00
|
|
|
return nb_node->cbs.rpc(&args);
|
2017-12-07 20:31:48 +01:00
|
|
|
}
|
|
|
|
|
2024-02-10 01:11:13 +01:00
|
|
|
void nb_callback_notify(const struct nb_node *nb_node, const char *xpath,
|
|
|
|
struct lyd_node *dnode)
|
|
|
|
{
|
|
|
|
struct nb_cb_notify_args args = {};
|
|
|
|
|
|
|
|
DEBUGD(&nb_dbg_cbs_notify, "northbound notify: %s", xpath);
|
|
|
|
|
|
|
|
args.xpath = xpath;
|
|
|
|
args.dnode = dnode;
|
|
|
|
nb_node->cbs.notify(&args);
|
|
|
|
}
|
|
|
|
|
2017-12-07 20:31:48 +01:00
|
|
|
/*
|
|
|
|
* Call the northbound configuration callback associated to a given
|
|
|
|
* configuration change.
|
|
|
|
*/
|
2020-04-27 18:13:57 +02:00
|
|
|
static int nb_callback_configuration(struct nb_context *context,
|
|
|
|
const enum nb_event event,
|
2020-05-14 17:30:34 +02:00
|
|
|
struct nb_config_change *change,
|
|
|
|
char *errmsg, size_t errmsg_len)
|
2017-12-07 20:31:48 +01:00
|
|
|
{
|
2023-10-05 23:13:16 +02:00
|
|
|
enum nb_cb_operation operation = change->cb.operation;
|
2019-10-10 03:20:26 +02:00
|
|
|
char xpath[XPATH_MAXLEN];
|
2017-12-07 20:31:48 +01:00
|
|
|
const struct nb_node *nb_node = change->cb.nb_node;
|
|
|
|
const struct lyd_node *dnode = change->cb.dnode;
|
|
|
|
union nb_resource *resource;
|
|
|
|
int ret = NB_ERR;
|
|
|
|
|
2024-01-11 22:47:48 +01:00
|
|
|
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_IGNORE_CFG_CBS))
|
2023-02-28 16:00:19 +01:00
|
|
|
return NB_OK;
|
|
|
|
|
2017-12-07 20:31:48 +01:00
|
|
|
if (event == NB_EV_VALIDATE)
|
|
|
|
resource = NULL;
|
|
|
|
else
|
|
|
|
resource = &change->resource;
|
|
|
|
|
|
|
|
switch (operation) {
|
2023-10-05 23:13:16 +02:00
|
|
|
case NB_CB_CREATE:
|
2020-04-27 18:13:57 +02:00
|
|
|
ret = nb_callback_create(context, nb_node, event, dnode,
|
2020-05-14 17:30:34 +02:00
|
|
|
resource, errmsg, errmsg_len);
|
2017-12-07 20:31:48 +01:00
|
|
|
break;
|
2023-10-05 23:13:16 +02:00
|
|
|
case NB_CB_MODIFY:
|
2020-04-27 18:13:57 +02:00
|
|
|
ret = nb_callback_modify(context, nb_node, event, dnode,
|
2020-05-14 17:30:34 +02:00
|
|
|
resource, errmsg, errmsg_len);
|
2017-12-07 20:31:48 +01:00
|
|
|
break;
|
2023-10-05 23:13:16 +02:00
|
|
|
case NB_CB_DESTROY:
|
2020-05-14 17:30:34 +02:00
|
|
|
ret = nb_callback_destroy(context, nb_node, event, dnode,
|
|
|
|
errmsg, errmsg_len);
|
2017-12-07 20:31:48 +01:00
|
|
|
break;
|
2023-10-05 23:13:16 +02:00
|
|
|
case NB_CB_MOVE:
|
2020-05-14 17:30:34 +02:00
|
|
|
ret = nb_callback_move(context, nb_node, event, dnode, errmsg,
|
|
|
|
errmsg_len);
|
2017-12-07 20:31:48 +01:00
|
|
|
break;
|
2023-10-05 23:13:16 +02:00
|
|
|
case NB_CB_PRE_VALIDATE:
|
|
|
|
case NB_CB_APPLY_FINISH:
|
|
|
|
case NB_CB_GET_ELEM:
|
|
|
|
case NB_CB_GET_NEXT:
|
|
|
|
case NB_CB_GET_KEYS:
|
|
|
|
case NB_CB_LOOKUP_ENTRY:
|
|
|
|
case NB_CB_RPC:
|
2024-02-10 01:11:13 +01:00
|
|
|
case NB_CB_NOTIFY:
|
2019-10-10 03:20:26 +02:00
|
|
|
yang_dnode_get_path(dnode, xpath, sizeof(xpath));
|
2019-04-16 00:03:57 +02:00
|
|
|
flog_err(EC_LIB_DEVELOPMENT,
|
|
|
|
"%s: unknown operation (%u) [xpath %s]", __func__,
|
|
|
|
operation, xpath);
|
|
|
|
exit(1);
|
2017-12-07 20:31:48 +01:00
|
|
|
}
|
|
|
|
|
2018-11-09 16:13:12 +01:00
|
|
|
if (ret != NB_OK) {
|
2019-10-10 03:20:26 +02:00
|
|
|
yang_dnode_get_path(dnode, xpath, sizeof(xpath));
|
|
|
|
|
2018-11-09 16:13:12 +01:00
|
|
|
switch (event) {
|
|
|
|
case NB_EV_VALIDATE:
|
2021-03-02 20:06:24 +01:00
|
|
|
flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE,
|
|
|
|
"error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s",
|
|
|
|
nb_err_name(ret), nb_event_name(event),
|
2023-10-05 23:13:16 +02:00
|
|
|
nb_cb_operation_name(operation), xpath,
|
2021-03-02 20:06:24 +01:00
|
|
|
errmsg[0] ? " message: " : "", errmsg);
|
2018-11-09 16:13:12 +01:00
|
|
|
break;
|
|
|
|
case NB_EV_PREPARE:
|
2021-03-02 20:06:24 +01:00
|
|
|
flog_warn(EC_LIB_NB_CB_CONFIG_PREPARE,
|
|
|
|
"error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s",
|
|
|
|
nb_err_name(ret), nb_event_name(event),
|
2023-10-05 23:13:16 +02:00
|
|
|
nb_cb_operation_name(operation), xpath,
|
2021-03-02 20:06:24 +01:00
|
|
|
errmsg[0] ? " message: " : "", errmsg);
|
2018-11-09 16:13:12 +01:00
|
|
|
break;
|
|
|
|
case NB_EV_ABORT:
|
2021-03-02 20:06:24 +01:00
|
|
|
flog_warn(EC_LIB_NB_CB_CONFIG_ABORT,
|
|
|
|
"error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s",
|
|
|
|
nb_err_name(ret), nb_event_name(event),
|
2023-10-05 23:13:16 +02:00
|
|
|
nb_cb_operation_name(operation), xpath,
|
2021-03-02 20:06:24 +01:00
|
|
|
errmsg[0] ? " message: " : "", errmsg);
|
2018-11-09 16:13:12 +01:00
|
|
|
break;
|
|
|
|
case NB_EV_APPLY:
|
2021-03-02 20:06:24 +01:00
|
|
|
flog_err(EC_LIB_NB_CB_CONFIG_APPLY,
|
|
|
|
"error processing configuration change: error [%s] event [%s] operation [%s] xpath [%s]%s%s",
|
|
|
|
nb_err_name(ret), nb_event_name(event),
|
2023-10-05 23:13:16 +02:00
|
|
|
nb_cb_operation_name(operation), xpath,
|
2021-03-02 20:06:24 +01:00
|
|
|
errmsg[0] ? " message: " : "", errmsg);
|
2018-11-09 16:13:12 +01:00
|
|
|
break;
|
2019-04-16 00:03:57 +02:00
|
|
|
default:
|
|
|
|
flog_err(EC_LIB_DEVELOPMENT,
|
2020-05-15 02:23:36 +02:00
|
|
|
"%s: unknown event (%u) [xpath %s]", __func__,
|
|
|
|
event, xpath);
|
2019-04-16 00:03:57 +02:00
|
|
|
exit(1);
|
2018-11-09 16:13:12 +01:00
|
|
|
}
|
|
|
|
}
|
2017-12-07 20:31:48 +01:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-02-02 21:03:15 +01:00
|
|
|
static struct nb_transaction *
|
2023-02-24 02:23:51 +01:00
|
|
|
nb_transaction_new(struct nb_context context, struct nb_config *config,
|
2020-05-14 17:30:34 +02:00
|
|
|
struct nb_config_cbs *changes, const char *comment,
|
|
|
|
char *errmsg, size_t errmsg_len)
|
2017-12-07 20:31:48 +01:00
|
|
|
{
|
|
|
|
struct nb_transaction *transaction;
|
|
|
|
|
2023-02-24 02:23:51 +01:00
|
|
|
if (nb_running_lock_check(context.client, context.user)) {
|
2020-05-14 17:30:34 +02:00
|
|
|
strlcpy(errmsg,
|
|
|
|
"running configuration is locked by another client",
|
|
|
|
errmsg_len);
|
2019-02-02 21:03:15 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-12-07 20:31:48 +01:00
|
|
|
if (transaction_in_progress) {
|
2020-05-14 17:30:34 +02:00
|
|
|
strlcpy(errmsg,
|
|
|
|
"there's already another transaction in progress",
|
|
|
|
errmsg_len);
|
2017-12-07 20:31:48 +01:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
transaction_in_progress = true;
|
|
|
|
|
|
|
|
transaction = XCALLOC(MTYPE_TMP, sizeof(*transaction));
|
2020-04-27 18:13:57 +02:00
|
|
|
transaction->context = context;
|
2017-12-07 20:31:48 +01:00
|
|
|
if (comment)
|
|
|
|
strlcpy(transaction->comment, comment,
|
|
|
|
sizeof(transaction->comment));
|
|
|
|
transaction->config = config;
|
|
|
|
transaction->changes = *changes;
|
|
|
|
|
|
|
|
return transaction;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void nb_transaction_free(struct nb_transaction *transaction)
|
|
|
|
{
|
|
|
|
nb_config_diff_del_changes(&transaction->changes);
|
|
|
|
XFREE(MTYPE_TMP, transaction);
|
|
|
|
transaction_in_progress = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Process all configuration changes associated to a transaction. */
|
|
|
|
static int nb_transaction_process(enum nb_event event,
|
2020-05-14 17:30:34 +02:00
|
|
|
struct nb_transaction *transaction,
|
|
|
|
char *errmsg, size_t errmsg_len)
|
2017-12-07 20:31:48 +01:00
|
|
|
{
|
|
|
|
struct nb_config_cb *cb;
|
|
|
|
|
2019-09-17 02:51:11 +02:00
|
|
|
RB_FOREACH (cb, nb_config_cbs, &transaction->changes) {
|
|
|
|
struct nb_config_change *change = (struct nb_config_change *)cb;
|
|
|
|
int ret;
|
2017-12-07 20:31:48 +01:00
|
|
|
|
2019-09-17 02:51:11 +02:00
|
|
|
/*
|
|
|
|
* Only try to release resources that were allocated
|
|
|
|
* successfully.
|
|
|
|
*/
|
2020-03-04 14:04:23 +01:00
|
|
|
if (event == NB_EV_ABORT && !change->prepare_ok)
|
2019-09-17 02:51:11 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
/* Call the appropriate callback. */
|
2023-02-24 02:23:51 +01:00
|
|
|
ret = nb_callback_configuration(&transaction->context, event,
|
2020-05-14 17:30:34 +02:00
|
|
|
change, errmsg, errmsg_len);
|
2019-09-17 02:51:11 +02:00
|
|
|
switch (event) {
|
|
|
|
case NB_EV_PREPARE:
|
|
|
|
if (ret != NB_OK)
|
|
|
|
return ret;
|
|
|
|
change->prepare_ok = true;
|
|
|
|
break;
|
|
|
|
case NB_EV_ABORT:
|
|
|
|
case NB_EV_APPLY:
|
2017-12-07 20:31:48 +01:00
|
|
|
/*
|
2019-09-17 02:51:11 +02:00
|
|
|
* At this point it's not possible to reject the
|
|
|
|
* transaction anymore, so any failure here can lead to
|
|
|
|
* inconsistencies and should be treated as a bug.
|
|
|
|
* Operations prone to errors, like validations and
|
|
|
|
* resource allocations, should be performed during the
|
|
|
|
* 'prepare' phase.
|
2017-12-07 20:31:48 +01:00
|
|
|
*/
|
2019-09-17 02:51:11 +02:00
|
|
|
break;
|
2023-01-27 14:48:47 +01:00
|
|
|
case NB_EV_VALIDATE:
|
2019-09-17 02:51:11 +02:00
|
|
|
break;
|
2017-12-07 20:31:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NB_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct nb_config_cb *
|
2019-10-10 03:20:26 +02:00
|
|
|
nb_apply_finish_cb_new(struct nb_config_cbs *cbs, const struct nb_node *nb_node,
|
2017-12-07 20:31:48 +01:00
|
|
|
const struct lyd_node *dnode)
|
|
|
|
{
|
|
|
|
struct nb_config_cb *cb;
|
|
|
|
|
|
|
|
cb = XCALLOC(MTYPE_TMP, sizeof(*cb));
|
|
|
|
cb->nb_node = nb_node;
|
|
|
|
cb->dnode = dnode;
|
|
|
|
RB_INSERT(nb_config_cbs, cbs, cb);
|
|
|
|
|
|
|
|
return cb;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct nb_config_cb *
|
2019-10-10 04:24:04 +02:00
|
|
|
nb_apply_finish_cb_find(struct nb_config_cbs *cbs,
|
|
|
|
const struct nb_node *nb_node,
|
|
|
|
const struct lyd_node *dnode)
|
2017-12-07 20:31:48 +01:00
|
|
|
{
|
|
|
|
struct nb_config_cb s;
|
|
|
|
|
2019-10-10 04:24:04 +02:00
|
|
|
s.seq = 0;
|
2017-12-07 20:31:48 +01:00
|
|
|
s.nb_node = nb_node;
|
2019-10-10 04:24:04 +02:00
|
|
|
s.dnode = dnode;
|
2017-12-07 20:31:48 +01:00
|
|
|
return RB_FIND(nb_config_cbs, cbs, &s);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Call the 'apply_finish' callbacks. */
|
2020-05-14 17:30:34 +02:00
|
|
|
static void nb_transaction_apply_finish(struct nb_transaction *transaction,
|
|
|
|
char *errmsg, size_t errmsg_len)
|
2017-12-07 20:31:48 +01:00
|
|
|
{
|
|
|
|
struct nb_config_cbs cbs;
|
|
|
|
struct nb_config_cb *cb;
|
|
|
|
|
|
|
|
/* Initialize tree of 'apply_finish' callbacks. */
|
|
|
|
RB_INIT(nb_config_cbs, &cbs);
|
|
|
|
|
|
|
|
/* Identify the 'apply_finish' callbacks that need to be called. */
|
|
|
|
RB_FOREACH (cb, nb_config_cbs, &transaction->changes) {
|
|
|
|
struct nb_config_change *change = (struct nb_config_change *)cb;
|
|
|
|
const struct lyd_node *dnode = change->cb.dnode;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Iterate up to the root of the data tree. When a node is being
|
|
|
|
* deleted, skip its 'apply_finish' callback if one is defined
|
|
|
|
* (the 'apply_finish' callbacks from the node ancestors should
|
|
|
|
* be called though).
|
|
|
|
*/
|
2023-10-05 23:13:16 +02:00
|
|
|
if (change->cb.operation == NB_CB_DESTROY) {
|
2020-04-22 02:27:47 +02:00
|
|
|
char xpath[XPATH_MAXLEN];
|
|
|
|
|
2021-05-04 16:41:58 +02:00
|
|
|
dnode = lyd_parent(dnode);
|
2017-12-07 20:31:48 +01:00
|
|
|
if (!dnode)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The dnode from 'delete' callbacks point to elements
|
|
|
|
* from the running configuration. Use yang_dnode_get()
|
|
|
|
* to get the corresponding dnode from the candidate
|
|
|
|
* configuration that is being committed.
|
|
|
|
*/
|
|
|
|
yang_dnode_get_path(dnode, xpath, sizeof(xpath));
|
|
|
|
dnode = yang_dnode_get(transaction->config->dnode,
|
|
|
|
xpath);
|
|
|
|
}
|
|
|
|
while (dnode) {
|
|
|
|
struct nb_node *nb_node;
|
|
|
|
|
|
|
|
nb_node = dnode->schema->priv;
|
2020-09-11 15:43:49 +02:00
|
|
|
if (!nb_node || !nb_node->cbs.apply_finish)
|
2017-12-07 20:31:48 +01:00
|
|
|
goto next;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Don't call the callback more than once for the same
|
|
|
|
* data node.
|
|
|
|
*/
|
2019-10-10 04:24:04 +02:00
|
|
|
if (nb_apply_finish_cb_find(&cbs, nb_node, dnode))
|
2017-12-07 20:31:48 +01:00
|
|
|
goto next;
|
|
|
|
|
2019-10-10 03:20:26 +02:00
|
|
|
nb_apply_finish_cb_new(&cbs, nb_node, dnode);
|
2017-12-07 20:31:48 +01:00
|
|
|
|
|
|
|
next:
|
2021-05-04 16:41:58 +02:00
|
|
|
dnode = lyd_parent(dnode);
|
2017-12-07 20:31:48 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Call the 'apply_finish' callbacks, sorted by their priorities. */
|
2020-04-22 02:27:47 +02:00
|
|
|
RB_FOREACH (cb, nb_config_cbs, &cbs)
|
2023-02-24 02:23:51 +01:00
|
|
|
nb_callback_apply_finish(&transaction->context, cb->nb_node,
|
2020-05-14 17:30:34 +02:00
|
|
|
cb->dnode, errmsg, errmsg_len);
|
2017-12-07 20:31:48 +01:00
|
|
|
|
|
|
|
/* Release memory. */
|
|
|
|
while (!RB_EMPTY(nb_config_cbs, &cbs)) {
|
|
|
|
cb = RB_ROOT(nb_config_cbs, &cbs);
|
|
|
|
RB_REMOVE(nb_config_cbs, &cbs, cb);
|
|
|
|
XFREE(MTYPE_TMP, cb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-05 23:13:16 +02:00
|
|
|
bool nb_cb_operation_is_valid(enum nb_cb_operation operation,
|
|
|
|
const struct lysc_node *snode)
|
2017-12-07 20:31:48 +01:00
|
|
|
{
|
2018-11-13 23:36:48 +01:00
|
|
|
struct nb_node *nb_node = snode->priv;
|
2021-05-04 16:41:58 +02:00
|
|
|
struct lysc_node_container *scontainer;
|
|
|
|
struct lysc_node_leaf *sleaf;
|
2017-12-07 20:31:48 +01:00
|
|
|
|
|
|
|
switch (operation) {
|
2023-10-05 23:13:16 +02:00
|
|
|
case NB_CB_CREATE:
|
2018-11-03 01:07:07 +01:00
|
|
|
if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
|
2017-12-07 20:31:48 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
switch (snode->nodetype) {
|
|
|
|
case LYS_LEAF:
|
2021-05-04 16:41:58 +02:00
|
|
|
sleaf = (struct lysc_node_leaf *)snode;
|
|
|
|
if (sleaf->type->basetype != LY_TYPE_EMPTY)
|
2017-12-07 20:31:48 +01:00
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
case LYS_CONTAINER:
|
2021-05-04 16:41:58 +02:00
|
|
|
scontainer = (struct lysc_node_container *)snode;
|
|
|
|
if (!CHECK_FLAG(scontainer->flags, LYS_PRESENCE))
|
2017-12-07 20:31:48 +01:00
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
case LYS_LIST:
|
|
|
|
case LYS_LEAFLIST:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2023-10-05 23:13:16 +02:00
|
|
|
case NB_CB_MODIFY:
|
2018-11-03 01:07:07 +01:00
|
|
|
if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
|
2017-12-07 20:31:48 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
switch (snode->nodetype) {
|
|
|
|
case LYS_LEAF:
|
2021-05-04 16:41:58 +02:00
|
|
|
sleaf = (struct lysc_node_leaf *)snode;
|
|
|
|
if (sleaf->type->basetype == LY_TYPE_EMPTY)
|
2017-12-07 20:31:48 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
/* List keys can't be modified. */
|
2021-05-04 16:41:58 +02:00
|
|
|
if (lysc_is_key(sleaf))
|
2017-12-07 20:31:48 +01:00
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2023-10-05 23:13:16 +02:00
|
|
|
case NB_CB_DESTROY:
|
2018-11-03 01:07:07 +01:00
|
|
|
if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
|
2017-12-07 20:31:48 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
switch (snode->nodetype) {
|
|
|
|
case LYS_LEAF:
|
2021-05-04 16:41:58 +02:00
|
|
|
sleaf = (struct lysc_node_leaf *)snode;
|
2017-12-07 20:31:48 +01:00
|
|
|
|
|
|
|
/* List keys can't be deleted. */
|
2021-05-04 16:41:58 +02:00
|
|
|
if (lysc_is_key(sleaf))
|
2017-12-07 20:31:48 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Only optional leafs can be deleted, or leafs whose
|
|
|
|
* parent is a case statement.
|
|
|
|
*/
|
|
|
|
if (snode->parent->nodetype == LYS_CASE)
|
|
|
|
return true;
|
|
|
|
if (sleaf->when)
|
|
|
|
return true;
|
2018-11-03 01:07:07 +01:00
|
|
|
if (CHECK_FLAG(sleaf->flags, LYS_MAND_TRUE)
|
|
|
|
|| sleaf->dflt)
|
2017-12-07 20:31:48 +01:00
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
case LYS_CONTAINER:
|
2021-05-04 16:41:58 +02:00
|
|
|
scontainer = (struct lysc_node_container *)snode;
|
|
|
|
if (!CHECK_FLAG(scontainer->flags, LYS_PRESENCE))
|
2017-12-07 20:31:48 +01:00
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
case LYS_LIST:
|
|
|
|
case LYS_LEAFLIST:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2023-10-05 23:13:16 +02:00
|
|
|
case NB_CB_MOVE:
|
2018-11-03 01:07:07 +01:00
|
|
|
if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
|
2017-12-07 20:31:48 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
switch (snode->nodetype) {
|
|
|
|
case LYS_LIST:
|
|
|
|
case LYS_LEAFLIST:
|
2021-05-04 16:41:58 +02:00
|
|
|
if (!CHECK_FLAG(snode->flags, LYS_ORDBY_USER))
|
2017-12-07 20:31:48 +01:00
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2023-10-05 23:13:16 +02:00
|
|
|
case NB_CB_PRE_VALIDATE:
|
|
|
|
case NB_CB_APPLY_FINISH:
|
2018-11-03 01:07:07 +01:00
|
|
|
if (!CHECK_FLAG(snode->flags, LYS_CONFIG_W))
|
2017-12-07 20:31:48 +01:00
|
|
|
return false;
|
|
|
|
return true;
|
2023-10-05 23:13:16 +02:00
|
|
|
case NB_CB_GET_ELEM:
|
2018-11-03 01:07:07 +01:00
|
|
|
if (!CHECK_FLAG(snode->flags, LYS_CONFIG_R))
|
2017-12-07 20:31:48 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
switch (snode->nodetype) {
|
|
|
|
case LYS_LEAF:
|
2018-11-03 01:16:55 +01:00
|
|
|
case LYS_LEAFLIST:
|
2017-12-07 20:31:48 +01:00
|
|
|
break;
|
|
|
|
case LYS_CONTAINER:
|
2021-05-04 16:41:58 +02:00
|
|
|
scontainer = (struct lysc_node_container *)snode;
|
|
|
|
if (!CHECK_FLAG(scontainer->flags, LYS_PRESENCE))
|
2017-12-07 20:31:48 +01:00
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2023-10-05 23:13:16 +02:00
|
|
|
case NB_CB_GET_NEXT:
|
2018-11-03 01:16:55 +01:00
|
|
|
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;
|
2023-10-05 23:13:16 +02:00
|
|
|
case NB_CB_GET_KEYS:
|
|
|
|
case NB_CB_LOOKUP_ENTRY:
|
2017-12-07 20:31:48 +01:00
|
|
|
switch (snode->nodetype) {
|
|
|
|
case LYS_LIST:
|
2018-11-13 23:36:48 +01:00
|
|
|
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_CONFIG_ONLY))
|
|
|
|
return false;
|
2018-12-08 20:31:16 +01:00
|
|
|
if (CHECK_FLAG(nb_node->flags, F_NB_NODE_KEYLESS_LIST))
|
|
|
|
return false;
|
2017-12-07 20:31:48 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2023-10-05 23:13:16 +02:00
|
|
|
case NB_CB_RPC:
|
2018-11-03 01:07:07 +01:00
|
|
|
if (CHECK_FLAG(snode->flags, LYS_CONFIG_W | LYS_CONFIG_R))
|
2017-12-07 20:31:48 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
switch (snode->nodetype) {
|
|
|
|
case LYS_RPC:
|
|
|
|
case LYS_ACTION:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
2024-02-10 01:11:13 +01:00
|
|
|
case NB_CB_NOTIFY:
|
|
|
|
if (snode->nodetype != LYS_NOTIF)
|
|
|
|
return false;
|
|
|
|
return true;
|
2017-12-07 20:31:48 +01:00
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DEFINE_HOOK(nb_notification_send, (const char *xpath, struct list *arguments),
|
|
|
|
(xpath, arguments));
|
|
|
|
|
|
|
|
int nb_notification_send(const char *xpath, struct list *arguments)
|
|
|
|
{
|
2024-02-12 19:03:32 +01:00
|
|
|
struct lyd_node *root = NULL;
|
|
|
|
struct lyd_node *dnode;
|
|
|
|
struct yang_data *data;
|
|
|
|
struct listnode *ln;
|
|
|
|
LY_ERR err;
|
2017-12-07 20:31:48 +01:00
|
|
|
int ret;
|
|
|
|
|
2019-04-12 23:00:26 +02:00
|
|
|
DEBUGD(&nb_dbg_notif, "northbound notification: %s", xpath);
|
|
|
|
|
2024-02-12 19:03:32 +01:00
|
|
|
/*
|
|
|
|
* Call old hook functions
|
|
|
|
*/
|
2017-12-07 20:31:48 +01:00
|
|
|
ret = hook_call(nb_notification_send, xpath, arguments);
|
2024-02-12 19:03:32 +01:00
|
|
|
|
|
|
|
if (!hook_have_hooks(nb_notification_tree_send))
|
|
|
|
goto done;
|
|
|
|
/*
|
|
|
|
* Convert yang data arguments list to a libyang data tree for new hook
|
|
|
|
* functions.
|
|
|
|
*/
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(arguments, ln, data)) {
|
|
|
|
err = lyd_new_path(root, ly_native_ctx, data->xpath,
|
|
|
|
data->value, LYD_NEW_PATH_UPDATE, &dnode);
|
|
|
|
if (err != LY_SUCCESS)
|
|
|
|
goto lyerr;
|
|
|
|
if (!root) {
|
|
|
|
root = dnode;
|
|
|
|
while (root->parent)
|
|
|
|
root = lyd_parent(root);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!root) {
|
|
|
|
err = lyd_new_path(NULL, ly_native_ctx, xpath, "", 0, &root);
|
|
|
|
if (err) {
|
|
|
|
lyerr:
|
|
|
|
flog_err(EC_LIB_LIBYANG,
|
|
|
|
"%s: error creating notification data: %s",
|
|
|
|
__func__, ly_strerrcode(err));
|
|
|
|
ret += 1;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Call new hook functions
|
|
|
|
*/
|
|
|
|
ret += nb_notification_tree_send(xpath, root);
|
|
|
|
|
|
|
|
done:
|
|
|
|
if (root)
|
|
|
|
lyd_free_all(root);
|
2017-12-07 20:31:48 +01:00
|
|
|
if (arguments)
|
|
|
|
list_delete(&arguments);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2024-02-12 19:03:32 +01:00
|
|
|
DEFINE_HOOK(nb_notification_tree_send,
|
|
|
|
(const char *xpath, const struct lyd_node *tree), (xpath, tree));
|
2024-01-30 09:05:18 +01:00
|
|
|
|
2024-02-12 19:03:32 +01:00
|
|
|
int nb_notification_tree_send(const char *xpath, const struct lyd_node *tree)
|
2024-01-30 09:05:18 +01:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
assert(tree);
|
|
|
|
|
|
|
|
DEBUGD(&nb_dbg_notif, "northbound tree notification: %s",
|
|
|
|
tree->schema->name);
|
|
|
|
|
2024-02-12 19:03:32 +01:00
|
|
|
ret = hook_call(nb_notification_tree_send, xpath, tree);
|
2024-01-30 09:05:18 +01:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-04-18 16:55:52 +02:00
|
|
|
/* Running configuration user pointers management. */
|
|
|
|
struct nb_config_entry {
|
|
|
|
char xpath[XPATH_MAXLEN];
|
|
|
|
void *entry;
|
|
|
|
};
|
|
|
|
|
|
|
|
static bool running_config_entry_cmp(const void *value1, const void *value2)
|
|
|
|
{
|
|
|
|
const struct nb_config_entry *c1 = value1;
|
|
|
|
const struct nb_config_entry *c2 = value2;
|
|
|
|
|
|
|
|
return strmatch(c1->xpath, c2->xpath);
|
|
|
|
}
|
|
|
|
|
2019-05-14 22:19:07 +02:00
|
|
|
static unsigned int running_config_entry_key_make(const void *value)
|
2019-04-18 16:55:52 +02:00
|
|
|
{
|
|
|
|
return string_hash_make(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void *running_config_entry_alloc(void *p)
|
|
|
|
{
|
|
|
|
struct nb_config_entry *new, *key = p;
|
|
|
|
|
|
|
|
new = XCALLOC(MTYPE_NB_CONFIG_ENTRY, sizeof(*new));
|
|
|
|
strlcpy(new->xpath, key->xpath, sizeof(new->xpath));
|
|
|
|
|
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void running_config_entry_free(void *arg)
|
|
|
|
{
|
|
|
|
XFREE(MTYPE_NB_CONFIG_ENTRY, arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void nb_running_set_entry(const struct lyd_node *dnode, void *entry)
|
|
|
|
{
|
|
|
|
struct nb_config_entry *config, s;
|
|
|
|
|
|
|
|
yang_dnode_get_path(dnode, s.xpath, sizeof(s.xpath));
|
|
|
|
config = hash_get(running_config_entries, &s,
|
|
|
|
running_config_entry_alloc);
|
|
|
|
config->entry = entry;
|
|
|
|
}
|
|
|
|
|
2020-06-05 01:26:42 +02:00
|
|
|
void nb_running_move_tree(const char *xpath_from, const char *xpath_to)
|
|
|
|
{
|
|
|
|
struct nb_config_entry *entry;
|
|
|
|
struct list *entries = hash_to_list(running_config_entries);
|
|
|
|
struct listnode *ln;
|
|
|
|
|
|
|
|
for (ALL_LIST_ELEMENTS_RO(entries, ln, entry)) {
|
|
|
|
if (!frrstr_startswith(entry->xpath, xpath_from))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
hash_release(running_config_entries, entry);
|
|
|
|
|
|
|
|
char *newpath =
|
|
|
|
frrstr_replace(entry->xpath, xpath_from, xpath_to);
|
|
|
|
strlcpy(entry->xpath, newpath, sizeof(entry->xpath));
|
|
|
|
XFREE(MTYPE_TMP, newpath);
|
|
|
|
|
*: remove the checking returned value for hash_get()
Firstly, *keep no change* for `hash_get()` with NULL
`alloc_func`.
Only focus on cases with non-NULL `alloc_func` of
`hash_get()`.
Since `hash_get()` with non-NULL `alloc_func` parameter
shall not fail, just ignore the returned value of it.
The returned value must not be NULL.
So in this case, remove the unnecessary checking NULL
or not for the returned value and add `void` in front
of it.
Importantly, also *keep no change* for the two cases with
non-NULL `alloc_func` -
1) Use `assert(<returned_data> == <searching_data>)` to
ensure it is a created node, not a found node.
Refer to `isis_vertex_queue_insert()` of isisd, there
are many examples of this case in isid.
2) Use `<returned_data> != <searching_data>` to judge it
is a found node, then free <searching_data>.
Refer to `aspath_intern()` of bgpd, there are many
examples of this case in bgpd.
Here, <returned_data> is the returned value from `hash_get()`,
and <searching_data> is the data, which is to be put into
hash table.
Signed-off-by: anlan_cs <vic.lan@pica8.com>
2022-04-21 08:37:12 +02:00
|
|
|
(void)hash_get(running_config_entries, entry,
|
|
|
|
hash_alloc_intern);
|
2020-06-05 01:26:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
list_delete(&entries);
|
|
|
|
}
|
|
|
|
|
2019-04-18 16:55:52 +02:00
|
|
|
static void *nb_running_unset_entry_helper(const struct lyd_node *dnode)
|
|
|
|
{
|
|
|
|
struct nb_config_entry *config, s;
|
|
|
|
struct lyd_node *child;
|
|
|
|
void *entry = NULL;
|
|
|
|
|
|
|
|
yang_dnode_get_path(dnode, s.xpath, sizeof(s.xpath));
|
|
|
|
config = hash_release(running_config_entries, &s);
|
|
|
|
if (config) {
|
|
|
|
entry = config->entry;
|
|
|
|
running_config_entry_free(config);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Unset user pointers from the child nodes. */
|
|
|
|
if (CHECK_FLAG(dnode->schema->nodetype, LYS_LIST | LYS_CONTAINER)) {
|
2021-05-04 16:41:58 +02:00
|
|
|
LY_LIST_FOR (lyd_child(dnode), child) {
|
2019-04-18 16:55:52 +02:00
|
|
|
(void)nb_running_unset_entry_helper(child);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *nb_running_unset_entry(const struct lyd_node *dnode)
|
|
|
|
{
|
|
|
|
void *entry;
|
|
|
|
|
|
|
|
entry = nb_running_unset_entry_helper(dnode);
|
|
|
|
assert(entry);
|
|
|
|
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
|
2020-03-10 10:30:20 +01:00
|
|
|
static void *nb_running_get_entry_worker(const struct lyd_node *dnode,
|
|
|
|
const char *xpath,
|
|
|
|
bool abort_if_not_found,
|
|
|
|
bool rec_search)
|
2019-04-18 16:55:52 +02:00
|
|
|
{
|
|
|
|
const struct lyd_node *orig_dnode = dnode;
|
|
|
|
char xpath_buf[XPATH_MAXLEN];
|
2020-03-10 10:30:20 +01:00
|
|
|
bool rec_flag = true;
|
2019-04-18 16:55:52 +02:00
|
|
|
|
|
|
|
assert(dnode || xpath);
|
|
|
|
|
|
|
|
if (!dnode)
|
|
|
|
dnode = yang_dnode_get(running_config->dnode, xpath);
|
|
|
|
|
2020-03-10 10:30:20 +01:00
|
|
|
while (rec_flag && dnode) {
|
2019-04-18 16:55:52 +02:00
|
|
|
struct nb_config_entry *config, s;
|
|
|
|
|
|
|
|
yang_dnode_get_path(dnode, s.xpath, sizeof(s.xpath));
|
|
|
|
config = hash_lookup(running_config_entries, &s);
|
|
|
|
if (config)
|
|
|
|
return config->entry;
|
|
|
|
|
2020-03-10 10:30:20 +01:00
|
|
|
rec_flag = rec_search;
|
|
|
|
|
2021-05-04 16:41:58 +02:00
|
|
|
dnode = lyd_parent(dnode);
|
2019-04-18 16:55:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!abort_if_not_found)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
yang_dnode_get_path(orig_dnode, xpath_buf, sizeof(xpath_buf));
|
|
|
|
flog_err(EC_LIB_YANG_DNODE_NOT_FOUND,
|
|
|
|
"%s: failed to find entry [xpath %s]", __func__, xpath_buf);
|
|
|
|
zlog_backtrace(LOG_ERR);
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
2020-03-10 10:30:20 +01:00
|
|
|
void *nb_running_get_entry(const struct lyd_node *dnode, const char *xpath,
|
|
|
|
bool abort_if_not_found)
|
|
|
|
{
|
|
|
|
return nb_running_get_entry_worker(dnode, xpath, abort_if_not_found,
|
|
|
|
true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void *nb_running_get_entry_non_rec(const struct lyd_node *dnode,
|
|
|
|
const char *xpath, bool abort_if_not_found)
|
|
|
|
{
|
|
|
|
return nb_running_get_entry_worker(dnode, xpath, abort_if_not_found,
|
|
|
|
false);
|
|
|
|
}
|
|
|
|
|
2019-04-18 16:55:52 +02:00
|
|
|
/* Logging functions. */
|
2017-12-07 20:31:48 +01:00
|
|
|
const char *nb_event_name(enum nb_event event)
|
|
|
|
{
|
|
|
|
switch (event) {
|
|
|
|
case NB_EV_VALIDATE:
|
|
|
|
return "validate";
|
|
|
|
case NB_EV_PREPARE:
|
|
|
|
return "prepare";
|
|
|
|
case NB_EV_ABORT:
|
|
|
|
return "abort";
|
|
|
|
case NB_EV_APPLY:
|
|
|
|
return "apply";
|
|
|
|
}
|
2023-01-27 14:48:47 +01:00
|
|
|
|
|
|
|
assert(!"Reached end of function we should never hit");
|
2017-12-07 20:31:48 +01:00
|
|
|
}
|
|
|
|
|
2023-10-05 23:13:16 +02:00
|
|
|
const char *nb_cb_operation_name(enum nb_cb_operation operation)
|
2017-12-07 20:31:48 +01:00
|
|
|
{
|
|
|
|
switch (operation) {
|
2023-10-05 23:13:16 +02:00
|
|
|
case NB_CB_CREATE:
|
2017-12-07 20:31:48 +01:00
|
|
|
return "create";
|
2023-10-05 23:13:16 +02:00
|
|
|
case NB_CB_MODIFY:
|
2017-12-07 20:31:48 +01:00
|
|
|
return "modify";
|
2023-10-05 23:13:16 +02:00
|
|
|
case NB_CB_DESTROY:
|
2019-01-30 10:54:25 +01:00
|
|
|
return "destroy";
|
2023-10-05 23:13:16 +02:00
|
|
|
case NB_CB_MOVE:
|
2017-12-07 20:31:48 +01:00
|
|
|
return "move";
|
2023-10-05 23:13:16 +02:00
|
|
|
case NB_CB_PRE_VALIDATE:
|
2019-09-13 00:07:54 +02:00
|
|
|
return "pre_validate";
|
2023-10-05 23:13:16 +02:00
|
|
|
case NB_CB_APPLY_FINISH:
|
2017-12-07 20:31:48 +01:00
|
|
|
return "apply_finish";
|
2023-10-05 23:13:16 +02:00
|
|
|
case NB_CB_GET_ELEM:
|
2017-12-07 20:31:48 +01:00
|
|
|
return "get_elem";
|
2023-10-05 23:13:16 +02:00
|
|
|
case NB_CB_GET_NEXT:
|
2017-12-07 20:31:48 +01:00
|
|
|
return "get_next";
|
2023-10-05 23:13:16 +02:00
|
|
|
case NB_CB_GET_KEYS:
|
2017-12-07 20:31:48 +01:00
|
|
|
return "get_keys";
|
2023-10-05 23:13:16 +02:00
|
|
|
case NB_CB_LOOKUP_ENTRY:
|
2017-12-07 20:31:48 +01:00
|
|
|
return "lookup_entry";
|
2023-10-05 23:13:16 +02:00
|
|
|
case NB_CB_RPC:
|
2017-12-07 20:31:48 +01:00
|
|
|
return "rpc";
|
2024-02-10 01:11:13 +01:00
|
|
|
case NB_CB_NOTIFY:
|
|
|
|
return "notify";
|
2017-12-07 20:31:48 +01:00
|
|
|
}
|
2023-01-27 14:48:47 +01:00
|
|
|
|
|
|
|
assert(!"Reached end of function we should never hit");
|
2017-12-07 20:31:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const char *nb_err_name(enum nb_error error)
|
|
|
|
{
|
|
|
|
switch (error) {
|
|
|
|
case NB_OK:
|
|
|
|
return "ok";
|
|
|
|
case NB_ERR:
|
|
|
|
return "generic error";
|
|
|
|
case NB_ERR_NO_CHANGES:
|
|
|
|
return "no changes";
|
|
|
|
case NB_ERR_NOT_FOUND:
|
|
|
|
return "element not found";
|
|
|
|
case NB_ERR_LOCKED:
|
|
|
|
return "resource is locked";
|
|
|
|
case NB_ERR_VALIDATION:
|
2020-05-14 17:30:34 +02:00
|
|
|
return "validation";
|
2017-12-07 20:31:48 +01:00
|
|
|
case NB_ERR_RESOURCE:
|
|
|
|
return "failed to allocate resource";
|
|
|
|
case NB_ERR_INCONSISTENCY:
|
|
|
|
return "internal inconsistency";
|
2023-10-30 10:09:19 +01:00
|
|
|
case NB_YIELD:
|
|
|
|
return "should yield";
|
2017-12-07 20:31:48 +01:00
|
|
|
}
|
2023-01-27 14:48:47 +01:00
|
|
|
|
|
|
|
assert(!"Reached end of function we should never hit");
|
2017-12-07 20:31:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const char *nb_client_name(enum nb_client client)
|
|
|
|
{
|
|
|
|
switch (client) {
|
|
|
|
case NB_CLIENT_CLI:
|
|
|
|
return "CLI";
|
2018-05-24 01:11:59 +02:00
|
|
|
case NB_CLIENT_CONFD:
|
|
|
|
return "ConfD";
|
2018-05-24 01:12:29 +02:00
|
|
|
case NB_CLIENT_SYSREPO:
|
|
|
|
return "Sysrepo";
|
2019-01-25 21:54:16 +01:00
|
|
|
case NB_CLIENT_GRPC:
|
|
|
|
return "gRPC";
|
2023-01-27 14:48:47 +01:00
|
|
|
case NB_CLIENT_PCEP:
|
|
|
|
return "Pcep";
|
2023-03-08 23:22:09 +01:00
|
|
|
case NB_CLIENT_MGMTD_SERVER:
|
|
|
|
return "MGMTD Server";
|
2023-02-28 16:00:19 +01:00
|
|
|
case NB_CLIENT_MGMTD_BE:
|
|
|
|
return "MGMT Backend";
|
2023-01-27 14:48:47 +01:00
|
|
|
case NB_CLIENT_NONE:
|
|
|
|
return "None";
|
2017-12-07 20:31:48 +01:00
|
|
|
}
|
2023-01-27 14:48:47 +01:00
|
|
|
|
|
|
|
assert(!"Reached end of function we should never hit");
|
2017-12-07 20:31:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void nb_load_callbacks(const struct frr_yang_module_info *module)
|
|
|
|
{
|
|
|
|
for (size_t i = 0; module->nodes[i].xpath; i++) {
|
|
|
|
struct nb_node *nb_node;
|
|
|
|
uint32_t priority;
|
|
|
|
|
2020-04-04 01:10:04 +02:00
|
|
|
if (i > YANG_MODULE_MAX_NODES) {
|
|
|
|
zlog_err(
|
|
|
|
"%s: %s.yang has more than %u nodes. Please increase YANG_MODULE_MAX_NODES to fix this problem.",
|
|
|
|
__func__, module->name, YANG_MODULE_MAX_NODES);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2017-12-07 20:31:48 +01:00
|
|
|
nb_node = nb_node_find(module->nodes[i].xpath);
|
|
|
|
if (!nb_node) {
|
|
|
|
flog_warn(EC_LIB_YANG_UNKNOWN_DATA_PATH,
|
|
|
|
"%s: unknown data path: %s", __func__,
|
|
|
|
module->nodes[i].xpath);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
nb_node->cbs = module->nodes[i].cbs;
|
|
|
|
priority = module->nodes[i].priority;
|
|
|
|
if (priority != 0)
|
|
|
|
nb_node->priority = priority;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-20 05:20:48 +02:00
|
|
|
void nb_validate_callbacks(void)
|
2017-12-07 20:31:48 +01:00
|
|
|
{
|
|
|
|
unsigned int errors = 0;
|
|
|
|
|
2020-10-23 03:19:10 +02:00
|
|
|
yang_snodes_iterate(NULL, nb_node_validate, 0, &errors);
|
2017-12-07 20:31:48 +01:00
|
|
|
if (errors > 0) {
|
|
|
|
flog_err(
|
|
|
|
EC_LIB_NB_CBS_VALIDATION,
|
|
|
|
"%s: failed to validate northbound callbacks: %u error(s)",
|
|
|
|
__func__, errors);
|
|
|
|
exit(1);
|
|
|
|
}
|
2020-10-20 05:20:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-03-07 20:14:41 +01:00
|
|
|
void nb_init(struct event_loop *tm,
|
2020-10-20 05:20:48 +02:00
|
|
|
const struct frr_yang_module_info *const modules[],
|
|
|
|
size_t nmodules, bool db_enabled)
|
|
|
|
{
|
2021-05-04 16:41:58 +02:00
|
|
|
struct yang_module *loaded[nmodules], **loadedp = loaded;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Currently using this explicit compile feature in libyang2 leads to
|
|
|
|
* incorrect behavior in FRR. The functionality suppresses the compiling
|
|
|
|
* of modules until they have all been loaded into the context. This
|
|
|
|
* avoids multiple recompiles of the same modules as they are
|
|
|
|
* imported/augmented etc.
|
2023-09-20 15:27:23 +02:00
|
|
|
* (Done as a #define to make coverity happy)
|
2021-05-04 16:41:58 +02:00
|
|
|
*/
|
2023-09-20 15:27:23 +02:00
|
|
|
#define explicit_compile false
|
2021-05-04 16:41:58 +02:00
|
|
|
|
2020-08-20 21:09:53 +02:00
|
|
|
nb_db_enabled = db_enabled;
|
|
|
|
|
2021-05-04 16:41:58 +02:00
|
|
|
yang_init(true, explicit_compile);
|
|
|
|
|
2020-10-20 05:20:48 +02:00
|
|
|
/* Load YANG modules and their corresponding northbound callbacks. */
|
2021-05-04 16:41:58 +02:00
|
|
|
for (size_t i = 0; i < nmodules; i++) {
|
|
|
|
DEBUGD(&nb_dbg_events, "northbound: loading %s.yang",
|
|
|
|
modules[i]->name);
|
2024-01-24 02:02:15 +01:00
|
|
|
*loadedp++ = yang_module_load(modules[i]->name,
|
|
|
|
modules[i]->features);
|
2021-05-04 16:41:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (explicit_compile)
|
|
|
|
yang_init_loading_complete();
|
|
|
|
|
|
|
|
/* Initialize the compiled nodes with northbound data */
|
|
|
|
for (size_t i = 0; i < nmodules; i++) {
|
2023-02-28 16:00:19 +01:00
|
|
|
yang_snodes_iterate(loaded[i]->info, nb_node_new_cb, 0,
|
|
|
|
(void *)modules[i]);
|
2021-05-04 16:41:58 +02:00
|
|
|
nb_load_callbacks(modules[i]);
|
|
|
|
}
|
2020-10-20 05:20:48 +02:00
|
|
|
|
|
|
|
/* Validate northbound callbacks. */
|
|
|
|
nb_validate_callbacks();
|
|
|
|
|
2017-12-07 20:31:48 +01:00
|
|
|
/* Create an empty running configuration. */
|
|
|
|
running_config = nb_config_new(NULL);
|
2019-04-18 16:55:52 +02:00
|
|
|
running_config_entries = hash_create(running_config_entry_key_make,
|
|
|
|
running_config_entry_cmp,
|
|
|
|
"Running Configuration Entries");
|
2019-02-02 21:03:15 +01:00
|
|
|
pthread_mutex_init(&running_config_mgmt_lock.mtx, NULL);
|
2017-12-07 20:31:48 +01:00
|
|
|
|
|
|
|
/* Initialize the northbound CLI. */
|
2018-12-06 23:37:05 +01:00
|
|
|
nb_cli_init(tm);
|
2023-10-30 10:09:19 +01:00
|
|
|
|
|
|
|
/* Initialize oper-state */
|
|
|
|
nb_oper_init(tm);
|
2017-12-07 20:31:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void nb_terminate(void)
|
|
|
|
{
|
2023-10-30 10:09:19 +01:00
|
|
|
nb_oper_terminate();
|
|
|
|
|
2017-12-07 20:31:48 +01:00
|
|
|
/* Terminate the northbound CLI. */
|
|
|
|
nb_cli_terminate();
|
|
|
|
|
|
|
|
/* Delete all nb_node's from all YANG modules. */
|
2018-11-13 23:36:48 +01:00
|
|
|
nb_nodes_delete();
|
2017-12-07 20:31:48 +01:00
|
|
|
|
|
|
|
/* Delete the running configuration. */
|
2023-03-21 13:54:21 +01:00
|
|
|
hash_clean_and_free(&running_config_entries, running_config_entry_free);
|
2017-12-07 20:31:48 +01:00
|
|
|
nb_config_free(running_config);
|
2019-02-02 21:03:15 +01:00
|
|
|
pthread_mutex_destroy(&running_config_mgmt_lock.mtx);
|
2017-12-07 20:31:48 +01:00
|
|
|
}
|