mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-04-30 09:56:23 +00:00
lib: introduce a read-write lock for northbound configurations
The upcoming gRPC-based northbound plugin will run on a separate pthread, and it will need to have access to the running configuration global variable. Introduce a rw-lock to control concurrent access to the running configuration. Add the lock inside the "nb_config" structure so that it can be used to protect candidate configurations as well (this might be necessary depending on the threading scheme of future northbound plugins). Signed-off-by: Renato Westphal <renato@opensourcerouting.org>
This commit is contained in:
parent
364ad673c8
commit
83981138fe
111
isisd/isis_cli.c
111
isisd/isis_cli.c
@ -188,10 +188,14 @@ DEFPY(ip_router_isis, ip_router_isis_cmd, "ip router isis WORD$tag",
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* check if the interface is a loopback and if so set it as passive */
|
/* check if the interface is a loopback and if so set it as passive */
|
||||||
ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false);
|
pthread_rwlock_rdlock(&running_config->lock);
|
||||||
if (ifp && if_is_loopback(ifp))
|
{
|
||||||
nb_cli_enqueue_change(vty, "./frr-isisd:isis/passive",
|
ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false);
|
||||||
NB_OP_MODIFY, "true");
|
if (ifp && if_is_loopback(ifp))
|
||||||
|
nb_cli_enqueue_change(vty, "./frr-isisd:isis/passive",
|
||||||
|
NB_OP_MODIFY, "true");
|
||||||
|
}
|
||||||
|
pthread_rwlock_unlock(&running_config->lock);
|
||||||
|
|
||||||
return nb_cli_apply_changes(vty, NULL);
|
return nb_cli_apply_changes(vty, NULL);
|
||||||
}
|
}
|
||||||
@ -258,10 +262,14 @@ DEFPY(ip6_router_isis, ip6_router_isis_cmd, "ipv6 router isis WORD$tag",
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* check if the interface is a loopback and if so set it as passive */
|
/* check if the interface is a loopback and if so set it as passive */
|
||||||
ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false);
|
pthread_rwlock_rdlock(&running_config->lock);
|
||||||
if (ifp && if_is_loopback(ifp))
|
{
|
||||||
nb_cli_enqueue_change(vty, "./frr-isisd:isis/passive",
|
ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false);
|
||||||
NB_OP_MODIFY, "true");
|
if (ifp && if_is_loopback(ifp))
|
||||||
|
nb_cli_enqueue_change(vty, "./frr-isisd:isis/passive",
|
||||||
|
NB_OP_MODIFY, "true");
|
||||||
|
}
|
||||||
|
pthread_rwlock_unlock(&running_config->lock);
|
||||||
|
|
||||||
return nb_cli_apply_changes(vty, NULL);
|
return nb_cli_apply_changes(vty, NULL);
|
||||||
}
|
}
|
||||||
@ -368,20 +376,26 @@ DEFPY(no_is_type, no_is_type_cmd,
|
|||||||
"Act as both a station router and an area router\n"
|
"Act as both a station router and an area router\n"
|
||||||
"Act as an area router only\n")
|
"Act as an area router only\n")
|
||||||
{
|
{
|
||||||
const char *value = NULL;
|
const char *value;
|
||||||
struct isis_area *area;
|
|
||||||
|
|
||||||
area = nb_running_get_entry(NULL, VTY_CURR_XPATH, false);
|
pthread_rwlock_rdlock(&running_config->lock);
|
||||||
|
{
|
||||||
|
struct isis_area *area;
|
||||||
|
|
||||||
|
area = nb_running_get_entry(NULL, VTY_CURR_XPATH, false);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Put the is-type back to defaults:
|
||||||
|
* - level-1-2 on first area
|
||||||
|
* - level-1 for the rest
|
||||||
|
*/
|
||||||
|
if (area && listgetdata(listhead(isis->area_list)) == area)
|
||||||
|
value = "level-1-2";
|
||||||
|
else
|
||||||
|
value = NULL;
|
||||||
|
}
|
||||||
|
pthread_rwlock_unlock(&running_config->lock);
|
||||||
|
|
||||||
/*
|
|
||||||
* Put the is-type back to defaults:
|
|
||||||
* - level-1-2 on first area
|
|
||||||
* - level-1 for the rest
|
|
||||||
*/
|
|
||||||
if (area && listgetdata(listhead(isis->area_list)) == area)
|
|
||||||
value = "level-1-2";
|
|
||||||
else
|
|
||||||
value = NULL;
|
|
||||||
nb_cli_enqueue_change(vty, "./is-type", NB_OP_MODIFY, value);
|
nb_cli_enqueue_change(vty, "./is-type", NB_OP_MODIFY, value);
|
||||||
|
|
||||||
return nb_cli_apply_changes(vty, NULL);
|
return nb_cli_apply_changes(vty, NULL);
|
||||||
@ -1769,52 +1783,45 @@ DEFPY(no_isis_circuit_type, no_isis_circuit_type_cmd,
|
|||||||
"Level-1-2 adjacencies are formed\n"
|
"Level-1-2 adjacencies are formed\n"
|
||||||
"Level-2 only adjacencies are formed\n")
|
"Level-2 only adjacencies are formed\n")
|
||||||
{
|
{
|
||||||
struct interface *ifp;
|
const char *circ_type = NULL;
|
||||||
struct isis_circuit *circuit;
|
|
||||||
int is_type;
|
|
||||||
const char *circ_type;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Default value depends on whether the circuit is part of an area,
|
* Default value depends on whether the circuit is part of an area,
|
||||||
* and the is-type of the area if there is one. So we need to do this
|
* and the is-type of the area if there is one. So we need to do this
|
||||||
* here.
|
* here.
|
||||||
*/
|
*/
|
||||||
ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false);
|
pthread_rwlock_rdlock(&running_config->lock);
|
||||||
if (!ifp)
|
{
|
||||||
goto def_val;
|
struct interface *ifp;
|
||||||
|
struct isis_circuit *circuit;
|
||||||
|
|
||||||
circuit = circuit_scan_by_ifp(ifp);
|
ifp = nb_running_get_entry(NULL, VTY_CURR_XPATH, false);
|
||||||
if (!circuit)
|
if (!ifp)
|
||||||
goto def_val;
|
goto unlock;
|
||||||
|
|
||||||
if (circuit->state == C_STATE_UP)
|
circuit = circuit_scan_by_ifp(ifp);
|
||||||
is_type = circuit->area->is_type;
|
if (!circuit || circuit->state != C_STATE_UP)
|
||||||
else
|
goto unlock;
|
||||||
goto def_val;
|
|
||||||
|
|
||||||
switch (is_type) {
|
switch (circuit->area->is_type) {
|
||||||
case IS_LEVEL_1:
|
case IS_LEVEL_1:
|
||||||
circ_type = "level-1";
|
circ_type = "level-1";
|
||||||
break;
|
break;
|
||||||
case IS_LEVEL_2:
|
case IS_LEVEL_2:
|
||||||
circ_type = "level-2";
|
circ_type = "level-2";
|
||||||
break;
|
break;
|
||||||
case IS_LEVEL_1_AND_2:
|
case IS_LEVEL_1_AND_2:
|
||||||
circ_type = "level-1-2";
|
circ_type = "level-1-2";
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
return CMD_ERR_NO_MATCH;
|
|
||||||
}
|
}
|
||||||
|
unlock:
|
||||||
|
pthread_rwlock_unlock(&running_config->lock);
|
||||||
|
|
||||||
nb_cli_enqueue_change(vty, "./frr-isisd:isis/circuit-type",
|
nb_cli_enqueue_change(vty, "./frr-isisd:isis/circuit-type",
|
||||||
NB_OP_MODIFY, circ_type);
|
NB_OP_MODIFY, circ_type);
|
||||||
|
|
||||||
return nb_cli_apply_changes(vty, NULL);
|
return nb_cli_apply_changes(vty, NULL);
|
||||||
|
|
||||||
def_val:
|
|
||||||
nb_cli_enqueue_change(vty, "./frr-isisd:isis/circuit-type",
|
|
||||||
NB_OP_MODIFY, NULL);
|
|
||||||
|
|
||||||
return nb_cli_apply_changes(vty, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cli_show_ip_isis_circ_type(struct vty *vty, struct lyd_node *dnode,
|
void cli_show_ip_isis_circ_type(struct vty *vty, struct lyd_node *dnode,
|
||||||
|
@ -1705,12 +1705,16 @@ static int vty_write_config(struct vty *vty)
|
|||||||
vty_out(vty, "frr defaults %s\n", DFLT_NAME);
|
vty_out(vty, "frr defaults %s\n", DFLT_NAME);
|
||||||
vty_out(vty, "!\n");
|
vty_out(vty, "!\n");
|
||||||
|
|
||||||
for (i = 0; i < vector_active(cmdvec); i++)
|
pthread_rwlock_rdlock(&running_config->lock);
|
||||||
if ((node = vector_slot(cmdvec, i)) && node->func
|
{
|
||||||
&& (node->vtysh || vty->type != VTY_SHELL)) {
|
for (i = 0; i < vector_active(cmdvec); i++)
|
||||||
if ((*node->func)(vty))
|
if ((node = vector_slot(cmdvec, i)) && node->func
|
||||||
vty_out(vty, "!\n");
|
&& (node->vtysh || vty->type != VTY_SHELL)) {
|
||||||
}
|
if ((*node->func)(vty))
|
||||||
|
vty_out(vty, "!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pthread_rwlock_unlock(&running_config->lock);
|
||||||
|
|
||||||
if (vty->type == VTY_TERM) {
|
if (vty->type == VTY_TERM) {
|
||||||
vty_out(vty, "end\n");
|
vty_out(vty, "end\n");
|
||||||
|
19
lib/if.c
19
lib/if.c
@ -187,18 +187,21 @@ void if_update_to_new_vrf(struct interface *ifp, vrf_id_t vrf_id)
|
|||||||
if (yang_module_find("frr-interface")) {
|
if (yang_module_find("frr-interface")) {
|
||||||
struct lyd_node *if_dnode;
|
struct lyd_node *if_dnode;
|
||||||
|
|
||||||
if_dnode = yang_dnode_get(
|
pthread_rwlock_wrlock(&running_config->lock);
|
||||||
running_config->dnode,
|
{
|
||||||
"/frr-interface:lib/interface[name='%s'][vrf='%s']/vrf",
|
if_dnode = yang_dnode_get(
|
||||||
ifp->name, old_vrf->name);
|
running_config->dnode,
|
||||||
if (if_dnode) {
|
"/frr-interface:lib/interface[name='%s'][vrf='%s']/vrf",
|
||||||
yang_dnode_change_leaf(if_dnode, vrf->name);
|
ifp->name, old_vrf->name);
|
||||||
running_config->version++;
|
if (if_dnode) {
|
||||||
|
yang_dnode_change_leaf(if_dnode, vrf->name);
|
||||||
|
running_config->version++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
pthread_rwlock_unlock(&running_config->lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Delete interface structure. */
|
/* Delete interface structure. */
|
||||||
void if_delete_retain(struct interface *ifp)
|
void if_delete_retain(struct interface *ifp)
|
||||||
{
|
{
|
||||||
|
@ -830,7 +830,12 @@ static int frr_config_read_in(struct thread *t)
|
|||||||
/*
|
/*
|
||||||
* Update the shared candidate after reading the startup configuration.
|
* Update the shared candidate after reading the startup configuration.
|
||||||
*/
|
*/
|
||||||
nb_config_replace(vty_shared_candidate_config, running_config, true);
|
pthread_rwlock_rdlock(&running_config->lock);
|
||||||
|
{
|
||||||
|
nb_config_replace(vty_shared_candidate_config, running_config,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
pthread_rwlock_unlock(&running_config->lock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
152
lib/northbound.c
152
lib/northbound.c
@ -264,6 +264,7 @@ struct nb_config *nb_config_new(struct lyd_node *dnode)
|
|||||||
else
|
else
|
||||||
config->dnode = yang_dnode_new(ly_native_ctx, true);
|
config->dnode = yang_dnode_new(ly_native_ctx, true);
|
||||||
config->version = 0;
|
config->version = 0;
|
||||||
|
pthread_rwlock_init(&config->lock, NULL);
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
@ -272,6 +273,7 @@ void nb_config_free(struct nb_config *config)
|
|||||||
{
|
{
|
||||||
if (config->dnode)
|
if (config->dnode)
|
||||||
yang_dnode_free(config->dnode);
|
yang_dnode_free(config->dnode);
|
||||||
|
pthread_rwlock_destroy(&config->lock);
|
||||||
XFREE(MTYPE_NB_CONFIG, config);
|
XFREE(MTYPE_NB_CONFIG, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,6 +284,7 @@ struct nb_config *nb_config_dup(const struct nb_config *config)
|
|||||||
dup = XCALLOC(MTYPE_NB_CONFIG, sizeof(*dup));
|
dup = XCALLOC(MTYPE_NB_CONFIG, sizeof(*dup));
|
||||||
dup->dnode = yang_dnode_dup(config->dnode);
|
dup->dnode = yang_dnode_dup(config->dnode);
|
||||||
dup->version = config->version;
|
dup->version = config->version;
|
||||||
|
pthread_rwlock_init(&dup->lock, NULL);
|
||||||
|
|
||||||
return dup;
|
return dup;
|
||||||
}
|
}
|
||||||
@ -529,17 +532,28 @@ int nb_candidate_edit(struct nb_config *candidate,
|
|||||||
|
|
||||||
bool nb_candidate_needs_update(const struct nb_config *candidate)
|
bool nb_candidate_needs_update(const struct nb_config *candidate)
|
||||||
{
|
{
|
||||||
if (candidate->version < running_config->version)
|
bool ret = false;
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
pthread_rwlock_rdlock(&running_config->lock);
|
||||||
|
{
|
||||||
|
if (candidate->version < running_config->version)
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
pthread_rwlock_unlock(&running_config->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int nb_candidate_update(struct nb_config *candidate)
|
int nb_candidate_update(struct nb_config *candidate)
|
||||||
{
|
{
|
||||||
struct nb_config *updated_config;
|
struct nb_config *updated_config;
|
||||||
|
|
||||||
updated_config = nb_config_dup(running_config);
|
pthread_rwlock_rdlock(&running_config->lock);
|
||||||
|
{
|
||||||
|
updated_config = nb_config_dup(running_config);
|
||||||
|
}
|
||||||
|
pthread_rwlock_unlock(&running_config->lock);
|
||||||
|
|
||||||
if (nb_config_merge(updated_config, candidate, true) != NB_OK)
|
if (nb_config_merge(updated_config, candidate, true) != NB_OK)
|
||||||
return NB_ERR;
|
return NB_ERR;
|
||||||
|
|
||||||
@ -591,9 +605,13 @@ int nb_candidate_validate(struct nb_config *candidate)
|
|||||||
return NB_ERR_VALIDATION;
|
return NB_ERR_VALIDATION;
|
||||||
|
|
||||||
RB_INIT(nb_config_cbs, &changes);
|
RB_INIT(nb_config_cbs, &changes);
|
||||||
nb_config_diff(running_config, candidate, &changes);
|
pthread_rwlock_rdlock(&running_config->lock);
|
||||||
ret = nb_candidate_validate_changes(candidate, &changes);
|
{
|
||||||
nb_config_diff_del_changes(&changes);
|
nb_config_diff(running_config, candidate, &changes);
|
||||||
|
ret = nb_candidate_validate_changes(candidate, &changes);
|
||||||
|
nb_config_diff_del_changes(&changes);
|
||||||
|
}
|
||||||
|
pthread_rwlock_unlock(&running_config->lock);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -613,26 +631,36 @@ int nb_candidate_commit_prepare(struct nb_config *candidate,
|
|||||||
}
|
}
|
||||||
|
|
||||||
RB_INIT(nb_config_cbs, &changes);
|
RB_INIT(nb_config_cbs, &changes);
|
||||||
nb_config_diff(running_config, candidate, &changes);
|
pthread_rwlock_rdlock(&running_config->lock);
|
||||||
if (RB_EMPTY(nb_config_cbs, &changes))
|
{
|
||||||
return NB_ERR_NO_CHANGES;
|
nb_config_diff(running_config, candidate, &changes);
|
||||||
|
if (RB_EMPTY(nb_config_cbs, &changes)) {
|
||||||
|
pthread_rwlock_unlock(&running_config->lock);
|
||||||
|
return NB_ERR_NO_CHANGES;
|
||||||
|
}
|
||||||
|
|
||||||
if (nb_candidate_validate_changes(candidate, &changes) != NB_OK) {
|
if (nb_candidate_validate_changes(candidate, &changes)
|
||||||
flog_warn(EC_LIB_NB_CANDIDATE_INVALID,
|
!= NB_OK) {
|
||||||
"%s: failed to validate candidate configuration",
|
flog_warn(
|
||||||
__func__);
|
EC_LIB_NB_CANDIDATE_INVALID,
|
||||||
nb_config_diff_del_changes(&changes);
|
"%s: failed to validate candidate configuration",
|
||||||
return NB_ERR_VALIDATION;
|
__func__);
|
||||||
}
|
nb_config_diff_del_changes(&changes);
|
||||||
|
pthread_rwlock_unlock(&running_config->lock);
|
||||||
|
return NB_ERR_VALIDATION;
|
||||||
|
}
|
||||||
|
|
||||||
*transaction =
|
*transaction = nb_transaction_new(candidate, &changes, client,
|
||||||
nb_transaction_new(candidate, &changes, client, user, comment);
|
user, comment);
|
||||||
if (*transaction == NULL) {
|
if (*transaction == NULL) {
|
||||||
flog_warn(EC_LIB_NB_TRANSACTION_CREATION_FAILED,
|
flog_warn(EC_LIB_NB_TRANSACTION_CREATION_FAILED,
|
||||||
"%s: failed to create transaction", __func__);
|
"%s: failed to create transaction", __func__);
|
||||||
nb_config_diff_del_changes(&changes);
|
nb_config_diff_del_changes(&changes);
|
||||||
return NB_ERR_LOCKED;
|
pthread_rwlock_unlock(&running_config->lock);
|
||||||
|
return NB_ERR_LOCKED;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
pthread_rwlock_unlock(&running_config->lock);
|
||||||
|
|
||||||
return nb_transaction_process(NB_EV_PREPARE, *transaction);
|
return nb_transaction_process(NB_EV_PREPARE, *transaction);
|
||||||
}
|
}
|
||||||
@ -651,7 +679,11 @@ void nb_candidate_commit_apply(struct nb_transaction *transaction,
|
|||||||
|
|
||||||
/* Replace running by candidate. */
|
/* Replace running by candidate. */
|
||||||
transaction->config->version++;
|
transaction->config->version++;
|
||||||
nb_config_replace(running_config, transaction->config, true);
|
pthread_rwlock_wrlock(&running_config->lock);
|
||||||
|
{
|
||||||
|
nb_config_replace(running_config, transaction->config, true);
|
||||||
|
}
|
||||||
|
pthread_rwlock_unlock(&running_config->lock);
|
||||||
|
|
||||||
/* Record transaction. */
|
/* Record transaction. */
|
||||||
if (save_transaction
|
if (save_transaction
|
||||||
@ -931,40 +963,52 @@ static int nb_transaction_process(enum nb_event event,
|
|||||||
{
|
{
|
||||||
struct nb_config_cb *cb;
|
struct nb_config_cb *cb;
|
||||||
|
|
||||||
RB_FOREACH (cb, nb_config_cbs, &transaction->changes) {
|
/*
|
||||||
struct nb_config_change *change = (struct nb_config_change *)cb;
|
* Need to lock the running configuration since transaction->changes
|
||||||
int ret;
|
* can contain pointers to data nodes from the running configuration.
|
||||||
|
*/
|
||||||
|
pthread_rwlock_rdlock(&running_config->lock);
|
||||||
|
{
|
||||||
|
RB_FOREACH (cb, nb_config_cbs, &transaction->changes) {
|
||||||
|
struct nb_config_change *change =
|
||||||
|
(struct nb_config_change *)cb;
|
||||||
|
int ret;
|
||||||
|
|
||||||
/*
|
|
||||||
* Only try to release resources that were allocated
|
|
||||||
* successfully.
|
|
||||||
*/
|
|
||||||
if (event == NB_EV_ABORT && change->prepare_ok == false)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Call the appropriate callback. */
|
|
||||||
ret = nb_callback_configuration(event, change);
|
|
||||||
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:
|
|
||||||
/*
|
/*
|
||||||
* At this point it's not possible to reject the
|
* Only try to release resources that were allocated
|
||||||
* transaction anymore, so any failure here can lead to
|
* successfully.
|
||||||
* inconsistencies and should be treated as a bug.
|
|
||||||
* Operations prone to errors, like validations and
|
|
||||||
* resource allocations, should be performed during the
|
|
||||||
* 'prepare' phase.
|
|
||||||
*/
|
*/
|
||||||
break;
|
if (event == NB_EV_ABORT && change->prepare_ok == false)
|
||||||
default:
|
break;
|
||||||
break;
|
|
||||||
|
/* Call the appropriate callback. */
|
||||||
|
ret = nb_callback_configuration(event, change);
|
||||||
|
switch (event) {
|
||||||
|
case NB_EV_PREPARE:
|
||||||
|
if (ret != NB_OK) {
|
||||||
|
pthread_rwlock_unlock(
|
||||||
|
&running_config->lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
change->prepare_ok = true;
|
||||||
|
break;
|
||||||
|
case NB_EV_ABORT:
|
||||||
|
case NB_EV_APPLY:
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pthread_rwlock_unlock(&running_config->lock);
|
||||||
|
|
||||||
return NB_OK;
|
return NB_OK;
|
||||||
}
|
}
|
||||||
|
@ -422,8 +422,19 @@ enum nb_client {
|
|||||||
|
|
||||||
/* Northbound configuration. */
|
/* Northbound configuration. */
|
||||||
struct nb_config {
|
struct nb_config {
|
||||||
|
/* Configuration data. */
|
||||||
struct lyd_node *dnode;
|
struct lyd_node *dnode;
|
||||||
|
|
||||||
|
/* Configuration version. */
|
||||||
uint32_t version;
|
uint32_t version;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Lock protecting this structure. The use of this lock is always
|
||||||
|
* necessary when reading or modifying the global running configuration.
|
||||||
|
* For candidate configurations, use of this lock is optional depending
|
||||||
|
* on the threading scheme of the northbound plugin.
|
||||||
|
*/
|
||||||
|
pthread_rwlock_t lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Northbound configuration callback. */
|
/* Northbound configuration callback. */
|
||||||
|
@ -193,8 +193,13 @@ int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...)
|
|||||||
"Please check the logs for more details.\n");
|
"Please check the logs for more details.\n");
|
||||||
|
|
||||||
/* Regenerate candidate for consistency. */
|
/* Regenerate candidate for consistency. */
|
||||||
nb_config_replace(vty->candidate_config, running_config,
|
pthread_rwlock_rdlock(&running_config->lock);
|
||||||
true);
|
{
|
||||||
|
nb_config_replace(vty->candidate_config,
|
||||||
|
running_config, true);
|
||||||
|
}
|
||||||
|
pthread_rwlock_unlock(&running_config->lock);
|
||||||
|
|
||||||
return CMD_WARNING_CONFIG_FAILED;
|
return CMD_WARNING_CONFIG_FAILED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -302,7 +307,12 @@ static int nb_cli_commit(struct vty *vty, bool force,
|
|||||||
|
|
||||||
/* "confirm" parameter. */
|
/* "confirm" parameter. */
|
||||||
if (confirmed_timeout) {
|
if (confirmed_timeout) {
|
||||||
vty->confirmed_commit_rollback = nb_config_dup(running_config);
|
pthread_rwlock_rdlock(&running_config->lock);
|
||||||
|
{
|
||||||
|
vty->confirmed_commit_rollback =
|
||||||
|
nb_config_dup(running_config);
|
||||||
|
}
|
||||||
|
pthread_rwlock_unlock(&running_config->lock);
|
||||||
|
|
||||||
vty->t_confirmed_commit_timeout = NULL;
|
vty->t_confirmed_commit_timeout = NULL;
|
||||||
thread_add_timer(master, nb_cli_confirmed_commit_timeout, vty,
|
thread_add_timer(master, nb_cli_confirmed_commit_timeout, vty,
|
||||||
@ -316,8 +326,13 @@ static int nb_cli_commit(struct vty *vty, bool force,
|
|||||||
/* Map northbound return code to CLI return code. */
|
/* Map northbound return code to CLI return code. */
|
||||||
switch (ret) {
|
switch (ret) {
|
||||||
case NB_OK:
|
case NB_OK:
|
||||||
nb_config_replace(vty->candidate_config_base, running_config,
|
pthread_rwlock_rdlock(&running_config->lock);
|
||||||
true);
|
{
|
||||||
|
nb_config_replace(vty->candidate_config_base,
|
||||||
|
running_config, true);
|
||||||
|
}
|
||||||
|
pthread_rwlock_unlock(&running_config->lock);
|
||||||
|
|
||||||
vty_out(vty,
|
vty_out(vty,
|
||||||
"%% Configuration committed successfully (Transaction ID #%u).\n\n",
|
"%% Configuration committed successfully (Transaction ID #%u).\n\n",
|
||||||
transaction_id);
|
transaction_id);
|
||||||
@ -682,7 +697,12 @@ DEFPY (config_update,
|
|||||||
return CMD_WARNING;
|
return CMD_WARNING;
|
||||||
}
|
}
|
||||||
|
|
||||||
nb_config_replace(vty->candidate_config_base, running_config, true);
|
pthread_rwlock_rdlock(&running_config->lock);
|
||||||
|
{
|
||||||
|
nb_config_replace(vty->candidate_config_base, running_config,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
pthread_rwlock_unlock(&running_config->lock);
|
||||||
|
|
||||||
vty_out(vty, "%% Candidate configuration updated successfully.\n\n");
|
vty_out(vty, "%% Candidate configuration updated successfully.\n\n");
|
||||||
|
|
||||||
@ -782,8 +802,12 @@ DEFPY (show_config_running,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nb_cli_show_config(vty, running_config, format, translator,
|
pthread_rwlock_rdlock(&running_config->lock);
|
||||||
!!with_defaults);
|
{
|
||||||
|
nb_cli_show_config(vty, running_config, format, translator,
|
||||||
|
!!with_defaults);
|
||||||
|
}
|
||||||
|
pthread_rwlock_unlock(&running_config->lock);
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
@ -897,57 +921,68 @@ DEFPY (show_config_compare,
|
|||||||
struct nb_config *config2, *config_transaction2 = NULL;
|
struct nb_config *config2, *config_transaction2 = NULL;
|
||||||
int ret = CMD_WARNING;
|
int ret = CMD_WARNING;
|
||||||
|
|
||||||
if (c1_candidate)
|
/*
|
||||||
config1 = vty->candidate_config;
|
* For simplicity, lock the running configuration regardless if it's
|
||||||
else if (c1_running)
|
* going to be used or not.
|
||||||
config1 = running_config;
|
*/
|
||||||
else {
|
pthread_rwlock_rdlock(&running_config->lock);
|
||||||
config_transaction1 = nb_db_transaction_load(c1_tid);
|
{
|
||||||
if (!config_transaction1) {
|
if (c1_candidate)
|
||||||
vty_out(vty, "%% Transaction %u does not exist\n\n",
|
config1 = vty->candidate_config;
|
||||||
(unsigned int)c1_tid);
|
else if (c1_running)
|
||||||
goto exit;
|
config1 = running_config;
|
||||||
|
else {
|
||||||
|
config_transaction1 = nb_db_transaction_load(c1_tid);
|
||||||
|
if (!config_transaction1) {
|
||||||
|
vty_out(vty,
|
||||||
|
"%% Transaction %u does not exist\n\n",
|
||||||
|
(unsigned int)c1_tid);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
config1 = config_transaction1;
|
||||||
}
|
}
|
||||||
config1 = config_transaction1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c2_candidate)
|
if (c2_candidate)
|
||||||
config2 = vty->candidate_config;
|
config2 = vty->candidate_config;
|
||||||
else if (c2_running)
|
else if (c2_running)
|
||||||
config2 = running_config;
|
config2 = running_config;
|
||||||
else {
|
else {
|
||||||
config_transaction2 = nb_db_transaction_load(c2_tid);
|
config_transaction2 = nb_db_transaction_load(c2_tid);
|
||||||
if (!config_transaction2) {
|
if (!config_transaction2) {
|
||||||
vty_out(vty, "%% Transaction %u does not exist\n\n",
|
vty_out(vty,
|
||||||
(unsigned int)c2_tid);
|
"%% Transaction %u does not exist\n\n",
|
||||||
goto exit;
|
(unsigned int)c2_tid);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
config2 = config_transaction2;
|
||||||
}
|
}
|
||||||
config2 = config_transaction2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (json)
|
if (json)
|
||||||
format = NB_CFG_FMT_JSON;
|
format = NB_CFG_FMT_JSON;
|
||||||
else if (xml)
|
else if (xml)
|
||||||
format = NB_CFG_FMT_XML;
|
format = NB_CFG_FMT_XML;
|
||||||
else
|
else
|
||||||
format = NB_CFG_FMT_CMDS;
|
format = NB_CFG_FMT_CMDS;
|
||||||
|
|
||||||
if (translator_family) {
|
if (translator_family) {
|
||||||
translator = yang_translator_find(translator_family);
|
translator = yang_translator_find(translator_family);
|
||||||
if (!translator) {
|
if (!translator) {
|
||||||
vty_out(vty, "%% Module translator \"%s\" not found\n",
|
vty_out(vty,
|
||||||
translator_family);
|
"%% Module translator \"%s\" not found\n",
|
||||||
goto exit;
|
translator_family);
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
ret = nb_cli_show_config_compare(vty, config1, config2, format,
|
ret = nb_cli_show_config_compare(vty, config1, config2, format,
|
||||||
translator);
|
translator);
|
||||||
exit:
|
exit:
|
||||||
if (config_transaction1)
|
if (config_transaction1)
|
||||||
nb_config_free(config_transaction1);
|
nb_config_free(config_transaction1);
|
||||||
if (config_transaction2)
|
if (config_transaction2)
|
||||||
nb_config_free(config_transaction2);
|
nb_config_free(config_transaction2);
|
||||||
|
}
|
||||||
|
pthread_rwlock_unlock(&running_config->lock);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -289,7 +289,11 @@ static int frr_confd_cdb_read_cb_prepare(int fd, int *subp, int reslen)
|
|||||||
struct cdb_iter_args iter_args;
|
struct cdb_iter_args iter_args;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
candidate = nb_config_dup(running_config);
|
pthread_rwlock_rdlock(&running_config->lock);
|
||||||
|
{
|
||||||
|
candidate = nb_config_dup(running_config);
|
||||||
|
}
|
||||||
|
pthread_rwlock_unlock(&running_config->lock);
|
||||||
|
|
||||||
/* Iterate over all configuration changes. */
|
/* Iterate over all configuration changes. */
|
||||||
iter_args.candidate = candidate;
|
iter_args.candidate = candidate;
|
||||||
|
@ -256,7 +256,11 @@ static int frr_sr_config_change_cb_verify(sr_session_ctx_t *session,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
candidate = nb_config_dup(running_config);
|
pthread_rwlock_rdlock(&running_config->lock);
|
||||||
|
{
|
||||||
|
candidate = nb_config_dup(running_config);
|
||||||
|
}
|
||||||
|
pthread_rwlock_unlock(&running_config->lock);
|
||||||
|
|
||||||
while ((ret = sr_get_change_next(session, it, &sr_op, &sr_old_val,
|
while ((ret = sr_get_change_next(session, it, &sr_op, &sr_old_val,
|
||||||
&sr_new_val))
|
&sr_new_val))
|
||||||
|
21
lib/vty.c
21
lib/vty.c
@ -2608,17 +2608,22 @@ int vty_config_enter(struct vty *vty, bool private_config, bool exclusive)
|
|||||||
vty->private_config = private_config;
|
vty->private_config = private_config;
|
||||||
vty->xpath_index = 0;
|
vty->xpath_index = 0;
|
||||||
|
|
||||||
if (private_config) {
|
pthread_rwlock_rdlock(&running_config->lock);
|
||||||
vty->candidate_config = nb_config_dup(running_config);
|
{
|
||||||
vty->candidate_config_base = nb_config_dup(running_config);
|
if (private_config) {
|
||||||
vty_out(vty,
|
vty->candidate_config = nb_config_dup(running_config);
|
||||||
"Warning: uncommitted changes will be discarded on exit.\n\n");
|
|
||||||
} else {
|
|
||||||
vty->candidate_config = vty_shared_candidate_config;
|
|
||||||
if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL)
|
|
||||||
vty->candidate_config_base =
|
vty->candidate_config_base =
|
||||||
nb_config_dup(running_config);
|
nb_config_dup(running_config);
|
||||||
|
vty_out(vty,
|
||||||
|
"Warning: uncommitted changes will be discarded on exit.\n\n");
|
||||||
|
} else {
|
||||||
|
vty->candidate_config = vty_shared_candidate_config;
|
||||||
|
if (frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL)
|
||||||
|
vty->candidate_config_base =
|
||||||
|
nb_config_dup(running_config);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
pthread_rwlock_unlock(&running_config->lock);
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user