mgmtd: Add MGMT Transaction Framework

This commit introduces the MGMT Transaction framework that takes
management requests from one (or more) frontend client sessions,
translates them into transactions and drives them to completion
in co-oridination with one (or more) backend client daemons
involved in the request.

This commit includes the following functionalities in the changeset:
1. Introduces the actual Transaction module. Commands added related to
   transaction are:
   a. show mgmt transaction all
2. Adds support for commit rollback feature which stores upto the 10
   commit buffers. Each commit has a commit-id which can be used to
   rollback to the exact configuration state.
   Commands supported for this feature are:
   a. show mgmt commit-history
   b. mgmt rollback commit-id COMMIT_ID
3. Add hidden commands to enable record various performance metrics:
   a. mgmt performance-measurement
   b. mgmt reset-statistic

Co-authored-by: Pushpasis Sarkar <pushpasis@gmail.com>
Co-authored-by: Abhinay Ramesh <rabhinay@vmware.com>
Co-authored-by: Ujwal P <ujwalp@vmware.com>
Signed-off-by: Yash Ranjan <ranjany@vmware.com>
This commit is contained in:
Yash Ranjan 2021-10-28 00:07:11 -07:00 committed by Christian Hopps
parent 7d65b7b7f4
commit 74335ceb27
21 changed files with 4075 additions and 262 deletions

View File

@ -82,6 +82,10 @@ ForEachMacros:
# ospfd
- LSDB_LOOP
# mgmtd
- FOREACH_CMT_REC
- FOREACH_TXN_CFG_BATCH_IN_LIST
- FOREACH_TXN_REQ_IN_LIST
- FOREACH_TXN_IN_LIST
- FOREACH_MGMTD_DB_ID
- FOREACH_ADAPTER_IN_LIST
- FOREACH_SESSION_IN_LIST

View File

@ -618,6 +618,8 @@ AC_ARG_ENABLE([bgpd],
AS_HELP_STRING([--disable-bgpd], [do not build bgpd]))
AC_ARG_ENABLE([mgmtd],
AS_HELP_STRING([--disable-mgmtd], [do not build mgmtd]))
AC_ARG_ENABLE([mgmtd_local_validations],
AS_HELP_STRING([--enable-mgmtd-local-validations], [dev: unimplemented local validation]))
AC_ARG_ENABLE([ripd],
AS_HELP_STRING([--disable-ripd], [do not build ripd]))
AC_ARG_ENABLE([ripngd],
@ -1732,6 +1734,11 @@ AS_IF([test "$enable_bgpd" != "no"], [
AS_IF([test "$enable_mgmtd" != "no"], [
AC_DEFINE([HAVE_MGMTD], [1], [mgmtd])
# Enable MGMTD local validations
AS_IF([test "$enable_mgmtd_local_validations" == "yes"], [
AC_DEFINE([MGMTD_LOCAL_VALIDATIONS_ENABLED], [1], [Enable mgmtd local validations.])
])
])
AS_IF([test "$enable_ripd" != "no"], [

View File

@ -943,11 +943,12 @@ int nb_candidate_update(struct nb_config *candidate)
* WARNING: lyd_validate() can change the configuration as part of the
* validation process.
*/
int nb_candidate_validate_yang(struct nb_config *candidate, char *errmsg,
size_t errmsg_len)
int nb_candidate_validate_yang(struct nb_config *candidate, bool no_state,
char *errmsg, size_t errmsg_len)
{
if (lyd_validate_all(&candidate->dnode, ly_native_ctx,
LYD_VALIDATE_NO_STATE, NULL)
no_state ? LYD_VALIDATE_NO_STATE :
LYD_VALIDATE_PRESENT, NULL)
!= 0) {
yang_print_errors(ly_native_ctx, errmsg, errmsg_len);
return NB_ERR_VALIDATION;
@ -1003,7 +1004,8 @@ int nb_candidate_diff_and_validate_yang(struct nb_context *context,
struct nb_config_cbs *changes,
char *errmsg, size_t errmsg_len)
{
if (nb_candidate_validate_yang(candidate, errmsg, sizeof(errmsg_len))
if (nb_candidate_validate_yang(candidate, true, errmsg,
sizeof(errmsg_len))
!= NB_OK)
return NB_ERR_VALIDATION;
@ -1042,7 +1044,7 @@ int nb_candidate_commit_prepare(struct nb_context context,
struct nb_config_cbs changes;
if (!skip_validate
&& nb_candidate_validate_yang(candidate, errmsg, errmsg_len)
&& nb_candidate_validate_yang(candidate, true, errmsg, errmsg_len)
!= NB_OK) {
flog_warn(EC_LIB_NB_CANDIDATE_INVALID,
"%s: failed to validate candidate configuration",

View File

@ -875,6 +875,19 @@ extern int nb_candidate_edit(struct nb_config *candidate,
const struct yang_data *previous,
const struct yang_data *data);
/*
* Create diff for configuration.
*
* dnode
* Pointer to a libyang data node containing the configuration data. If NULL
* is given, an empty configuration will be created.
*
* seq
* Returns sequence number assigned to the specific change.
*
* changes
* Northbound config callback head.
*/
extern void nb_config_diff_created(const struct lyd_node *dnode, uint32_t *seq,
struct nb_config_cbs *changes);
@ -889,25 +902,134 @@ extern void nb_config_diff_created(const struct lyd_node *dnode, uint32_t *seq,
*/
extern bool nb_candidate_needs_update(const struct nb_config *candidate);
/*
* Edit candidate configuration changes.
*
* candidate_config
* Candidate configuration to edit.
*
* cfg_changes
* Northbound config changes.
*
* num_cfg_changes
* Number of config changes.
*
* xpath_base
* Base xpath for config.
*
* curr_xpath
* Current xpath for config.
*
* xpath_index
* Index of xpath being processed.
*
* err_buf
* Buffer to store human-readable error message in case of error.
*
* err_bufsize
* Size of err_buf.
*
* error
* TRUE on error, FALSE on success
*/
extern 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, const char *curr_xpath,
int xpath_index, char *err_buf, int err_bufsize, bool *error);
/*
* Delete candidate configuration changes.
*
* changes
* Northbound config changes.
*/
extern void nb_config_diff_del_changes(struct nb_config_cbs *changes);
/*
* Create candidate diff and validate on yang tree
*
* context
* Context of the northbound transaction.
*
* candidate
* Candidate DB configuration.
*
* changes
* Northbound config changes.
*
* errmsg
* Buffer to store human-readable error message in case of error.
*
* errmsg_len
* Size of errmsg.
*
* Returns:
* NB_OK on success, NB_ERR_VALIDATION otherwise
*/
extern 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);
/*
* Calculate the delta between two different configurations.
*
* reference
* Running DB config changes to be compared against.
*
* incremental
* Candidate DB config changes that will be compared against reference.
*
* changes
* Will hold the final diff generated.
*
*/
extern void nb_config_diff(const struct nb_config *reference,
const struct nb_config *incremental,
struct nb_config_cbs *changes);
extern int nb_candidate_validate_yang(struct nb_config *candidate, char *errmsg,
size_t errmsg_len);
/*
* Perform YANG syntactic and semantic validation.
*
* WARNING: lyd_validate() can change the configuration as part of the
* validation process.
*
* candidate
* Candidate DB configuration.
*
* errmsg
* Buffer to store human-readable error message in case of error.
*
* errmsg_len
* Size of errmsg.
*
* Returns:
* NB_OK on success, NB_ERR_VALIDATION otherwise
*/
extern int nb_candidate_validate_yang(struct nb_config *candidate, bool no_state,
char *errmsg, size_t errmsg_len);
/*
* Perform code-level validation using the northbound callbacks.
*
* context
* Context of the northbound transaction.
*
* candidate
* Candidate DB configuration.
*
* changes
* Northbound config changes.
*
* errmsg
* Buffer to store human-readable error message in case of error.
*
* errmsg_len
* Size of errmsg.
*
* Returns:
* NB_OK on success, NB_ERR_VALIDATION otherwise
*/
extern int nb_candidate_validate_code(struct nb_context *context,
struct nb_config *candidate,
struct nb_config_cbs *changes,
@ -972,6 +1094,12 @@ extern int nb_candidate_validate(struct nb_context *context,
* nb_candidate_commit_abort() or committed using
* nb_candidate_commit_apply().
*
* skip_validate
* TRUE to skip commit validation, FALSE otherwise.
*
* ignore_zero_change
* TRUE to ignore if zero changes, FALSE otherwise.
*
* errmsg
* Buffer to store human-readable error message in case of error.
*

View File

@ -192,6 +192,12 @@ int nb_cli_apply_changes(struct vty *vty, const char *xpath_base_fmt, ...)
vsnprintf(xpath_base, sizeof(xpath_base), xpath_base_fmt, ap);
va_end(ap);
}
if (vty_mgmt_fe_enabled()) {
VTY_CHECK_XPATH;
return vty_mgmt_send_config_data(vty);
}
return nb_cli_apply_changes_internal(vty, xpath_base, false);
}
@ -208,6 +214,12 @@ int nb_cli_apply_changes_clear_pending(struct vty *vty,
vsnprintf(xpath_base, sizeof(xpath_base), xpath_base_fmt, ap);
va_end(ap);
}
if (vty_mgmt_fe_enabled()) {
VTY_CHECK_XPATH;
return vty_mgmt_send_config_data(vty);
}
return nb_cli_apply_changes_internal(vty, xpath_base, true);
}

View File

@ -395,7 +395,12 @@ struct lyd_node *yang_dnode_get(const struct lyd_node *dnode, const char *xpath)
xpath += 2;
if (lyd_find_xpath(dnode, xpath, &set)) {
assert(0); /* XXX replicates old libyang1 base code */
/*
* Commenting out the below assert failure as it crashes mgmtd
* when bad xpath is passed.
*
* assert(0); XXX replicates old libyang1 base code
*/
goto exit;
}
if (set->count == 0)

View File

@ -13,6 +13,7 @@
#include "mgmtd/mgmt_fe_server.h"
#include "mgmtd/mgmt_fe_adapter.h"
#include "mgmtd/mgmt_ds.h"
#include "mgmtd/mgmt_history.h"
#include "mgmtd/mgmt_memory.h"
bool mgmt_debug_be;
@ -49,6 +50,12 @@ void mgmt_init(void)
/* Initialize datastores */
mgmt_ds_init(mm);
/* Initialize history */
mgmt_history_init();
/* Initialize MGMTD Transaction module */
mgmt_txn_init(mm, mm->master);
/* Initialize the MGMTD Backend Adapter Module */
mgmt_be_adapter_init(mm->master);
@ -61,7 +68,7 @@ void mgmt_init(void)
/* Start the MGMTD Frontend Server for clients to connect */
mgmt_fe_server_init(mm->master);
/* MGMTD VTY commands installation. */
/* MGMTD VTY commands installation. */
mgmt_vty_init();
}
@ -71,5 +78,7 @@ void mgmt_terminate(void)
mgmt_fe_adapter_destroy();
mgmt_be_server_destroy();
mgmt_be_adapter_destroy();
mgmt_txn_destroy();
mgmt_history_destroy();
mgmt_ds_destroy();
}

View File

@ -10,21 +10,26 @@
#define _FRR_MGMTD_H
#include "vrf.h"
#include "defaults.h"
#include "stream.h"
#include "mgmtd/mgmt_memory.h"
#include "mgmtd/mgmt_defines.h"
#include "mgmtd/mgmt_history.h"
#include "mgmtd/mgmt_txn.h"
#include "mgmtd/mgmt_ds.h"
#define MGMTD_VTY_PORT 2622
#define MGMTD_SOCKET_BUF_SIZE 65535
#define MGMTD_MAX_COMMIT_LIST 10
extern bool mgmt_debug_be;
extern bool mgmt_debug_fe;
extern bool mgmt_debug_ds;
extern bool mgmt_debug_txn;
struct mgmt_txn_ctx;
/*
* MGMTD master for system wide configurations and variables.
*/
@ -34,6 +39,16 @@ struct mgmt_master {
/* How big should we set the socket buffer size */
uint32_t socket_buffer;
/* The single instance of config transaction allowed at any time */
struct mgmt_txns_head txn_list;
/* Map of Transactions and its ID */
struct hash *txn_hash;
uint64_t next_txn_id;
/* The single instance of config transaction allowed at any time */
struct mgmt_txn_ctx *cfg_txn;
/* Datastores */
struct mgmt_ds_ctx *running_ds;
struct mgmt_ds_ctx *candidate_ds;
@ -41,6 +56,9 @@ struct mgmt_master {
bool terminating; /* global flag that sigint terminate seen */
bool perf_stats_en; /* to enable performance stats measurement */
/* List of commit infos */
struct mgmt_cmt_infos_head cmts; /* List of last 10 commits executed. */
};
extern struct mgmt_master *mm;
@ -86,16 +104,12 @@ extern void mgmt_vty_init(void);
static inline char *mgmt_realtime_to_string(struct timeval *tv, char *buf,
size_t sz)
{
char tmp[50];
struct tm *lm;
lm = localtime((const time_t *)&tv->tv_sec);
if (lm) {
strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S", lm);
snprintf(buf, sz, "%s.%06lu", tmp,
(unsigned long int)tv->tv_usec);
}
struct tm tm;
size_t n;
localtime_r((const time_t *)&tv->tv_sec, &tm);
n = strftime(buf, sz, "%Y-%m-%dT%H:%M:%S", &tm);
snprintf(&buf[n], sz - n, ",%06u000", (unsigned int)tv->tv_usec);
return buf;
}

View File

@ -332,9 +332,9 @@ static void mgmt_be_adapter_disconnect(struct mgmt_be_client_adapter *adapter)
}
/*
* TODO: Notify about client disconnect for appropriate cleanup
* mgmt_txn_notify_be_adapter_conn(adapter, false);
* Notify about client disconnect for appropriate cleanup
*/
mgmt_txn_notify_be_adapter_conn(adapter, false);
if (adapter->id < MGMTD_BE_CLIENT_ID_MAX) {
mgmt_be_adapters_by_id[adapter->id] = NULL;
@ -402,12 +402,12 @@ mgmt_be_adapter_handle_msg(struct mgmt_be_client_adapter *adapter,
adapter->name,
be_msg->txn_reply->success ? "success" : "failure");
/*
* TODO: Forward the TXN_REPLY to txn module.
* mgmt_txn_notify_be_txn_reply(
* be_msg->txn_reply->txn_id,
* be_msg->txn_reply->create,
* be_msg->txn_reply->success, adapter);
* Forward the TXN_REPLY to txn module.
*/
mgmt_txn_notify_be_txn_reply(
be_msg->txn_reply->txn_id,
be_msg->txn_reply->create,
be_msg->txn_reply->success, adapter);
break;
case MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REPLY:
MGMTD_BE_ADAPTER_DBG(
@ -419,13 +419,13 @@ mgmt_be_adapter_handle_msg(struct mgmt_be_client_adapter *adapter,
? be_msg->cfg_data_reply->error_if_any
: "None");
/*
* TODO: Forward the CGFData-create reply to txn module.
* mgmt_txn_notify_be_cfgdata_reply(
* be_msg->cfg_data_reply->txn_id,
* be_msg->cfg_data_reply->batch_id,
* be_msg->cfg_data_reply->success,
* be_msg->cfg_data_reply->error_if_any, adapter);
* Forward the CGFData-create reply to txn module.
*/
mgmt_txn_notify_be_cfgdata_reply(
be_msg->cfg_data_reply->txn_id,
be_msg->cfg_data_reply->batch_id,
be_msg->cfg_data_reply->success,
be_msg->cfg_data_reply->error_if_any, adapter);
break;
case MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REPLY:
MGMTD_BE_ADAPTER_DBG(
@ -445,14 +445,15 @@ mgmt_be_adapter_handle_msg(struct mgmt_be_client_adapter *adapter,
be_msg->cfg_apply_reply->error_if_any
? be_msg->cfg_apply_reply->error_if_any
: "None");
/* TODO: Forward the CGFData-apply reply to txn module.
* mgmt_txn_notify_be_cfg_apply_reply(
* be_msg->cfg_apply_reply->txn_id,
* be_msg->cfg_apply_reply->success,
* (uint64_t *)be_msg->cfg_apply_reply->batch_ids,
* be_msg->cfg_apply_reply->n_batch_ids,
* be_msg->cfg_apply_reply->error_if_any, adapter);
/*
* Forward the CGFData-apply reply to txn module.
*/
mgmt_txn_notify_be_cfg_apply_reply(
be_msg->cfg_apply_reply->txn_id,
be_msg->cfg_apply_reply->success,
(uint64_t *)be_msg->cfg_apply_reply->batch_ids,
be_msg->cfg_apply_reply->n_batch_ids,
be_msg->cfg_apply_reply->error_if_any, adapter);
break;
case MGMTD__BE_MESSAGE__MESSAGE_GET_REPLY:
case MGMTD__BE_MESSAGE__MESSAGE_CFG_CMD_REPLY:
@ -899,27 +900,26 @@ static void mgmt_be_adapter_conn_init(struct thread *thread)
assert(adapter && adapter->conn_fd >= 0);
/*
* TODO: Check first if the current session can run a CONFIG
* Check first if the current session can run a CONFIG
* transaction or not. Reschedule if a CONFIG transaction
* from another session is already in progress.
*/
if (mgmt_config_txn_in_progress() != MGMTD_SESSION_ID_NONE) {
mgmt_be_adapter_register_event(adapter, MGMTD_BE_CONN_INIT);
return 0;
return;
}
*/
/*
* TODO: Notify TXN module to create a CONFIG transaction and
* download the CONFIGs identified for this new client.
* If the TXN module fails to initiate the CONFIG transaction
* disconnect from the client forcing a reconnect later.
* That should also take care of destroying the adapter.
*
/*
* Notify TXN module to create a CONFIG transaction and
* download the CONFIGs identified for this new client.
* If the TXN module fails to initiate the CONFIG transaction
* disconnect from the client forcing a reconnect later.
* That should also take care of destroying the adapter.
*/
if (mgmt_txn_notify_be_adapter_conn(adapter, true) != 0) {
mgmt_be_adapter_disconnect(adapter);
adapter = NULL;
}
*/
}
static void

View File

@ -20,6 +20,7 @@
#define MGMTD_MAX_NUM_XPATH_REG 128
#define MGMTD_MAX_NUM_DATA_REQ_IN_BATCH 32
#define MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH 8
enum mgmt_result {
MGMTD_SUCCESS = 0,
@ -54,4 +55,6 @@ enum mgmt_be_event {
#define MGMTD_TXN_ID_NONE 0
#define MGMTD_TXN_BATCH_ID_NONE 0
#endif /* _FRR_MGMTD_DEFINES_H */

View File

@ -11,6 +11,8 @@
#include "mgmtd/mgmt.h"
#include "mgmtd/mgmt_memory.h"
#include "mgmtd/mgmt_ds.h"
#include "mgmtd/mgmt_history.h"
#include "mgmtd/mgmt_txn.h"
#include "libyang/libyang.h"
#ifdef REDIRECT_DEBUG_TO_STDERR
@ -22,7 +24,7 @@
#define MGMTD_DS_DBG(fmt, ...) \
do { \
if (mgmt_debug_ds) \
zlog_debug("%s: " fmt, __func__, ##__VA_ARGS__); \
zlog_err("%s: " fmt, __func__, ##__VA_ARGS__); \
} while (0)
#define MGMTD_DS_ERR(fmt, ...) \
zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
@ -107,6 +109,14 @@ static int mgmt_ds_replace_dst_with_src_ds(struct mgmt_ds_ctx *src,
else
dst->root.dnode_root = dst_dnode;
if (src->ds_id == MGMTD_DS_CANDIDATE) {
/*
* Drop the changes in scratch-buffer.
*/
MGMTD_DS_DBG("Emptying Candidate Scratch buffer!");
nb_config_diff_del_changes(&src->root.cfg_root->cfg_chgs);
}
if (dst->ds_id == MGMTD_DS_RUNNING) {
if (ly_out_new_filepath(MGMTD_STARTUP_DS_FILE_PATH, &out)
== LY_SUCCESS)
@ -141,6 +151,14 @@ static int mgmt_ds_merge_src_with_dst_ds(struct mgmt_ds_ctx *src,
return ret;
}
if (src->ds_id == MGMTD_DS_CANDIDATE) {
/*
* Drop the changes in scratch-buffer.
*/
MGMTD_DS_DBG("Emptying Candidate Scratch buffer!");
nb_config_diff_del_changes(&src->root.cfg_root->cfg_chgs);
}
if (dst->ds_id == MGMTD_DS_RUNNING) {
if (ly_out_new_filepath(MGMTD_STARTUP_DS_FILE_PATH, &out)
== LY_SUCCESS)
@ -169,6 +187,17 @@ static int mgmt_ds_load_cfg_from_file(const char *filepath,
return 0;
}
void mgmt_ds_reset_candidate(void)
{
struct lyd_node *dnode = mm->candidate_ds->root.cfg_root->dnode;
if (dnode)
yang_dnode_free(dnode);
dnode = yang_dnode_new(ly_native_ctx, true);
mm->candidate_ds->root.cfg_root->dnode = dnode;
}
int mgmt_ds_init(struct mgmt_master *mm)
{
struct lyd_node *root;
@ -194,6 +223,12 @@ int mgmt_ds_init(struct mgmt_master *mm)
candidate.config_ds = true;
candidate.ds_id = MGMTD_DS_CANDIDATE;
/*
* Redirect lib/vty candidate-config datastore to the global candidate
* config Ds on the MGMTD process.
*/
vty_mgmt_candidate_config = candidate.root.cfg_root;
oper.root.dnode_root = yang_dnode_new(ly_native_ctx, true);
oper.config_ds = false;
oper.ds_id = MGMTD_DS_OPERATIONAL;
@ -208,7 +243,6 @@ int mgmt_ds_init(struct mgmt_master *mm)
void mgmt_ds_destroy(void)
{
/*
* TODO: Free the datastores.
*/
@ -277,21 +311,15 @@ int mgmt_ds_unlock(struct mgmt_ds_ctx *ds_ctx)
return 0;
}
int mgmt_ds_merge_dss(struct mgmt_ds_ctx *src_ds_ctx,
struct mgmt_ds_ctx *dst_ds_ctx, bool updt_cmt_rec)
{
if (mgmt_ds_merge_src_with_dst_ds(src_ds_ctx, dst_ds_ctx) != 0)
return -1;
return 0;
}
int mgmt_ds_copy_dss(struct mgmt_ds_ctx *src_ds_ctx,
struct mgmt_ds_ctx *dst_ds_ctx, bool updt_cmt_rec)
{
if (mgmt_ds_replace_dst_with_src_ds(src_ds_ctx, dst_ds_ctx) != 0)
return -1;
if (updt_cmt_rec && dst_ds_ctx->ds_id == MGMTD_DS_RUNNING)
mgmt_history_new_record(dst_ds_ctx);
return 0;
}
@ -377,14 +405,16 @@ static int mgmt_walk_ds_nodes(
num_left--;
}
/* If the base_xpath points to leaf node, we can skip the tree walk */
if (base_dnode->schema->nodetype & LYD_NODE_TERM)
/*
* If the base_xpath points to a leaf node, or we don't need to
* visit any children we can skip the tree walk.
*/
if (!childs_as_well || base_dnode->schema->nodetype & LYD_NODE_TERM)
return 0;
indx = 0;
LY_LIST_FOR (lyd_child(base_dnode), dnode) {
assert(dnode->schema && dnode->schema->priv);
nbnode = (struct nb_node *)dnode->schema->priv;
xpath = NULL;
if (xpaths) {
@ -407,9 +437,6 @@ static int mgmt_walk_ds_nodes(
assert(xpath);
MGMTD_DS_DBG(" -- XPATH: %s", xpath);
if (!childs_as_well)
continue;
if (num_nodes)
num_found = num_left;

View File

@ -13,6 +13,8 @@
#include "northbound.h"
#include "mgmtd/mgmt_defines.h"
#include "mgmtd/mgmt_be_adapter.h"
#include "mgmtd/mgmt_fe_adapter.h"
#define MGMTD_MAX_NUM_DSNODES_PER_BATCH 128
@ -35,16 +37,10 @@
#define MGMTD_COMMIT_INDEX_FILE_NAME DAEMON_DB_DIR "/commit-index.dat"
#define MGMTD_COMMIT_TIME_STR_LEN 100
struct mgmt_master;
extern struct nb_config *running_config;
struct mgmt_ds_ctx;
typedef void (*mgmt_ds_node_iter_fn)(uint64_t ds_hndl, char *xpath,
struct lyd_node *node,
struct nb_node *nb_node, void *ctx);
/***************************************************************
* Global data exported
***************************************************************/
@ -202,25 +198,6 @@ extern int mgmt_ds_write_lock(struct mgmt_ds_ctx *ds_ctx);
*/
extern int mgmt_ds_unlock(struct mgmt_ds_ctx *ds_ctx);
/*
* Merge two datastores.
*
* src_ds
* Source datastore handle.
*
* dst_ds
* Destination datastore handle.
*
* update_cmd_rec
* TRUE if need to update commit record, FALSE otherwise.
*
* Returns:
* 0 on success, -1 on failure.
*/
extern int mgmt_ds_merge_dss(struct mgmt_ds_ctx *src_ds_ctx,
struct mgmt_ds_ctx *dst_ds_ctx,
bool update_cmt_rec);
/*
* Copy from source to destination datastore.
*
@ -388,4 +365,10 @@ extern void mgmt_ds_status_write_one(struct vty *vty,
*/
extern void mgmt_ds_status_write(struct vty *vty);
/*
* Reset the candidate DS to empty state
*/
void mgmt_ds_reset_candidate(void);
#endif /* _FRR_MGMTD_DS_H_ */

View File

@ -180,10 +180,11 @@ mgmt_fe_session_cfg_txn_cleanup(struct mgmt_fe_session_ctx *session)
}
}
/* TODO: Destroy the actual transaction created earlier.
* if (session->cfg_txn_id != MGMTD_TXN_ID_NONE)
* mgmt_destroy_txn(&session->cfg_txn_id);
/*
* Destroy the actual transaction created earlier.
*/
if (session->cfg_txn_id != MGMTD_TXN_ID_NONE)
mgmt_destroy_txn(&session->cfg_txn_id);
}
static void
@ -200,10 +201,11 @@ mgmt_fe_session_show_txn_cleanup(struct mgmt_fe_session_ctx *session)
}
}
/* TODO: Destroy the transaction created recently.
* if (session->txn_id != MGMTD_TXN_ID_NONE)
* mgmt_destroy_txn(&session->txn_id);
/*
* Destroy the transaction created recently.
*/
if (session->txn_id != MGMTD_TXN_ID_NONE)
mgmt_destroy_txn(&session->txn_id);
}
static void
@ -687,9 +689,6 @@ mgmt_fe_session_register_event(struct mgmt_fe_session_ctx *session,
&tv, &session->proc_show_txn_clnp);
assert(session->proc_show_txn_clnp);
break;
default:
assert(!"mgmt_fe_adapter_post_event() called incorrectly");
break;
}
}
@ -834,7 +833,7 @@ static int
mgmt_fe_session_handle_setcfg_req_msg(struct mgmt_fe_session_ctx *session,
Mgmtd__FeSetConfigReq *setcfg_req)
{
/* uint64_t cfg_session_id; */
uint64_t cfg_session_id;
struct mgmt_ds_ctx *ds_ctx, *dst_ds_ctx;
if (mm->perf_stats_en)
@ -867,20 +866,20 @@ mgmt_fe_session_handle_setcfg_req_msg(struct mgmt_fe_session_ctx *session,
if (session->cfg_txn_id == MGMTD_TXN_ID_NONE) {
/*
* TODO: Check first if the current session can run a CONFIG
* Check first if the current session can run a CONFIG
* transaction or not. Report failure if a CONFIG transaction
* from another session is already in progress.
* cfg_session_id = mgmt_config_txn_in_progress();
* if (cfg_session_id != MGMTD_SESSION_ID_NONE
* && cfg_session_id != session->session_id) {
* mgmt_fe_send_setcfg_reply(
* session, setcfg_req->ds_id, setcfg_req->req_id,
* false,
* "Configuration already in-progress through a
*different user session!", setcfg_req->implicit_commit); goto
*mgmt_fe_sess_handle_setcfg_req_failed;
*}
*/
cfg_session_id = mgmt_config_txn_in_progress();
if (cfg_session_id != MGMTD_SESSION_ID_NONE
&& cfg_session_id != session->session_id) {
mgmt_fe_send_setcfg_reply(
session, setcfg_req->ds_id, setcfg_req->req_id,
false,
"Configuration already in-progress through a different user session!",
setcfg_req->implicit_commit);
goto mgmt_fe_sess_handle_setcfg_req_failed;
}
/*
@ -902,18 +901,18 @@ mgmt_fe_session_handle_setcfg_req_msg(struct mgmt_fe_session_ctx *session,
}
/*
* TODO: Start a CONFIG Transaction (if not started already)
* session->cfg_txn_id = mgmt_create_txn(session->session_id,
* MGMTD_TXN_TYPE_CONFIG);
* if (session->cfg_txn_id == MGMTD_SESSION_ID_NONE) {
* mgmt_fe_send_setcfg_reply(
* session, setcfg_req->ds_id, setcfg_req->req_id,
* false,
* "Failed to create a Configuration session!",
* setcfg_req->implicit_commit);
* goto mgmt_fe_sess_handle_setcfg_req_failed;
* }
* Start a CONFIG Transaction (if not started already)
*/
session->cfg_txn_id = mgmt_create_txn(session->session_id,
MGMTD_TXN_TYPE_CONFIG);
if (session->cfg_txn_id == MGMTD_SESSION_ID_NONE) {
mgmt_fe_send_setcfg_reply(
session, setcfg_req->ds_id, setcfg_req->req_id,
false,
"Failed to create a Configuration session!",
setcfg_req->implicit_commit);
goto mgmt_fe_sess_handle_setcfg_req_failed;
}
MGMTD_FE_ADAPTER_DBG(
"Created new Config Txn 0x%llx for session %p",
@ -950,36 +949,31 @@ mgmt_fe_session_handle_setcfg_req_msg(struct mgmt_fe_session_ctx *session,
}
}
/* TODO: Create the SETConfig request under the transaction.
* if (mgmt_txn_send_set_config_req(
* session->cfg_txn_id, setcfg_req->req_id, setcfg_req->ds_id,
* ds_ctx, setcfg_req->data, setcfg_req->n_data,
* setcfg_req->implicit_commit, setcfg_req->commit_ds_id,
* dst_ds_ctx)
* != 0) {
* mgmt_fe_send_setcfg_reply(
* session, setcfg_req->ds_id, setcfg_req->req_id, false,
* "Request processing for SET-CONFIG failed!",
* setcfg_req->implicit_commit);
* goto mgmt_fe_sess_handle_setcfg_req_failed;
* }
*
* For now send a failure reply.
/*
* Create the SETConfig request under the transaction.
*/
mgmt_fe_send_setcfg_reply(
session, setcfg_req->ds_id, setcfg_req->req_id, false,
"Request processing for SET-CONFIG failed!",
setcfg_req->implicit_commit);
goto mgmt_fe_sess_handle_setcfg_req_failed;
if (mgmt_txn_send_set_config_req(
session->cfg_txn_id, setcfg_req->req_id, setcfg_req->ds_id,
ds_ctx, setcfg_req->data, setcfg_req->n_data,
setcfg_req->implicit_commit, setcfg_req->commit_ds_id,
dst_ds_ctx)
!= 0) {
mgmt_fe_send_setcfg_reply(
session, setcfg_req->ds_id, setcfg_req->req_id, false,
"Request processing for SET-CONFIG failed!",
setcfg_req->implicit_commit);
goto mgmt_fe_sess_handle_setcfg_req_failed;
}
return 0;
mgmt_fe_sess_handle_setcfg_req_failed:
/* TODO: Delete transaction created recently.
* if (session->cfg_txn_id != MGMTD_TXN_ID_NONE)
* mgmt_destroy_txn(&session->cfg_txn_id);
/*
* Delete transaction created recently.
*/
if (session->cfg_txn_id != MGMTD_TXN_ID_NONE)
mgmt_destroy_txn(&session->cfg_txn_id);
if (ds_ctx && session->ds_write_locked[setcfg_req->ds_id])
mgmt_fe_session_unlock_ds(setcfg_req->ds_id, ds_ctx, session,
true, false);
@ -1042,22 +1036,17 @@ mgmt_fe_session_handle_getcfg_req_msg(struct mgmt_fe_session_ctx *session,
}
/*
* TODO: Start a SHOW Transaction (if not started already)
* session->txn_id = mgmt_create_txn(session->session_id,
* MGMTD_TXN_TYPE_SHOW);
* if (session->txn_id == MGMTD_SESSION_ID_NONE) {
* mgmt_fe_send_getcfg_reply(
* session, getcfg_req->ds_id, getcfg_req->req_id,
* false, NULL,
* "Failed to create a Show transaction!");
* goto mgmt_fe_sess_handle_getcfg_req_failed;
* }
* Start a SHOW Transaction (if not started already)
*/
mgmt_fe_send_getcfg_reply(
session, getcfg_req->ds_id, getcfg_req->req_id, false,
NULL, "Failed to create a Show transaction!");
goto mgmt_fe_sess_handle_getcfg_req_failed;
session->txn_id = mgmt_create_txn(session->session_id,
MGMTD_TXN_TYPE_SHOW);
if (session->txn_id == MGMTD_SESSION_ID_NONE) {
mgmt_fe_send_getcfg_reply(
session, getcfg_req->ds_id, getcfg_req->req_id,
false, NULL,
"Failed to create a Show transaction!");
goto mgmt_fe_sess_handle_getcfg_req_failed;
}
MGMTD_FE_ADAPTER_DBG(
"Created new Show Txn 0x%llx for session %p",
@ -1068,32 +1057,28 @@ mgmt_fe_session_handle_getcfg_req_msg(struct mgmt_fe_session_ctx *session,
(unsigned long long)session->txn_id, session);
}
/* TODO: Create a GETConfig request under the transaction.
* if (mgmt_txn_send_get_config_req(session->txn_id, getcfg_req->req_id,
* getcfg_req->ds_id, ds_ctx,
* getcfg_req->data, getcfg_req->n_data)
* != 0) {
* mgmt_fe_send_getcfg_reply(
* session, getcfg_req->ds_id, getcfg_req->req_id, false,
* NULL, "Request processing for GET-CONFIG failed!");
* goto mgmt_fe_sess_handle_getcfg_req_failed;
* }
*
* For now send back a failure reply.
/*
* Create a GETConfig request under the transaction.
*/
mgmt_fe_send_getcfg_reply(
session, getcfg_req->ds_id, getcfg_req->req_id, false, NULL,
"Request processing for GET-CONFIG failed!");
goto mgmt_fe_sess_handle_getcfg_req_failed;
if (mgmt_txn_send_get_config_req(session->txn_id, getcfg_req->req_id,
getcfg_req->ds_id, ds_ctx,
getcfg_req->data, getcfg_req->n_data)
!= 0) {
mgmt_fe_send_getcfg_reply(
session, getcfg_req->ds_id, getcfg_req->req_id, false,
NULL, "Request processing for GET-CONFIG failed!");
goto mgmt_fe_sess_handle_getcfg_req_failed;
}
return 0;
mgmt_fe_sess_handle_getcfg_req_failed:
/* TODO: Destroy the transaction created recently.
* if (session->txn_id != MGMTD_TXN_ID_NONE)
* mgmt_destroy_txn(&session->txn_id);
/*
* Destroy the transaction created recently.
*/
if (session->txn_id != MGMTD_TXN_ID_NONE)
mgmt_destroy_txn(&session->txn_id);
if (ds_ctx && session->ds_read_locked[getcfg_req->ds_id])
mgmt_fe_session_unlock_ds(getcfg_req->ds_id, ds_ctx, session,
false, true);
@ -1142,23 +1127,17 @@ mgmt_fe_session_handle_getdata_req_msg(struct mgmt_fe_session_ctx *session,
}
/*
* TODO: Start a SHOW Transaction (if not started already)
* session->txn_id =
* mgmt_create_txn(session->session_id,
* MGMTD_TXN_TYPE_SHOW);
* if (session->txn_id == MGMTD_SESSION_ID_NONE) {
* mgmt_fe_send_getdata_reply(
* session, getdata_req->ds_id, getdata_req->req_id,
* false, NULL,
* "Failed to create a Show transaction!");
* goto mgmt_fe_sess_handle_getdata_req_failed;
* }
* Start a SHOW Transaction (if not started already)
*/
mgmt_fe_send_getdata_reply(
session, getdata_req->ds_id, getdata_req->req_id, false,
NULL, "Failed to create a Show transaction!");
goto mgmt_fe_sess_handle_getdata_req_failed;
session->txn_id = mgmt_create_txn(session->session_id,
MGMTD_TXN_TYPE_SHOW);
if (session->txn_id == MGMTD_SESSION_ID_NONE) {
mgmt_fe_send_getdata_reply(
session, getdata_req->ds_id, getdata_req->req_id,
false, NULL,
"Failed to create a Show transaction!");
goto mgmt_fe_sess_handle_getdata_req_failed;
}
MGMTD_FE_ADAPTER_DBG(
"Created new Show Txn 0x%llx for session %p",
@ -1169,32 +1148,28 @@ mgmt_fe_session_handle_getdata_req_msg(struct mgmt_fe_session_ctx *session,
(unsigned long long)session->txn_id, session);
}
/* TODO: Create a GETData request under the transaction.
* if (mgmt_txn_send_get_data_req(session->txn_id, getdata_req->req_id,
* getdata_req->ds_id, ds_ctx,
* getdata_req->data, getdata_req->n_data)
* != 0) {
* mgmt_fe_send_getdata_reply(
* session, getdata_req->ds_id, getdata_req->req_id, false,
* NULL, "Request processing for GET-CONFIG failed!");
* goto mgmt_fe_sess_handle_getdata_req_failed;
* }
*
* For now send back a failure reply.
/*
* Create a GETData request under the transaction.
*/
mgmt_fe_send_getdata_reply(
session, getdata_req->ds_id, getdata_req->req_id, false, NULL,
"Request processing for GET-CONFIG failed!");
goto mgmt_fe_sess_handle_getdata_req_failed;
if (mgmt_txn_send_get_data_req(session->txn_id, getdata_req->req_id,
getdata_req->ds_id, ds_ctx,
getdata_req->data, getdata_req->n_data)
!= 0) {
mgmt_fe_send_getdata_reply(
session, getdata_req->ds_id, getdata_req->req_id, false,
NULL, "Request processing for GET-CONFIG failed!");
goto mgmt_fe_sess_handle_getdata_req_failed;
}
return 0;
mgmt_fe_sess_handle_getdata_req_failed:
/* TODO: Destroy the transaction created recently.
* if (session->txn_id != MGMTD_TXN_ID_NONE)
* mgmt_destroy_txn(&session->txn_id);
/*
* Destroy the transaction created recently.
*/
if (session->txn_id != MGMTD_TXN_ID_NONE)
mgmt_destroy_txn(&session->txn_id);
if (ds_ctx && session->ds_read_locked[getdata_req->ds_id])
mgmt_fe_session_unlock_ds(getdata_req->ds_id, ds_ctx,
@ -1256,25 +1231,19 @@ static int mgmt_fe_session_handle_commit_config_req_msg(
if (session->cfg_txn_id == MGMTD_TXN_ID_NONE) {
/*
* TODO: Start a CONFIG Transaction (if not started already)
* session->cfg_txn_id = mgmt_create_txn(session->session_id,
* MGMTD_TXN_TYPE_CONFIG);
* if (session->cfg_txn_id == MGMTD_SESSION_ID_NONE) {
* mgmt_fe_send_commitcfg_reply(
* session, commcfg_req->src_ds_id,
* commcfg_req->dst_ds_id, commcfg_req->req_id,
* MGMTD_INTERNAL_ERROR,
* commcfg_req->validate_only,
* "Failed to create a Configuration session!");
* return 0;
* }
* Start a CONFIG Transaction (if not started already)
*/
mgmt_fe_send_commitcfg_reply(
session, commcfg_req->src_ds_id, commcfg_req->dst_ds_id,
commcfg_req->req_id, MGMTD_INTERNAL_ERROR,
commcfg_req->validate_only,
"Failed to create a Configuration session!");
return 0;
session->cfg_txn_id = mgmt_create_txn(session->session_id,
MGMTD_TXN_TYPE_CONFIG);
if (session->cfg_txn_id == MGMTD_SESSION_ID_NONE) {
mgmt_fe_send_commitcfg_reply(
session, commcfg_req->src_ds_id,
commcfg_req->dst_ds_id, commcfg_req->req_id,
MGMTD_INTERNAL_ERROR,
commcfg_req->validate_only,
"Failed to create a Configuration session!");
return 0;
}
}
@ -1297,28 +1266,22 @@ static int mgmt_fe_session_handle_commit_config_req_msg(
session->ds_locked_implict[commcfg_req->dst_ds_id] = true;
}
/* TODO: Create COMMITConfig request under the transaction
* if (mgmt_txn_send_commit_config_req(
* session->cfg_txn_id, commcfg_req->req_id,
* commcfg_req->src_ds_id, src_ds_ctx, commcfg_req->dst_ds_id,
* dst_ds_ctx, commcfg_req->validate_only, commcfg_req->abort,
* false)
* != 0) {
* mgmt_fe_send_commitcfg_reply(
* session, commcfg_req->src_ds_id, commcfg_req->dst_ds_id,
* commcfg_req->req_id, MGMTD_INTERNAL_ERROR,
* commcfg_req->validate_only,
* "Request processing for COMMIT-CONFIG failed!");
* return 0;
* }
*
* For now due to lack of txn modules send a unsuccessfull reply.
/*
* Create COMMITConfig request under the transaction
*/
mgmt_fe_send_commitcfg_reply(
session, commcfg_req->src_ds_id, commcfg_req->dst_ds_id,
commcfg_req->req_id, MGMTD_INTERNAL_ERROR,
commcfg_req->validate_only,
"Request processing for COMMIT-CONFIG failed!");
if (mgmt_txn_send_commit_config_req(
session->cfg_txn_id, commcfg_req->req_id,
commcfg_req->src_ds_id, src_ds_ctx, commcfg_req->dst_ds_id,
dst_ds_ctx, commcfg_req->validate_only, commcfg_req->abort,
false)
!= 0) {
mgmt_fe_send_commitcfg_reply(
session, commcfg_req->src_ds_id, commcfg_req->dst_ds_id,
commcfg_req->req_id, MGMTD_INTERNAL_ERROR,
commcfg_req->validate_only,
"Request processing for COMMIT-CONFIG failed!");
return 0;
}
return 0;
}

356
mgmtd/mgmt_history.c Normal file
View File

@ -0,0 +1,356 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2021 Vmware, Inc.
* Pushpasis Sarkar <spushpasis@vmware.com>
* Copyright (c) 2023, LabN Consulting, L.L.C.
*/
#include <zebra.h>
#include "md5.h"
#include "thread.h"
#include "xref.h"
#include "mgmt_fe_client.h"
#include "mgmtd/mgmt.h"
#include "mgmtd/mgmt_ds.h"
#include "mgmtd/mgmt_history.h"
struct mgmt_cmt_info_t {
struct mgmt_cmt_infos_item cmts;
char cmtid_str[MGMTD_MD5_HASH_STR_HEX_LEN];
char time_str[MGMTD_COMMIT_TIME_STR_LEN];
char cmt_json_file[PATH_MAX];
};
DECLARE_DLIST(mgmt_cmt_infos, struct mgmt_cmt_info_t, cmts);
#define FOREACH_CMT_REC(mm, cmt_info) \
frr_each_safe (mgmt_cmt_infos, &mm->cmts, cmt_info)
static bool mgmt_history_record_exists(char *file_path)
{
int exist;
exist = access(file_path, F_OK);
if (exist == 0)
return true;
else
return false;
}
static void mgmt_history_remove_file(char *name)
{
if (remove(name) == 0)
zlog_debug("Old commit info deletion succeeded");
else
zlog_err("Old commit info deletion failed");
}
static void mgmt_history_hash(const char *input_str, char *hash)
{
int i;
unsigned char digest[MGMTD_MD5_HASH_LEN];
MD5_CTX ctx;
memset(&ctx, 0, sizeof(ctx));
MD5Init(&ctx);
MD5Update(&ctx, input_str, strlen(input_str));
MD5Final(digest, &ctx);
for (i = 0; i < MGMTD_MD5_HASH_LEN; i++)
snprintf(&hash[i * 2], MGMTD_MD5_HASH_STR_HEX_LEN, "%02x",
(unsigned int)digest[i]);
}
static struct mgmt_cmt_info_t *mgmt_history_create_cmt_rec(void)
{
struct mgmt_cmt_info_t *new;
struct mgmt_cmt_info_t *cmt_info;
struct mgmt_cmt_info_t *last_cmt_info = NULL;
struct timeval cmt_recd_tv;
new = XCALLOC(MTYPE_MGMTD_CMT_INFO, sizeof(struct mgmt_cmt_info_t));
gettimeofday(&cmt_recd_tv, NULL);
mgmt_realtime_to_string(&cmt_recd_tv, new->time_str,
sizeof(new->time_str));
mgmt_history_hash(new->time_str, new->cmtid_str);
snprintf(new->cmt_json_file, sizeof(new->cmt_json_file),
MGMTD_COMMIT_FILE_PATH, new->cmtid_str);
if (mgmt_cmt_infos_count(&mm->cmts) == MGMTD_MAX_COMMIT_LIST) {
FOREACH_CMT_REC (mm, cmt_info)
last_cmt_info = cmt_info;
if (last_cmt_info) {
mgmt_history_remove_file(last_cmt_info->cmt_json_file);
mgmt_cmt_infos_del(&mm->cmts, last_cmt_info);
XFREE(MTYPE_MGMTD_CMT_INFO, last_cmt_info);
}
}
mgmt_cmt_infos_add_head(&mm->cmts, new);
return new;
}
static struct mgmt_cmt_info_t *mgmt_history_find_cmt_record(const char *cmtid_str)
{
struct mgmt_cmt_info_t *cmt_info;
FOREACH_CMT_REC (mm, cmt_info) {
if (strncmp(cmt_info->cmtid_str, cmtid_str,
MGMTD_MD5_HASH_STR_HEX_LEN) == 0)
return cmt_info;
}
return NULL;
}
static bool mgmt_history_read_cmt_record_index(void)
{
FILE *fp;
struct mgmt_cmt_info_t cmt_info;
struct mgmt_cmt_info_t *new;
int cnt = 0;
fp = fopen(MGMTD_COMMIT_INDEX_FILE_NAME, "rb");
if (!fp) {
zlog_err("Failed to open file %s rb mode",
MGMTD_COMMIT_INDEX_FILE_NAME);
return false;
}
while ((fread(&cmt_info, sizeof(cmt_info), 1, fp)) > 0) {
if (cnt < MGMTD_MAX_COMMIT_LIST) {
if (!mgmt_history_record_exists(cmt_info.cmt_json_file)) {
zlog_err(
"Commit record present in index_file, but commit file %s missing",
cmt_info.cmt_json_file);
continue;
}
new = XCALLOC(MTYPE_MGMTD_CMT_INFO,
sizeof(struct mgmt_cmt_info_t));
memcpy(new, &cmt_info, sizeof(struct mgmt_cmt_info_t));
mgmt_cmt_infos_add_tail(&mm->cmts, new);
} else {
zlog_err("More records found in index file %s",
MGMTD_COMMIT_INDEX_FILE_NAME);
return false;
}
cnt++;
}
fclose(fp);
return true;
}
static bool mgmt_history_dump_cmt_record_index(void)
{
FILE *fp;
int ret = 0;
struct mgmt_cmt_info_t *cmt_info;
struct mgmt_cmt_info_t cmt_info_set[10];
int cnt = 0;
mgmt_history_remove_file((char *)MGMTD_COMMIT_INDEX_FILE_NAME);
fp = fopen(MGMTD_COMMIT_INDEX_FILE_NAME, "ab");
if (!fp) {
zlog_err("Failed to open file %s ab mode",
MGMTD_COMMIT_INDEX_FILE_NAME);
return false;
}
FOREACH_CMT_REC (mm, cmt_info) {
memcpy(&cmt_info_set[cnt], cmt_info,
sizeof(struct mgmt_cmt_info_t));
cnt++;
}
if (!cnt) {
fclose(fp);
return false;
}
ret = fwrite(&cmt_info_set, sizeof(struct mgmt_cmt_info_t), cnt, fp);
fclose(fp);
if (ret != cnt) {
zlog_err("Write record failed");
return false;
} else {
return true;
}
}
static int mgmt_history_rollback_to_cmt(struct vty *vty,
struct mgmt_cmt_info_t *cmt_info,
bool skip_file_load)
{
struct mgmt_ds_ctx *src_ds_ctx;
struct mgmt_ds_ctx *dst_ds_ctx;
int ret = 0;
src_ds_ctx = mgmt_ds_get_ctx_by_id(mm, MGMTD_DS_CANDIDATE);
if (!src_ds_ctx) {
vty_out(vty, "ERROR: Couldnot access Candidate datastore!\n");
return -1;
}
/*
* Note: Write lock on src_ds is not required. This is already
* taken in 'conf te'.
*/
dst_ds_ctx = mgmt_ds_get_ctx_by_id(mm, MGMTD_DS_RUNNING);
if (!dst_ds_ctx) {
vty_out(vty, "ERROR: Couldnot access Running datastore!\n");
return -1;
}
ret = mgmt_ds_write_lock(dst_ds_ctx);
if (ret != 0) {
vty_out(vty,
"Failed to lock the DS %u for rollback Reason: %s!\n",
MGMTD_DS_RUNNING, strerror(ret));
return -1;
}
if (!skip_file_load) {
ret = mgmt_ds_load_config_from_file(
src_ds_ctx, cmt_info->cmt_json_file, false);
if (ret != 0) {
mgmt_ds_unlock(dst_ds_ctx);
vty_out(vty,
"Error with parsing the file with error code %d\n",
ret);
return ret;
}
}
/* Internally trigger a commit-request. */
ret = mgmt_txn_rollback_trigger_cfg_apply(src_ds_ctx, dst_ds_ctx);
if (ret != 0) {
mgmt_ds_unlock(dst_ds_ctx);
vty_out(vty,
"Error with creating commit apply txn with error code %d\n",
ret);
return ret;
}
mgmt_history_dump_cmt_record_index();
return 0;
}
int mgmt_history_rollback_by_id(struct vty *vty, const char *cmtid_str)
{
int ret = 0;
struct mgmt_cmt_info_t *cmt_info;
if (!mgmt_cmt_infos_count(&mm->cmts) ||
!mgmt_history_find_cmt_record(cmtid_str)) {
vty_out(vty, "Invalid commit Id\n");
return -1;
}
FOREACH_CMT_REC (mm, cmt_info) {
if (strncmp(cmt_info->cmtid_str, cmtid_str,
MGMTD_MD5_HASH_STR_HEX_LEN) == 0) {
ret = mgmt_history_rollback_to_cmt(vty, cmt_info, false);
return ret;
}
mgmt_history_remove_file(cmt_info->cmt_json_file);
mgmt_cmt_infos_del(&mm->cmts, cmt_info);
XFREE(MTYPE_MGMTD_CMT_INFO, cmt_info);
}
return 0;
}
int mgmt_history_rollback_n(struct vty *vty, int num_cmts)
{
int ret = 0;
int cnt = 0;
struct mgmt_cmt_info_t *cmt_info;
size_t cmts;
if (!num_cmts)
num_cmts = 1;
cmts = mgmt_cmt_infos_count(&mm->cmts);
if ((int)cmts < num_cmts) {
vty_out(vty,
"Number of commits found (%d) less than required to rollback\n",
(int)cmts);
return -1;
}
if ((int)cmts == 1 || (int)cmts == num_cmts) {
vty_out(vty,
"Number of commits found (%d), Rollback of last commit is not supported\n",
(int)cmts);
return -1;
}
FOREACH_CMT_REC (mm, cmt_info) {
if (cnt == num_cmts) {
ret = mgmt_history_rollback_to_cmt(vty, cmt_info, false);
return ret;
}
cnt++;
mgmt_history_remove_file(cmt_info->cmt_json_file);
mgmt_cmt_infos_del(&mm->cmts, cmt_info);
XFREE(MTYPE_MGMTD_CMT_INFO, cmt_info);
}
if (!mgmt_cmt_infos_count(&mm->cmts)) {
mgmt_ds_reset_candidate();
ret = mgmt_history_rollback_to_cmt(vty, cmt_info, true);
}
return ret;
}
void show_mgmt_cmt_history(struct vty *vty)
{
struct mgmt_cmt_info_t *cmt_info;
int slno = 0;
vty_out(vty, "Last 10 commit history:\n");
vty_out(vty, " Sl.No\tCommit-ID(HEX)\t\t\t Commit-Record-Time\n");
FOREACH_CMT_REC (mm, cmt_info) {
vty_out(vty, " %d\t%s %s\n", slno, cmt_info->cmtid_str,
cmt_info->time_str);
slno++;
}
}
void mgmt_history_new_record(struct mgmt_ds_ctx *ds_ctx)
{
struct mgmt_cmt_info_t *cmt_info = mgmt_history_create_cmt_rec();
mgmt_ds_dump_ds_to_file(cmt_info->cmt_json_file, ds_ctx);
mgmt_history_dump_cmt_record_index();
}
void mgmt_history_init(void)
{
/* Create commit record for previously stored commit-apply */
mgmt_cmt_infos_init(&mm->cmts);
mgmt_history_read_cmt_record_index();
}
void mgmt_history_destroy(void)
{
struct mgmt_cmt_info_t *cmt_info;
FOREACH_CMT_REC(mm, cmt_info) {
mgmt_cmt_infos_del(&mm->cmts, cmt_info);
XFREE(MTYPE_MGMTD_CMT_INFO, cmt_info);
}
mgmt_cmt_infos_fini(&mm->cmts);
}

55
mgmtd/mgmt_history.h Normal file
View File

@ -0,0 +1,55 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2021 Vmware, Inc.
* Pushpasis Sarkar <spushpasis@vmware.com>
* Copyright (c) 2023, LabN Consulting, L.L.C.
*
*/
#ifndef _FRR_MGMTD_HISTORY_H_
#define _FRR_MGMTD_HISTORY_H_
#include "vrf.h"
PREDECL_DLIST(mgmt_cmt_infos);
struct mgmt_ds_ctx;
/*
* Rollback specific commit from commit history.
*
* vty
* VTY context.
*
* cmtid_str
* Specific commit id from commit history.
*
* Returns:
* 0 on success, -1 on failure.
*/
extern int mgmt_history_rollback_by_id(struct vty *vty, const char *cmtid_str);
/*
* Rollback n commits from commit history.
*
* vty
* VTY context.
*
* num_cmts
* Number of commits to be rolled back.
*
* Returns:
* 0 on success, -1 on failure.
*/
extern int mgmt_history_rollback_n(struct vty *vty, int num_cmts);
/*
* Show mgmt commit history.
*/
extern void show_mgmt_cmt_history(struct vty *vty);
extern void mgmt_history_new_record(struct mgmt_ds_ctx *ds_ctx);
extern void mgmt_history_destroy(void);
extern void mgmt_history_init(void);
#endif /* _FRR_MGMTD_HISTORY_H_ */

View File

@ -22,3 +22,15 @@ DEFINE_MTYPE(MGMTD, MGMTD, "MGMTD instance");
DEFINE_MTYPE(MGMTD, MGMTD_BE_ADPATER, "MGMTD backend adapter");
DEFINE_MTYPE(MGMTD, MGMTD_FE_ADPATER, "MGMTD Frontend adapter");
DEFINE_MTYPE(MGMTD, MGMTD_FE_SESSION, "MGMTD Frontend Client Session");
DEFINE_MTYPE(MGMTD, MGMTD_TXN, "MGMTD Transaction");
DEFINE_MTYPE(MGMTD, MGMTD_TXN_REQ, "MGMTD Transaction Requests");
DEFINE_MTYPE(MGMTD, MGMTD_TXN_SETCFG_REQ,
"MGMTD Transaction Set-Config Requests");
DEFINE_MTYPE(MGMTD, MGMTD_TXN_COMMCFG_REQ,
"MGMTD Transaction Commit-Config Requests");
DEFINE_MTYPE(MGMTD, MGMTD_TXN_GETDATA_REQ,
"MGMTD Transaction Get-Data Requests");
DEFINE_MTYPE(MGMTD, MGMTD_TXN_GETDATA_REPLY,
"MGMTD Transaction Get-Data Replies");
DEFINE_MTYPE(MGMTD, MGMTD_TXN_CFG_BATCH, "MGMTD Transaction Gonfig Batches");
DEFINE_MTYPE(MGMTD, MGMTD_CMT_INFO, "MGMTD commit info for tracking commits");

View File

@ -16,4 +16,13 @@ DECLARE_MTYPE(MGMTD);
DECLARE_MTYPE(MGMTD_BE_ADPATER);
DECLARE_MTYPE(MGMTD_FE_ADPATER);
DECLARE_MTYPE(MGMTD_FE_SESSION);
DECLARE_MTYPE(MGMTD_TXN);
DECLARE_MTYPE(MGMTD_TXN_REQ);
DECLARE_MTYPE(MGMTD_TXN_SETCFG_REQ);
DECLARE_MTYPE(MGMTD_TXN_COMMCFG_REQ);
DECLARE_MTYPE(MGMTD_TXN_GETDATA_REQ);
DECLARE_MTYPE(MGMTD_TXN_GETDATA_REPLY);
DECLARE_MTYPE(MGMTD_TXN_CFG_BATCH);
DECLARE_MTYPE(MGMTD_BE_ADAPTER_MSG_BUF);
DECLARE_MTYPE(MGMTD_CMT_INFO);
#endif /* _FRR_MGMTD_MEMORY_H */

2875
mgmtd/mgmt_txn.c Normal file

File diff suppressed because it is too large Load Diff

267
mgmtd/mgmt_txn.h Normal file
View File

@ -0,0 +1,267 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* MGMTD Transactions
*
* Copyright (C) 2021 Vmware, Inc.
* Pushpasis Sarkar <spushpasis@vmware.com>
*/
#ifndef _FRR_MGMTD_TXN_H_
#define _FRR_MGMTD_TXN_H_
#include "mgmtd/mgmt_be_adapter.h"
#include "mgmtd/mgmt.h"
#include "mgmtd/mgmt_ds.h"
#define MGMTD_TXN_PROC_DELAY_MSEC 5
#define MGMTD_TXN_PROC_DELAY_USEC 10
#define MGMTD_TXN_MAX_NUM_SETCFG_PROC 128
#define MGMTD_TXN_MAX_NUM_GETCFG_PROC 128
#define MGMTD_TXN_MAX_NUM_GETDATA_PROC 128
#define MGMTD_TXN_SEND_CFGVALIDATE_DELAY_MSEC 100
#define MGMTD_TXN_SEND_CFGAPPLY_DELAY_MSEC 100
#define MGMTD_TXN_CFG_COMMIT_MAX_DELAY_MSEC 30000 /* 30 seconds */
#define MGMTD_TXN_CLEANUP_DELAY_MSEC 100
#define MGMTD_TXN_CLEANUP_DELAY_USEC 10
/*
* The following definition enables local validation of config
* on the MGMTD process by loading client-defined NB callbacks
* and calling them locally before sening CNFG_APPLY_REQ to
* backend for actual apply of configuration on internal state
* of the backend application.
*
* #define MGMTD_LOCAL_VALIDATIONS_ENABLED
*
* Note: Enabled by default in configure.ac, if this needs to be
* disabled then pass --enable-mgmtd-local-validations=no to
* the list of arguments passed to ./configure
*/
PREDECL_LIST(mgmt_txns);
struct mgmt_master;
enum mgmt_txn_type {
MGMTD_TXN_TYPE_NONE = 0,
MGMTD_TXN_TYPE_CONFIG,
MGMTD_TXN_TYPE_SHOW
};
static inline const char *mgmt_txn_type2str(enum mgmt_txn_type type)
{
switch (type) {
case MGMTD_TXN_TYPE_NONE:
return "None";
case MGMTD_TXN_TYPE_CONFIG:
return "CONFIG";
case MGMTD_TXN_TYPE_SHOW:
return "SHOW";
}
return "Unknown";
}
/* Initialise transaction module. */
extern int mgmt_txn_init(struct mgmt_master *cm, struct thread_master *tm);
/* Destroy the transaction module. */
extern void mgmt_txn_destroy(void);
/*
* Check if transaction is in progress.
*
* Returns:
* session ID if in-progress, MGMTD_SESSION_ID_NONE otherwise.
*/
extern uint64_t mgmt_config_txn_in_progress(void);
/*
* Create transaction.
*
* session_id
* Session ID.
*
* type
* Transaction type (CONFIG/SHOW/NONE)
*
* Returns:
* transaction ID.
*/
extern uint64_t mgmt_create_txn(uint64_t session_id, enum mgmt_txn_type type);
/*
* Destroy transaction.
*
* txn_id
* Unique transaction identifier.
*/
extern void mgmt_destroy_txn(uint64_t *txn_id);
/*
* Check if transaction is valid given an ID.
*/
extern bool mgmt_txn_id_is_valid(uint64_t txn_id);
/*
* Returns the type of transaction given an ID.
*/
extern enum mgmt_txn_type mgmt_get_txn_type(uint64_t txn_id);
/*
* Send set-config request to be processed later in transaction.
*
* txn_id
* Unique transaction identifier.
*
* req_id
* Unique transaction request identifier.
*
* ds_id
* Datastore ID.
*
* ds_hndl
* Datastore handle.
*
* cfg_req
* Config requests.
*
* num_req
* Number of config requests.
*
* implicit_commit
* TRUE if the commit is implicit, FALSE otherwise.
*
* dst_ds_id
* Destination datastore ID.
*
* dst_ds_handle
* Destination datastore handle.
*
* Returns:
* 0 on success, -1 on failures.
*/
extern int mgmt_txn_send_set_config_req(uint64_t txn_id, uint64_t req_id,
Mgmtd__DatastoreId ds_id,
struct mgmt_ds_ctx *ds_ctx,
Mgmtd__YangCfgDataReq **cfg_req,
size_t num_req, bool implicit_commit,
Mgmtd__DatastoreId dst_ds_id,
struct mgmt_ds_ctx *dst_ds_ctx);
/*
* Send commit-config request to be processed later in transaction.
*
* txn_id
* Unique transaction identifier.
*
* req_id
* Unique transaction request identifier.
*
* src_ds_id
* Source datastore ID.
*
* src_ds_hndl
* Source Datastore handle.
*
* validate_only
* TRUE if commit request needs to be validated only, FALSE otherwise.
*
* abort
* TRUE if need to restore Src DS back to Dest DS, FALSE otherwise.
*
* implicit
* TRUE if the commit is implicit, FALSE otherwise.
*
* Returns:
* 0 on success, -1 on failures.
*/
extern int mgmt_txn_send_commit_config_req(uint64_t txn_id, uint64_t req_id,
Mgmtd__DatastoreId src_ds_id,
struct mgmt_ds_ctx *dst_ds_ctx,
Mgmtd__DatastoreId dst_ds_id,
struct mgmt_ds_ctx *src_ds_ctx,
bool validate_only, bool abort,
bool implicit);
extern int mgmt_txn_send_commit_config_reply(uint64_t txn_id,
enum mgmt_result result,
const char *error_if_any);
/*
* Send get-config request to be processed later in transaction.
*
* Similar to set-config request.
*/
extern int mgmt_txn_send_get_config_req(uint64_t txn_id, uint64_t req_id,
Mgmtd__DatastoreId ds_id,
struct mgmt_ds_ctx *ds_ctx,
Mgmtd__YangGetDataReq **data_req,
size_t num_reqs);
/*
* Send get-data request to be processed later in transaction.
*
* Similar to get-config request, but here data is fetched from backedn client.
*/
extern int mgmt_txn_send_get_data_req(uint64_t txn_id, uint64_t req_id,
Mgmtd__DatastoreId ds_id,
struct mgmt_ds_ctx *ds_ctx,
Mgmtd__YangGetDataReq **data_req,
size_t num_reqs);
/*
* Notifiy backend adapter on connection.
*/
extern int
mgmt_txn_notify_be_adapter_conn(struct mgmt_be_client_adapter *adapter,
bool connect);
/*
* Reply to backend adapter about transaction create/delete.
*/
extern int
mgmt_txn_notify_be_txn_reply(uint64_t txn_id, bool create, bool success,
struct mgmt_be_client_adapter *adapter);
/*
* Reply to backend adapater with config data create request.
*/
extern int
mgmt_txn_notify_be_cfgdata_reply(uint64_t txn_id, uint64_t batch_id,
bool success, char *error_if_any,
struct mgmt_be_client_adapter *adapter);
/*
* Reply to backend adapater with config data validate request.
*/
extern int mgmt_txn_notify_be_cfg_validate_reply(
uint64_t txn_id, bool success, uint64_t batch_ids[],
size_t num_batch_ids, char *error_if_any,
struct mgmt_be_client_adapter *adapter);
/*
* Reply to backend adapater with config data apply request.
*/
extern int
mgmt_txn_notify_be_cfg_apply_reply(uint64_t txn_id, bool success,
uint64_t batch_ids[],
size_t num_batch_ids, char *error_if_any,
struct mgmt_be_client_adapter *adapter);
/*
* Dump transaction status to vty.
*/
extern void mgmt_txn_status_write(struct vty *vty);
/*
* Trigger rollback config apply.
*
* Creates a new transaction and commit request for rollback.
*/
extern int
mgmt_txn_rollback_trigger_cfg_apply(struct mgmt_ds_ctx *src_ds_ctx,
struct mgmt_ds_ctx *dst_ds_ctx);
#endif /* _FRR_MGMTD_TXN_H_ */

View File

@ -16,6 +16,7 @@
#include "mgmtd/mgmt_fe_server.h"
#include "mgmtd/mgmt_fe_adapter.h"
#include "mgmtd/mgmt_ds.h"
#include "mgmtd/mgmt_history.h"
#include "mgmtd/mgmt_vty_clippy.c"
@ -57,6 +58,45 @@ DEFPY(show_mgmt_fe_adapter, show_mgmt_fe_adapter_cmd,
return CMD_SUCCESS;
}
DEFPY_HIDDEN(mgmt_performance_measurement,
mgmt_performance_measurement_cmd,
"[no] mgmt performance-measurement",
NO_STR
MGMTD_STR
"Enable performance measurement\n")
{
if (no)
mgmt_fe_adapter_perf_measurement(vty, false);
else
mgmt_fe_adapter_perf_measurement(vty, true);
return CMD_SUCCESS;
}
DEFPY(mgmt_reset_performance_stats,
mgmt_reset_performance_stats_cmd,
"mgmt reset-statistics",
MGMTD_STR
"Reset the Performance measurement statistics\n")
{
mgmt_fe_adapter_reset_perf_stats(vty);
return CMD_SUCCESS;
}
DEFPY(show_mgmt_txn,
show_mgmt_txn_cmd,
"show mgmt transaction all",
SHOW_STR
MGMTD_STR
MGMTD_TXN_STR
"Display all Transactions\n")
{
mgmt_txn_status_write(vty);
return CMD_SUCCESS;
}
DEFPY(show_mgmt_ds,
show_mgmt_ds_cmd,
"show mgmt datastore [all|candidate|operational|running]$dsname",
@ -309,6 +349,43 @@ DEFPY(mgmt_save_config,
return CMD_SUCCESS;
}
DEFPY(show_mgmt_cmt_hist,
show_mgmt_cmt_hist_cmd,
"show mgmt commit-history",
SHOW_STR
MGMTD_STR
"Show commit history\n")
{
show_mgmt_cmt_history(vty);
return CMD_SUCCESS;
}
DEFPY(mgmt_rollback,
mgmt_rollback_cmd,
"mgmt rollback <commit-id WORD$commit | last [(1-10)]$last>",
MGMTD_STR
"Rollback commits\n"
"Rollback to commit ID\n"
"Commit-ID\n"
"Rollbak n commits\n"
"Number of commits\n")
{
if (commit)
mgmt_history_rollback_by_id(vty, commit);
else
mgmt_history_rollback_n(vty, last);
return CMD_SUCCESS;
}
static int config_write_mgmt_debug(struct vty *vty);
static struct cmd_node debug_node = {
.name = "debug",
.node = DEBUG_NODE,
.prompt = "",
.config_write = config_write_mgmt_debug,
};
static int config_write_mgmt_debug(struct vty *vty)
{
int n = mgmt_debug_be + mgmt_debug_fe + mgmt_debug_ds + mgmt_debug_txn;
@ -333,12 +410,6 @@ static int config_write_mgmt_debug(struct vty *vty)
return 0;
}
static struct cmd_node debug_node = {
.name = "debug",
.node = DEBUG_NODE,
.prompt = "",
.config_write = config_write_mgmt_debug,
};
DEFPY(debug_mgmt,
debug_mgmt_cmd,
@ -388,22 +459,29 @@ void mgmt_vty_init(void)
install_element(VIEW_NODE, &show_mgmt_be_adapter_cmd);
install_element(VIEW_NODE, &show_mgmt_be_xpath_reg_cmd);
install_element(VIEW_NODE, &show_mgmt_fe_adapter_cmd);
install_element(VIEW_NODE, &show_mgmt_txn_cmd);
install_element(VIEW_NODE, &show_mgmt_ds_cmd);
install_element(VIEW_NODE, &show_mgmt_get_config_cmd);
install_element(VIEW_NODE, &show_mgmt_get_data_cmd);
install_element(VIEW_NODE, &show_mgmt_dump_data_cmd);
install_element(VIEW_NODE, &show_mgmt_map_xpath_cmd);
install_element(VIEW_NODE, &show_mgmt_cmt_hist_cmd);
install_element(CONFIG_NODE, &mgmt_commit_cmd);
install_element(CONFIG_NODE, &mgmt_set_config_data_cmd);
install_element(CONFIG_NODE, &mgmt_delete_config_data_cmd);
install_element(CONFIG_NODE, &mgmt_load_config_cmd);
install_element(CONFIG_NODE, &mgmt_save_config_cmd);
install_element(CONFIG_NODE, &mgmt_rollback_cmd);
install_element(VIEW_NODE, &debug_mgmt_cmd);
install_element(CONFIG_NODE, &debug_mgmt_cmd);
/* Enable view */
install_element(ENABLE_NODE, &mgmt_performance_measurement_cmd);
install_element(ENABLE_NODE, &mgmt_reset_performance_stats_cmd);
/*
* TODO: Register and handlers for auto-completion here (if any).
* TODO: Register and handlers for auto-completion here.
*/
}

View File

@ -22,7 +22,9 @@ mgmtd_libmgmtd_a_SOURCES = \
mgmtd/mgmt_be_adapter.c \
mgmtd/mgmt_fe_server.c \
mgmtd/mgmt_fe_adapter.c \
mgmtd/mgmt_history.c \
mgmtd/mgmt_memory.c \
mgmtd/mgmt_txn.c \
mgmtd/mgmt_vty.c \
# end
@ -38,7 +40,9 @@ noinst_HEADERS += \
mgmtd/mgmt_ds.h \
mgmtd/mgmt_fe_server.h \
mgmtd/mgmt_fe_adapter.h \
mgmtd/mgmt_history.h \
mgmtd/mgmt_memory.h \
mgmtd/mgmt_txn.h \
# end
sbin_PROGRAMS += mgmtd/mgmtd