]> git.puffer.fish Git - matthieu/frr.git/commitdiff
mgmtd: Add MGMT Transaction Framework
authorYash Ranjan <ranjany@vmware.com>
Thu, 28 Oct 2021 07:07:11 +0000 (00:07 -0700)
committerChristian Hopps <chopps@labn.net>
Wed, 6 Apr 2022 01:41:23 +0000 (21:41 -0400)
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>
19 files changed:
.clang-format
configure.ac
lib/northbound.c
lib/northbound.h
lib/northbound_cli.c
lib/yang.c
mgmtd/mgmt.c
mgmtd/mgmt.h
mgmtd/mgmt_bcknd_adapter.c
mgmtd/mgmt_db.c
mgmtd/mgmt_db.h
mgmtd/mgmt_defines.h
mgmtd/mgmt_frntnd_adapter.c
mgmtd/mgmt_memory.c
mgmtd/mgmt_memory.h
mgmtd/mgmt_trxn.c [new file with mode: 0644]
mgmtd/mgmt_trxn.h [new file with mode: 0644]
mgmtd/mgmt_vty.c
mgmtd/subdir.am

index 097be710885d77cc2a3ca68dd7242a538bd1ec83..a024531b11a8bc11a882e7fbbe24624d73193f06 100644 (file)
@@ -82,6 +82,10 @@ ForEachMacros:
   # ospfd
   - LSDB_LOOP
   # mgmtd
+  - FOREACH_CMT_REC
+  - FOREACH_TRXN_CFG_BATCH_IN_LIST
+  - FOREACH_TRXN_REQ_IN_LIST
+  - FOREACH_TRXN_IN_LIST
   - FOREACH_MGMTD_DB_ID
   - FOREACH_ADPTR_IN_LIST
   - FOREACH_SESSN_IN_LIST
index 1f5f9fff314a315c3de51c669d8997581bed3af5..129b2ecb67f4112e678c6af57adccd0480ebfc6e 100644 (file)
@@ -1713,6 +1713,11 @@ AS_IF([test "$enable_mgmtd" != "no"], [
   ])
 
   AC_DEFINE([HAVE_MGMTD], [1], [mgmtd])
+
+  # Enable MGMTD local validations
+  AS_IF([test "$enable_mgmtd_local_validations" != "no"], [
+    AC_DEFINE([MGMTD_LOCAL_VALIDATIONS_ENABLED], [1], [Enable mgmtd local validations.])
+  ])
 ])
 
 AS_IF([test "$enable_ripd" != "no"], [
index 8927e4fc9b3e080f7e98dfb84e7c5be2228351d1..e02493409547708d675a2a78e4280b2772361ab4 100644 (file)
@@ -933,11 +933,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;
@@ -993,7 +994,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;
 
@@ -1032,7 +1034,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",
index 60613730fdb01cc224219d511f527266df1a69c1..3666eb39f635bcb0dccd9e0e67ef7721ef510c46 100644 (file)
@@ -896,6 +896,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);
 
@@ -910,25 +923,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,
@@ -993,6 +1115,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.
  *
index 69ebda78b30b1bf76f7617076a7f69e445039ad1..7dc4d9481bb815c6c3c2f6e2f4e4071e133c86f4 100644 (file)
@@ -207,6 +207,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_frntnd_enabled()) {
+               VTY_CHECK_XPATH;
+               return vty_mgmt_send_config_data(vty);
+       }
+
        return nb_cli_apply_changes_internal(vty, xpath_base, false);
 }
 
@@ -223,6 +229,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_frntnd_enabled()) {
+               VTY_CHECK_XPATH;
+               return vty_mgmt_send_config_data(vty);
+       }
+
        return nb_cli_apply_changes_internal(vty, xpath_base, true);
 }
 
index ef1cf898aaf4fec6347430e0a1f520a8fece8c65..adf9f7bd18affec510f37049bf54ff1121c35a30 100644 (file)
@@ -407,7 +407,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)
index ba3fb4f13aed60aca2e999a589d7ae9386f1cc7c..ecdde8f4a26474ec55cd02808c8f2884dbb1d76f 100644 (file)
@@ -61,6 +61,9 @@ void mgmt_init(void)
        /* Initialize databases */
        mgmt_db_init(mm);
 
+       /* Initialize MGMTD Transaction module */
+       mgmt_trxn_init(mm, mm->master);
+
        /* Initialize the MGMTD Backend Adapter Module */
        mgmt_bcknd_adapter_init(mm->master);
 
@@ -73,7 +76,7 @@ void mgmt_init(void)
        /* Start the MGMTD Frontend Server for clients to connect */
        mgmt_frntnd_server_init(mm->master);
 
-       /* MGMTD VTY commands installation.  */
+       /* MGMTD VTY commands installation. */
        mgmt_vty_init();
 }
 
@@ -83,5 +86,6 @@ void mgmt_terminate(void)
        mgmt_frntnd_adapter_destroy();
        mgmt_bcknd_server_destroy();
        mgmt_bcknd_adapter_destroy();
+       mgmt_trxn_destroy();
        mgmt_db_destroy();
 }
index c694e6bb918f4445f8669c9d523cca58fe141c61..22db5658665c9b2de4dfd661a5eb2479289e34e3 100644 (file)
 #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_trxn.h"
 #include "mgmtd/mgmt_db.h"
 
 #define MGMTD_VTY_PORT 2622
 #define MGMTD_SOCKET_BUF_SIZE 65535
+#define MGMTD_MAX_COMMIT_LIST 10
 
 extern bool mgmt_debug_bcknd;
 extern bool mgmt_debug_frntnd;
 extern bool mgmt_debug_db;
 extern bool mgmt_debug_trxn;
 
+struct mgmt_trxn_ctxt;
+
 /*
  * MGMTD master for system wide configurations and variables.
  */
@@ -45,6 +49,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_trxn_list_head trxn_list;
+
+       /* Map of Transactions and its ID */
+       struct hash *trxn_hash;
+       uint64_t next_trxn_id;
+
+       /* The single instance of config transaction allowed at any time */
+       struct mgmt_trxn_ctxt *cfg_trxn;
+
        /* Databases */
        struct mgmt_db_ctxt *running_db;
        struct mgmt_db_ctxt *candidate_db;
@@ -52,6 +66,10 @@ 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_info_dlist_head
+               cmt_dlist; /* List of last 10 commits executed. */
 };
 
 extern struct mgmt_master *mm;
index 03bed86c131138bf6227f5da01776d2dba4fe422..3176b078cb4bebabaf8bdcfe9c807d50bc1dec03 100644 (file)
@@ -341,9 +341,9 @@ mgmt_bcknd_adapter_disconnect(struct mgmt_bcknd_client_adapter *adptr)
        }
 
        /*
-        * TODO: Notify about client disconnect for appropriate cleanup
-        * mgmt_trxn_notify_bcknd_adapter_conn(adptr, false);
+        * Notify about client disconnect for appropriate cleanup
         */
+       mgmt_trxn_notify_bcknd_adapter_conn(adptr, false);
 
        if (adptr->id < MGMTD_BCKND_CLIENT_ID_MAX) {
                mgmt_bcknd_adptrs_by_id[adptr->id] = NULL;
@@ -411,12 +411,12 @@ mgmt_bcknd_adapter_handle_msg(struct mgmt_bcknd_client_adapter *adptr,
                        adptr->name,
                        bcknd_msg->trxn_reply->success ? "success" : "failure");
                /*
-                * TODO: Forward the TRXN_REPLY to trxn module.
-                * mgmt_trxn_notify_bcknd_trxn_reply(
-                *      bcknd_msg->trxn_reply->trxn_id,
-                *      bcknd_msg->trxn_reply->create,
-                *      bcknd_msg->trxn_reply->success, adptr);
+                * Forward the TRXN_REPLY to trxn module.
                 */
+               mgmt_trxn_notify_bcknd_trxn_reply(
+                       bcknd_msg->trxn_reply->trxn_id,
+                       bcknd_msg->trxn_reply->create,
+                       bcknd_msg->trxn_reply->success, adptr);
                break;
        case MGMTD__BCKND_MESSAGE__MESSAGE_CFG_DATA_REPLY:
                MGMTD_BCKND_ADPTR_DBG(
@@ -428,13 +428,13 @@ mgmt_bcknd_adapter_handle_msg(struct mgmt_bcknd_client_adapter *adptr,
                                ? bcknd_msg->cfg_data_reply->error_if_any
                                : "None");
                /*
-                * TODO: Forward the CGFData-create reply to trxn module.
-                * mgmt_trxn_notify_bcknd_cfgdata_reply(
-                *      bcknd_msg->cfg_data_reply->trxn_id,
-                *      bcknd_msg->cfg_data_reply->batch_id,
-                *      bcknd_msg->cfg_data_reply->success,
-                *      bcknd_msg->cfg_data_reply->error_if_any, adptr);
+                * Forward the CGFData-create reply to trxn module.
                 */
+               mgmt_trxn_notify_bcknd_cfgdata_reply(
+                       bcknd_msg->cfg_data_reply->trxn_id,
+                       bcknd_msg->cfg_data_reply->batch_id,
+                       bcknd_msg->cfg_data_reply->success,
+                       bcknd_msg->cfg_data_reply->error_if_any, adptr);
                break;
        case MGMTD__BCKND_MESSAGE__MESSAGE_CFG_VALIDATE_REPLY:
                MGMTD_BCKND_ADPTR_DBG(
@@ -455,14 +455,14 @@ mgmt_bcknd_adapter_handle_msg(struct mgmt_bcknd_client_adapter *adptr,
                                ? bcknd_msg->cfg_validate_reply->error_if_any
                                : "None");
                /*
-                * TODO: Forward the CGFData-validate reply to trxn module.
-                * mgmt_trxn_notify_bcknd_cfg_validate_reply(
-                *      bcknd_msg->cfg_validate_reply->trxn_id,
-                *      bcknd_msg->cfg_validate_reply->success,
-                *      (uint64_t *)bcknd_msg->cfg_validate_reply->batch_ids,
-                *      bcknd_msg->cfg_validate_reply->n_batch_ids,
-                *      bcknd_msg->cfg_validate_reply->error_if_any, adptr);
+                * Forward the CGFData-validate reply to trxn module.
                 */
+               mgmt_trxn_notify_bcknd_cfg_validate_reply(
+                       bcknd_msg->cfg_validate_reply->trxn_id,
+                       bcknd_msg->cfg_validate_reply->success,
+                       (uint64_t *)bcknd_msg->cfg_validate_reply->batch_ids,
+                       bcknd_msg->cfg_validate_reply->n_batch_ids,
+                       bcknd_msg->cfg_validate_reply->error_if_any, adptr);
                break;
        case MGMTD__BCKND_MESSAGE__MESSAGE_CFG_APPLY_REPLY:
                MGMTD_BCKND_ADPTR_DBG(
@@ -482,14 +482,15 @@ mgmt_bcknd_adapter_handle_msg(struct mgmt_bcknd_client_adapter *adptr,
                        bcknd_msg->cfg_apply_reply->error_if_any
                                ? bcknd_msg->cfg_apply_reply->error_if_any
                                : "None");
-               /* TODO: Forward the CGFData-apply reply to trxn module.
-                * mgmt_trxn_notify_bcknd_cfg_apply_reply(
-                *      bcknd_msg->cfg_apply_reply->trxn_id,
-                *      bcknd_msg->cfg_apply_reply->success,
-                *      (uint64_t *)bcknd_msg->cfg_apply_reply->batch_ids,
-                *      bcknd_msg->cfg_apply_reply->n_batch_ids,
-                *      bcknd_msg->cfg_apply_reply->error_if_any, adptr);
+               /*
+                * Forward the CGFData-apply reply to trxn module.
                 */
+               mgmt_trxn_notify_bcknd_cfg_apply_reply(
+                       bcknd_msg->cfg_apply_reply->trxn_id,
+                       bcknd_msg->cfg_apply_reply->success,
+                       (uint64_t *)bcknd_msg->cfg_apply_reply->batch_ids,
+                       bcknd_msg->cfg_apply_reply->n_batch_ids,
+                       bcknd_msg->cfg_apply_reply->error_if_any, adptr);
                break;
        case MGMTD__BCKND_MESSAGE__MESSAGE_GET_REPLY:
        case MGMTD__BCKND_MESSAGE__MESSAGE_CFG_CMD_REPLY:
@@ -960,29 +961,26 @@ static void mgmt_bcknd_adapter_conn_init(struct thread *thread)
        assert(adptr && adptr->conn_fd);
 
        /*
-        * 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_trxn_in_progress() != MGMTD_SESSION_ID_NONE) {
                mgmt_bcknd_adptr_register_event(adptr, MGMTD_BCKND_CONN_INIT);
-               return 0;
+               return;
        }
-        */
 
-    /*
-     * TODO: Notify TRXN module to create a CONFIG transaction and
-     * download the CONFIGs identified for this new client.
-     * If the TRXN 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 TRXN module to create a CONFIG transaction and
+        * download the CONFIGs identified for this new client.
+        * If the TRXN 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_trxn_notify_bcknd_adapter_conn(adptr, true) != 0) {
                mgmt_bcknd_adapter_disconnect(adptr);
                adptr = NULL;
        }
-     */
-
-       return 0;
 }
 
 static void
index 39c916d102a7c839590cfe4c37e2db22377a5d6c..e1651a498a2b5839f8099c288b23125a2ae859e7 100644 (file)
@@ -23,6 +23,7 @@
 #include "mgmtd/mgmt.h"
 #include "mgmtd/mgmt_memory.h"
 #include "mgmtd/mgmt_db.h"
+#include "mgmtd/mgmt_trxn.h"
 #include "libyang/libyang.h"
 
 #ifdef REDIRECT_DEBUG_TO_STDERR
@@ -34,7 +35,7 @@
 #define MGMTD_DB_DBG(fmt, ...)                                                 \
        do {                                                                   \
                if (mgmt_debug_db)                                             \
-                       zlog_debug("%s: " fmt, __func__, ##__VA_ARGS__);       \
+                       zlog_err("%s: " fmt, __func__, ##__VA_ARGS__);         \
        } while (0)
 #define MGMTD_DB_ERR(fmt, ...)                                                 \
        zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
@@ -52,6 +53,19 @@ struct mgmt_db_ctxt {
        } root;
 };
 
+struct mgmt_cmt_info_t {
+       struct mgmt_cmt_info_dlist_item cmt_dlist;
+
+       char cmtid_str[MGMTD_MD5_HASH_STR_HEX_LEN];
+       char time_str[MGMTD_COMMIT_TIME_STR_LEN];
+       char cmt_json_file[MGMTD_MAX_COMMIT_FILE_PATH_LEN];
+};
+
+DECLARE_DLIST(mgmt_cmt_info_dlist, struct mgmt_cmt_info_t, cmt_dlist);
+
+#define FOREACH_CMT_REC(mm, cmt_info)                                          \
+       frr_each_safe(mgmt_cmt_info_dlist, &mm->cmt_dlist, cmt_info)
+
 const char *mgmt_db_names[MGMTD_DB_MAX_ID + 1] = {
        MGMTD_DB_NAME_NONE,     /* MGMTD_DB_NONE */
        MGMTD_DB_NAME_RUNNING,     /* MGMTD_DB_RUNNING */
@@ -119,6 +133,14 @@ static int mgmt_db_replace_dst_with_src_db(struct mgmt_db_ctxt *src,
        else
                dst->root.dnode_root = dst_dnode;
 
+       if (src->db_id == MGMTD_DB_CANDIDATE) {
+               /*
+                * Drop the changes in scratch-buffer.
+                */
+               MGMTD_DB_DBG("Emptying Candidate Scratch buffer!");
+               nb_config_diff_del_changes(&src->root.cfg_root->cfg_chgs);
+       }
+
        if (dst->db_id == MGMTD_DB_RUNNING) {
                if (ly_out_new_filepath(MGMTD_STARTUP_DB_FILE_PATH, &out)
                    == LY_SUCCESS)
@@ -153,6 +175,14 @@ static int mgmt_db_merge_src_with_dst_db(struct mgmt_db_ctxt *src,
                return ret;
        }
 
+       if (src->db_id == MGMTD_DB_CANDIDATE) {
+               /*
+                * Drop the changes in scratch-buffer.
+                */
+               MGMTD_DB_DBG("Emptying Candidate Scratch buffer!");
+               nb_config_diff_del_changes(&src->root.cfg_root->cfg_chgs);
+       }
+
        if (dst->db_id == MGMTD_DB_RUNNING) {
                if (ly_out_new_filepath(MGMTD_STARTUP_DB_FILE_PATH, &out)
                    == LY_SUCCESS)
@@ -181,6 +211,244 @@ static int mgmt_db_load_cfg_from_file(const char *filepath,
        return 0;
 }
 
+static bool mgmt_db_cmt_rec_exists(char *file_path)
+{
+       int exist;
+
+       exist = access(file_path, F_OK);
+       if (exist == 0)
+               return true;
+       else
+               return false;
+}
+
+static void mgmt_db_remove_cmt_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_db_compute_cmt_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_db_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_db_compute_cmt_hash(new->time_str, new->cmtid_str);
+       snprintf(new->cmt_json_file, MGMTD_MAX_COMMIT_FILE_PATH_LEN,
+                MGMTD_COMMIT_FILE_PATH, new->cmtid_str);
+
+       if (mgmt_cmt_info_dlist_count(&mm->cmt_dlist)
+           == MGMTD_MAX_COMMIT_LIST) {
+               FOREACH_CMT_REC (mm, cmt_info)
+                       last_cmt_info = cmt_info;
+
+               if (last_cmt_info) {
+                       mgmt_db_remove_cmt_file(last_cmt_info->cmt_json_file);
+                       mgmt_cmt_info_dlist_del(&mm->cmt_dlist, last_cmt_info);
+                       XFREE(MTYPE_MGMTD_CMT_INFO, last_cmt_info);
+               }
+       }
+
+       mgmt_cmt_info_dlist_add_head(&mm->cmt_dlist, new);
+       return new;
+}
+
+static struct mgmt_cmt_info_t *mgmt_db_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_db_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_db_cmt_rec_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_info_dlist_add_tail(&mm->cmt_dlist, 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_db_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_db_remove_cmt_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_db_reset(struct mgmt_db_ctxt *db_ctxt)
+{
+       struct lyd_node *dnode;
+
+       if (!db_ctxt)
+               return -1;
+
+       dnode = db_ctxt->config_db ? db_ctxt->root.cfg_root->dnode
+                                  : db_ctxt->root.dnode_root;
+
+       if (dnode)
+               yang_dnode_free(dnode);
+
+       dnode = yang_dnode_new(ly_native_ctx, true);
+
+       if (db_ctxt->config_db)
+               db_ctxt->root.cfg_root->dnode = dnode;
+       else
+               db_ctxt->root.dnode_root = dnode;
+
+       return 0;
+}
+
+static int mgmt_db_rollback_to_cmt(struct vty *vty,
+                                  struct mgmt_cmt_info_t *cmt_info,
+                                  bool skip_file_load)
+{
+       struct mgmt_db_ctxt *src_db_ctxt;
+       struct mgmt_db_ctxt *dst_db_ctxt;
+       int ret = 0;
+
+       src_db_ctxt = mgmt_db_get_hndl_by_id(mm, MGMTD_DB_CANDIDATE);
+       if (!src_db_ctxt) {
+               vty_out(vty, "ERROR: Couldnot access Candidate database!\n");
+               return -1;
+       }
+
+       /*
+        * Note: Write lock on src_db is not required. This is already
+        * taken in 'conf te'.
+        */
+       dst_db_ctxt = mgmt_db_get_hndl_by_id(mm, MGMTD_DB_RUNNING);
+       if (!dst_db_ctxt) {
+               vty_out(vty, "ERROR: Couldnot access Running database!\n");
+               return -1;
+       }
+
+       ret = mgmt_db_write_lock(dst_db_ctxt);
+       if (ret != 0) {
+               vty_out(vty,
+                       "Failed to lock the DB %u for rollback Reason: %s!\n",
+                       MGMTD_DB_RUNNING, strerror(ret));
+               return -1;
+       }
+
+       if (!skip_file_load) {
+               ret = mgmt_db_load_config_from_file(
+                       src_db_ctxt, cmt_info->cmt_json_file, false);
+               if (ret != 0) {
+                       mgmt_db_unlock(dst_db_ctxt);
+                       vty_out(vty,
+                               "Error with parsing the file with error code %d\n",
+                               ret);
+                       return ret;
+               }
+       }
+
+       /* Internally trigger a commit-request. */
+       ret = mgmt_trxn_rollback_trigger_cfg_apply(src_db_ctxt, dst_db_ctxt);
+       if (ret != 0) {
+               mgmt_db_unlock(dst_db_ctxt);
+               vty_out(vty,
+                       "Error with creating commit apply trxn with error code %d\n",
+                       ret);
+               return ret;
+       }
+
+       mgmt_db_dump_cmt_record_index();
+       return 0;
+}
+
 int mgmt_db_init(struct mgmt_master *mm)
 {
        struct lyd_node *root;
@@ -206,6 +474,12 @@ int mgmt_db_init(struct mgmt_master *mm)
        candidate.config_db = true;
        candidate.db_id = MGMTD_DB_CANDIDATE;
 
+       /*
+        * Redirect lib/vty candidate-config database to the global candidate
+        * config Db 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_db = false;
        oper.db_id = MGMTD_DB_OPERATIONAL;
@@ -215,15 +489,27 @@ int mgmt_db_init(struct mgmt_master *mm)
        mm->oper_db = &oper;
        mgmt_db_mm = mm;
 
+       /* Create commit record for previously stored commit-apply */
+       mgmt_cmt_info_dlist_init(&mgmt_db_mm->cmt_dlist);
+       mgmt_db_read_cmt_record_index();
+
        return 0;
 }
 
 void mgmt_db_destroy(void)
 {
+       struct mgmt_cmt_info_t *cmt_info;
 
        /*
         * TODO: Free the databases.
         */
+
+       FOREACH_CMT_REC (mgmt_db_mm, cmt_info) {
+               mgmt_cmt_info_dlist_del(&mgmt_db_mm->cmt_dlist, cmt_info);
+               XFREE(MTYPE_MGMTD_CMT_INFO, cmt_info);
+       }
+
+       mgmt_cmt_info_dlist_fini(&mgmt_db_mm->cmt_dlist);
 }
 
 struct mgmt_db_ctxt *mgmt_db_get_hndl_by_id(struct mgmt_master *mm,
@@ -289,18 +575,34 @@ int mgmt_db_unlock(struct mgmt_db_ctxt *db_ctxt)
 int mgmt_db_merge_dbs(struct mgmt_db_ctxt *src_db_ctxt,
                      struct mgmt_db_ctxt *dst_db_ctxt, bool updt_cmt_rec)
 {
+       struct mgmt_cmt_info_t *cmt_info;
+
        if (mgmt_db_merge_src_with_dst_db(src_db_ctxt, dst_db_ctxt) != 0)
                return -1;
 
+       if (updt_cmt_rec && dst_db_ctxt->db_id == MGMTD_DB_RUNNING) {
+               cmt_info = mgmt_db_create_cmt_rec();
+               mgmt_db_dump_db_to_file(cmt_info->cmt_json_file, dst_db_ctxt);
+               mgmt_db_dump_cmt_record_index();
+       }
+
        return 0;
 }
 
 int mgmt_db_copy_dbs(struct mgmt_db_ctxt *src_db_ctxt,
                     struct mgmt_db_ctxt *dst_db_ctxt, bool updt_cmt_rec)
 {
+       struct mgmt_cmt_info_t *cmt_info;
+
        if (mgmt_db_replace_dst_with_src_db(src_db_ctxt, dst_db_ctxt) != 0)
                return -1;
 
+       if (updt_cmt_rec && dst_db_ctxt->db_id == MGMTD_DB_RUNNING) {
+               cmt_info = mgmt_db_create_cmt_rec();
+               mgmt_db_dump_db_to_file(cmt_info->cmt_json_file, dst_db_ctxt);
+               mgmt_db_dump_cmt_record_index();
+       }
+
        return 0;
 }
 
@@ -386,14 +688,16 @@ static int mgmt_walk_db_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) {
@@ -416,9 +720,6 @@ static int mgmt_walk_db_nodes(
                assert(xpath);
                MGMTD_DB_DBG(" -- XPATH: %s", xpath);
 
-               if (!childs_as_well)
-                       continue;
-
                if (num_nodes)
                        num_found = num_left;
 
@@ -649,3 +950,91 @@ void mgmt_db_status_write(struct vty *vty)
 
        mgmt_db_status_write_one(vty, mgmt_db_mm->oper_db);
 }
+
+int mgmt_db_rollback_by_cmtid(struct vty *vty, const char *cmtid_str)
+{
+       int ret = 0;
+       struct mgmt_cmt_info_t *cmt_info;
+
+       if (!mgmt_cmt_info_dlist_count(&mm->cmt_dlist)
+           || !mgmt_db_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_db_rollback_to_cmt(vty, cmt_info, false);
+                       return ret;
+               }
+
+               mgmt_db_remove_cmt_file(cmt_info->cmt_json_file);
+               mgmt_cmt_info_dlist_del(&mm->cmt_dlist, cmt_info);
+               XFREE(MTYPE_MGMTD_CMT_INFO, cmt_info);
+       }
+
+       return 0;
+}
+
+int mgmt_db_rollback_commits(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_info_dlist_count(&mm->cmt_dlist);
+       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_db_rollback_to_cmt(vty, cmt_info, false);
+                       return ret;
+               }
+
+               cnt++;
+               mgmt_db_remove_cmt_file(cmt_info->cmt_json_file);
+               mgmt_cmt_info_dlist_del(&mm->cmt_dlist, cmt_info);
+               XFREE(MTYPE_MGMTD_CMT_INFO, cmt_info);
+       }
+
+       if (!mgmt_cmt_info_dlist_count(&mm->cmt_dlist)) {
+               ret = mgmt_db_reset((struct mgmt_db_ctxt *)mm->candidate_db);
+               if (ret < 0)
+                       return ret;
+               ret = mgmt_db_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++;
+       }
+}
index 9aec3aaca1a7cfac45d8505d5ec684591caa6cf0..942be5f3babc9d9fc20892624c6b80101585018a 100644 (file)
@@ -22,6 +22,8 @@
 #define _FRR_MGMTD_DB_H_
 
 #include "mgmtd/mgmt_defines.h"
+#include "mgmtd/mgmt_bcknd_adapter.h"
+#include "mgmtd/mgmt_frntnd_adapter.h"
 
 #define MGMTD_MAX_NUM_DBNODES_PER_BATCH 128
 
 #define MGMTD_COMMIT_INDEX_FILE_NAME "/etc/frr/commit-index.dat"
 #define MGMTD_COMMIT_TIME_STR_LEN 100
 
-struct mgmt_master;
-
 extern struct nb_config *running_config;
 
 struct mgmt_db_ctxt;
 
-typedef void (*mgmt_db_node_iter_fn)(uint64_t db_hndl, char *xpath,
-                                    struct lyd_node *node,
-                                    struct nb_node *nb_node, void *ctxt);
+PREDECL_DLIST(mgmt_cmt_info_dlist);
 
 /***************************************************************
  * Global data exported
@@ -387,6 +385,34 @@ extern void mgmt_db_dump_tree(struct vty *vty, struct mgmt_db_ctxt *db_ctxt,
 extern int mgmt_db_dump_db_to_file(char *file_name,
                                   struct mgmt_db_ctxt *db_ctxt);
 
+/*
+ * 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_db_rollback_by_cmtid(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_db_rollback_commits(struct vty *vty, int num_cmts);
+
 /*
  * Dump information about specific database.
  */
@@ -398,4 +424,9 @@ extern void mgmt_db_status_write_one(struct vty *vty,
  */
 extern void mgmt_db_status_write(struct vty *vty);
 
+/*
+ * Show mgmt commit history.
+ */
+extern void show_mgmt_cmt_history(struct vty *vty);
+
 #endif /* _FRR_MGMTD_DB_H_ */
index c164bd55b6c02fe0fa3ab89a99faed21edfed676..f1793d8045b466ec5b1a9f8a98cdf77a283f2bcc 100644 (file)
@@ -29,6 +29,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
 
 #define MGMTD_MAX_CFG_CHANGES_IN_BATCH                                         \
        ((10 * MGMTD_BCKND_MSG_MAX_LEN)                                        \
@@ -67,4 +68,6 @@ enum mgmt_bcknd_event {
 
 #define MGMTD_TRXN_ID_NONE 0
 
+#define MGMTD_TRXN_BATCH_ID_NONE 0
+
 #endif /* _FRR_MGMTD_DEFINES_H */
index 3c49b828d4b3e2d1ce69e169eb85be7dece64696..2b64cc71de0c4978c1c442769fcb9dd8a1ec1d7c 100644 (file)
@@ -193,10 +193,11 @@ mgmt_frntnd_sessn_cfg_trxn_cleanup(struct mgmt_frntnd_sessn_ctxt *sessn)
                }
        }
 
-       /* TODO: Destroy the actual transaction created earlier.
-        * if (sessn->cfg_trxn_id != MGMTD_TRXN_ID_NONE)
-        *      mgmt_destroy_trxn(&sessn->cfg_trxn_id);
+       /*
+        * Destroy the actual transaction created earlier.
         */
+       if (sessn->cfg_trxn_id != MGMTD_TRXN_ID_NONE)
+               mgmt_destroy_trxn(&sessn->cfg_trxn_id);
 }
 
 static void
@@ -213,10 +214,11 @@ mgmt_frntnd_sessn_show_trxn_cleanup(struct mgmt_frntnd_sessn_ctxt *sessn)
                }
        }
 
-       /* TODO: Destroy the transaction created recently.
-        * if (sessn->trxn_id != MGMTD_TRXN_ID_NONE)
-        *      mgmt_destroy_trxn(&sessn->trxn_id);
+       /*
+        * Destroy the transaction created recently.
         */
+       if (sessn->trxn_id != MGMTD_TRXN_ID_NONE)
+               mgmt_destroy_trxn(&sessn->trxn_id);
 }
 
 static void
@@ -700,9 +702,6 @@ mgmt_frntnd_session_register_event(struct mgmt_frntnd_sessn_ctxt *sessn,
                                    &tv, &sessn->proc_show_trxn_clnp);
                assert(sessn->proc_show_trxn_clnp);
                break;
-       default:
-               assert(!"mgmt_frntnd_adptr_post_event() called incorrectly");
-               break;
        }
 }
 
@@ -848,7 +847,7 @@ static int
 mgmt_frntnd_session_handle_setcfg_req_msg(struct mgmt_frntnd_sessn_ctxt *sessn,
                                          Mgmtd__FrntndSetConfigReq *setcfg_req)
 {
-       /* uint64_t cfg_sessn_id; */
+       uint64_t cfg_sessn_id;
        struct mgmt_db_ctxt *db_ctxt, *dst_db_ctxt;
 
        if (mm->perf_stats_en)
@@ -881,20 +880,20 @@ mgmt_frntnd_session_handle_setcfg_req_msg(struct mgmt_frntnd_sessn_ctxt *sessn,
 
        if (sessn->cfg_trxn_id == MGMTD_TRXN_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_sessn_id = mgmt_config_trxn_in_progress();
-                * if (cfg_sessn_id != MGMTD_SESSION_ID_NONE
-                *   && cfg_sessn_id != sessn->session_id) {
-                *      mgmt_frntnd_send_setcfg_reply(
-                *              sessn, setcfg_req->db_id, setcfg_req->req_id,
-                *              false,
-                *              "Configuration already in-progress through a
-                *different user session!", setcfg_req->implicit_commit); goto
-                *mgmt_frntnd_sess_handle_setcfg_req_failed;
-                *}
                 */
+               cfg_sessn_id = mgmt_config_trxn_in_progress();
+               if (cfg_sessn_id != MGMTD_SESSION_ID_NONE
+                  && cfg_sessn_id != sessn->session_id) {
+                       mgmt_frntnd_send_setcfg_reply(
+                               sessn, setcfg_req->db_id, setcfg_req->req_id,
+                               false,
+                               "Configuration already in-progress through a different user session!",
+                               setcfg_req->implicit_commit);
+                       goto mgmt_frntnd_sess_handle_setcfg_req_failed;
+               }
 
 
                /*
@@ -916,18 +915,18 @@ mgmt_frntnd_session_handle_setcfg_req_msg(struct mgmt_frntnd_sessn_ctxt *sessn,
                }
 
                /*
-                * TODO: Start a CONFIG Transaction (if not started already)
-                * sessn->cfg_trxn_id = mgmt_create_trxn(sessn->session_id,
-                *                                    MGMTD_TRXN_TYPE_CONFIG);
-                * if (sessn->cfg_trxn_id == MGMTD_SESSION_ID_NONE) {
-                *      mgmt_frntnd_send_setcfg_reply(
-                *              sessn, setcfg_req->db_id, setcfg_req->req_id,
-                *              false,
-                *              "Failed to create a Configuration session!",
-                *              setcfg_req->implicit_commit);
-                *      goto mgmt_frntnd_sess_handle_setcfg_req_failed;
-                * }
+                * Start a CONFIG Transaction (if not started already)
                 */
+               sessn->cfg_trxn_id = mgmt_create_trxn(sessn->session_id,
+                                                     MGMTD_TRXN_TYPE_CONFIG);
+               if (sessn->cfg_trxn_id == MGMTD_SESSION_ID_NONE) {
+                       mgmt_frntnd_send_setcfg_reply(
+                               sessn, setcfg_req->db_id, setcfg_req->req_id,
+                               false,
+                               "Failed to create a Configuration session!",
+                               setcfg_req->implicit_commit);
+                       goto mgmt_frntnd_sess_handle_setcfg_req_failed;
+               }
 
                MGMTD_FRNTND_ADPTR_DBG(
                        "Created new Config Trxn 0x%llx for session %p",
@@ -964,36 +963,31 @@ mgmt_frntnd_session_handle_setcfg_req_msg(struct mgmt_frntnd_sessn_ctxt *sessn,
                }
        }
 
-       /* TODO: Create the SETConfig request under the transaction.
-        * if (mgmt_trxn_send_set_config_req(
-        *      sessn->cfg_trxn_id, setcfg_req->req_id, setcfg_req->db_id,
-        *      db_ctxt, setcfg_req->data, setcfg_req->n_data,
-        *      setcfg_req->implicit_commit, setcfg_req->commit_db_id,
-        *      dst_db_ctxt)
-        *      != 0) {
-        *      mgmt_frntnd_send_setcfg_reply(
-        *              sessn, setcfg_req->db_id, setcfg_req->req_id, false,
-        *              "Request processing for SET-CONFIG failed!",
-        *              setcfg_req->implicit_commit);
-        *      goto mgmt_frntnd_sess_handle_setcfg_req_failed;
-        * }
-        *
-        * For now send a failure reply.
+       /*
+        * Create the SETConfig request under the transaction.
         */
-       mgmt_frntnd_send_setcfg_reply(
-               sessn, setcfg_req->db_id, setcfg_req->req_id, false,
-               "Request processing for SET-CONFIG failed!",
-               setcfg_req->implicit_commit);
-       goto mgmt_frntnd_sess_handle_setcfg_req_failed;
+       if (mgmt_trxn_send_set_config_req(
+                   sessn->cfg_trxn_id, setcfg_req->req_id, setcfg_req->db_id,
+                   db_ctxt, setcfg_req->data, setcfg_req->n_data,
+                   setcfg_req->implicit_commit, setcfg_req->commit_db_id,
+                   dst_db_ctxt)
+           != 0) {
+               mgmt_frntnd_send_setcfg_reply(
+                       sessn, setcfg_req->db_id, setcfg_req->req_id, false,
+                       "Request processing for SET-CONFIG failed!",
+                       setcfg_req->implicit_commit);
+               goto mgmt_frntnd_sess_handle_setcfg_req_failed;
+       }
 
        return 0;
 
 mgmt_frntnd_sess_handle_setcfg_req_failed:
 
-       /* TODO: Delete transaction created recently.
-        * if (sessn->cfg_trxn_id != MGMTD_TRXN_ID_NONE)
-        *      mgmt_destroy_trxn(&sessn->cfg_trxn_id);
+       /*
+        * Delete transaction created recently.
         */
+       if (sessn->cfg_trxn_id != MGMTD_TRXN_ID_NONE)
+               mgmt_destroy_trxn(&sessn->cfg_trxn_id);
        if (db_ctxt && sessn->db_write_locked[setcfg_req->db_id])
                mgmt_frntnd_session_unlock_db(setcfg_req->db_id, db_ctxt, sessn,
                                              true, false);
@@ -1056,22 +1050,17 @@ mgmt_frntnd_session_handle_getcfg_req_msg(struct mgmt_frntnd_sessn_ctxt *sessn,
                }
 
                /*
-                * TODO: Start a SHOW Transaction (if not started already)
-                * sessn->trxn_id = mgmt_create_trxn(sessn->session_id,
-                *                              MGMTD_TRXN_TYPE_SHOW);
-                * if (sessn->trxn_id == MGMTD_SESSION_ID_NONE) {
-                *      mgmt_frntnd_send_getcfg_reply(
-                *              sessn, getcfg_req->db_id, getcfg_req->req_id,
-                *              false, NULL,
-                *              "Failed to create a Show transaction!");
-                *      goto mgmt_frntnd_sess_handle_getcfg_req_failed;
-                * }
+                * Start a SHOW Transaction (if not started already)
                 */
-               mgmt_frntnd_send_getcfg_reply(
-                       sessn, getcfg_req->db_id, getcfg_req->req_id, false,
-                       NULL, "Failed to create a Show transaction!");
-               goto mgmt_frntnd_sess_handle_getcfg_req_failed;
-
+               sessn->trxn_id = mgmt_create_trxn(sessn->session_id,
+                                                 MGMTD_TRXN_TYPE_SHOW);
+               if (sessn->trxn_id == MGMTD_SESSION_ID_NONE) {
+                       mgmt_frntnd_send_getcfg_reply(
+                               sessn, getcfg_req->db_id, getcfg_req->req_id,
+                               false, NULL,
+                               "Failed to create a Show transaction!");
+                       goto mgmt_frntnd_sess_handle_getcfg_req_failed;
+               }
 
                MGMTD_FRNTND_ADPTR_DBG(
                        "Created new Show Trxn 0x%llx for session %p",
@@ -1082,32 +1071,28 @@ mgmt_frntnd_session_handle_getcfg_req_msg(struct mgmt_frntnd_sessn_ctxt *sessn,
                        (unsigned long long)sessn->trxn_id, sessn);
        }
 
-       /* TODO: Create a GETConfig request under the transaction.
-        * if (mgmt_trxn_send_get_config_req(sessn->trxn_id, getcfg_req->req_id,
-        *                              getcfg_req->db_id, db_ctxt,
-        *                              getcfg_req->data, getcfg_req->n_data)
-        *      != 0) {
-        *      mgmt_frntnd_send_getcfg_reply(
-        *              sessn, getcfg_req->db_id, getcfg_req->req_id, false,
-        *              NULL, "Request processing for GET-CONFIG failed!");
-        *      goto mgmt_frntnd_sess_handle_getcfg_req_failed;
-        * }
-        *
-        * For now send back a failure reply.
+       /*
+        * Create a GETConfig request under the transaction.
         */
-       mgmt_frntnd_send_getcfg_reply(
-               sessn, getcfg_req->db_id, getcfg_req->req_id, false, NULL,
-               "Request processing for GET-CONFIG failed!");
-       goto mgmt_frntnd_sess_handle_getcfg_req_failed;
+       if (mgmt_trxn_send_get_config_req(sessn->trxn_id, getcfg_req->req_id,
+                                         getcfg_req->db_id, db_ctxt,
+                                         getcfg_req->data, getcfg_req->n_data)
+           != 0) {
+               mgmt_frntnd_send_getcfg_reply(
+                       sessn, getcfg_req->db_id, getcfg_req->req_id, false,
+                       NULL, "Request processing for GET-CONFIG failed!");
+               goto mgmt_frntnd_sess_handle_getcfg_req_failed;
+       }
 
        return 0;
 
 mgmt_frntnd_sess_handle_getcfg_req_failed:
 
-       /* TODO: Destroy the transaction created recently.
-        * if (sessn->trxn_id != MGMTD_TRXN_ID_NONE)
-        *      mgmt_destroy_trxn(&sessn->trxn_id);
+       /*
+        * Destroy the transaction created recently.
         */
+       if (sessn->trxn_id != MGMTD_TRXN_ID_NONE)
+               mgmt_destroy_trxn(&sessn->trxn_id);
        if (db_ctxt && sessn->db_read_locked[getcfg_req->db_id])
                mgmt_frntnd_session_unlock_db(getcfg_req->db_id, db_ctxt, sessn,
                                              false, true);
@@ -1156,23 +1141,17 @@ mgmt_frntnd_session_handle_getdata_req_msg(struct mgmt_frntnd_sessn_ctxt *sessn,
                }
 
                /*
-                * TODO: Start a SHOW Transaction (if not started already)
-                * sessn->trxn_id =
-                *      mgmt_create_trxn(sessn->session_id,
-                *                      MGMTD_TRXN_TYPE_SHOW);
-                * if (sessn->trxn_id == MGMTD_SESSION_ID_NONE) {
-                *      mgmt_frntnd_send_getdata_reply(
-                *              sessn, getdata_req->db_id, getdata_req->req_id,
-                *              false, NULL,
-                *              "Failed to create a Show transaction!");
-                *      goto mgmt_frntnd_sess_handle_getdata_req_failed;
-                * }
+                * Start a SHOW Transaction (if not started already)
                 */
-               mgmt_frntnd_send_getdata_reply(
-                       sessn, getdata_req->db_id, getdata_req->req_id, false,
-                       NULL, "Failed to create a Show transaction!");
-               goto mgmt_frntnd_sess_handle_getdata_req_failed;
-
+               sessn->trxn_id = mgmt_create_trxn(sessn->session_id,
+                                                 MGMTD_TRXN_TYPE_SHOW);
+               if (sessn->trxn_id == MGMTD_SESSION_ID_NONE) {
+                       mgmt_frntnd_send_getdata_reply(
+                               sessn, getdata_req->db_id, getdata_req->req_id,
+                               false, NULL,
+                               "Failed to create a Show transaction!");
+                       goto mgmt_frntnd_sess_handle_getdata_req_failed;
+               }
 
                MGMTD_FRNTND_ADPTR_DBG(
                        "Created new Show Trxn 0x%llx for session %p",
@@ -1183,32 +1162,28 @@ mgmt_frntnd_session_handle_getdata_req_msg(struct mgmt_frntnd_sessn_ctxt *sessn,
                        (unsigned long long)sessn->trxn_id, sessn);
        }
 
-       /* TODO: Create a GETData request under the transaction.
-        * if (mgmt_trxn_send_get_data_req(sessn->trxn_id, getdata_req->req_id,
-        *                              getdata_req->db_id, db_ctxt,
-        *                              getdata_req->data, getdata_req->n_data)
-        *      != 0) {
-        *      mgmt_frntnd_send_getdata_reply(
-        *              sessn, getdata_req->db_id, getdata_req->req_id, false,
-        *              NULL, "Request processing for GET-CONFIG failed!");
-        *      goto mgmt_frntnd_sess_handle_getdata_req_failed;
-        * }
-        *
-        * For now send back a failure reply.
+       /*
+        * Create a GETData request under the transaction.
         */
-       mgmt_frntnd_send_getdata_reply(
-               sessn, getdata_req->db_id, getdata_req->req_id, false, NULL,
-               "Request processing for GET-CONFIG failed!");
-       goto mgmt_frntnd_sess_handle_getdata_req_failed;
+       if (mgmt_trxn_send_get_data_req(sessn->trxn_id, getdata_req->req_id,
+                                       getdata_req->db_id, db_ctxt,
+                                       getdata_req->data, getdata_req->n_data)
+           != 0) {
+               mgmt_frntnd_send_getdata_reply(
+                       sessn, getdata_req->db_id, getdata_req->req_id, false,
+                       NULL, "Request processing for GET-CONFIG failed!");
+               goto mgmt_frntnd_sess_handle_getdata_req_failed;
+       }
 
        return 0;
 
 mgmt_frntnd_sess_handle_getdata_req_failed:
 
-       /* TODO: Destroy the transaction created recently.
-        * if (sessn->trxn_id != MGMTD_TRXN_ID_NONE)
-        *      mgmt_destroy_trxn(&sessn->trxn_id);
+       /*
+        * Destroy the transaction created recently.
         */
+       if (sessn->trxn_id != MGMTD_TRXN_ID_NONE)
+               mgmt_destroy_trxn(&sessn->trxn_id);
 
        if (db_ctxt && sessn->db_read_locked[getdata_req->db_id])
                mgmt_frntnd_session_unlock_db(getdata_req->db_id, db_ctxt,
@@ -1270,25 +1245,19 @@ static int mgmt_frntnd_session_handle_commit_config_req_msg(
 
        if (sessn->cfg_trxn_id == MGMTD_TRXN_ID_NONE) {
                /*
-                * TODO: Start a CONFIG Transaction (if not started already)
-                * sessn->cfg_trxn_id = mgmt_create_trxn(sessn->session_id,
-                *                              MGMTD_TRXN_TYPE_CONFIG);
-                * if (sessn->cfg_trxn_id == MGMTD_SESSION_ID_NONE) {
-                *      mgmt_frntnd_send_commitcfg_reply(
-                *              sessn, commcfg_req->src_db_id,
-                *              commcfg_req->dst_db_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_frntnd_send_commitcfg_reply(
-                       sessn, commcfg_req->src_db_id, commcfg_req->dst_db_id,
-                       commcfg_req->req_id, MGMTD_INTERNAL_ERROR,
-                       commcfg_req->validate_only,
-                       "Failed to create a Configuration session!");
-               return 0;
+               sessn->cfg_trxn_id = mgmt_create_trxn(sessn->session_id,
+                                               MGMTD_TRXN_TYPE_CONFIG);
+               if (sessn->cfg_trxn_id == MGMTD_SESSION_ID_NONE) {
+                       mgmt_frntnd_send_commitcfg_reply(
+                               sessn, commcfg_req->src_db_id,
+                               commcfg_req->dst_db_id, commcfg_req->req_id,
+                               MGMTD_INTERNAL_ERROR,
+                               commcfg_req->validate_only,
+                               "Failed to create a Configuration session!");
+                       return 0;
+               }
        }
 
 
@@ -1311,28 +1280,22 @@ static int mgmt_frntnd_session_handle_commit_config_req_msg(
                sessn->db_locked_implict[commcfg_req->dst_db_id] = true;
        }
 
-       /* TODO: Create COMMITConfig request under the transaction
-        * if (mgmt_trxn_send_commit_config_req(
-        *      sessn->cfg_trxn_id, commcfg_req->req_id,
-        *      commcfg_req->src_db_id, src_db_ctxt, commcfg_req->dst_db_id,
-        *      dst_db_ctxt, commcfg_req->validate_only, commcfg_req->abort,
-        *      false)
-        *      != 0) {
-        *      mgmt_frntnd_send_commitcfg_reply(
-        *              sessn, commcfg_req->src_db_id, commcfg_req->dst_db_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 trxn modules send a unsuccessfull reply.
+       /*
+        * Create COMMITConfig request under the transaction
         */
-       mgmt_frntnd_send_commitcfg_reply(
-               sessn, commcfg_req->src_db_id, commcfg_req->dst_db_id,
-               commcfg_req->req_id, MGMTD_INTERNAL_ERROR,
-               commcfg_req->validate_only,
-               "Request processing for COMMIT-CONFIG failed!");
+       if (mgmt_trxn_send_commit_config_req(
+                   sessn->cfg_trxn_id, commcfg_req->req_id,
+                   commcfg_req->src_db_id, src_db_ctxt, commcfg_req->dst_db_id,
+                   dst_db_ctxt, commcfg_req->validate_only, commcfg_req->abort,
+                   false)
+           != 0) {
+               mgmt_frntnd_send_commitcfg_reply(
+                       sessn, commcfg_req->src_db_id, commcfg_req->dst_db_id,
+                       commcfg_req->req_id, MGMTD_INTERNAL_ERROR,
+                       commcfg_req->validate_only,
+                       "Request processing for COMMIT-CONFIG failed!");
+               return 0;
+       }
 
        return 0;
 }
index e3fd7af0be03d650b0248c312c69a69c2cb139cb..31b12126d15ac9076223808a60e590525d81d606 100644 (file)
@@ -33,3 +33,15 @@ DEFINE_MTYPE(MGMTD, MGMTD, "MGMTD instance");
 DEFINE_MTYPE(MGMTD, MGMTD_BCKND_ADPATER, "MGMTD backend adapter");
 DEFINE_MTYPE(MGMTD, MGMTD_FRNTND_ADPATER, "MGMTD Frontend adapter");
 DEFINE_MTYPE(MGMTD, MGMTD_FRNTND_SESSN, "MGMTD Frontend Client Session");
+DEFINE_MTYPE(MGMTD, MGMTD_TRXN, "MGMTD Transaction");
+DEFINE_MTYPE(MGMTD, MGMTD_TRXN_REQ, "MGMTD Transaction Requests");
+DEFINE_MTYPE(MGMTD, MGMTD_TRXN_SETCFG_REQ,
+            "MGMTD Transaction Set-Config Requests");
+DEFINE_MTYPE(MGMTD, MGMTD_TRXN_COMMCFG_REQ,
+            "MGMTD Transaction Commit-Config Requests");
+DEFINE_MTYPE(MGMTD, MGMTD_TRXN_GETDATA_REQ,
+            "MGMTD Transaction Get-Data Requests");
+DEFINE_MTYPE(MGMTD, MGMTD_TRXN_GETDATA_REPLY,
+            "MGMTD Transaction Get-Data Replies");
+DEFINE_MTYPE(MGMTD, MGMTD_TRXN_CFG_BATCH, "MGMTD Transaction Gonfig Batches");
+DEFINE_MTYPE(MGMTD, MGMTD_CMT_INFO, "MGMTD commit info for tracking commits");
index f697eca79740d5d8a82078f5e0e3ab2dd8dfc60e..6a89aa72d4bd9da61e5103e16100e1cd0714b6e5 100644 (file)
@@ -27,4 +27,13 @@ DECLARE_MTYPE(MGMTD);
 DECLARE_MTYPE(MGMTD_BCKND_ADPATER);
 DECLARE_MTYPE(MGMTD_FRNTND_ADPATER);
 DECLARE_MTYPE(MGMTD_FRNTND_SESSN);
+DECLARE_MTYPE(MGMTD_TRXN);
+DECLARE_MTYPE(MGMTD_TRXN_REQ);
+DECLARE_MTYPE(MGMTD_TRXN_SETCFG_REQ);
+DECLARE_MTYPE(MGMTD_TRXN_COMMCFG_REQ);
+DECLARE_MTYPE(MGMTD_TRXN_GETDATA_REQ);
+DECLARE_MTYPE(MGMTD_TRXN_GETDATA_REPLY);
+DECLARE_MTYPE(MGMTD_TRXN_CFG_BATCH);
+DECLARE_MTYPE(MGMTD_BCKND_ADPTR_MSG_BUF);
+DECLARE_MTYPE(MGMTD_CMT_INFO);
 #endif /* _FRR_MGMTD_MEMORY_H */
diff --git a/mgmtd/mgmt_trxn.c b/mgmtd/mgmt_trxn.c
new file mode 100644 (file)
index 0000000..267d692
--- /dev/null
@@ -0,0 +1,2990 @@
+/*
+ * MGMTD Transactions
+ * Copyright (C) 2021  Vmware, Inc.
+ *                    Pushpasis Sarkar <spushpasis@vmware.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+#include "hash.h"
+#include "jhash.h"
+#include "libfrr.h"
+#include "mgmtd/mgmt.h"
+#include "mgmtd/mgmt_memory.h"
+#include "mgmtd/mgmt_trxn.h"
+
+#ifdef REDIRECT_DEBUG_TO_STDERR
+#define MGMTD_TRXN_DBG(fmt, ...)                                               \
+       fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
+#define MGMTD_TRXN_ERR(fmt, ...)                                               \
+       fprintf(stderr, "%s: ERROR, " fmt "\n", __func__, ##__VA_ARGS__)
+#else /* REDIRECT_DEBUG_TO_STDERR */
+#define MGMTD_TRXN_DBG(fmt, ...)                                               \
+       do {                                                                   \
+               if (mgmt_debug_trxn)                                           \
+                       zlog_err("%s: " fmt, __func__, ##__VA_ARGS__);         \
+       } while (0)
+#define MGMTD_TRXN_ERR(fmt, ...)                                               \
+       zlog_err("%s: ERROR: " fmt, __func__, ##__VA_ARGS__)
+#endif /* REDIRECT_DEBUG_TO_STDERR */
+
+#define MGMTD_TRXN_LOCK(trxn) mgmt_trxn_lock(trxn, __FILE__, __LINE__)
+#define MGMTD_TRXN_UNLOCK(trxn) mgmt_trxn_unlock(trxn, __FILE__, __LINE__)
+
+enum mgmt_trxn_event {
+       MGMTD_TRXN_PROC_SETCFG = 1,
+       MGMTD_TRXN_PROC_COMMITCFG,
+       MGMTD_TRXN_PROC_GETCFG,
+       MGMTD_TRXN_PROC_GETDATA,
+       MGMTD_TRXN_COMMITCFG_TIMEOUT,
+       MGMTD_TRXN_CLEANUP
+};
+
+PREDECL_LIST(mgmt_trxn_req_list);
+
+struct mgmt_set_cfg_req {
+       Mgmtd__DatabaseId db_id;
+       struct mgmt_db_ctxt *db_ctxt;
+       struct nb_cfg_change cfg_changes[MGMTD_MAX_CFG_CHANGES_IN_BATCH];
+       uint16_t num_cfg_changes;
+       bool implicit_commit;
+       Mgmtd__DatabaseId dst_db_id;
+       struct mgmt_db_ctxt *dst_db_ctxt;
+       struct mgmt_setcfg_stats *setcfg_stats;
+};
+
+enum mgmt_commit_phase {
+       MGMTD_COMMIT_PHASE_PREPARE_CFG = 0,
+       MGMTD_COMMIT_PHASE_TRXN_CREATE,
+       MGMTD_COMMIT_PHASE_SEND_CFG,
+#ifndef MGMTD_LOCAL_VALIDATIONS_ENABLED
+       MGMTD_COMMIT_PHASE_VALIDATE_CFG,
+#endif /* ifndef MGMTD_LOCAL_VALIDATIONS_ENABLED */
+       MGMTD_COMMIT_PHASE_APPLY_CFG,
+       MGMTD_COMMIT_PHASE_TRXN_DELETE,
+       MGMTD_COMMIT_PHASE_MAX
+};
+
+static inline const char *
+mgmt_commit_phase2str(enum mgmt_commit_phase cmt_phase)
+{
+       switch (cmt_phase) {
+       case MGMTD_COMMIT_PHASE_PREPARE_CFG:
+               return "PREP-CFG";
+       case MGMTD_COMMIT_PHASE_TRXN_CREATE:
+               return "CREATE-TRXN";
+       case MGMTD_COMMIT_PHASE_SEND_CFG:
+               return "SEND-CFG";
+#ifndef MGMTD_LOCAL_VALIDATIONS_ENABLED
+       case MGMTD_COMMIT_PHASE_VALIDATE_CFG:
+               return "VALIDATE-CFG";
+#endif /* ifndef MGMTD_LOCAL_VALIDATIONS_ENABLED */
+       case MGMTD_COMMIT_PHASE_APPLY_CFG:
+               return "APPLY-CFG";
+       case MGMTD_COMMIT_PHASE_TRXN_DELETE:
+               return "DELETE-TRXN";
+       case MGMTD_COMMIT_PHASE_MAX:
+               return "Invalid/Unknown";
+       }
+
+       return "Invalid/Unknown";
+}
+
+PREDECL_LIST(mgmt_trxn_batch_list);
+
+struct mgmt_trxn_bcknd_cfg_batch {
+       struct mgmt_trxn_ctxt *trxn;
+       uint64_t batch_id;
+       enum mgmt_bcknd_client_id bcknd_id;
+       struct mgmt_bcknd_client_adapter *bcknd_adptr;
+       union mgmt_bcknd_xpath_subscr_info
+               xp_subscr[MGMTD_MAX_CFG_CHANGES_IN_BATCH];
+       Mgmtd__YangCfgDataReq cfg_data[MGMTD_MAX_CFG_CHANGES_IN_BATCH];
+       Mgmtd__YangCfgDataReq * cfg_datap[MGMTD_MAX_CFG_CHANGES_IN_BATCH];
+       Mgmtd__YangData data[MGMTD_MAX_CFG_CHANGES_IN_BATCH];
+       Mgmtd__YangDataValue value[MGMTD_MAX_CFG_CHANGES_IN_BATCH];
+       size_t num_cfg_data;
+       int buf_space_left;
+       enum mgmt_commit_phase comm_phase;
+       struct mgmt_trxn_batch_list_item list_linkage;
+};
+
+DECLARE_LIST(mgmt_trxn_batch_list, struct mgmt_trxn_bcknd_cfg_batch,
+            list_linkage);
+
+#define FOREACH_TRXN_CFG_BATCH_IN_LIST(list, batch)                            \
+       frr_each_safe(mgmt_trxn_batch_list, list, batch)
+
+struct mgmt_commit_cfg_req {
+       Mgmtd__DatabaseId src_db_id;
+       struct mgmt_db_ctxt *src_db_ctxt;
+       Mgmtd__DatabaseId dst_db_id;
+       struct mgmt_db_ctxt *dst_db_ctxt;
+       uint32_t nb_trxn_id;
+       uint8_t validate_only : 1;
+       uint8_t abort : 1;
+       uint8_t implicit : 1;
+       uint8_t rollback : 1;
+
+       /* Track commit phases */
+       enum mgmt_commit_phase curr_phase;
+       enum mgmt_commit_phase next_phase;
+
+       /*
+        * Set of config changes to commit. This is used only
+        * when changes are NOT to be determined by comparing
+        * candidate and running DBs. This is typically used
+        * for downloading all relevant configs for a new backend
+        * client that has recently come up and connected with
+        * MGMTD.
+        */
+       struct nb_config_cbs *cfg_chgs;
+
+       /*
+        * Details on all the Backend Clients associated with
+        * this commit.
+        */
+       struct mgmt_bcknd_client_subscr_info subscr_info;
+
+       /*
+        * List of backend batches for this commit to be validated
+        * and applied at the backend.
+        *
+        * FIXME: Need to re-think this design for the case set of
+        * validators for a given YANG data item is different from
+        * the set of notifiers for the same. We may need to have
+        * separate list of batches for VALIDATE and APPLY.
+        */
+       struct mgmt_trxn_batch_list_head
+               curr_batches[MGMTD_BCKND_CLIENT_ID_MAX];
+       struct mgmt_trxn_batch_list_head
+               next_batches[MGMTD_BCKND_CLIENT_ID_MAX];
+       /*
+        * The last batch added for any backend client. This is always on
+        * 'curr_batches'
+        */
+       struct mgmt_trxn_bcknd_cfg_batch
+               *last_bcknd_cfg_batch[MGMTD_BCKND_CLIENT_ID_MAX];
+       struct hash *batches;
+       uint64_t next_batch_id;
+
+       struct mgmt_commit_stats *cmt_stats;
+};
+
+struct mgmt_get_data_reply {
+       /* Buffer space for preparing data reply */
+       int num_reply;
+       int last_batch;
+       Mgmtd__YangDataReply data_reply;
+       Mgmtd__YangData reply_data[MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH];
+       Mgmtd__YangData * reply_datap[MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH];
+       Mgmtd__YangDataValue reply_value[MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH];
+       char *reply_xpathp[MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH];
+};
+
+struct mgmt_get_data_req {
+       Mgmtd__DatabaseId db_id;
+       struct mgmt_db_ctxt *db_ctxt;
+       char *xpaths[MGMTD_MAX_NUM_DATA_REQ_IN_BATCH];
+       int num_xpaths;
+
+       /*
+        * Buffer space for preparing reply.
+        * NOTE: Should only be malloc-ed on demand to reduce
+        * memory footprint. Freed up via mgmt_trx_req_free()
+        */
+       struct mgmt_get_data_reply *reply;
+
+       int total_reply;
+};
+
+struct mgmt_trxn_req {
+       struct mgmt_trxn_ctxt *trxn;
+       enum mgmt_trxn_event req_event;
+       uint64_t req_id;
+       union {
+               struct mgmt_set_cfg_req *set_cfg;
+               struct mgmt_get_data_req *get_data;
+               struct mgmt_commit_cfg_req commit_cfg;
+       } req;
+
+       bool pending_bknd_proc;
+       struct mgmt_trxn_req_list_item list_linkage;
+};
+
+DECLARE_LIST(mgmt_trxn_req_list, struct mgmt_trxn_req, list_linkage);
+
+#define FOREACH_TRXN_REQ_IN_LIST(list, req)                                    \
+       frr_each_safe(mgmt_trxn_req_list, list, req)
+
+struct mgmt_trxn_ctxt {
+       uint64_t session_id; /* One transaction per client session */
+       uint64_t trxn_id;
+       enum mgmt_trxn_type type;
+
+       /* struct mgmt_master *mm; */
+
+       struct thread *proc_set_cfg;
+       struct thread *proc_comm_cfg;
+       struct thread *proc_get_cfg;
+       struct thread *proc_get_data;
+       struct thread *comm_cfg_timeout;
+       struct thread *clnup;
+
+       /* List of backend adapters involved in this transaction */
+       struct mgmt_trxn_badptr_list_head bcknd_adptrs;
+
+       int refcount;
+
+       struct mgmt_trxn_list_item list_linkage;
+
+       /*
+        * List of pending set-config requests for a given
+        * transaction/session. Just one list for requests
+        * not processed at all. There's no backend interaction
+        * involved.
+        */
+       struct mgmt_trxn_req_list_head set_cfg_reqs;
+       /*
+        * List of pending get-config requests for a given
+        * transaction/session. Just one list for requests
+        * not processed at all. There's no backend interaction
+        * involved.
+        */
+       struct mgmt_trxn_req_list_head get_cfg_reqs;
+       /*
+        * List of pending get-data requests for a given
+        * transaction/session Two lists, one for requests
+        * not processed at all, and one for requests that
+        * has been sent to backend for processing.
+        */
+       struct mgmt_trxn_req_list_head get_data_reqs;
+       struct mgmt_trxn_req_list_head pending_get_datas;
+       /*
+        * There will always be one commit-config allowed for a given
+        * transaction/session. No need to maintain lists for it.
+        */
+       struct mgmt_trxn_req *commit_cfg_req;
+};
+
+DECLARE_LIST(mgmt_trxn_list, struct mgmt_trxn_ctxt, list_linkage);
+
+#define FOREACH_TRXN_IN_LIST(mm, trxn)                                         \
+       frr_each_safe(mgmt_trxn_list, &(mm)->trxn_list, (trxn))
+
+static int mgmt_trxn_send_commit_cfg_reply(struct mgmt_trxn_ctxt *trxn,
+                                          enum mgmt_result result,
+                                          const char *error_if_any);
+
+static inline const char *
+mgmt_trxn_commit_phase_str(struct mgmt_trxn_ctxt *trxn, bool curr)
+{
+       if (!trxn->commit_cfg_req)
+               return "None";
+
+       return (mgmt_commit_phase2str(
+               curr ? trxn->commit_cfg_req->req.commit_cfg.curr_phase
+                    : trxn->commit_cfg_req->req.commit_cfg.next_phase));
+}
+
+static void mgmt_trxn_lock(struct mgmt_trxn_ctxt *trxn, const char *file,
+                          int line);
+static void mgmt_trxn_unlock(struct mgmt_trxn_ctxt **trxn, const char *file,
+                            int line);
+static int
+mgmt_trxn_send_bcknd_trxn_delete(struct mgmt_trxn_ctxt *trxn,
+                                struct mgmt_bcknd_client_adapter *adptr);
+
+static struct thread_master *mgmt_trxn_tm;
+static struct mgmt_master *mgmt_trxn_mm;
+
+static void mgmt_trxn_register_event(struct mgmt_trxn_ctxt *trxn,
+                                    enum mgmt_trxn_event event);
+
+static int
+mgmt_move_bcknd_commit_to_next_phase(struct mgmt_trxn_ctxt *trxn,
+                                    struct mgmt_bcknd_client_adapter *adptr);
+
+static struct mgmt_trxn_bcknd_cfg_batch *
+mgmt_trxn_cfg_batch_alloc(struct mgmt_trxn_ctxt *trxn,
+                         enum mgmt_bcknd_client_id id,
+                         struct mgmt_bcknd_client_adapter *bcknd_adptr)
+{
+       struct mgmt_trxn_bcknd_cfg_batch *cfg_btch;
+
+       cfg_btch = XCALLOC(MTYPE_MGMTD_TRXN_CFG_BATCH,
+                          sizeof(struct mgmt_trxn_bcknd_cfg_batch));
+       assert(cfg_btch);
+       cfg_btch->bcknd_id = id;
+
+       cfg_btch->trxn = trxn;
+       MGMTD_TRXN_LOCK(trxn);
+       assert(trxn->commit_cfg_req);
+       mgmt_trxn_batch_list_add_tail(
+               &trxn->commit_cfg_req->req.commit_cfg.curr_batches[id],
+               cfg_btch);
+       cfg_btch->bcknd_adptr = bcknd_adptr;
+       cfg_btch->buf_space_left = MGMTD_BCKND_CFGDATA_MAX_MSG_LEN;
+       if (bcknd_adptr)
+               mgmt_bcknd_adapter_lock(bcknd_adptr);
+
+       trxn->commit_cfg_req->req.commit_cfg.last_bcknd_cfg_batch[id] =
+               cfg_btch;
+       if (!trxn->commit_cfg_req->req.commit_cfg.next_batch_id)
+               trxn->commit_cfg_req->req.commit_cfg.next_batch_id++;
+       cfg_btch->batch_id =
+               trxn->commit_cfg_req->req.commit_cfg.next_batch_id++;
+       hash_get(trxn->commit_cfg_req->req.commit_cfg.batches, cfg_btch,
+                hash_alloc_intern);
+
+       return cfg_btch;
+}
+
+static void
+mgmt_trxn_cfg_batch_free(struct mgmt_trxn_bcknd_cfg_batch **cfg_btch)
+{
+       size_t indx;
+       struct mgmt_commit_cfg_req *cmtcfg_req;
+
+       MGMTD_TRXN_DBG(" Batch: %p, Trxn: %p", *cfg_btch, (*cfg_btch)->trxn);
+
+       assert((*cfg_btch)->trxn
+              && (*cfg_btch)->trxn->type == MGMTD_TRXN_TYPE_CONFIG);
+
+       cmtcfg_req = &(*cfg_btch)->trxn->commit_cfg_req->req.commit_cfg;
+       hash_release(cmtcfg_req->batches, *cfg_btch);
+       mgmt_trxn_batch_list_del(
+               &cmtcfg_req->curr_batches[(*cfg_btch)->bcknd_id], *cfg_btch);
+       mgmt_trxn_batch_list_del(
+               &cmtcfg_req->next_batches[(*cfg_btch)->bcknd_id], *cfg_btch);
+
+       if ((*cfg_btch)->bcknd_adptr)
+               mgmt_bcknd_adapter_unlock(&(*cfg_btch)->bcknd_adptr);
+
+       for (indx = 0; indx < (*cfg_btch)->num_cfg_data; indx++) {
+               if ((*cfg_btch)->data[indx].xpath) {
+                       free((*cfg_btch)->data[indx].xpath);
+                       (*cfg_btch)->data[indx].xpath = NULL;
+               }
+       }
+
+       MGMTD_TRXN_UNLOCK(&(*cfg_btch)->trxn);
+
+       XFREE(MTYPE_MGMTD_TRXN_CFG_BATCH, *cfg_btch);
+       *cfg_btch = NULL;
+}
+
+static unsigned int mgmt_trxn_cfgbatch_hash_key(const void *data)
+{
+       const struct mgmt_trxn_bcknd_cfg_batch *batch = data;
+
+       return jhash2((uint32_t *) &batch->batch_id,
+                     sizeof(batch->batch_id) / sizeof(uint32_t), 0);
+}
+
+static bool mgmt_trxn_cfgbatch_hash_cmp(const void *d1, const void *d2)
+{
+       const struct mgmt_trxn_bcknd_cfg_batch *batch1 = d1;
+       const struct mgmt_trxn_bcknd_cfg_batch *batch2 = d2;
+
+       return (batch1->batch_id == batch2->batch_id);
+}
+
+static void mgmt_trxn_cfgbatch_hash_free(void *data)
+{
+       struct mgmt_trxn_bcknd_cfg_batch *batch = data;
+
+       mgmt_trxn_cfg_batch_free(&batch);
+}
+
+static inline struct mgmt_trxn_bcknd_cfg_batch *
+mgmt_trxn_cfgbatch_id2ctxt(struct mgmt_trxn_ctxt *trxn, uint64_t batch_id)
+{
+       struct mgmt_trxn_bcknd_cfg_batch key = {0};
+       struct mgmt_trxn_bcknd_cfg_batch *batch;
+
+       if (!trxn->commit_cfg_req)
+               return NULL;
+
+       key.batch_id = batch_id;
+       batch = hash_lookup(trxn->commit_cfg_req->req.commit_cfg.batches,
+                           &key);
+
+       return batch;
+}
+
+static void mgmt_trxn_cleanup_bcknd_cfg_batches(struct mgmt_trxn_ctxt *trxn,
+                                               enum mgmt_bcknd_client_id id)
+{
+       struct mgmt_trxn_bcknd_cfg_batch *cfg_btch;
+       struct mgmt_trxn_batch_list_head *list;
+
+       list = &trxn->commit_cfg_req->req.commit_cfg.curr_batches[id];
+       FOREACH_TRXN_CFG_BATCH_IN_LIST (list, cfg_btch)
+               mgmt_trxn_cfg_batch_free(&cfg_btch);
+
+       mgmt_trxn_batch_list_fini(list);
+
+       list = &trxn->commit_cfg_req->req.commit_cfg.next_batches[id];
+       FOREACH_TRXN_CFG_BATCH_IN_LIST (list, cfg_btch)
+               mgmt_trxn_cfg_batch_free(&cfg_btch);
+
+       mgmt_trxn_batch_list_fini(list);
+
+       trxn->commit_cfg_req->req.commit_cfg.last_bcknd_cfg_batch[id] = NULL;
+}
+
+static struct mgmt_trxn_req *mgmt_trxn_req_alloc(struct mgmt_trxn_ctxt *trxn,
+                                                uint64_t req_id,
+                                                enum mgmt_trxn_event req_event)
+{
+       struct mgmt_trxn_req *trxn_req;
+       enum mgmt_bcknd_client_id id;
+
+       trxn_req = XCALLOC(MTYPE_MGMTD_TRXN_REQ, sizeof(struct mgmt_trxn_req));
+       assert(trxn_req);
+       trxn_req->trxn = trxn;
+       trxn_req->req_id = req_id;
+       trxn_req->req_event = req_event;
+       trxn_req->pending_bknd_proc = false;
+
+       switch (trxn_req->req_event) {
+       case MGMTD_TRXN_PROC_SETCFG:
+               trxn_req->req.set_cfg =
+                       XCALLOC(MTYPE_MGMTD_TRXN_SETCFG_REQ,
+                               sizeof(struct mgmt_set_cfg_req));
+               assert(trxn_req->req.set_cfg);
+               mgmt_trxn_req_list_add_tail(&trxn->set_cfg_reqs, trxn_req);
+               MGMTD_TRXN_DBG(
+                       "Added a new SETCFG Req: %p for Trxn: %p, Sessn: 0x%llx",
+                       trxn_req, trxn, (unsigned long long)trxn->session_id);
+               break;
+       case MGMTD_TRXN_PROC_COMMITCFG:
+               trxn->commit_cfg_req = trxn_req;
+               MGMTD_TRXN_DBG(
+                       "Added a new COMMITCFG Req: %p for Trxn: %p, Sessn: 0x%llx",
+                       trxn_req, trxn, (unsigned long long)trxn->session_id);
+
+               FOREACH_MGMTD_BCKND_CLIENT_ID (id) {
+                       mgmt_trxn_batch_list_init(
+                               &trxn_req->req.commit_cfg.curr_batches[id]);
+                       mgmt_trxn_batch_list_init(
+                               &trxn_req->req.commit_cfg.next_batches[id]);
+               }
+
+               trxn_req->req.commit_cfg.batches =
+                       hash_create(mgmt_trxn_cfgbatch_hash_key,
+                                   mgmt_trxn_cfgbatch_hash_cmp,
+                                   "MGMT Config Batches");
+               break;
+       case MGMTD_TRXN_PROC_GETCFG:
+               trxn_req->req.get_data =
+                       XCALLOC(MTYPE_MGMTD_TRXN_GETDATA_REQ,
+                               sizeof(struct mgmt_get_data_req));
+               assert(trxn_req->req.get_data);
+               mgmt_trxn_req_list_add_tail(&trxn->get_cfg_reqs, trxn_req);
+               MGMTD_TRXN_DBG(
+                       "Added a new GETCFG Req: %p for Trxn: %p, Sessn: 0x%llx",
+                       trxn_req, trxn, (unsigned long long)trxn->session_id);
+               break;
+       case MGMTD_TRXN_PROC_GETDATA:
+               trxn_req->req.get_data =
+                       XCALLOC(MTYPE_MGMTD_TRXN_GETDATA_REQ,
+                               sizeof(struct mgmt_get_data_req));
+               assert(trxn_req->req.get_data);
+               mgmt_trxn_req_list_add_tail(&trxn->get_data_reqs, trxn_req);
+               MGMTD_TRXN_DBG(
+                       "Added a new GETDATA Req: %p for Trxn: %p, Sessn: 0x%llx",
+                       trxn_req, trxn, (unsigned long long)trxn->session_id);
+               break;
+       case MGMTD_TRXN_COMMITCFG_TIMEOUT:
+       case MGMTD_TRXN_CLEANUP:
+               break;
+       }
+
+       MGMTD_TRXN_LOCK(trxn);
+
+       return trxn_req;
+}
+
+static void mgmt_trxn_req_free(struct mgmt_trxn_req **trxn_req)
+{
+       int indx;
+       struct mgmt_trxn_req_list_head *req_list = NULL;
+       struct mgmt_trxn_req_list_head *pending_list = NULL;
+       enum mgmt_bcknd_client_id id;
+       struct mgmt_bcknd_client_adapter *adptr;
+
+       switch ((*trxn_req)->req_event) {
+       case MGMTD_TRXN_PROC_SETCFG:
+               for (indx = 0; indx < (*trxn_req)->req.set_cfg->num_cfg_changes;
+                    indx++) {
+                       if ((*trxn_req)->req.set_cfg->cfg_changes[indx].value) {
+                               MGMTD_TRXN_DBG(
+                                       "Freeing value for %s at %p ==> '%s'",
+                                       (*trxn_req)
+                                               ->req.set_cfg->cfg_changes[indx]
+                                               .xpath,
+                                       (*trxn_req)
+                                               ->req.set_cfg->cfg_changes[indx]
+                                               .value,
+                                       (*trxn_req)
+                                               ->req.set_cfg->cfg_changes[indx]
+                                               .value);
+                               free((void *)(*trxn_req)
+                                            ->req.set_cfg->cfg_changes[indx]
+                                            .value);
+                       }
+               }
+               req_list = &(*trxn_req)->trxn->set_cfg_reqs;
+               MGMTD_TRXN_DBG("Deleting SETCFG Req: %p for Trxn: %p",
+                              *trxn_req, (*trxn_req)->trxn);
+               XFREE(MTYPE_MGMTD_TRXN_SETCFG_REQ, (*trxn_req)->req.set_cfg);
+               break;
+       case MGMTD_TRXN_PROC_COMMITCFG:
+               MGMTD_TRXN_DBG("Deleting COMMITCFG Req: %p for Trxn: %p",
+                              *trxn_req, (*trxn_req)->trxn);
+               FOREACH_MGMTD_BCKND_CLIENT_ID (id) {
+                       /*
+                        * Send TRXN_DELETE to cleanup state for this
+                        * transaction on backend
+                        */
+                       if ((*trxn_req)->req.commit_cfg.curr_phase
+                                   >= MGMTD_COMMIT_PHASE_TRXN_CREATE
+                           && (*trxn_req)->req.commit_cfg.curr_phase
+                                      < MGMTD_COMMIT_PHASE_TRXN_DELETE
+                           && (*trxn_req)
+                                      ->req.commit_cfg.subscr_info
+                                      .xpath_subscr[id]
+                                      .subscribed) {
+                               adptr = mgmt_bcknd_get_adapter_by_id(id);
+                               if (adptr)
+                                       mgmt_trxn_send_bcknd_trxn_delete(
+                                               (*trxn_req)->trxn, adptr);
+                       }
+
+                       mgmt_trxn_cleanup_bcknd_cfg_batches((*trxn_req)->trxn,
+                                                           id);
+                       if ((*trxn_req)->req.commit_cfg.batches) {
+                               hash_clean((*trxn_req)->req.commit_cfg.batches,
+                                          mgmt_trxn_cfgbatch_hash_free);
+                               hash_free((*trxn_req)->req.commit_cfg.batches);
+                               (*trxn_req)->req.commit_cfg.batches = NULL;
+                       }
+               }
+               break;
+       case MGMTD_TRXN_PROC_GETCFG:
+               for (indx = 0; indx < (*trxn_req)->req.get_data->num_xpaths;
+                    indx++) {
+                       if ((*trxn_req)->req.get_data->xpaths[indx])
+                               free((void *)(*trxn_req)
+                                            ->req.get_data->xpaths[indx]);
+               }
+               req_list = &(*trxn_req)->trxn->get_cfg_reqs;
+               MGMTD_TRXN_DBG("Deleting GETCFG Req: %p for Trxn: %p",
+                              *trxn_req, (*trxn_req)->trxn);
+               if ((*trxn_req)->req.get_data->reply)
+                       XFREE(MTYPE_MGMTD_TRXN_GETDATA_REPLY,
+                             (*trxn_req)->req.get_data->reply);
+               XFREE(MTYPE_MGMTD_TRXN_GETDATA_REQ, (*trxn_req)->req.get_data);
+               break;
+       case MGMTD_TRXN_PROC_GETDATA:
+               for (indx = 0; indx < (*trxn_req)->req.get_data->num_xpaths;
+                    indx++) {
+                       if ((*trxn_req)->req.get_data->xpaths[indx])
+                               free((void *)(*trxn_req)
+                                            ->req.get_data->xpaths[indx]);
+               }
+               pending_list = &(*trxn_req)->trxn->pending_get_datas;
+               req_list = &(*trxn_req)->trxn->get_data_reqs;
+               MGMTD_TRXN_DBG("Deleting GETDATA Req: %p for Trxn: %p",
+                              *trxn_req, (*trxn_req)->trxn);
+               if ((*trxn_req)->req.get_data->reply)
+                       XFREE(MTYPE_MGMTD_TRXN_GETDATA_REPLY,
+                             (*trxn_req)->req.get_data->reply);
+               XFREE(MTYPE_MGMTD_TRXN_GETDATA_REQ, (*trxn_req)->req.get_data);
+               break;
+       case MGMTD_TRXN_COMMITCFG_TIMEOUT:
+       case MGMTD_TRXN_CLEANUP:
+               break;
+       }
+
+       if ((*trxn_req)->pending_bknd_proc && pending_list) {
+               mgmt_trxn_req_list_del(pending_list, *trxn_req);
+               MGMTD_TRXN_DBG("Removed Req: %p from pending-list (left:%d)",
+                              *trxn_req,
+                              (int)mgmt_trxn_req_list_count(pending_list));
+       } else if (req_list) {
+               mgmt_trxn_req_list_del(req_list, *trxn_req);
+               MGMTD_TRXN_DBG("Removed Req: %p from request-list (left:%d)",
+                              *trxn_req,
+                              (int)mgmt_trxn_req_list_count(req_list));
+       }
+
+       (*trxn_req)->pending_bknd_proc = false;
+       MGMTD_TRXN_UNLOCK(&(*trxn_req)->trxn);
+       XFREE(MTYPE_MGMTD_TRXN_REQ, (*trxn_req));
+       *trxn_req = NULL;
+}
+
+static void mgmt_trxn_process_set_cfg(struct thread *thread)
+{
+       struct mgmt_trxn_ctxt *trxn;
+       struct mgmt_trxn_req *trxn_req;
+       struct mgmt_db_ctxt *db_ctxt;
+       struct nb_config *nb_config;
+       char err_buf[1024];
+       bool error;
+       int num_processed = 0;
+       size_t left;
+       struct mgmt_commit_stats *cmt_stats;
+       int ret = 0;
+
+       trxn = (struct mgmt_trxn_ctxt *)THREAD_ARG(thread);
+       assert(trxn);
+       cmt_stats = mgmt_frntnd_get_sessn_commit_stats(trxn->session_id);
+
+       MGMTD_TRXN_DBG(
+               "Processing %d SET_CONFIG requests for Trxn:%p Session:0x%llx",
+               (int)mgmt_trxn_req_list_count(&trxn->set_cfg_reqs), trxn,
+               (unsigned long long)trxn->session_id);
+
+       FOREACH_TRXN_REQ_IN_LIST (&trxn->set_cfg_reqs, trxn_req) {
+               error = false;
+               assert(trxn_req->req_event == MGMTD_TRXN_PROC_SETCFG);
+               db_ctxt = trxn_req->req.set_cfg->db_ctxt;
+               if (!db_ctxt) {
+                       mgmt_frntnd_send_set_cfg_reply(
+                               trxn->session_id, trxn->trxn_id,
+                               trxn_req->req.set_cfg->db_id, trxn_req->req_id,
+                               MGMTD_INTERNAL_ERROR, "No such database!",
+                               trxn_req->req.set_cfg->implicit_commit);
+                       error = true;
+                       goto mgmt_trxn_process_set_cfg_done;
+               }
+
+               nb_config = mgmt_db_get_nb_config(db_ctxt);
+               if (!nb_config) {
+                       mgmt_frntnd_send_set_cfg_reply(
+                               trxn->session_id, trxn->trxn_id,
+                               trxn_req->req.set_cfg->db_id, trxn_req->req_id,
+                               MGMTD_INTERNAL_ERROR,
+                               "Unable to retrieve DB Config Tree!",
+                               trxn_req->req.set_cfg->implicit_commit);
+                       error = true;
+                       goto mgmt_trxn_process_set_cfg_done;
+               }
+
+               error = false;
+               nb_candidate_edit_config_changes(
+                       nb_config, trxn_req->req.set_cfg->cfg_changes,
+                       (size_t)trxn_req->req.set_cfg->num_cfg_changes, NULL,
+                       NULL, 0, err_buf, sizeof(err_buf), &error);
+               if (error) {
+                       mgmt_frntnd_send_set_cfg_reply(
+                               trxn->session_id, trxn->trxn_id,
+                               trxn_req->req.set_cfg->db_id, trxn_req->req_id,
+                               MGMTD_INTERNAL_ERROR, err_buf,
+                               trxn_req->req.set_cfg->implicit_commit);
+                       goto mgmt_trxn_process_set_cfg_done;
+               }
+
+               if (trxn_req->req.set_cfg->implicit_commit) {
+                       assert(mgmt_trxn_req_list_count(&trxn->set_cfg_reqs)
+                              == 1);
+                       assert(trxn_req->req.set_cfg->dst_db_ctxt);
+
+                       ret = mgmt_db_write_lock(
+                               trxn_req->req.set_cfg->dst_db_ctxt);
+                       if (ret != 0) {
+                               MGMTD_TRXN_ERR(
+                                       "Failed to lock the DB %u for trxn: %p sessn 0x%llx, errstr %s!",
+                                       trxn_req->req.set_cfg->dst_db_id, trxn,
+                                       (unsigned long long)trxn->session_id,
+                                       strerror(ret));
+                               mgmt_trxn_send_commit_cfg_reply(
+                                       trxn, MGMTD_DB_LOCK_FAILED,
+                                       "Lock running DB before implicit commit failed!");
+                               goto mgmt_trxn_process_set_cfg_done;
+                       }
+
+                       mgmt_trxn_send_commit_config_req(
+                               trxn->trxn_id, trxn_req->req_id,
+                               trxn_req->req.set_cfg->db_id,
+                               trxn_req->req.set_cfg->db_ctxt,
+                               trxn_req->req.set_cfg->dst_db_id,
+                               trxn_req->req.set_cfg->dst_db_ctxt, false,
+                               false, true);
+
+                       if (mm->perf_stats_en)
+                               gettimeofday(&cmt_stats->last_start, NULL);
+                       cmt_stats->commit_cnt++;
+               } else if (mgmt_frntnd_send_set_cfg_reply(
+                                  trxn->session_id, trxn->trxn_id,
+                                  trxn_req->req.set_cfg->db_id,
+                                  trxn_req->req_id, MGMTD_SUCCESS, NULL, false)
+                          != 0) {
+                       MGMTD_TRXN_ERR(
+                               "Failed to send SET_CONFIG_REPLY for trxn %p sessn 0x%llx",
+                               trxn, (unsigned long long)trxn->session_id);
+                       error = true;
+               }
+
+       mgmt_trxn_process_set_cfg_done:
+
+               /*
+                * Note: The following will remove it from the list as well.
+                */
+               mgmt_trxn_req_free(&trxn_req);
+
+               num_processed++;
+               if (num_processed == MGMTD_TRXN_MAX_NUM_SETCFG_PROC)
+                       break;
+       }
+
+       left = mgmt_trxn_req_list_count(&trxn->set_cfg_reqs);
+       if (left) {
+               MGMTD_TRXN_DBG(
+                       "Processed maximum number of Set-Config requests (%d/%d/%d). Rescheduling for rest.",
+                       num_processed, MGMTD_TRXN_MAX_NUM_SETCFG_PROC,
+                       (int)left);
+               mgmt_trxn_register_event(trxn, MGMTD_TRXN_PROC_SETCFG);
+       }
+}
+
+static int mgmt_trxn_send_commit_cfg_reply(struct mgmt_trxn_ctxt *trxn,
+                                          enum mgmt_result result,
+                                          const char *error_if_any)
+{
+       int ret = 0;
+       bool success, create_cmt_info_rec;
+
+       if (!trxn->commit_cfg_req)
+               return -1;
+
+       success = result == MGMTD_SUCCESS || result == MGMTD_NO_CFG_CHANGES
+                         ? true
+                         : false;
+       if (!trxn->commit_cfg_req->req.commit_cfg.implicit && trxn->session_id
+           && mgmt_frntnd_send_commit_cfg_reply(
+                      trxn->session_id, trxn->trxn_id,
+                      trxn->commit_cfg_req->req.commit_cfg.src_db_id,
+                      trxn->commit_cfg_req->req.commit_cfg.dst_db_id,
+                      trxn->commit_cfg_req->req_id,
+                      trxn->commit_cfg_req->req.commit_cfg.validate_only,
+                      result, error_if_any)
+                      != 0) {
+               MGMTD_TRXN_ERR(
+                       "Failed to send COMMIT-CONFIG-REPLY for Trxn %p Sessn 0x%llx",
+                       trxn, (unsigned long long)trxn->session_id);
+       }
+
+       if (trxn->commit_cfg_req->req.commit_cfg.implicit && trxn->session_id
+           && mgmt_frntnd_send_set_cfg_reply(
+                      trxn->session_id, trxn->trxn_id,
+                      trxn->commit_cfg_req->req.commit_cfg.src_db_id,
+                      trxn->commit_cfg_req->req_id,
+                      success ? MGMTD_SUCCESS : MGMTD_INTERNAL_ERROR,
+                      error_if_any, true)
+                      != 0) {
+               MGMTD_TRXN_ERR(
+                       "Failed to send SET-CONFIG-REPLY for Trxn %p Sessn 0x%llx",
+                       trxn, (unsigned long long)trxn->session_id);
+       }
+
+       if (success) {
+               /* Stop the commit-timeout timer */
+               THREAD_OFF(trxn->comm_cfg_timeout);
+
+               create_cmt_info_rec =
+                       result != MGMTD_NO_CFG_CHANGES
+                                       && !trxn->commit_cfg_req->req.commit_cfg
+                                                   .rollback
+                               ? true
+                               : false;
+
+               /*
+                * Successful commit: Merge Src DB into Dst DB if and only if
+                * this was not a validate-only or abort request.
+                */
+               if ((trxn->session_id
+                    && !trxn->commit_cfg_req->req.commit_cfg.validate_only
+                    && !trxn->commit_cfg_req->req.commit_cfg.abort)
+                   || trxn->commit_cfg_req->req.commit_cfg.rollback) {
+                       mgmt_db_copy_dbs(trxn->commit_cfg_req->req.commit_cfg
+                                                .src_db_ctxt,
+                                        trxn->commit_cfg_req->req.commit_cfg
+                                                .dst_db_ctxt,
+                                        create_cmt_info_rec);
+               }
+
+               /*
+                * Restore Src DB back to Dest DB only through a commit abort
+                * request.
+                */
+               if (trxn->session_id
+                   && trxn->commit_cfg_req->req.commit_cfg.abort)
+                       mgmt_db_copy_dbs(trxn->commit_cfg_req->req.commit_cfg
+                                                .dst_db_ctxt,
+                                        trxn->commit_cfg_req->req.commit_cfg
+                                                .src_db_ctxt,
+                                        false);
+       } else {
+               /*
+                * The commit has failied. For implicit commit requests restore
+                * back the contents of the candidate DB.
+                */
+               if (trxn->commit_cfg_req->req.commit_cfg.implicit)
+                       mgmt_db_copy_dbs(trxn->commit_cfg_req->req.commit_cfg
+                                                .dst_db_ctxt,
+                                        trxn->commit_cfg_req->req.commit_cfg
+                                                .src_db_ctxt,
+                                        false);
+       }
+
+       if (trxn->commit_cfg_req->req.commit_cfg.rollback) {
+               ret = mgmt_db_unlock(
+                       trxn->commit_cfg_req->req.commit_cfg.dst_db_ctxt);
+               if (ret != 0)
+                       MGMTD_TRXN_ERR(
+                               "Failed to unlock the dst DB during rollback : %s",
+                               strerror(ret));
+       }
+
+       if (trxn->commit_cfg_req->req.commit_cfg.implicit)
+               if (mgmt_db_unlock(
+                           trxn->commit_cfg_req->req.commit_cfg.dst_db_ctxt)
+                   != 0)
+                       MGMTD_TRXN_ERR(
+                               "Failed to unlock the dst DB during implicit : %s",
+                               strerror(ret));
+
+       trxn->commit_cfg_req->req.commit_cfg.cmt_stats = NULL;
+       mgmt_trxn_req_free(&trxn->commit_cfg_req);
+
+       /*
+        * The CONFIG Transaction should be destroyed from Frontend-adapter.
+        * But in case the transaction is not triggered from a front-end session
+        * we need to cleanup by itself.
+        */
+       if (!trxn->session_id)
+               mgmt_trxn_register_event(trxn, MGMTD_TRXN_CLEANUP);
+
+       return 0;
+}
+
+static void
+mgmt_move_trxn_cfg_batch_to_next(struct mgmt_commit_cfg_req *cmtcfg_req,
+                                struct mgmt_trxn_bcknd_cfg_batch *cfg_btch,
+                                struct mgmt_trxn_batch_list_head *src_list,
+                                struct mgmt_trxn_batch_list_head *dst_list,
+                                bool update_commit_phase,
+                                enum mgmt_commit_phase to_phase)
+{
+       mgmt_trxn_batch_list_del(src_list, cfg_btch);
+
+       if (update_commit_phase) {
+               MGMTD_TRXN_DBG(
+                       "Move Trxn-Id %p Batch-Id %p from '%s' --> '%s'",
+                       cfg_btch->trxn, cfg_btch,
+                       mgmt_commit_phase2str(cfg_btch->comm_phase),
+                       mgmt_trxn_commit_phase_str(cfg_btch->trxn, false));
+               cfg_btch->comm_phase = to_phase;
+       }
+
+       mgmt_trxn_batch_list_add_tail(dst_list, cfg_btch);
+}
+
+static void mgmt_move_trxn_cfg_batches(
+       struct mgmt_trxn_ctxt *trxn, struct mgmt_commit_cfg_req *cmtcfg_req,
+       struct mgmt_trxn_batch_list_head *src_list,
+       struct mgmt_trxn_batch_list_head *dst_list, bool update_commit_phase,
+       enum mgmt_commit_phase to_phase)
+{
+       struct mgmt_trxn_bcknd_cfg_batch *cfg_btch;
+
+       FOREACH_TRXN_CFG_BATCH_IN_LIST (src_list, cfg_btch) {
+               mgmt_move_trxn_cfg_batch_to_next(cmtcfg_req, cfg_btch, src_list,
+                                                dst_list, update_commit_phase,
+                                                to_phase);
+       }
+}
+
+static int
+mgmt_try_move_commit_to_next_phase(struct mgmt_trxn_ctxt *trxn,
+                                  struct mgmt_commit_cfg_req *cmtcfg_req)
+{
+       struct mgmt_trxn_batch_list_head *curr_list, *next_list;
+       enum mgmt_bcknd_client_id id;
+
+       MGMTD_TRXN_DBG("Trxn-Id %p, Phase(current:'%s' next:'%s')", trxn,
+                      mgmt_trxn_commit_phase_str(trxn, true),
+                      mgmt_trxn_commit_phase_str(trxn, false));
+
+       /*
+        * Check if all clients has moved to next phase or not.
+        */
+       FOREACH_MGMTD_BCKND_CLIENT_ID (id) {
+               if (cmtcfg_req->subscr_info.xpath_subscr[id].subscribed
+                   && mgmt_trxn_batch_list_count(
+                              &cmtcfg_req->curr_batches[id])) {
+                       /*
+                        * There's atleast once client who hasn't moved to
+                        * next phase.
+                        *
+                        * TODO: Need to re-think this design for the case
+                        * set of validators for a given YANG data item is
+                        * different from the set of notifiers for the same.
+                        */
+                       return -1;
+               }
+       }
+
+       MGMTD_TRXN_DBG("Move entire Trxn-Id %p from '%s' to '%s'", trxn,
+                      mgmt_trxn_commit_phase_str(trxn, true),
+                      mgmt_trxn_commit_phase_str(trxn, false));
+
+       /*
+        * If we are here, it means all the clients has moved to next phase.
+        * So we can move the whole commit to next phase.
+        */
+       cmtcfg_req->curr_phase = cmtcfg_req->next_phase;
+       cmtcfg_req->next_phase++;
+       MGMTD_TRXN_DBG(
+               "Move back all config batches for Trxn %p from next to current branch",
+               trxn);
+       FOREACH_MGMTD_BCKND_CLIENT_ID (id) {
+               curr_list = &cmtcfg_req->curr_batches[id];
+               next_list = &cmtcfg_req->next_batches[id];
+               mgmt_move_trxn_cfg_batches(trxn, cmtcfg_req, next_list,
+                                          curr_list, false, 0);
+       }
+
+       mgmt_trxn_register_event(trxn, MGMTD_TRXN_PROC_COMMITCFG);
+
+       return 0;
+}
+
+static int
+mgmt_move_bcknd_commit_to_next_phase(struct mgmt_trxn_ctxt *trxn,
+                                    struct mgmt_bcknd_client_adapter *adptr)
+{
+       struct mgmt_commit_cfg_req *cmtcfg_req;
+       struct mgmt_trxn_batch_list_head *curr_list, *next_list;
+
+       if (trxn->type != MGMTD_TRXN_TYPE_CONFIG || !trxn->commit_cfg_req)
+               return -1;
+
+       cmtcfg_req = &trxn->commit_cfg_req->req.commit_cfg;
+
+       MGMTD_TRXN_DBG(
+               "Move Trxn-Id %p for '%s' Phase(current: '%s' next:'%s')", trxn,
+               adptr->name, mgmt_trxn_commit_phase_str(trxn, true),
+               mgmt_trxn_commit_phase_str(trxn, false));
+
+       MGMTD_TRXN_DBG(
+               "Move all config batches for '%s' from current to next list",
+               adptr->name);
+       curr_list = &cmtcfg_req->curr_batches[adptr->id];
+       next_list = &cmtcfg_req->next_batches[adptr->id];
+       mgmt_move_trxn_cfg_batches(trxn, cmtcfg_req, curr_list, next_list, true,
+                                  cmtcfg_req->next_phase);
+
+       MGMTD_TRXN_DBG("Trxn-Id %p, Phase(current:'%s' next:'%s')", trxn,
+                      mgmt_trxn_commit_phase_str(trxn, true),
+                      mgmt_trxn_commit_phase_str(trxn, false));
+
+       /*
+        * Check if all clients has moved to next phase or not.
+        */
+       mgmt_try_move_commit_to_next_phase(trxn, cmtcfg_req);
+
+       return 0;
+}
+
+static int mgmt_trxn_create_config_batches(struct mgmt_trxn_req *trxn_req,
+                                          struct nb_config_cbs *changes)
+{
+       struct nb_config_cb *cb, *nxt;
+       struct nb_config_change *chg;
+       struct mgmt_trxn_bcknd_cfg_batch *cfg_btch;
+       struct mgmt_bcknd_client_subscr_info subscr_info;
+       char *xpath = NULL, *value = NULL;
+       char err_buf[1024];
+       enum mgmt_bcknd_client_id id;
+       struct mgmt_bcknd_client_adapter *adptr;
+       struct mgmt_commit_cfg_req *cmtcfg_req;
+       bool found_validator;
+       int num_chgs = 0;
+       int xpath_len, value_len;
+
+       cmtcfg_req = &trxn_req->req.commit_cfg;
+
+       RB_FOREACH_SAFE (cb, nb_config_cbs, changes, nxt) {
+               chg = (struct nb_config_change *)cb;
+
+               /*
+                * Could have directly pointed to xpath in nb_node.
+                * But dont want to mess with it now.
+                * xpath = chg->cb.nb_node->xpath;
+                */
+               xpath = lyd_path(chg->cb.dnode, LYD_PATH_STD, NULL, 0);
+               if (!xpath) {
+                       (void)mgmt_trxn_send_commit_cfg_reply(
+                               trxn_req->trxn, MGMTD_INTERNAL_ERROR,
+                               "Internal error! Could not get Xpath from Db node!");
+                       goto mgmt_trxn_create_config_batches_failed;
+               }
+
+               value = (char *)lyd_get_value(chg->cb.dnode);
+               if (!value)
+                       value = (char *)MGMTD_BCKND_CONTAINER_NODE_VAL;
+
+               MGMTD_TRXN_DBG("XPATH: %s, Value: '%s'", xpath,
+                              value ? value : "NIL");
+
+               if (mgmt_bcknd_get_subscr_info_for_xpath(xpath, &subscr_info)
+                   != 0) {
+                       snprintf(err_buf, sizeof(err_buf),
+                                "No backend module found for XPATH: '%s",
+                                xpath);
+                       (void)mgmt_trxn_send_commit_cfg_reply(
+                               trxn_req->trxn, MGMTD_INTERNAL_ERROR, err_buf);
+                       goto mgmt_trxn_create_config_batches_failed;
+               }
+
+               xpath_len = strlen(xpath) + 1;
+               value_len = strlen(value) + 1;
+               found_validator = false;
+               FOREACH_MGMTD_BCKND_CLIENT_ID (id) {
+                       if (!subscr_info.xpath_subscr[id].validate_config
+                           && !subscr_info.xpath_subscr[id].notify_config)
+                               continue;
+
+                       adptr = mgmt_bcknd_get_adapter_by_id(id);
+                       if (!adptr)
+                               continue;
+
+                       cfg_btch = cmtcfg_req->last_bcknd_cfg_batch[id];
+                       if (!cfg_btch
+                           || (cfg_btch->num_cfg_data
+                               == MGMTD_MAX_CFG_CHANGES_IN_BATCH)
+                           || (cfg_btch->buf_space_left
+                               < (xpath_len + value_len))) {
+                               /* Allocate a new config batch */
+                               cfg_btch = mgmt_trxn_cfg_batch_alloc(
+                                       trxn_req->trxn, id, adptr);
+                       }
+
+                       cfg_btch->buf_space_left -= (xpath_len + value_len);
+                       memcpy(&cfg_btch->xp_subscr[cfg_btch->num_cfg_data],
+                              &subscr_info.xpath_subscr[id],
+                              sizeof(cfg_btch->xp_subscr[0]));
+
+                       mgmt_yang_cfg_data_req_init(
+                               &cfg_btch->cfg_data[cfg_btch->num_cfg_data]);
+                       cfg_btch->cfg_datap[cfg_btch->num_cfg_data] =
+                               &cfg_btch->cfg_data[cfg_btch->num_cfg_data];
+
+                       if (chg->cb.operation == NB_OP_DESTROY)
+                               cfg_btch->cfg_data[cfg_btch->num_cfg_data]
+                                       .req_type =
+                                       MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA;
+                       else
+                               cfg_btch->cfg_data[cfg_btch->num_cfg_data]
+                                       .req_type =
+                                       MGMTD__CFG_DATA_REQ_TYPE__SET_DATA;
+
+                       mgmt_yang_data_init(
+                               &cfg_btch->data[cfg_btch->num_cfg_data]);
+                       cfg_btch->cfg_data[cfg_btch->num_cfg_data].data =
+                               &cfg_btch->data[cfg_btch->num_cfg_data];
+                       cfg_btch->data[cfg_btch->num_cfg_data].xpath = xpath;
+                       xpath = NULL;
+
+                       mgmt_yang_data_value_init(
+                               &cfg_btch->value[cfg_btch->num_cfg_data]);
+                       cfg_btch->data[cfg_btch->num_cfg_data].value =
+                               &cfg_btch->value[cfg_btch->num_cfg_data];
+                       cfg_btch->value[cfg_btch->num_cfg_data].value_case =
+                               MGMTD__YANG_DATA_VALUE__VALUE_ENCODED_STR_VAL;
+                       cfg_btch->value[cfg_btch->num_cfg_data]
+                               .encoded_str_val = value;
+                       value = NULL;
+
+                       if (subscr_info.xpath_subscr[id].validate_config)
+                               found_validator = true;
+
+                       cmtcfg_req->subscr_info.xpath_subscr[id].subscribed |=
+                               subscr_info.xpath_subscr[id].subscribed;
+                       MGMTD_TRXN_DBG(
+                               " -- %s, {V:%d, N:%d}, Batch: %p, Item:%d",
+                               adptr->name,
+                               subscr_info.xpath_subscr[id].validate_config,
+                               subscr_info.xpath_subscr[id].notify_config,
+                               cfg_btch, (int)cfg_btch->num_cfg_data);
+
+                       cfg_btch->num_cfg_data++;
+                       num_chgs++;
+               }
+
+               if (!found_validator) {
+                       snprintf(err_buf, sizeof(err_buf),
+                                "No validator module found for XPATH: '%s",
+                                xpath);
+                       MGMTD_TRXN_ERR("***** %s", err_buf);
+               }
+       }
+
+       cmtcfg_req->cmt_stats->last_batch_cnt = num_chgs;
+       if (!num_chgs) {
+               (void)mgmt_trxn_send_commit_cfg_reply(
+                       trxn_req->trxn, MGMTD_NO_CFG_CHANGES,
+                       "No changes found to commit!");
+               goto mgmt_trxn_create_config_batches_failed;
+       }
+
+       cmtcfg_req->next_phase = MGMTD_COMMIT_PHASE_TRXN_CREATE;
+       return 0;
+
+mgmt_trxn_create_config_batches_failed:
+
+       if (xpath)
+               free(xpath);
+
+       return -1;
+}
+
+static int mgmt_trxn_prepare_config(struct mgmt_trxn_ctxt *trxn)
+{
+       struct nb_context nb_ctxt;
+       struct nb_config *nb_config;
+       char err_buf[1024] = {0};
+       struct nb_config_cbs changes;
+       struct nb_config_cbs *cfg_chgs = NULL;
+       int ret;
+       bool del_cfg_chgs = false;
+
+       ret = 0;
+       memset(&nb_ctxt, 0, sizeof(nb_ctxt));
+       memset(&changes, 0, sizeof(changes));
+       if (trxn->commit_cfg_req->req.commit_cfg.cfg_chgs) {
+               cfg_chgs = trxn->commit_cfg_req->req.commit_cfg.cfg_chgs;
+               del_cfg_chgs = true;
+               goto mgmt_trxn_prep_config_validation_done;
+       }
+
+       if (trxn->commit_cfg_req->req.commit_cfg.src_db_id
+           != MGMTD_DB_CANDIDATE) {
+               (void)mgmt_trxn_send_commit_cfg_reply(
+                       trxn, MGMTD_INVALID_PARAM,
+                       "Source DB cannot be any other than CANDIDATE!");
+               ret = -1;
+               goto mgmt_trxn_prepare_config_done;
+       }
+
+       if (trxn->commit_cfg_req->req.commit_cfg.dst_db_id
+           != MGMTD_DB_RUNNING) {
+               (void)mgmt_trxn_send_commit_cfg_reply(
+                       trxn, MGMTD_INVALID_PARAM,
+                       "Destination DB cannot be any other than RUNNING!");
+               ret = -1;
+               goto mgmt_trxn_prepare_config_done;
+       }
+
+       if (!trxn->commit_cfg_req->req.commit_cfg.src_db_ctxt) {
+               (void)mgmt_trxn_send_commit_cfg_reply(
+                       trxn, MGMTD_INVALID_PARAM, "No such source database!");
+               ret = -1;
+               goto mgmt_trxn_prepare_config_done;
+       }
+
+       if (!trxn->commit_cfg_req->req.commit_cfg.dst_db_ctxt) {
+               (void)mgmt_trxn_send_commit_cfg_reply(
+                       trxn, MGMTD_INVALID_PARAM,
+                       "No such destination database!");
+               ret = -1;
+               goto mgmt_trxn_prepare_config_done;
+       }
+
+       if (trxn->commit_cfg_req->req.commit_cfg.abort) {
+               /*
+                * This is a commit abort request. Return back success.
+                * That should trigger a restore of Candidate database to
+                * Running.
+                */
+               (void)mgmt_trxn_send_commit_cfg_reply(trxn, MGMTD_SUCCESS,
+                                                     NULL);
+               goto mgmt_trxn_prepare_config_done;
+       }
+
+       nb_config = mgmt_db_get_nb_config(
+               trxn->commit_cfg_req->req.commit_cfg.src_db_ctxt);
+       if (!nb_config) {
+               (void)mgmt_trxn_send_commit_cfg_reply(
+                       trxn, MGMTD_INTERNAL_ERROR,
+                       "Unable to retrieve Commit DB Config Tree!");
+               ret = -1;
+               goto mgmt_trxn_prepare_config_done;
+       }
+
+       /*
+        * Check for diffs from scratch buffer. If found empty
+        * get the diff from Candidate DB itself.
+        */
+       cfg_chgs = &nb_config->cfg_chgs;
+       if (RB_EMPTY(nb_config_cbs, cfg_chgs)) {
+               /*
+                * This could be the case when the config is directly
+                * loaded onto the candidate DB from a file. Get the
+                * diff from a full comparison of the candidate and
+                * running DBs.
+                */
+               nb_config_diff(
+                       mgmt_db_get_nb_config(trxn->commit_cfg_req->req
+                                                     .commit_cfg.dst_db_ctxt),
+                       nb_config, &changes);
+               cfg_chgs = &changes;
+               del_cfg_chgs = true;
+       }
+
+       if (RB_EMPTY(nb_config_cbs, cfg_chgs)) {
+               /*
+                * This means there's no changes to commit whatsoever
+                * is the source of the changes in config.
+                */
+               (void)mgmt_trxn_send_commit_cfg_reply(
+                       trxn, MGMTD_NO_CFG_CHANGES,
+                       "No changes found to be committed!");
+               ret = -1;
+               goto mgmt_trxn_prepare_config_done;
+       }
+
+       /*
+        * Validate YANG contents of the source DB and get the diff
+        * between source and destination DB contents.
+        */
+       nb_ctxt.client = NB_CLIENT_MGMTD_SERVER;
+       nb_ctxt.user = (void *)trxn;
+       ret = nb_candidate_validate_yang(nb_config, false, err_buf,
+                                        sizeof(err_buf) - 1);
+       if (ret != NB_OK) {
+               if (strncmp(err_buf, " ", strlen(err_buf)) == 0)
+                       strlcpy(err_buf, "Validation failed", sizeof(err_buf));
+               (void)mgmt_trxn_send_commit_cfg_reply(trxn, MGMTD_INVALID_PARAM,
+                                                     err_buf);
+               ret = -1;
+               goto mgmt_trxn_prepare_config_done;
+       }
+#ifdef MGMTD_LOCAL_VALIDATIONS_ENABLED
+       if (mm->perf_stats_en)
+               gettimeofday(&trxn->commit_cfg_req->req.commit_cfg.cmt_stats
+                                     ->validate_start,
+                            NULL);
+
+       /*
+        * Perform application level validations locally on the MGMTD
+        * process by calling application specific validation routines
+        * loaded onto MGMTD process using libraries.
+        */
+       ret = nb_candidate_validate_code(&nb_ctxt, nb_config, &changes, err_buf,
+                                        sizeof(err_buf) - 1);
+       if (ret != NB_OK) {
+               if (strncmp(err_buf, " ", strlen(err_buf)) == 0)
+                       strlcpy(err_buf, "Validation failed", sizeof(err_buf));
+               (void)mgmt_trxn_send_commit_cfg_reply(trxn, MGMTD_INVALID_PARAM,
+                                                     err_buf);
+               ret = -1;
+               goto mgmt_trxn_prepare_config_done;
+       }
+
+       if (trxn->commit_cfg_req->req.commit_cfg.validate_only) {
+               /*
+                * This was a validate-only COMMIT request return success.
+                */
+               (void)mgmt_trxn_send_commit_cfg_reply(trxn, MGMTD_SUCCESS,
+                                                     NULL);
+               goto mgmt_trxn_prepare_config_done;
+       }
+#endif /* ifdef MGMTD_LOCAL_VALIDATIONS_ENABLED */
+
+mgmt_trxn_prep_config_validation_done:
+
+       if (mm->perf_stats_en)
+               gettimeofday(&trxn->commit_cfg_req->req.commit_cfg.cmt_stats
+                                     ->prep_cfg_start,
+                            NULL);
+
+       /*
+        * Iterate over the diffs and create ordered batches of config
+        * commands to be validated.
+        */
+       ret = mgmt_trxn_create_config_batches(trxn->commit_cfg_req, cfg_chgs);
+       if (ret != 0) {
+               ret = -1;
+               goto mgmt_trxn_prepare_config_done;
+       }
+
+       /* Move to the Transaction Create Phase */
+       trxn->commit_cfg_req->req.commit_cfg.curr_phase =
+               MGMTD_COMMIT_PHASE_TRXN_CREATE;
+       mgmt_trxn_register_event(trxn, MGMTD_TRXN_PROC_COMMITCFG);
+
+       /*
+        * Start the COMMIT Timeout Timer to abort Trxn if things get stuck at
+        * backend.
+        */
+       mgmt_trxn_register_event(trxn, MGMTD_TRXN_COMMITCFG_TIMEOUT);
+mgmt_trxn_prepare_config_done:
+
+       if (cfg_chgs && del_cfg_chgs)
+               nb_config_diff_del_changes(cfg_chgs);
+
+       return ret;
+}
+
+static int mgmt_trxn_send_bcknd_trxn_create(struct mgmt_trxn_ctxt *trxn)
+{
+       enum mgmt_bcknd_client_id id;
+       struct mgmt_bcknd_client_adapter *adptr;
+       struct mgmt_commit_cfg_req *cmtcfg_req;
+       struct mgmt_trxn_bcknd_cfg_batch *cfg_btch;
+
+       assert(trxn->type == MGMTD_TRXN_TYPE_CONFIG && trxn->commit_cfg_req);
+
+       cmtcfg_req = &trxn->commit_cfg_req->req.commit_cfg;
+       FOREACH_MGMTD_BCKND_CLIENT_ID (id) {
+               if (cmtcfg_req->subscr_info.xpath_subscr[id].subscribed) {
+                       adptr = mgmt_bcknd_get_adapter_by_id(id);
+                       if (mgmt_bcknd_create_trxn(adptr, trxn->trxn_id)
+                           != 0) {
+                               (void)mgmt_trxn_send_commit_cfg_reply(
+                                       trxn, MGMTD_INTERNAL_ERROR,
+                                       "Could not send TRXN_CREATE to backend adapter");
+                               return -1;
+                       }
+
+                       FOREACH_TRXN_CFG_BATCH_IN_LIST (
+                               &trxn->commit_cfg_req->req.commit_cfg
+                                        .curr_batches[id],
+                               cfg_btch)
+                               cfg_btch->comm_phase =
+                                       MGMTD_COMMIT_PHASE_TRXN_CREATE;
+               }
+       }
+
+       trxn->commit_cfg_req->req.commit_cfg.next_phase =
+               MGMTD_COMMIT_PHASE_SEND_CFG;
+
+       /*
+        * Dont move the commit to next phase yet. Wait for the TRXN_REPLY to
+        * come back.
+        */
+
+       MGMTD_TRXN_DBG(
+               "Trxn:%p Session:0x%llx, Phase(Current:'%s', Next: '%s')", trxn,
+               (unsigned long long)trxn->session_id,
+               mgmt_trxn_commit_phase_str(trxn, true),
+               mgmt_trxn_commit_phase_str(trxn, false));
+
+       return 0;
+}
+
+static int
+mgmt_trxn_send_bcknd_cfg_data(struct mgmt_trxn_ctxt *trxn,
+                             struct mgmt_bcknd_client_adapter *adptr)
+{
+       struct mgmt_commit_cfg_req *cmtcfg_req;
+       struct mgmt_trxn_bcknd_cfg_batch *cfg_btch;
+       struct mgmt_bcknd_cfgreq cfg_req = {0};
+       size_t num_batches, indx;
+
+       assert(trxn->type == MGMTD_TRXN_TYPE_CONFIG && trxn->commit_cfg_req);
+
+       cmtcfg_req = &trxn->commit_cfg_req->req.commit_cfg;
+       assert(cmtcfg_req->subscr_info.xpath_subscr[adptr->id].subscribed);
+
+       indx = 0;
+       num_batches = mgmt_trxn_batch_list_count(
+               &cmtcfg_req->curr_batches[adptr->id]);
+       FOREACH_TRXN_CFG_BATCH_IN_LIST (&cmtcfg_req->curr_batches[adptr->id],
+                                       cfg_btch) {
+               assert(cmtcfg_req->next_phase == MGMTD_COMMIT_PHASE_SEND_CFG);
+
+               cfg_req.cfgdata_reqs = cfg_btch->cfg_datap;
+               cfg_req.num_reqs = cfg_btch->num_cfg_data;
+               indx++;
+               if (mgmt_bcknd_send_cfg_data_create_req(
+                           adptr, trxn->trxn_id, cfg_btch->batch_id, &cfg_req,
+                           indx == num_batches ? true : false)
+                   != 0) {
+                       (void)mgmt_trxn_send_commit_cfg_reply(
+                               trxn, MGMTD_INTERNAL_ERROR,
+                               "Internal Error! Could not send config data to backend!");
+                       MGMTD_TRXN_ERR(
+                               "Could not send CFGDATA_CREATE for Trxn %p Batch %p to client '%s",
+                               trxn, cfg_btch, adptr->name);
+                       return -1;
+               }
+
+               cmtcfg_req->cmt_stats->last_num_cfgdata_reqs++;
+               mgmt_move_trxn_cfg_batch_to_next(
+                       cmtcfg_req, cfg_btch,
+                       &cmtcfg_req->curr_batches[adptr->id],
+                       &cmtcfg_req->next_batches[adptr->id], true,
+                       MGMTD_COMMIT_PHASE_SEND_CFG);
+       }
+
+       /*
+        * This could ne the last Backend Client to send CFGDATA_CREATE_REQ to.
+        * Try moving the commit to next phase.
+        */
+       mgmt_try_move_commit_to_next_phase(trxn, cmtcfg_req);
+
+       return 0;
+}
+
+static int
+mgmt_trxn_send_bcknd_trxn_delete(struct mgmt_trxn_ctxt *trxn,
+                                struct mgmt_bcknd_client_adapter *adptr)
+{
+       struct mgmt_commit_cfg_req *cmtcfg_req;
+       struct mgmt_trxn_bcknd_cfg_batch *cfg_btch;
+
+       assert(trxn->type == MGMTD_TRXN_TYPE_CONFIG && trxn->commit_cfg_req);
+
+       cmtcfg_req = &trxn->commit_cfg_req->req.commit_cfg;
+       if (cmtcfg_req->subscr_info.xpath_subscr[adptr->id].subscribed) {
+               adptr = mgmt_bcknd_get_adapter_by_id(adptr->id);
+               (void)mgmt_bcknd_destroy_trxn(adptr, trxn->trxn_id);
+
+               FOREACH_TRXN_CFG_BATCH_IN_LIST (
+                       &trxn->commit_cfg_req->req.commit_cfg
+                                .curr_batches[adptr->id],
+                       cfg_btch)
+                       cfg_btch->comm_phase = MGMTD_COMMIT_PHASE_TRXN_DELETE;
+       }
+
+       return 0;
+}
+
+static void mgmt_trxn_cfg_commit_timedout(struct thread *thread)
+{
+       struct mgmt_trxn_ctxt *trxn;
+
+       trxn = (struct mgmt_trxn_ctxt *)THREAD_ARG(thread);
+       assert(trxn);
+
+       assert(trxn->type == MGMTD_TRXN_TYPE_CONFIG);
+
+       if (!trxn->commit_cfg_req)
+               return;
+
+       MGMTD_TRXN_ERR(
+               "Backend operations for Config Trxn %p has timedout! Aborting commit!!",
+               trxn);
+
+       /*
+        * Send a COMMIT_CONFIG_REPLY with failure.
+        * NOTE: The transaction cleanup will be triggered from Front-end
+        * adapter.
+        */
+       mgmt_trxn_send_commit_cfg_reply(
+               trxn, MGMTD_INTERNAL_ERROR,
+               "Operation on the backend timed-out. Aborting commit!");
+}
+
+/*
+ * Send CFG_APPLY_REQs to all the backend client.
+ *
+ * NOTE: This is always dispatched when all CFGDATA_CREATE_REQs
+ * for all backend clients has been generated. Please see
+ * mgmt_trxn_register_event() and mgmt_trxn_process_commit_cfg()
+ * for details.
+ */
+static int mgmt_trxn_send_bcknd_cfg_apply(struct mgmt_trxn_ctxt *trxn)
+{
+       enum mgmt_bcknd_client_id id;
+       struct mgmt_bcknd_client_adapter *adptr;
+       struct mgmt_commit_cfg_req *cmtcfg_req;
+       struct mgmt_trxn_batch_list_head *btch_list;
+       struct mgmt_trxn_bcknd_cfg_batch *cfg_btch;
+
+       assert(trxn->type == MGMTD_TRXN_TYPE_CONFIG && trxn->commit_cfg_req);
+
+       cmtcfg_req = &trxn->commit_cfg_req->req.commit_cfg;
+       if (cmtcfg_req->validate_only) {
+               /*
+                * If this was a validate-only COMMIT request return success.
+                */
+               (void)mgmt_trxn_send_commit_cfg_reply(trxn, MGMTD_SUCCESS,
+                                                     NULL);
+               return 0;
+       }
+
+       FOREACH_MGMTD_BCKND_CLIENT_ID (id) {
+               if (cmtcfg_req->subscr_info.xpath_subscr[id].notify_config) {
+                       adptr = mgmt_bcknd_get_adapter_by_id(id);
+                       if (!adptr)
+                               return -1;
+
+                       btch_list = &cmtcfg_req->curr_batches[id];
+                       if (mgmt_bcknd_send_cfg_apply_req(adptr, trxn->trxn_id)
+                           != 0) {
+                               (void)mgmt_trxn_send_commit_cfg_reply(
+                                       trxn, MGMTD_INTERNAL_ERROR,
+                                       "Could not send CFG_APPLY_REQ to backend adapter");
+                               return -1;
+                       }
+                       cmtcfg_req->cmt_stats->last_num_apply_reqs++;
+
+                       UNSET_FLAG(adptr->flags,
+                                  MGMTD_BCKND_ADPTR_FLAGS_CFG_SYNCED);
+
+                       FOREACH_TRXN_CFG_BATCH_IN_LIST (btch_list, cfg_btch)
+                               cfg_btch->comm_phase =
+                                       MGMTD_COMMIT_PHASE_APPLY_CFG;
+               }
+       }
+
+       trxn->commit_cfg_req->req.commit_cfg.next_phase =
+               MGMTD_COMMIT_PHASE_TRXN_DELETE;
+
+       /*
+        * Dont move the commit to next phase yet. Wait for all VALIDATE_REPLIES
+        * to come back.
+        */
+
+       return 0;
+}
+
+#ifndef MGMTD_LOCAL_VALIDATIONS_ENABLED
+/*
+ * Send CFG_VALIDATE_REQs to all the backend client.
+ */
+static int mgmt_trxn_send_bcknd_cfg_validate(struct mgmt_trxn_ctxt *trxn)
+{
+       enum mgmt_bcknd_client_id id;
+       struct mgmt_bcknd_client_adapter *adptr;
+       struct mgmt_commit_cfg_req *cmtcfg_req;
+       uint64_t *batch_ids;
+       size_t indx, num_batches;
+       struct mgmt_trxn_batch_list_head *btch_list;
+       struct mgmt_trxn_bcknd_cfg_batch *cfg_btch;
+
+       assert(trxn->type == MGMTD_TRXN_TYPE_CONFIG && trxn->commit_cfg_req);
+
+       cmtcfg_req = &trxn->commit_cfg_req->req.commit_cfg;
+       FOREACH_MGMTD_BCKND_CLIENT_ID (id) {
+               if (cmtcfg_req->subscr_info.xpath_subscr[id].validate_config) {
+                       adptr = mgmt_bcknd_get_adapter_by_id(id);
+                       if (!adptr)
+                               return -1;
+
+                       btch_list = &cmtcfg_req->curr_batches[id];
+                       num_batches = mgmt_trxn_batch_list_count(btch_list);
+                       batch_ids = (uint64_t *)calloc(num_batches,
+                                                      sizeof(uint64_t));
+
+                       indx = 0;
+                       FOREACH_TRXN_CFG_BATCH_IN_LIST (btch_list, cfg_btch) {
+                               batch_ids[indx] = cfg_btch->batch_id;
+                               indx++;
+                               assert(indx <= num_batches);
+                       }
+
+                       if (mgmt_bcknd_send_cfg_validate_req(
+                                   adptr, trxn->trxn_id, batch_ids, indx)
+                           != 0) {
+                               (void)mgmt_trxn_send_commit_cfg_reply(
+                                       trxn, MGMTD_INTERNAL_ERROR,
+                                       "Could not send CFG_VALIDATE_REQ to backend adapter");
+                               return -1;
+                       }
+
+                       FOREACH_TRXN_CFG_BATCH_IN_LIST (btch_list, cfg_btch) {
+                               cfg_btch->comm_phase =
+                                       MGMTD_COMMIT_PHASE_VALIDATE_CFG;
+                       }
+
+                       free(batch_ids);
+               }
+       }
+
+       trxn->commit_cfg_req->req.commit_cfg.next_phase =
+               MGMTD_COMMIT_PHASE_APPLY_CFG;
+
+       /*
+        * Dont move the commit to next phase yet. Wait for all VALIDATE_REPLIES
+        * to come back.
+        */
+
+       return 0;
+}
+#endif /* iddef MGMTD_LOCAL_VALIDATIONS_ENABLED */
+
+static void mgmt_trxn_process_commit_cfg(struct thread *thread)
+{
+       struct mgmt_trxn_ctxt *trxn;
+       struct mgmt_commit_cfg_req *cmtcfg_req;
+
+       trxn = (struct mgmt_trxn_ctxt *)THREAD_ARG(thread);
+       assert(trxn);
+
+       MGMTD_TRXN_DBG(
+               "Processing COMMIT_CONFIG for Trxn:%p Session:0x%llx, Phase(Current:'%s', Next: '%s')",
+               trxn, (unsigned long long)trxn->session_id,
+               mgmt_trxn_commit_phase_str(trxn, true),
+               mgmt_trxn_commit_phase_str(trxn, false));
+
+       assert(trxn->commit_cfg_req);
+       cmtcfg_req = &trxn->commit_cfg_req->req.commit_cfg;
+       switch (cmtcfg_req->curr_phase) {
+       case MGMTD_COMMIT_PHASE_PREPARE_CFG:
+               mgmt_trxn_prepare_config(trxn);
+               break;
+       case MGMTD_COMMIT_PHASE_TRXN_CREATE:
+               if (mm->perf_stats_en)
+                       gettimeofday(&cmtcfg_req->cmt_stats->trxn_create_start,
+                                    NULL);
+               /*
+                * Send TRXN_CREATE_REQ to all Backend now.
+                */
+               mgmt_trxn_send_bcknd_trxn_create(trxn);
+               break;
+       case MGMTD_COMMIT_PHASE_SEND_CFG:
+               if (mm->perf_stats_en)
+                       gettimeofday(&cmtcfg_req->cmt_stats->send_cfg_start,
+                                    NULL);
+                       /*
+                        * All CFGDATA_CREATE_REQ should have been sent to
+                        * Backend by now.
+                        */
+#ifndef MGMTD_LOCAL_VALIDATIONS_ENABLED
+               assert(cmtcfg_req->next_phase
+                      == MGMTD_COMMIT_PHASE_VALIDATE_CFG);
+               MGMTD_TRXN_DBG(
+                       "Trxn:%p Session:0x%llx, trigger sending CFG_VALIDATE_REQ to all backend clients",
+                       trxn, (unsigned long long)trxn->session_id);
+#else  /* ifndef MGMTD_LOCAL_VALIDATIONS_ENABLED */
+               assert(cmtcfg_req->next_phase == MGMTD_COMMIT_PHASE_APPLY_CFG);
+               MGMTD_TRXN_DBG(
+                       "Trxn:%p Session:0x%llx, trigger sending CFG_APPLY_REQ to all backend clients",
+                       trxn, (unsigned long long)trxn->session_id);
+#endif /* ifndef MGMTD_LOCAL_VALIDATIONS_ENABLED */
+               break;
+#ifndef MGMTD_LOCAL_VALIDATIONS_ENABLED
+       case MGMTD_COMMIT_PHASE_VALIDATE_CFG:
+               if (mm->perf_stats_en)
+                       gettimeofday(&cmtcfg_req->cmt_stats->validate_start,
+                                    NULL);
+               /*
+                * We should have received successful CFFDATA_CREATE_REPLY from
+                * all concerned Backend Clients by now. Send out the
+                * CFG_VALIDATE_REQs now.
+                */
+               mgmt_trxn_send_bcknd_cfg_validate(trxn);
+               break;
+#endif /* ifndef MGMTD_LOCAL_VALIDATIONS_ENABLED */
+       case MGMTD_COMMIT_PHASE_APPLY_CFG:
+               if (mm->perf_stats_en)
+                       gettimeofday(&cmtcfg_req->cmt_stats->apply_cfg_start,
+                                    NULL);
+               /*
+                * We should have received successful CFG_VALIDATE_REPLY from
+                * all concerned Backend Clients by now. Send out the
+                * CFG_APPLY_REQs now.
+                */
+               mgmt_trxn_send_bcknd_cfg_apply(trxn);
+               break;
+       case MGMTD_COMMIT_PHASE_TRXN_DELETE:
+               if (mm->perf_stats_en)
+                       gettimeofday(&cmtcfg_req->cmt_stats->trxn_del_start,
+                                    NULL);
+               /*
+                * We would have sent TRXN_DELETE_REQ to all backend by now.
+                * Send a successful CONFIG_COMMIT_REPLY back to front-end.
+                * NOTE: This should also trigger DB merge/unlock and Trxn
+                * cleanup. Please see mgmt_frntnd_send_commit_cfg_reply() for
+                * more details.
+                */
+               THREAD_OFF(trxn->comm_cfg_timeout);
+               mgmt_trxn_send_commit_cfg_reply(trxn, MGMTD_SUCCESS, NULL);
+               break;
+       case MGMTD_COMMIT_PHASE_MAX:
+               break;
+       }
+
+       MGMTD_TRXN_DBG(
+               "Trxn:%p Session:0x%llx, Phase updated to (Current:'%s', Next: '%s')",
+               trxn, (unsigned long long)trxn->session_id,
+               mgmt_trxn_commit_phase_str(trxn, true),
+               mgmt_trxn_commit_phase_str(trxn, false));
+}
+
+static void mgmt_init_get_data_reply(struct mgmt_get_data_reply *get_reply)
+{
+       size_t indx;
+
+       for (indx = 0; indx < array_size(get_reply->reply_data); indx++)
+               get_reply->reply_datap[indx] = &get_reply->reply_data[indx];
+}
+
+static void mgmt_reset_get_data_reply(struct mgmt_get_data_reply *get_reply)
+{
+       int indx;
+
+       for (indx = 0; indx < get_reply->num_reply; indx++) {
+               if (get_reply->reply_xpathp[indx]) {
+                       free(get_reply->reply_xpathp[indx]);
+                       get_reply->reply_xpathp[indx] = 0;
+               }
+               if (get_reply->reply_data[indx].xpath) {
+                       zlog_debug("%s free xpath %p", __func__,
+                                  get_reply->reply_data[indx].xpath);
+                       free(get_reply->reply_data[indx].xpath);
+                       get_reply->reply_data[indx].xpath = 0;
+               }
+       }
+
+       get_reply->num_reply = 0;
+       memset(&get_reply->data_reply, 0, sizeof(get_reply->data_reply));
+       memset(&get_reply->reply_data, 0, sizeof(get_reply->reply_data));
+       memset(&get_reply->reply_datap, 0, sizeof(get_reply->reply_datap));
+
+       memset(&get_reply->reply_value, 0, sizeof(get_reply->reply_value));
+
+       mgmt_init_get_data_reply(get_reply);
+}
+
+static void mgmt_reset_get_data_reply_buf(struct mgmt_get_data_req *get_data)
+{
+       if (get_data->reply)
+               mgmt_reset_get_data_reply(get_data->reply);
+}
+
+static void mgmt_trxn_send_getcfg_reply_data(struct mgmt_trxn_req *trxn_req,
+                                            struct mgmt_get_data_req *get_req)
+{
+       struct mgmt_get_data_reply *get_reply;
+       Mgmtd__YangDataReply *data_reply;
+
+       get_reply = get_req->reply;
+       if (!get_reply)
+               return;
+
+       data_reply = &get_reply->data_reply;
+       mgmt_yang_data_reply_init(data_reply);
+       data_reply->n_data = get_reply->num_reply;
+       data_reply->data = get_reply->reply_datap;
+       data_reply->next_indx =
+               (!get_reply->last_batch ? get_req->total_reply : -1);
+
+       MGMTD_TRXN_DBG("Sending %d Get-Config/Data replies (next-idx:%lld)",
+               (int) data_reply->n_data,
+               (long long)data_reply->next_indx);
+
+       switch (trxn_req->req_event) {
+       case MGMTD_TRXN_PROC_GETCFG:
+               if (mgmt_frntnd_send_get_cfg_reply(
+                           trxn_req->trxn->session_id, trxn_req->trxn->trxn_id,
+                           get_req->db_id, trxn_req->req_id, MGMTD_SUCCESS,
+                           data_reply, NULL)
+                   != 0) {
+                       MGMTD_TRXN_ERR(
+                               "Failed to send GET-CONFIG-REPLY for Trxn %p, Sessn: 0x%llx, Req: %llu",
+                               trxn_req->trxn,
+                               (unsigned long long)trxn_req->trxn->session_id,
+                               (unsigned long long)trxn_req->req_id);
+               }
+               break;
+       case MGMTD_TRXN_PROC_GETDATA:
+               if (mgmt_frntnd_send_get_data_reply(
+                           trxn_req->trxn->session_id, trxn_req->trxn->trxn_id,
+                           get_req->db_id, trxn_req->req_id, MGMTD_SUCCESS,
+                           data_reply, NULL)
+                   != 0) {
+                       MGMTD_TRXN_ERR(
+                               "Failed to send GET-DATA-REPLY for Trxn %p, Sessn: 0x%llx, Req: %llu",
+                               trxn_req->trxn,
+                               (unsigned long long)trxn_req->trxn->session_id,
+                               (unsigned long long)trxn_req->req_id);
+               }
+               break;
+       case MGMTD_TRXN_PROC_SETCFG:
+       case MGMTD_TRXN_PROC_COMMITCFG:
+       case MGMTD_TRXN_COMMITCFG_TIMEOUT:
+       case MGMTD_TRXN_CLEANUP:
+               MGMTD_TRXN_ERR("Invalid Trxn-Req-Event %u",
+                              trxn_req->req_event);
+               break;
+       }
+
+       /*
+        * Reset reply buffer for next reply.
+        */
+       mgmt_reset_get_data_reply_buf(get_req);
+}
+
+static void mgmt_trxn_iter_and_send_get_cfg_reply(struct mgmt_db_ctxt *db_ctxt,
+                                                 char *xpath,
+                                                 struct lyd_node *node,
+                                                 struct nb_node *nb_node,
+                                                 void *ctxt)
+{
+       struct mgmt_trxn_req *trxn_req;
+       struct mgmt_get_data_req *get_req;
+       struct mgmt_get_data_reply *get_reply;
+       Mgmtd__YangData *data;
+       Mgmtd__YangDataValue *data_value;
+
+       trxn_req = (struct mgmt_trxn_req *)ctxt;
+       if (!trxn_req)
+               goto mgmtd_ignore_get_cfg_reply_data;
+
+       if (!(node->schema->nodetype & LYD_NODE_TERM))
+               goto mgmtd_ignore_get_cfg_reply_data;
+
+       assert(trxn_req->req_event == MGMTD_TRXN_PROC_GETCFG
+              || trxn_req->req_event == MGMTD_TRXN_PROC_GETDATA);
+
+       get_req = trxn_req->req.get_data;
+       assert(get_req);
+       get_reply = get_req->reply;
+       data = &get_reply->reply_data[get_reply->num_reply];
+       data_value = &get_reply->reply_value[get_reply->num_reply];
+
+       mgmt_yang_data_init(data);
+       data->xpath = xpath;
+       mgmt_yang_data_value_init(data_value);
+       data_value->value_case = MGMTD__YANG_DATA_VALUE__VALUE_ENCODED_STR_VAL;
+       data_value->encoded_str_val = (char *)lyd_get_value(node);
+       data->value = data_value;
+
+       get_reply->num_reply++;
+       get_req->total_reply++;
+       MGMTD_TRXN_DBG(" [%d] XPATH: '%s', Value: '%s'", get_req->total_reply,
+                      data->xpath, data_value->encoded_str_val);
+
+       if (get_reply->num_reply == MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH)
+               mgmt_trxn_send_getcfg_reply_data(trxn_req, get_req);
+
+       return;
+
+mgmtd_ignore_get_cfg_reply_data:
+       if (xpath)
+               free(xpath);
+}
+
+static int mgmt_trxn_get_config(struct mgmt_trxn_ctxt *trxn,
+                               struct mgmt_trxn_req *trxn_req,
+                               struct mgmt_db_ctxt *db_ctxt)
+{
+       struct mgmt_trxn_req_list_head *req_list = NULL;
+       struct mgmt_trxn_req_list_head *pending_list = NULL;
+       int indx;
+       struct mgmt_get_data_req *get_data;
+       struct mgmt_get_data_reply *get_reply;
+
+       switch (trxn_req->req_event) {
+       case MGMTD_TRXN_PROC_GETCFG:
+               req_list = &trxn->get_cfg_reqs;
+               break;
+       case MGMTD_TRXN_PROC_GETDATA:
+               req_list = &trxn->get_data_reqs;
+               break;
+       case MGMTD_TRXN_PROC_SETCFG:
+       case MGMTD_TRXN_PROC_COMMITCFG:
+       case MGMTD_TRXN_COMMITCFG_TIMEOUT:
+       case MGMTD_TRXN_CLEANUP:
+               assert(!"Wrong trxn request type!");
+               break;
+       }
+
+       get_data = trxn_req->req.get_data;
+
+       if (!get_data->reply) {
+               get_data->reply = XCALLOC(MTYPE_MGMTD_TRXN_GETDATA_REPLY,
+                                         sizeof(struct mgmt_get_data_reply));
+               if (!get_data->reply) {
+                       mgmt_frntnd_send_get_cfg_reply(
+                               trxn->session_id, trxn->trxn_id,
+                               get_data->db_id, trxn_req->req_id,
+                               MGMTD_INTERNAL_ERROR, NULL,
+                               "Internal error: Unable to allocate reply buffers!");
+                       goto mgmt_trxn_get_config_failed;
+               }
+       }
+
+       /*
+        * Read data contents from the DB and respond back directly.
+        * No need to go to backend for getting data.
+        */
+       get_reply = get_data->reply;
+       for (indx = 0; indx < get_data->num_xpaths; indx++) {
+               MGMTD_TRXN_DBG("Trying to get all data under '%s'",
+                              get_data->xpaths[indx]);
+               mgmt_init_get_data_reply(get_reply);
+               if (mgmt_db_iter_data(get_data->db_ctxt, get_data->xpaths[indx],
+                                     mgmt_trxn_iter_and_send_get_cfg_reply,
+                                     (void *)trxn_req, true)
+                   == -1) {
+                       MGMTD_TRXN_DBG("Invalid Xpath '%s",
+                                      get_data->xpaths[indx]);
+                       mgmt_frntnd_send_get_cfg_reply(
+                               trxn->session_id, trxn->trxn_id,
+                               get_data->db_id, trxn_req->req_id,
+                               MGMTD_INTERNAL_ERROR, NULL, "Invalid xpath");
+                       goto mgmt_trxn_get_config_failed;
+               }
+               MGMTD_TRXN_DBG("Got %d remaining data-replies for xpath '%s'",
+                              get_reply->num_reply, get_data->xpaths[indx]);
+               get_reply->last_batch = true;
+               mgmt_trxn_send_getcfg_reply_data(trxn_req, get_data);
+       }
+
+mgmt_trxn_get_config_failed:
+
+       if (pending_list) {
+               /*
+                * Move the transaction to corresponding pending list.
+                */
+               if (req_list)
+                       mgmt_trxn_req_list_del(req_list, trxn_req);
+               trxn_req->pending_bknd_proc = true;
+               mgmt_trxn_req_list_add_tail(pending_list, trxn_req);
+               MGMTD_TRXN_DBG(
+                       "Moved Req: %p for Trxn: %p from Req-List to Pending-List",
+                       trxn_req, trxn_req->trxn);
+       } else {
+               /*
+                * Delete the trxn request. It will also remove it from request
+                * list.
+                */
+               mgmt_trxn_req_free(&trxn_req);
+       }
+
+       return 0;
+}
+
+static void mgmt_trxn_process_get_cfg(struct thread *thread)
+{
+       struct mgmt_trxn_ctxt *trxn;
+       struct mgmt_trxn_req *trxn_req;
+       struct mgmt_db_ctxt *db_ctxt;
+       int num_processed = 0;
+       bool error;
+
+       trxn = (struct mgmt_trxn_ctxt *)THREAD_ARG(thread);
+       assert(trxn);
+
+       MGMTD_TRXN_DBG(
+               "Processing %d GET_CONFIG requests for Trxn:%p Session:0x%llx",
+               (int)mgmt_trxn_req_list_count(&trxn->get_cfg_reqs), trxn,
+               (unsigned long long)trxn->session_id);
+
+       FOREACH_TRXN_REQ_IN_LIST (&trxn->get_cfg_reqs, trxn_req) {
+               error = false;
+               assert(trxn_req->req_event == MGMTD_TRXN_PROC_GETCFG);
+               db_ctxt = trxn_req->req.get_data->db_ctxt;
+               if (!db_ctxt) {
+                       mgmt_frntnd_send_get_cfg_reply(
+                               trxn->session_id, trxn->trxn_id,
+                               trxn_req->req.get_data->db_id, trxn_req->req_id,
+                               MGMTD_INTERNAL_ERROR, NULL,
+                               "No such database!");
+                       error = true;
+                       goto mgmt_trxn_process_get_cfg_done;
+               }
+
+               if (mgmt_trxn_get_config(trxn, trxn_req, db_ctxt) != 0) {
+                       MGMTD_TRXN_ERR(
+                               "Unable to retrieve Config from DB %d for Trxn %p, Sessn: 0x%llx, Req: %llu!",
+                               trxn_req->req.get_data->db_id, trxn,
+                               (unsigned long long)trxn->session_id,
+                               (unsigned long long)trxn_req->req_id);
+                       error = true;
+               }
+
+       mgmt_trxn_process_get_cfg_done:
+
+               if (error) {
+                       /*
+                        * Delete the trxn request.
+                        * Note: The following will remove it from the list
+                        * as well.
+                        */
+                       mgmt_trxn_req_free(&trxn_req);
+               }
+
+               /*
+                * Else the transaction would have been already deleted or
+                * moved to corresponding pending list. No need to delete it.
+                */
+               num_processed++;
+               if (num_processed == MGMTD_TRXN_MAX_NUM_GETCFG_PROC)
+                       break;
+       }
+
+       if (mgmt_trxn_req_list_count(&trxn->get_cfg_reqs)) {
+               MGMTD_TRXN_DBG(
+                       "Processed maximum number of Get-Config requests (%d/%d). Rescheduling for rest.",
+                       num_processed, MGMTD_TRXN_MAX_NUM_GETCFG_PROC);
+               mgmt_trxn_register_event(trxn, MGMTD_TRXN_PROC_GETCFG);
+       }
+}
+
+static void mgmt_trxn_process_get_data(struct thread *thread)
+{
+       struct mgmt_trxn_ctxt *trxn;
+       struct mgmt_trxn_req *trxn_req;
+       struct mgmt_db_ctxt *db_ctxt;
+       int num_processed = 0;
+       bool error;
+
+       trxn = (struct mgmt_trxn_ctxt *)THREAD_ARG(thread);
+       assert(trxn);
+
+       MGMTD_TRXN_DBG(
+               "Processing %d GET_DATA requests for Trxn:%p Session:0x%llx",
+               (int)mgmt_trxn_req_list_count(&trxn->get_data_reqs), trxn,
+               (unsigned long long)trxn->session_id);
+
+       FOREACH_TRXN_REQ_IN_LIST (&trxn->get_data_reqs, trxn_req) {
+               error = false;
+               assert(trxn_req->req_event == MGMTD_TRXN_PROC_GETDATA);
+               db_ctxt = trxn_req->req.get_data->db_ctxt;
+               if (!db_ctxt) {
+                       mgmt_frntnd_send_get_data_reply(
+                               trxn->session_id, trxn->trxn_id,
+                               trxn_req->req.get_data->db_id, trxn_req->req_id,
+                               MGMTD_INTERNAL_ERROR, NULL,
+                               "No such database!");
+                       error = true;
+                       goto mgmt_trxn_process_get_data_done;
+               }
+
+               if (mgmt_db_is_config(db_ctxt)) {
+                       if (mgmt_trxn_get_config(trxn, trxn_req, db_ctxt)
+                           != 0) {
+                               MGMTD_TRXN_ERR(
+                                       "Unable to retrieve Config from DB %d for Trxn %p, Sessn: 0x%llx, Req: %llu!",
+                                       trxn_req->req.get_data->db_id, trxn,
+                                       (unsigned long long)trxn->session_id,
+                                       (unsigned long long)trxn_req->req_id);
+                               error = true;
+                       }
+               } else {
+                       /*
+                        * TODO: Trigger GET procedures for Backend
+                        * For now return back error.
+                        */
+                       mgmt_frntnd_send_get_data_reply(
+                               trxn->session_id, trxn->trxn_id,
+                               trxn_req->req.get_data->db_id, trxn_req->req_id,
+                               MGMTD_INTERNAL_ERROR, NULL,
+                               "GET-DATA on Oper DB is not supported yet!");
+                       error = true;
+               }
+
+       mgmt_trxn_process_get_data_done:
+
+               if (error) {
+                       /*
+                        * Delete the trxn request.
+                        * Note: The following will remove it from the list
+                        * as well.
+                        */
+                       mgmt_trxn_req_free(&trxn_req);
+               }
+
+               /*
+                * Else the transaction would have been already deleted or
+                * moved to corresponding pending list. No need to delete it.
+                */
+               num_processed++;
+               if (num_processed == MGMTD_TRXN_MAX_NUM_GETDATA_PROC)
+                       break;
+       }
+
+       if (mgmt_trxn_req_list_count(&trxn->get_data_reqs)) {
+               MGMTD_TRXN_DBG(
+                       "Processed maximum number of Get-Data requests (%d/%d). Rescheduling for rest.",
+                       num_processed, MGMTD_TRXN_MAX_NUM_GETDATA_PROC);
+               mgmt_trxn_register_event(trxn, MGMTD_TRXN_PROC_GETDATA);
+       }
+}
+
+static struct mgmt_trxn_ctxt *
+mgmt_frntnd_find_trxn_by_session_id(struct mgmt_master *cm, uint64_t session_id,
+                                   enum mgmt_trxn_type type)
+{
+       struct mgmt_trxn_ctxt *trxn;
+
+       FOREACH_TRXN_IN_LIST (cm, trxn) {
+               if (trxn->session_id == session_id && trxn->type == type)
+                       return trxn;
+       }
+
+       return NULL;
+}
+
+static struct mgmt_trxn_ctxt *mgmt_trxn_create_new(uint64_t session_id,
+                                                  enum mgmt_trxn_type type)
+{
+       struct mgmt_trxn_ctxt *trxn = NULL;
+
+       /*
+        * For 'CONFIG' transaction check if one is already created
+        * or not.
+        */
+       if (type == MGMTD_TRXN_TYPE_CONFIG && mgmt_trxn_mm->cfg_trxn) {
+               if (mgmt_config_trxn_in_progress() == session_id)
+                       trxn = mgmt_trxn_mm->cfg_trxn;
+               goto mgmt_create_trxn_done;
+       }
+
+       trxn = mgmt_frntnd_find_trxn_by_session_id(mgmt_trxn_mm, session_id,
+                                                  type);
+       if (!trxn) {
+               trxn = XCALLOC(MTYPE_MGMTD_TRXN, sizeof(struct mgmt_trxn_ctxt));
+               assert(trxn);
+
+               trxn->session_id = session_id;
+               trxn->type = type;
+               mgmt_trxn_badptr_list_init(&trxn->bcknd_adptrs);
+               mgmt_trxn_list_add_tail(&mgmt_trxn_mm->trxn_list, trxn);
+               mgmt_trxn_req_list_init(&trxn->set_cfg_reqs);
+               mgmt_trxn_req_list_init(&trxn->get_cfg_reqs);
+               mgmt_trxn_req_list_init(&trxn->get_data_reqs);
+               mgmt_trxn_req_list_init(&trxn->pending_get_datas);
+               trxn->commit_cfg_req = NULL;
+               trxn->refcount = 0;
+               if (!mgmt_trxn_mm->next_trxn_id)
+                       mgmt_trxn_mm->next_trxn_id++;
+               trxn->trxn_id = mgmt_trxn_mm->next_trxn_id++;
+               hash_get(mgmt_trxn_mm->trxn_hash, trxn, hash_alloc_intern);
+
+               MGMTD_TRXN_DBG("Added new '%s' MGMTD Transaction '%p'",
+                              mgmt_trxn_type2str(type), trxn);
+
+               if (type == MGMTD_TRXN_TYPE_CONFIG)
+                       mgmt_trxn_mm->cfg_trxn = trxn;
+
+               MGMTD_TRXN_LOCK(trxn);
+       }
+
+mgmt_create_trxn_done:
+       return trxn;
+}
+
+static void mgmt_trxn_delete(struct mgmt_trxn_ctxt **trxn)
+{
+       MGMTD_TRXN_UNLOCK(trxn);
+}
+
+static unsigned int mgmt_trxn_hash_key(const void *data)
+{
+       const struct mgmt_trxn_ctxt *trxn = data;
+
+       return jhash2((uint32_t *) &trxn->trxn_id,
+                     sizeof(trxn->trxn_id) / sizeof(uint32_t), 0);
+}
+
+static bool mgmt_trxn_hash_cmp(const void *d1, const void *d2)
+{
+       const struct mgmt_trxn_ctxt *trxn1 = d1;
+       const struct mgmt_trxn_ctxt *trxn2 = d2;
+
+       return (trxn1->trxn_id == trxn2->trxn_id);
+}
+
+static void mgmt_trxn_hash_free(void *data)
+{
+       struct mgmt_trxn_ctxt *trxn = data;
+
+       mgmt_trxn_delete(&trxn);
+}
+
+static void mgmt_trxn_hash_init(void)
+{
+       if (!mgmt_trxn_mm || mgmt_trxn_mm->trxn_hash)
+               return;
+
+       mgmt_trxn_mm->trxn_hash = hash_create(mgmt_trxn_hash_key,
+                                             mgmt_trxn_hash_cmp,
+                                             "MGMT Transactions");
+}
+
+static void mgmt_trxn_hash_destroy(void)
+{
+       if (!mgmt_trxn_mm || !mgmt_trxn_mm->trxn_hash)
+               return;
+
+       hash_clean(mgmt_trxn_mm->trxn_hash,
+                  mgmt_trxn_hash_free);
+       hash_free(mgmt_trxn_mm->trxn_hash);
+       mgmt_trxn_mm->trxn_hash = NULL;
+}
+
+static inline struct mgmt_trxn_ctxt *
+mgmt_trxn_id2ctxt(uint64_t trxn_id)
+{
+       struct mgmt_trxn_ctxt key = {0};
+       struct mgmt_trxn_ctxt *trxn;
+
+       if (!mgmt_trxn_mm || !mgmt_trxn_mm->trxn_hash)
+               return NULL;
+
+       key.trxn_id = trxn_id;
+       trxn = hash_lookup(mgmt_trxn_mm->trxn_hash, &key);
+
+       return trxn;
+}
+
+static void mgmt_trxn_lock(struct mgmt_trxn_ctxt *trxn, const char *file,
+                          int line)
+{
+       trxn->refcount++;
+       MGMTD_TRXN_DBG("%s:%d --> Lock %s Trxn %p, Count: %d", file, line,
+                      mgmt_trxn_type2str(trxn->type), trxn, trxn->refcount);
+}
+
+static void mgmt_trxn_unlock(struct mgmt_trxn_ctxt **trxn, const char *file,
+                            int line)
+{
+       assert(*trxn && (*trxn)->refcount);
+
+       (*trxn)->refcount--;
+       MGMTD_TRXN_DBG("%s:%d --> Unlock %s Trxn %p, Count: %d", file, line,
+                      mgmt_trxn_type2str((*trxn)->type), *trxn,
+                      (*trxn)->refcount);
+       if (!(*trxn)->refcount) {
+               if ((*trxn)->type == MGMTD_TRXN_TYPE_CONFIG)
+                       if (mgmt_trxn_mm->cfg_trxn == *trxn)
+                               mgmt_trxn_mm->cfg_trxn = NULL;
+               THREAD_OFF((*trxn)->proc_get_cfg);
+               THREAD_OFF((*trxn)->proc_get_data);
+               THREAD_OFF((*trxn)->proc_comm_cfg);
+               THREAD_OFF((*trxn)->comm_cfg_timeout);
+               hash_release(mgmt_trxn_mm->trxn_hash, *trxn);
+               mgmt_trxn_list_del(&mgmt_trxn_mm->trxn_list, *trxn);
+
+               MGMTD_TRXN_DBG("Deleted %s Trxn %p for Sessn: 0x%llx",
+                              mgmt_trxn_type2str((*trxn)->type), *trxn,
+                              (unsigned long long)(*trxn)->session_id);
+
+               XFREE(MTYPE_MGMTD_TRXN, *trxn);
+       }
+
+       *trxn = NULL;
+}
+
+static void mgmt_trxn_cleanup_trxn(struct mgmt_trxn_ctxt **trxn)
+{
+       /* TODO: Any other cleanup applicable */
+
+       mgmt_trxn_delete(trxn);
+}
+
+static void
+mgmt_trxn_cleanup_all_trxns(void)
+{
+       struct mgmt_trxn_ctxt *trxn;
+
+       if (!mgmt_trxn_mm || !mgmt_trxn_mm->trxn_hash)
+               return;
+
+       FOREACH_TRXN_IN_LIST (mgmt_trxn_mm, trxn)
+               mgmt_trxn_cleanup_trxn(&trxn);
+}
+
+static void mgmt_trxn_cleanup(struct thread *thread)
+{
+       struct mgmt_trxn_ctxt *trxn;
+
+       trxn = (struct mgmt_trxn_ctxt *)THREAD_ARG(thread);
+       assert(trxn);
+
+       mgmt_trxn_cleanup_trxn(&trxn);
+}
+
+static void mgmt_trxn_register_event(struct mgmt_trxn_ctxt *trxn,
+                                    enum mgmt_trxn_event event)
+{
+       struct timeval tv = {.tv_sec = 0,
+                            .tv_usec = MGMTD_TRXN_PROC_DELAY_USEC};
+
+       assert(mgmt_trxn_mm && mgmt_trxn_tm);
+
+       switch (event) {
+       case MGMTD_TRXN_PROC_SETCFG:
+               thread_add_timer_tv(mgmt_trxn_tm, mgmt_trxn_process_set_cfg,
+                                   trxn, &tv, &trxn->proc_set_cfg);
+               assert(trxn->proc_set_cfg);
+               break;
+       case MGMTD_TRXN_PROC_COMMITCFG:
+               thread_add_timer_tv(mgmt_trxn_tm, mgmt_trxn_process_commit_cfg,
+                                   trxn, &tv, &trxn->proc_comm_cfg);
+               assert(trxn->proc_comm_cfg);
+               break;
+       case MGMTD_TRXN_PROC_GETCFG:
+               thread_add_timer_tv(mgmt_trxn_tm, mgmt_trxn_process_get_cfg,
+                                   trxn, &tv, &trxn->proc_get_cfg);
+               assert(trxn->proc_get_cfg);
+               break;
+       case MGMTD_TRXN_PROC_GETDATA:
+               thread_add_timer_tv(mgmt_trxn_tm, mgmt_trxn_process_get_data,
+                                   trxn, &tv, &trxn->proc_get_data);
+               assert(trxn->proc_get_data);
+               break;
+       case MGMTD_TRXN_COMMITCFG_TIMEOUT:
+               thread_add_timer_msec(mgmt_trxn_tm,
+                                     mgmt_trxn_cfg_commit_timedout, trxn,
+                                     MGMTD_TRXN_CFG_COMMIT_MAX_DELAY_MSEC,
+                                     &trxn->comm_cfg_timeout);
+               assert(trxn->comm_cfg_timeout);
+               break;
+       case MGMTD_TRXN_CLEANUP:
+               tv.tv_usec = MGMTD_TRXN_CLEANUP_DELAY_USEC;
+               thread_add_timer_tv(mgmt_trxn_tm, mgmt_trxn_cleanup, trxn, &tv,
+                                   &trxn->clnup);
+               assert(trxn->clnup);
+       }
+}
+
+int mgmt_trxn_init(struct mgmt_master *mm, struct thread_master *tm)
+{
+       if (mgmt_trxn_mm || mgmt_trxn_tm)
+               assert(!"MGMTD TRXN: Call trxn_init() only once");
+
+       mgmt_trxn_mm = mm;
+       mgmt_trxn_tm = tm;
+       mgmt_trxn_list_init(&mm->trxn_list);
+       mgmt_trxn_hash_init();
+       assert(!mm->cfg_trxn);
+       mm->cfg_trxn = NULL;
+
+       return 0;
+}
+
+void mgmt_trxn_destroy(void)
+{
+       mgmt_trxn_cleanup_all_trxns();
+       mgmt_trxn_hash_destroy();
+}
+
+uint64_t mgmt_config_trxn_in_progress(void)
+{
+       if (mgmt_trxn_mm && mgmt_trxn_mm->cfg_trxn)
+               return mgmt_trxn_mm->cfg_trxn->session_id;
+
+       return MGMTD_SESSION_ID_NONE;
+}
+
+uint64_t mgmt_create_trxn(uint64_t session_id, enum mgmt_trxn_type type)
+{
+       struct mgmt_trxn_ctxt *trxn;
+
+       trxn = mgmt_trxn_create_new(session_id, type);
+       return trxn ? trxn->trxn_id : MGMTD_TRXN_ID_NONE;
+}
+
+bool mgmt_trxn_id_is_valid(uint64_t trxn_id)
+{
+       return mgmt_trxn_id2ctxt(trxn_id) ? true : false;
+}
+
+void mgmt_destroy_trxn(uint64_t *trxn_id)
+{
+       struct mgmt_trxn_ctxt *trxn;
+
+       trxn = mgmt_trxn_id2ctxt(*trxn_id);
+       if (!trxn)
+               return;
+
+       mgmt_trxn_delete(&trxn);
+       *trxn_id = MGMTD_TRXN_ID_NONE;
+}
+
+enum mgmt_trxn_type mgmt_get_trxn_type(uint64_t trxn_id)
+{
+       struct mgmt_trxn_ctxt *trxn;
+
+       trxn = mgmt_trxn_id2ctxt(trxn_id);
+       if (!trxn)
+               return MGMTD_TRXN_TYPE_NONE;
+
+       return trxn->type;
+}
+
+int mgmt_trxn_send_set_config_req(uint64_t trxn_id, uint64_t req_id,
+                                 Mgmtd__DatabaseId db_id,
+                                 struct mgmt_db_ctxt *db_ctxt,
+                                 Mgmtd__YangCfgDataReq **cfg_req,
+                                 size_t num_req, bool implicit_commit,
+                                 Mgmtd__DatabaseId dst_db_id,
+                                 struct mgmt_db_ctxt *dst_db_ctxt)
+{
+       struct mgmt_trxn_ctxt *trxn;
+       struct mgmt_trxn_req *trxn_req;
+       size_t indx;
+       uint16_t *num_chgs;
+       struct nb_cfg_change *cfg_chg;
+
+       trxn = mgmt_trxn_id2ctxt(trxn_id);
+       if (!trxn)
+               return -1;
+
+       if (implicit_commit && mgmt_trxn_req_list_count(&trxn->set_cfg_reqs)) {
+               MGMTD_TRXN_ERR(
+                       "For implicit commit config only one SETCFG-REQ can be allowed!");
+               return -1;
+       }
+
+       trxn_req = mgmt_trxn_req_alloc(trxn, req_id, MGMTD_TRXN_PROC_SETCFG);
+       trxn_req->req.set_cfg->db_id = db_id;
+       trxn_req->req.set_cfg->db_ctxt = db_ctxt;
+       num_chgs = &trxn_req->req.set_cfg->num_cfg_changes;
+       for (indx = 0; indx < num_req; indx++) {
+               cfg_chg = &trxn_req->req.set_cfg->cfg_changes[*num_chgs];
+
+               if (cfg_req[indx]->req_type
+                   == MGMTD__CFG_DATA_REQ_TYPE__DELETE_DATA)
+                       cfg_chg->operation = NB_OP_DESTROY;
+               else if (cfg_req[indx]->req_type
+                        == MGMTD__CFG_DATA_REQ_TYPE__SET_DATA)
+                       cfg_chg->operation =
+                               mgmt_db_find_data_node_by_xpath(
+                                       db_ctxt, cfg_req[indx]->data->xpath)
+                                       ? NB_OP_MODIFY
+                                       : NB_OP_CREATE;
+               else
+                       continue;
+
+               MGMTD_TRXN_DBG(
+                       "XPath: '%s', Value: '%s'", cfg_req[indx]->data->xpath,
+                       (cfg_req[indx]->data->value
+                                        && cfg_req[indx]
+                                                   ->data->value
+                                                   ->encoded_str_val
+                                ? cfg_req[indx]->data->value->encoded_str_val
+                                : "NULL"));
+               strlcpy(cfg_chg->xpath, cfg_req[indx]->data->xpath,
+                       sizeof(cfg_chg->xpath));
+               cfg_chg->value = (cfg_req[indx]->data->value
+                                                 && cfg_req[indx]
+                                                            ->data->value
+                                                            ->encoded_str_val
+                                         ? strdup(cfg_req[indx]
+                                                          ->data->value
+                                                          ->encoded_str_val)
+                                         : NULL);
+               if (cfg_chg->value)
+                       MGMTD_TRXN_DBG("Allocated value at %p ==> '%s'",
+                                      cfg_chg->value, cfg_chg->value);
+
+               (*num_chgs)++;
+       }
+       trxn_req->req.set_cfg->implicit_commit = implicit_commit;
+       trxn_req->req.set_cfg->dst_db_id = dst_db_id;
+       trxn_req->req.set_cfg->dst_db_ctxt = dst_db_ctxt;
+       trxn_req->req.set_cfg->setcfg_stats =
+               mgmt_frntnd_get_sessn_setcfg_stats(trxn->session_id);
+       mgmt_trxn_register_event(trxn, MGMTD_TRXN_PROC_SETCFG);
+
+       return 0;
+}
+
+int mgmt_trxn_send_commit_config_req(uint64_t trxn_id, uint64_t req_id,
+                                    Mgmtd__DatabaseId src_db_id,
+                                    struct mgmt_db_ctxt *src_db_ctxt,
+                                    Mgmtd__DatabaseId dst_db_id,
+                                    struct mgmt_db_ctxt *dst_db_ctxt,
+                                    bool validate_only, bool abort,
+                                    bool implicit)
+{
+       struct mgmt_trxn_ctxt *trxn;
+       struct mgmt_trxn_req *trxn_req;
+
+       trxn = mgmt_trxn_id2ctxt(trxn_id);
+       if (!trxn)
+               return -1;
+
+       if (trxn->commit_cfg_req) {
+               MGMTD_TRXN_ERR(
+                       "A commit is already in-progress for Trxn %p, session 0x%llx. Cannot start another!",
+                       trxn, (unsigned long long)trxn->session_id);
+               return -1;
+       }
+
+       trxn_req = mgmt_trxn_req_alloc(trxn, req_id, MGMTD_TRXN_PROC_COMMITCFG);
+       trxn_req->req.commit_cfg.src_db_id = src_db_id;
+       trxn_req->req.commit_cfg.src_db_ctxt = src_db_ctxt;
+       trxn_req->req.commit_cfg.dst_db_id = dst_db_id;
+       trxn_req->req.commit_cfg.dst_db_ctxt = dst_db_ctxt;
+       trxn_req->req.commit_cfg.validate_only = validate_only;
+       trxn_req->req.commit_cfg.abort = abort;
+       trxn_req->req.commit_cfg.implicit = implicit;
+       trxn_req->req.commit_cfg.cmt_stats =
+               mgmt_frntnd_get_sessn_commit_stats(trxn->session_id);
+
+       /*
+        * Trigger a COMMIT-CONFIG process.
+        */
+       mgmt_trxn_register_event(trxn, MGMTD_TRXN_PROC_COMMITCFG);
+       return 0;
+}
+
+int mgmt_trxn_notify_bcknd_adapter_conn(struct mgmt_bcknd_client_adapter *adptr,
+                                       bool connect)
+{
+       struct mgmt_trxn_ctxt *trxn;
+       struct mgmt_trxn_req *trxn_req;
+       struct mgmt_commit_cfg_req *cmtcfg_req;
+       static struct mgmt_commit_stats dummy_stats;
+       struct nb_config_cbs *adptr_cfgs = NULL;
+
+       memset(&dummy_stats, 0, sizeof(dummy_stats));
+       if (connect) {
+               /* Get config for this single backend client */
+               mgmt_bcknd_get_adapter_config(adptr, mm->running_db,
+                                             &adptr_cfgs);
+
+               if (!adptr_cfgs || RB_EMPTY(nb_config_cbs, adptr_cfgs)) {
+                       SET_FLAG(adptr->flags,
+                                MGMTD_BCKND_ADPTR_FLAGS_CFG_SYNCED);
+                       return 0;
+               }
+
+               /*
+                * Create a CONFIG transaction to push the config changes
+                * provided to the backend client.
+                */
+               trxn = mgmt_trxn_create_new(0, MGMTD_TRXN_TYPE_CONFIG);
+               if (!trxn) {
+                       MGMTD_TRXN_ERR(
+                               "Failed to create CONFIG Transaction for downloading CONFIGs for client '%s'",
+                               adptr->name);
+                       return -1;
+               }
+
+               /*
+                * Set the changeset for transaction to commit and trigger the
+                * commit request.
+                */
+               trxn_req =
+                       mgmt_trxn_req_alloc(trxn, 0, MGMTD_TRXN_PROC_COMMITCFG);
+               trxn_req->req.commit_cfg.src_db_id = MGMTD_DB_NONE;
+               trxn_req->req.commit_cfg.src_db_ctxt = 0;
+               trxn_req->req.commit_cfg.dst_db_id = MGMTD_DB_NONE;
+               trxn_req->req.commit_cfg.dst_db_ctxt = 0;
+               trxn_req->req.commit_cfg.validate_only = false;
+               trxn_req->req.commit_cfg.abort = false;
+               trxn_req->req.commit_cfg.cmt_stats = &dummy_stats;
+               trxn_req->req.commit_cfg.cfg_chgs = adptr_cfgs;
+
+               /*
+                * Trigger a COMMIT-CONFIG process.
+                */
+               mgmt_trxn_register_event(trxn, MGMTD_TRXN_PROC_COMMITCFG);
+
+       } else {
+               /*
+                * Check if any transaction is currently on-going that
+                * involves this backend client. If so, report the transaction
+                * has failed.
+                */
+               FOREACH_TRXN_IN_LIST (mgmt_trxn_mm, trxn) {
+                       if (trxn->type == MGMTD_TRXN_TYPE_CONFIG) {
+                               cmtcfg_req = trxn->commit_cfg_req
+                                                    ? &trxn->commit_cfg_req
+                                                               ->req.commit_cfg
+                                                    : NULL;
+                               if (cmtcfg_req
+                                   && cmtcfg_req->subscr_info
+                                              .xpath_subscr[adptr->id]
+                                              .subscribed) {
+                                       mgmt_trxn_send_commit_cfg_reply(
+                                               trxn, MGMTD_INTERNAL_ERROR,
+                                               "Backend daemon disconnected while processing commit!");
+                               }
+                       }
+               }
+       }
+
+       return 0;
+}
+
+int mgmt_trxn_notify_bcknd_trxn_reply(uint64_t trxn_id, bool create,
+                                     bool success,
+                                     struct mgmt_bcknd_client_adapter *adptr)
+{
+       struct mgmt_trxn_ctxt *trxn;
+       struct mgmt_commit_cfg_req *cmtcfg_req = NULL;
+
+       trxn = mgmt_trxn_id2ctxt(trxn_id);
+       if (!trxn || trxn->type != MGMTD_TRXN_TYPE_CONFIG)
+               return -1;
+
+       if (!create && !trxn->commit_cfg_req)
+               return 0;
+
+       assert(trxn->commit_cfg_req);
+       cmtcfg_req = &trxn->commit_cfg_req->req.commit_cfg;
+       if (create) {
+               if (success) {
+                       /*
+                        * Done with TRXN_CREATE. Move the backend client to
+                        * next phase.
+                        */
+                       assert(cmtcfg_req->curr_phase
+                              == MGMTD_COMMIT_PHASE_TRXN_CREATE);
+
+                       /*
+                        * Send CFGDATA_CREATE-REQs to the backend immediately.
+                        */
+                       mgmt_trxn_send_bcknd_cfg_data(trxn, adptr);
+               } else {
+                       mgmt_trxn_send_commit_cfg_reply(
+                               trxn, MGMTD_INTERNAL_ERROR,
+                               "Internal error! Failed to initiate transaction at backend!");
+               }
+       } else {
+               /*
+                * Done with TRXN_DELETE. Move the backend client to next phase.
+                */
+               if (false)
+                       mgmt_move_bcknd_commit_to_next_phase(trxn, adptr);
+       }
+
+       return 0;
+}
+
+int mgmt_trxn_notify_bcknd_cfgdata_reply(
+       uint64_t trxn_id, uint64_t batch_id, bool success, char *error_if_any,
+       struct mgmt_bcknd_client_adapter *adptr)
+{
+       struct mgmt_trxn_ctxt *trxn;
+       struct mgmt_trxn_bcknd_cfg_batch *cfg_btch;
+       struct mgmt_commit_cfg_req *cmtcfg_req = NULL;
+
+       trxn = mgmt_trxn_id2ctxt(trxn_id);
+       if (!trxn || trxn->type != MGMTD_TRXN_TYPE_CONFIG)
+               return -1;
+
+       if (!trxn->commit_cfg_req)
+               return -1;
+       cmtcfg_req = &trxn->commit_cfg_req->req.commit_cfg;
+
+       cfg_btch = mgmt_trxn_cfgbatch_id2ctxt(trxn, batch_id);
+       if (!cfg_btch || cfg_btch->trxn != trxn)
+               return -1;
+
+       if (!success) {
+               MGMTD_TRXN_ERR(
+                       "CFGDATA_CREATE_REQ sent to '%s' failed for Trxn %p, Batch %p, Err: %s",
+                       adptr->name, trxn, cfg_btch,
+                       error_if_any ? error_if_any : "None");
+               mgmt_trxn_send_commit_cfg_reply(
+                       trxn, MGMTD_INTERNAL_ERROR,
+                       "Internal error! Failed to download config data to backend!");
+               return 0;
+       }
+
+       MGMTD_TRXN_DBG(
+               "CFGDATA_CREATE_REQ sent to '%s' was successful for Trxn %p, Batch %p, Err: %s",
+               adptr->name, trxn, cfg_btch,
+               error_if_any ? error_if_any : "None");
+       mgmt_move_trxn_cfg_batch_to_next(
+               cmtcfg_req, cfg_btch, &cmtcfg_req->curr_batches[adptr->id],
+               &cmtcfg_req->next_batches[adptr->id], true,
+#ifndef MGMTD_LOCAL_VALIDATIONS_ENABLED
+               MGMTD_COMMIT_PHASE_VALIDATE_CFG);
+#else  /* ifndef MGMTD_LOCAL_VALIDATIONS_ENABLED */
+               MGMTD_COMMIT_PHASE_APPLY_CFG);
+#endif /* ifndef MGMTD_LOCAL_VALIDATIONS_ENABLED */
+
+       mgmt_try_move_commit_to_next_phase(trxn, cmtcfg_req);
+
+       return 0;
+}
+
+int mgmt_trxn_notify_bcknd_cfg_validate_reply(
+       uint64_t trxn_id, bool success, uint64_t batch_ids[],
+       size_t num_batch_ids, char *error_if_any,
+       struct mgmt_bcknd_client_adapter *adptr)
+{
+       struct mgmt_trxn_ctxt *trxn;
+       struct mgmt_trxn_bcknd_cfg_batch *cfg_btch;
+       struct mgmt_commit_cfg_req *cmtcfg_req = NULL;
+       size_t indx;
+
+       trxn = mgmt_trxn_id2ctxt(trxn_id);
+       if (!trxn || trxn->type != MGMTD_TRXN_TYPE_CONFIG)
+               return -1;
+
+       assert(trxn->commit_cfg_req);
+       cmtcfg_req = &trxn->commit_cfg_req->req.commit_cfg;
+
+       if (!success) {
+               MGMTD_TRXN_ERR(
+                       "CFGDATA_VALIDATE_REQ sent to '%s' failed for Trxn %p, Batches [0x%llx - 0x%llx], Err: %s",
+                       adptr->name, trxn, (unsigned long long)batch_ids[0],
+                       (unsigned long long)batch_ids[num_batch_ids - 1],
+                       error_if_any ? error_if_any : "None");
+               mgmt_trxn_send_commit_cfg_reply(
+                       trxn, MGMTD_INTERNAL_ERROR,
+                       "Internal error! Failed to validate config data on backend!");
+               return 0;
+       }
+
+       for (indx = 0; indx < num_batch_ids; indx++) {
+               cfg_btch = mgmt_trxn_cfgbatch_id2ctxt(trxn, batch_ids[indx]);
+               if (cfg_btch->trxn != trxn)
+                       return -1;
+               mgmt_move_trxn_cfg_batch_to_next(
+                       cmtcfg_req, cfg_btch,
+                       &cmtcfg_req->curr_batches[adptr->id],
+                       &cmtcfg_req->next_batches[adptr->id], true,
+                       MGMTD_COMMIT_PHASE_APPLY_CFG);
+       }
+
+       mgmt_try_move_commit_to_next_phase(trxn, cmtcfg_req);
+
+       return 0;
+}
+
+extern int
+mgmt_trxn_notify_bcknd_cfg_apply_reply(uint64_t trxn_id, bool success,
+                                      uint64_t batch_ids[],
+                                      size_t num_batch_ids, char *error_if_any,
+                                      struct mgmt_bcknd_client_adapter *adptr)
+{
+       struct mgmt_trxn_ctxt *trxn;
+       struct mgmt_trxn_bcknd_cfg_batch *cfg_btch;
+       struct mgmt_commit_cfg_req *cmtcfg_req = NULL;
+       size_t indx;
+
+       trxn = mgmt_trxn_id2ctxt(trxn_id);
+       if (!trxn || trxn->type != MGMTD_TRXN_TYPE_CONFIG
+           || !trxn->commit_cfg_req)
+               return -1;
+
+       cmtcfg_req = &trxn->commit_cfg_req->req.commit_cfg;
+
+       if (!success) {
+               MGMTD_TRXN_ERR(
+                       "CFGDATA_APPLY_REQ sent to '%s' failed for Trxn %p, Batches [0x%llx - 0x%llx], Err: %s",
+                       adptr->name, trxn, (unsigned long long)batch_ids[0],
+                       (unsigned long long)batch_ids[num_batch_ids - 1],
+                       error_if_any ? error_if_any : "None");
+               mgmt_trxn_send_commit_cfg_reply(
+                       trxn, MGMTD_INTERNAL_ERROR,
+                       "Internal error! Failed to apply config data on backend!");
+               return 0;
+       }
+
+       for (indx = 0; indx < num_batch_ids; indx++) {
+               cfg_btch = mgmt_trxn_cfgbatch_id2ctxt(trxn, batch_ids[indx]);
+               if (cfg_btch->trxn != trxn)
+                       return -1;
+               mgmt_move_trxn_cfg_batch_to_next(
+                       cmtcfg_req, cfg_btch,
+                       &cmtcfg_req->curr_batches[adptr->id],
+                       &cmtcfg_req->next_batches[adptr->id], true,
+                       MGMTD_COMMIT_PHASE_TRXN_DELETE);
+       }
+
+       if (!mgmt_trxn_batch_list_count(&cmtcfg_req->curr_batches[adptr->id])) {
+               /*
+                * All configuration for the specific backend has been applied.
+                * Send TRXN-DELETE to wrap up the transaction for this backend.
+                */
+               SET_FLAG(adptr->flags, MGMTD_BCKND_ADPTR_FLAGS_CFG_SYNCED);
+               mgmt_trxn_send_bcknd_trxn_delete(trxn, adptr);
+       }
+
+       mgmt_try_move_commit_to_next_phase(trxn, cmtcfg_req);
+       if (mm->perf_stats_en)
+               gettimeofday(&cmtcfg_req->cmt_stats->apply_cfg_end, NULL);
+
+       return 0;
+}
+
+int mgmt_trxn_send_commit_config_reply(uint64_t trxn_id,
+                                      enum mgmt_result result,
+                                      const char *error_if_any)
+{
+       struct mgmt_trxn_ctxt *trxn;
+
+       trxn = mgmt_trxn_id2ctxt(trxn_id);
+       if (!trxn)
+               return -1;
+
+       if (!trxn->commit_cfg_req) {
+               MGMTD_TRXN_ERR(
+                       "NO commit in-progress for Trxn %p, session 0x%llx!",
+                       trxn, (unsigned long long)trxn->session_id);
+               return -1;
+       }
+
+       return mgmt_trxn_send_commit_cfg_reply(trxn, result, error_if_any);
+}
+
+int mgmt_trxn_send_get_config_req(uint64_t trxn_id, uint64_t req_id,
+                                 Mgmtd__DatabaseId db_id,
+                                 struct mgmt_db_ctxt *db_ctxt,
+                                 Mgmtd__YangGetDataReq **data_req,
+                                 size_t num_reqs)
+{
+       struct mgmt_trxn_ctxt *trxn;
+       struct mgmt_trxn_req *trxn_req;
+       size_t indx;
+
+       trxn = mgmt_trxn_id2ctxt(trxn_id);
+       if (!trxn)
+               return -1;
+
+       trxn_req = mgmt_trxn_req_alloc(trxn, req_id, MGMTD_TRXN_PROC_GETCFG);
+       trxn_req->req.get_data->db_id = db_id;
+       trxn_req->req.get_data->db_ctxt = db_ctxt;
+       for (indx = 0;
+            indx < num_reqs && indx < MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH;
+            indx++) {
+               MGMTD_TRXN_DBG("XPath: '%s'", data_req[indx]->data->xpath);
+               trxn_req->req.get_data->xpaths[indx] =
+                       strdup(data_req[indx]->data->xpath);
+               trxn_req->req.get_data->num_xpaths++;
+       }
+
+       mgmt_trxn_register_event(trxn, MGMTD_TRXN_PROC_GETCFG);
+
+       return 0;
+}
+
+int mgmt_trxn_send_get_data_req(uint64_t trxn_id, uint64_t req_id,
+                               Mgmtd__DatabaseId db_id,
+                               struct mgmt_db_ctxt *db_ctxt,
+                               Mgmtd__YangGetDataReq **data_req,
+                               size_t num_reqs)
+{
+       struct mgmt_trxn_ctxt *trxn;
+       struct mgmt_trxn_req *trxn_req;
+       size_t indx;
+
+       trxn = mgmt_trxn_id2ctxt(trxn_id);
+       if (!trxn)
+               return -1;
+
+       trxn_req = mgmt_trxn_req_alloc(trxn, req_id, MGMTD_TRXN_PROC_GETDATA);
+       trxn_req->req.get_data->db_id = db_id;
+       trxn_req->req.get_data->db_ctxt = db_ctxt;
+       for (indx = 0;
+            indx < num_reqs && indx < MGMTD_MAX_NUM_DATA_REPLY_IN_BATCH;
+            indx++) {
+               MGMTD_TRXN_DBG("XPath: '%s'", data_req[indx]->data->xpath);
+               trxn_req->req.get_data->xpaths[indx] =
+                       strdup(data_req[indx]->data->xpath);
+               trxn_req->req.get_data->num_xpaths++;
+       }
+
+       mgmt_trxn_register_event(trxn, MGMTD_TRXN_PROC_GETDATA);
+
+       return 0;
+}
+
+void mgmt_trxn_status_write(struct vty *vty)
+{
+       struct mgmt_trxn_ctxt *trxn;
+
+       vty_out(vty, "MGMTD Transactions\n");
+
+       FOREACH_TRXN_IN_LIST (mgmt_trxn_mm, trxn) {
+               vty_out(vty, "  Trxn: \t\t\t%p\n", trxn);
+               vty_out(vty, "    Trxn-Id: \t\t\t%llu\n",
+                       (unsigned long long)trxn->trxn_id);
+               vty_out(vty, "    Session-Id: \t\t%llu\n",
+                       (unsigned long long)trxn->session_id);
+               vty_out(vty, "    Type: \t\t\t%s\n",
+                       mgmt_trxn_type2str(trxn->type));
+               vty_out(vty, "    Ref-Count: \t\t\t%d\n", trxn->refcount);
+       }
+       vty_out(vty, "  Total: %d\n",
+               (int)mgmt_trxn_list_count(&mgmt_trxn_mm->trxn_list));
+}
+
+int mgmt_trxn_rollback_trigger_cfg_apply(struct mgmt_db_ctxt *src_db_ctxt,
+                                        struct mgmt_db_ctxt *dst_db_ctxt)
+{
+       static struct nb_config_cbs changes;
+       struct nb_config_cbs *cfg_chgs = NULL;
+       struct mgmt_trxn_ctxt *trxn;
+       struct mgmt_trxn_req *trxn_req;
+       static struct mgmt_commit_stats dummy_stats;
+
+       memset(&changes, 0, sizeof(changes));
+       memset(&dummy_stats, 0, sizeof(dummy_stats));
+       /*
+        * This could be the case when the config is directly
+        * loaded onto the candidate DB from a file. Get the
+        * diff from a full comparison of the candidate and
+        * running DBs.
+        */
+       nb_config_diff(mgmt_db_get_nb_config(dst_db_ctxt),
+                      mgmt_db_get_nb_config(src_db_ctxt), &changes);
+       cfg_chgs = &changes;
+
+       if (RB_EMPTY(nb_config_cbs, cfg_chgs)) {
+               /*
+                * This means there's no changes to commit whatsoever
+                * is the source of the changes in config.
+                */
+               return -1;
+       }
+
+       /*
+        * Create a CONFIG transaction to push the config changes
+        * provided to the backend client.
+        */
+       trxn = mgmt_trxn_create_new(0, MGMTD_TRXN_TYPE_CONFIG);
+       if (!trxn) {
+               MGMTD_TRXN_ERR(
+                       "Failed to create CONFIG Transaction for downloading CONFIGs");
+               return -1;
+       }
+
+       /*
+        * Set the changeset for transaction to commit and trigger the commit
+        * request.
+        */
+       trxn_req = mgmt_trxn_req_alloc(trxn, 0, MGMTD_TRXN_PROC_COMMITCFG);
+       trxn_req->req.commit_cfg.src_db_id = MGMTD_DB_CANDIDATE;
+       trxn_req->req.commit_cfg.src_db_ctxt = src_db_ctxt;
+       trxn_req->req.commit_cfg.dst_db_id = MGMTD_DB_RUNNING;
+       trxn_req->req.commit_cfg.dst_db_ctxt = dst_db_ctxt;
+       trxn_req->req.commit_cfg.validate_only = false;
+       trxn_req->req.commit_cfg.abort = false;
+       trxn_req->req.commit_cfg.rollback = true;
+       trxn_req->req.commit_cfg.cmt_stats = &dummy_stats;
+       trxn_req->req.commit_cfg.cfg_chgs = cfg_chgs;
+
+       /*
+        * Trigger a COMMIT-CONFIG process.
+        */
+       mgmt_trxn_register_event(trxn, MGMTD_TRXN_PROC_COMMITCFG);
+       return 0;
+}
diff --git a/mgmtd/mgmt_trxn.h b/mgmtd/mgmt_trxn.h
new file mode 100644 (file)
index 0000000..50ea5d8
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * MGMTD Transactions
+ * Copyright (C) 2021  Vmware, Inc.
+ *                    Pushpasis Sarkar <spushpasis@vmware.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_MGMTD_TRXN_H_
+#define _FRR_MGMTD_TRXN_H_
+
+#include "mgmtd/mgmt_bcknd_adapter.h"
+#include "mgmtd/mgmt.h"
+#include "mgmtd/mgmt_db.h"
+
+#define MGMTD_TRXN_PROC_DELAY_MSEC 5
+#define MGMTD_TRXN_PROC_DELAY_USEC 10
+#define MGMTD_TRXN_MAX_NUM_SETCFG_PROC 128
+#define MGMTD_TRXN_MAX_NUM_GETCFG_PROC 128
+#define MGMTD_TRXN_MAX_NUM_GETDATA_PROC 128
+
+#define MGMTD_TRXN_SEND_CFGVALIDATE_DELAY_MSEC 100
+#define MGMTD_TRXN_SEND_CFGAPPLY_DELAY_MSEC 100
+#define MGMTD_TRXN_CFG_COMMIT_MAX_DELAY_MSEC 30000 /* 30 seconds */
+
+#define MGMTD_TRXN_CLEANUP_DELAY_MSEC 100
+#define MGMTD_TRXN_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_trxn_list);
+
+struct mgmt_master;
+
+enum mgmt_trxn_type {
+       MGMTD_TRXN_TYPE_NONE = 0,
+       MGMTD_TRXN_TYPE_CONFIG,
+       MGMTD_TRXN_TYPE_SHOW
+};
+
+static inline const char *mgmt_trxn_type2str(enum mgmt_trxn_type type)
+{
+       switch (type) {
+       case MGMTD_TRXN_TYPE_NONE:
+               return "None";
+       case MGMTD_TRXN_TYPE_CONFIG:
+               return "CONFIG";
+       case MGMTD_TRXN_TYPE_SHOW:
+               return "SHOW";
+       }
+
+       return "Unknown";
+}
+
+/* Initialise transaction module. */
+extern int mgmt_trxn_init(struct mgmt_master *cm, struct thread_master *tm);
+
+/* Destroy the transaction module. */
+extern void mgmt_trxn_destroy(void);
+
+/*
+ * Check if transaction is in progress.
+ *
+ * Returns:
+ *    session ID if in-progress, MGMTD_SESSION_ID_NONE otherwise.
+ */
+extern uint64_t mgmt_config_trxn_in_progress(void);
+
+/*
+ * Create transaction.
+ *
+ * session_id
+ *    Session ID.
+ *
+ * type
+ *    Transaction type (CONFIG/SHOW/NONE)
+ *
+ * Returns:
+ *    transaction ID.
+ */
+extern uint64_t mgmt_create_trxn(uint64_t session_id, enum mgmt_trxn_type type);
+
+/*
+ * Destroy transaction.
+ *
+ * trxn_id
+ *     Unique transaction identifier.
+ */
+extern void mgmt_destroy_trxn(uint64_t *trxn_id);
+
+/*
+ * Check if transaction is valid given an ID.
+ */
+extern bool mgmt_trxn_id_is_valid(uint64_t trxn_id);
+
+/*
+ * Returns the type of transaction given an ID.
+ */
+extern enum mgmt_trxn_type mgmt_get_trxn_type(uint64_t trxn_id);
+
+/*
+ * Send set-config request to be processed later in transaction.
+ *
+ * trxn_id
+ *    Unique transaction identifier.
+ *
+ * req_id
+ *    Unique transaction request identifier.
+ *
+ * db_id
+ *    Database ID.
+ *
+ * db_hndl
+ *    Database handle.
+ *
+ * cfg_req
+ *    Config requests.
+ *
+ * num_req
+ *    Number of config requests.
+ *
+ * implicit_commit
+ *    TRUE if the commit is implicit, FALSE otherwise.
+ *
+ * dst_db_id
+ *    Destination database ID.
+ *
+ * dst_db_handle
+ *    Destination database handle.
+ *
+ * Returns:
+ *    0 on success, -1 on failures.
+ */
+extern int mgmt_trxn_send_set_config_req(uint64_t trxn_id, uint64_t req_id,
+                                        Mgmtd__DatabaseId db_id,
+                                        struct mgmt_db_ctxt *db_ctxt,
+                                        Mgmtd__YangCfgDataReq **cfg_req,
+                                        size_t num_req, bool implicit_commit,
+                                        Mgmtd__DatabaseId dst_db_id,
+                                        struct mgmt_db_ctxt *dst_db_ctxt);
+
+/*
+ * Send commit-config request to be processed later in transaction.
+ *
+ * trxn_id
+ *    Unique transaction identifier.
+ *
+ * req_id
+ *    Unique transaction request identifier.
+ *
+ * src_db_id
+ *    Source database ID.
+ *
+ * src_db_hndl
+ *    Source Database handle.
+ *
+ * validate_only
+ *    TRUE if commit request needs to be validated only, FALSE otherwise.
+ *
+ * abort
+ *    TRUE if need to restore Src DB back to Dest DB, FALSE otherwise.
+ *
+ * implicit
+ *    TRUE if the commit is implicit, FALSE otherwise.
+ *
+ * Returns:
+ *    0 on success, -1 on failures.
+ */
+extern int mgmt_trxn_send_commit_config_req(uint64_t trxn_id, uint64_t req_id,
+                                           Mgmtd__DatabaseId src_db_id,
+                                           struct mgmt_db_ctxt *dst_db_ctxt,
+                                           Mgmtd__DatabaseId dst_db_id,
+                                           struct mgmt_db_ctxt *src_db_ctxt,
+                                           bool validate_only, bool abort,
+                                           bool implicit);
+
+extern int mgmt_trxn_send_commit_config_reply(uint64_t trxn_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_trxn_send_get_config_req(uint64_t trxn_id, uint64_t req_id,
+                                        Mgmtd__DatabaseId db_id,
+                                        struct mgmt_db_ctxt *db_ctxt,
+                                        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_trxn_send_get_data_req(uint64_t trxn_id, uint64_t req_id,
+                                      Mgmtd__DatabaseId db_id,
+                                      struct mgmt_db_ctxt *db_ctxt,
+                                      Mgmtd__YangGetDataReq **data_req,
+                                      size_t num_reqs);
+
+/*
+ * Notifiy backend adapter on connection.
+ */
+extern int
+mgmt_trxn_notify_bcknd_adapter_conn(struct mgmt_bcknd_client_adapter *adptr,
+                                   bool connect);
+
+/*
+ * Reply to backend adapter about transaction create/delete.
+ */
+extern int
+mgmt_trxn_notify_bcknd_trxn_reply(uint64_t trxn_id, bool create, bool success,
+                                 struct mgmt_bcknd_client_adapter *adptr);
+
+/*
+ * Reply to backend adapater with config data create request.
+ */
+extern int
+mgmt_trxn_notify_bcknd_cfgdata_reply(uint64_t trxn_id, uint64_t batch_id,
+                                    bool success, char *error_if_any,
+                                    struct mgmt_bcknd_client_adapter *adptr);
+
+/*
+ * Reply to backend adapater with config data validate request.
+ */
+extern int mgmt_trxn_notify_bcknd_cfg_validate_reply(
+       uint64_t trxn_id, bool success, uint64_t batch_ids[],
+       size_t num_batch_ids, char *error_if_any,
+       struct mgmt_bcknd_client_adapter *adptr);
+
+/*
+ * Reply to backend adapater with config data apply request.
+ */
+extern int
+mgmt_trxn_notify_bcknd_cfg_apply_reply(uint64_t trxn_id, bool success,
+                                      uint64_t batch_ids[],
+                                      size_t num_batch_ids, char *error_if_any,
+                                      struct mgmt_bcknd_client_adapter *adptr);
+
+/*
+ * Dump transaction status to vty.
+ */
+extern void mgmt_trxn_status_write(struct vty *vty);
+
+/*
+ * Trigger rollback config apply.
+ *
+ * Creates a new transaction and commit request for rollback.
+ */
+extern int
+mgmt_trxn_rollback_trigger_cfg_apply(struct mgmt_db_ctxt *src_db_ctxt,
+                                    struct mgmt_db_ctxt *dst_db_ctxt);
+#endif /* _FRR_MGMTD_TRXN_H_ */
index 03d2981a88d60c096ab3e6c45a7cb9764cbebd26..b15a597a650e9117cd359aea0e555d2e99ce477e 100644 (file)
 #include "mgmtd/mgmt_vty_clippy.c"
 #endif
 
+/*
+ * mgmt_enqueue_nb_command
+ *
+ * Add a config command from VTYSH for further processing.
+ *
+ * NOTE: This function is ALWAYS called from one of the
+ * command handlers installed on MGMTD daemon that is invoked
+ * by lib/vty.c on receiving a command from VTYSH.
+ */
+void mgmt_enqueue_vty_nb_command(struct vty *vty, const char *xpath,
+                                enum nb_operation operation, const char *value)
+{
+       switch (operation) {
+       case NB_OP_CREATE:
+       case NB_OP_MODIFY:
+       case NB_OP_DESTROY:
+       case NB_OP_MOVE:
+       case NB_OP_PRE_VALIDATE:
+               /* Process on MGMTD daemon itself */
+               nb_cli_enqueue_change(vty, xpath, operation, value);
+               break;
+       case NB_OP_APPLY_FINISH:
+       case NB_OP_GET_ELEM:
+       case NB_OP_GET_NEXT:
+       case NB_OP_GET_KEYS:
+       case NB_OP_LOOKUP_ENTRY:
+       case NB_OP_RPC:
+               /* To be sent to backend for processing */
+               break;
+       }
+}
+
+/*
+ * mgmt_apply_nb_commands
+ *
+ * Apply all config command enqueued from VTYSH so far for further
+ * processing.
+ *
+ * NOTE: This function is ALWAYS called from one of the
+ * command handlers installed on MGMTD daemon that is invoked
+ * by lib/vty.c on receiving a command from VTYSH.
+ */
+int mgmt_apply_vty_nb_commands(struct vty *vty, const char *xpath_base_fmt, ...)
+{
+       char xpath_base[XPATH_MAXLEN] = {};
+
+       /* Parse the base XPath format string. */
+       if (xpath_base_fmt) {
+               va_list ap;
+
+               va_start(ap, xpath_base_fmt);
+               vsnprintf(xpath_base, sizeof(xpath_base), xpath_base_fmt, ap);
+               va_end(ap);
+       }
+
+       vty_mgmt_send_config_data(vty);
+       return 0;
+}
+
 DEFPY(show_mgmt_bcknd_adapter,
       show_mgmt_bcknd_adapter_cmd,
       "show mgmt backend-adapter all",
@@ -86,6 +145,45 @@ DEFPY(show_mgmt_frntnd_adapter_detail,
        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_frntnd_adapter_perf_measurement(vty, false);
+       else
+               mgmt_frntnd_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_frntnd_adapter_reset_perf_stats(vty);
+
+       return CMD_SUCCESS;
+}
+
+DEFPY(show_mgmt_trxn,
+      show_mgmt_trxn_cmd,
+      "show mgmt transaction all",
+      SHOW_STR
+      MGMTD_STR
+      MGMTD_TRXN_STR
+      "Display all Transactions\n")
+{
+       mgmt_trxn_status_write(vty);
+
+       return CMD_SUCCESS;
+}
+
 DEFPY(show_mgmt_db_all,
       show_mgmt_db_all_cmd,
       "show mgmt database all",
@@ -297,8 +395,6 @@ DEFPY(show_mgmt_get_data,
        return CMD_SUCCESS;
 }
 
-
-
 DEFPY(show_mgmt_dump_data,
       show_mgmt_dump_data_cmd,
       "show mgmt database-contents db-name WORD$dbname [xpath WORD$path] [file WORD$filepath] format WORD$format_str",
@@ -471,6 +567,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_db_rollback_by_cmtid(vty, commit);
+       else
+               mgmt_db_rollback_commits(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)
 {
        if (mgmt_debug_bcknd && mgmt_debug_frntnd && mgmt_debug_db
@@ -489,12 +622,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_bcknd,
       debug_mgmt_bcknd_cmd,
@@ -590,6 +717,7 @@ void mgmt_vty_init(void)
        install_element(VIEW_NODE, &show_mgmt_bcknd_xpath_reg_cmd);
        install_element(VIEW_NODE, &show_mgmt_frntnd_adapter_cmd);
        install_element(VIEW_NODE, &show_mgmt_frntnd_adapter_detail_cmd);
+       install_element(VIEW_NODE, &show_mgmt_trxn_cmd);
        install_element(VIEW_NODE, &show_mgmt_db_all_cmd);
        install_element(VIEW_NODE, &show_mgmt_db_runn_cmd);
        install_element(VIEW_NODE, &show_mgmt_db_cand_cmd);
@@ -598,6 +726,7 @@ void mgmt_vty_init(void)
        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_apply_cmd);
        install_element(CONFIG_NODE, &mgmt_commit_abort_cmd);
@@ -606,6 +735,7 @@ void mgmt_vty_init(void)
        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_bcknd_cmd);
        install_element(CONFIG_NODE, &debug_mgmt_bcknd_cmd);
@@ -618,7 +748,11 @@ void mgmt_vty_init(void)
        install_element(VIEW_NODE, &debug_mgmt_all_cmd);
        install_element(CONFIG_NODE, &debug_mgmt_all_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.
         */
 }
index 2b74f62db9a93c6f81ebb6cf837e28c70f8fffd3..2a5bd8e32df7414cc60295890567e0c3891144ab 100644 (file)
@@ -27,6 +27,7 @@ mgmtd_libmgmtd_a_SOURCES = \
        mgmtd/mgmt_bcknd_adapter.c \
        mgmtd/mgmt_frntnd_server.c \
        mgmtd/mgmt_frntnd_adapter.c \
+       mgmtd/mgmt_trxn.c \
        # end
 
 mgmtdheaderdir = $(pkgincludedir)/mgmtd
@@ -43,6 +44,7 @@ noinst_HEADERS += \
        mgmtd/mgmt_vty.h \
        mgmtd/mgmt_frntnd_adapter.h \
        mgmtd/mgmt_frntnd_server.h \
+       mgmtd/mgmt_trxn.h \
        # end
 
 sbin_PROGRAMS += mgmtd/mgmtd