]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
s3:cluster_level_db: add cluster_level_db_upgrade()
authorStefan Metzmacher <metze@samba.org>
Fri, 31 Jul 2026 08:07:58 +0000 (10:07 +0200)
committerRalph Boehme <slow@samba.org>
Tue, 4 Aug 2026 09:51:32 +0000 (09:51 +0000)
This verifies all nodes are supporting the same ranges
and then upgrades to the highest possible level.

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
source3/lib/cluster_level_db.c
source3/lib/cluster_level_db.h

index 18169bbcbfb2984a6ee3c36087b93e945338cfbd..9637700459a8f336369ec36608cde5e12d8fbff0 100644 (file)
@@ -18,6 +18,7 @@
 */
 
 #include "includes.h"
+#include "messages.h"
 #include "dbwrap/dbwrap.h"
 #include "dbwrap/dbwrap_ctdb.h"
 #include "lib/util/util_tdb.h"
@@ -850,3 +851,266 @@ NTSTATUS cluster_level_db_nodes_foreach(struct ctdbd_connection *ctdb_conn,
        TALLOC_FREE(frame);
        return NT_STATUS_OK;
 }
+
+struct cluster_level_db_upgrade_to_highest_state {
+       struct db_context *db;
+       uint32_t error_vnn;
+       NTSTATUS error;
+};
+
+static int cluster_level_db_upgrade_to_highest_cb(
+                               uint32_t total_nodes_count,
+                               const struct ctdb_node_and_flags *nf,
+                               void *private_data)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct cluster_level_db_upgrade_to_highest_state *state =
+               (struct cluster_level_db_upgrade_to_highest_state *)private_data;
+       struct timeval n_update_time = {};
+       struct timeval n_ctdb_start_time = {};
+       pid_t n_ctdb_pid = -1;
+       struct cluster_level_ranges *n_ranges = NULL;
+       const struct cluster_level_ranges *supported_ranges =
+               cluster_level_supported_ranges();
+       NTSTATUS status;
+       uint32_t i;
+
+       status = cluster_level_db_fetch_ranges(state->db,
+                                              nf->pnn,
+                                              frame,
+                                              &n_update_time,
+                                              &n_ctdb_start_time,
+                                              &n_ctdb_pid,
+                                              &n_ranges);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_ERR("node[%"PRIu32"] cluster_level_db_fetch_ranges() %s\n",
+                       nf->pnn, nt_errstr(status));
+               TALLOC_FREE(frame);
+               state->error_vnn = nf->pnn;
+               state->error = status;
+               return -1;
+       }
+
+       /*
+        * We expect the same software on all nodes, not
+        * only the highest level in common.
+        */
+
+       if (n_ranges->num_ranges != supported_ranges->num_ranges) {
+               DBG_ERR("node[%"PRIu32"] num_ranges[%"PRIu32"] != %"PRIu32".\n",
+                       nf->pnn,
+                       n_ranges->num_ranges,
+                       supported_ranges->num_ranges);
+               TALLOC_FREE(frame);
+               state->error_vnn = nf->pnn;
+               state->error = NT_STATUS_REVISION_MISMATCH;
+               return -1;
+       }
+
+       for (i = 0; i < n_ranges->num_ranges; i++) {
+               const struct cluster_level_range *nr =
+                       &n_ranges->ranges[i];
+               const struct cluster_level_range *sr =
+                       &supported_ranges->ranges[i];
+
+               if (nr->major != sr->major) {
+                       DBG_ERR("node[%"PRIu32"] range[%"PRIu32"] "
+                               "major[%"PRIu32"] != %"PRIu32".\n",
+                               nf->pnn,
+                               i,
+                               nr->major,
+                               sr->major);
+                       TALLOC_FREE(frame);
+                       state->error_vnn = nf->pnn;
+                       state->error = NT_STATUS_REVISION_MISMATCH;
+                       return -1;
+               }
+
+               if (nr->minor_max != sr->minor_max) {
+                       DBG_ERR("node[%"PRIu32"] range[%"PRIu32"] "
+                               "major[%"PRIu32"] "
+                               "minor_max[%"PRIu32"] != %"PRIu32".\n",
+                               nf->pnn,
+                               i,
+                               nr->major,
+                               nr->minor_max,
+                               sr->minor_max);
+                       TALLOC_FREE(frame);
+                       state->error_vnn = nf->pnn;
+                       state->error = NT_STATUS_REVISION_MISMATCH;
+                       return -1;
+               }
+
+               if (nr->minor_min != sr->minor_min) {
+                       DBG_ERR("node[%"PRIu32"] range[%"PRIu32"] "
+                               "major[%"PRIu32"] "
+                               "minor_min[%"PRIu32"] != %"PRIu32".\n",
+                               nf->pnn,
+                               i,
+                               nr->major,
+                               nr->minor_min,
+                               sr->minor_min);
+                       TALLOC_FREE(frame);
+                       state->error_vnn = nf->pnn;
+                       state->error = NT_STATUS_REVISION_MISMATCH;
+                       return -1;
+               }
+       }
+
+       TALLOC_FREE(frame);
+       return 0;
+}
+
+NTSTATUS cluster_level_db_upgrade(struct ctdbd_connection *ctdb_conn,
+                                 struct messaging_context *msg_ctx,
+                                 struct cluster_level_db_upgrade_req *req)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct cluster_level_db_upgrade_to_highest_state state = {
+               .db = NULL,
+       };
+       const struct cluster_level_ranges *supported_ranges =
+               cluster_level_supported_ranges();
+       const struct cluster_level_active *conn_level = NULL;
+       struct cluster_level_active active_level = {};
+       struct cluster_level_active highest_level = {};
+       enum cluster_level_db_version version = CLUSTER_LEVEL_DB_VERSION_1;
+       struct timeval now = timeval_current();
+       NTSTATUS status;
+       int ret;
+
+       SMB_ASSERT(msg_ctx != NULL);
+       req->out.error_vnn = ctdbd_vnn(ctdb_conn);
+
+       state.db = cluster_level_db_open(frame,
+                                  ctdb_conn,
+                                  msg_ctx);
+       if (state.db == NULL) {
+               DBG_ERR("cluster_level_db_open() failed\n");
+               TALLOC_FREE(frame);
+               return NT_STATUS_INTERNAL_DB_ERROR;
+       }
+
+       ret = dbwrap_transaction_start(state.db);
+       if (ret != 0) {
+               DBG_ERR("dbwrap_transaction_start() failed\n");
+               TALLOC_FREE(frame);
+               return NT_STATUS_INTERNAL_DB_ERROR;
+       }
+
+       highest_level = (struct cluster_level_active) {
+               .major = supported_ranges->ranges[0].major,
+               .minor = supported_ranges->ranges[0].minor_max,
+       };
+
+       conn_level = ctdbd_conn_get_cluster_level(ctdb_conn);
+       if (conn_level == NULL) {
+               dbwrap_transaction_cancel(state.db);
+               DBG_ERR("ctdbd_conn_get_cluster_level() failed.\n");
+               TALLOC_FREE(frame);
+               return NT_STATUS_INVALID_CONNECTION;
+       }
+
+       status = cluster_level_db_fetch_active(state.db, &active_level);
+       if (!NT_STATUS_IS_OK(status)) {
+               dbwrap_transaction_cancel(state.db);
+               DBG_ERR("cluster_level_db_fetch_active() %s\n",
+                       nt_errstr(status));
+               TALLOC_FREE(frame);
+               return status;
+       }
+       req->out.old_level = active_level;
+
+       if (conn_level->major != active_level.major ||
+           conn_level->minor != active_level.minor)
+       {
+               dbwrap_transaction_cancel(state.db);
+               DBG_ERR("conn_level[%"PRIu32".%"PRIu32"] != "
+                       "active_level[%"PRIu32".%"PRIu32"].\n",
+                       conn_level->major, conn_level->minor,
+                       active_level.major, active_level.minor);
+               TALLOC_FREE(frame);
+               return NT_STATUS_INVALID_CONNECTION;
+       }
+
+       if (highest_level.major == active_level.major &&
+           highest_level.minor == active_level.minor)
+       {
+               dbwrap_transaction_cancel(state.db);
+               DBG_ERR("highest_level[%"PRIu32".%"PRIu32"] == "
+                       "active_level[%"PRIu32".%"PRIu32"].\n",
+                       highest_level.major, highest_level.minor,
+                       active_level.major, active_level.minor);
+               TALLOC_FREE(frame);
+               return NT_STATUS_ALREADY_COMMITTED;
+       }
+
+       ret = ctdbd_nodes_foreach(ctdb_conn,
+                                 cluster_level_db_upgrade_to_highest_cb,
+                                 &state);
+       if (ret != 0) {
+               dbwrap_transaction_cancel(state.db);
+               if (NT_STATUS_IS_OK(state.error)) {
+                       state.error_vnn = ctdbd_vnn(ctdb_conn);
+                       state.error = NT_STATUS_INTERNAL_ERROR;
+               }
+               DBG_ERR("cluster_level_db_upgrade_to_highest_cb() "
+                       "vnn=%"PRIu32" %s\n",
+                       state.error_vnn, nt_errstr(state.error));
+               req->out.error_vnn = state.error_vnn;
+               TALLOC_FREE(frame);
+               return state.error;
+       }
+
+       active_level = highest_level;
+
+       /*
+        * If needed adjust version based on active_level!
+        */
+       version = CLUSTER_LEVEL_DB_VERSION_1;
+
+       status = cluster_level_db_store_active(state.db,
+                                              version,
+                                              now,
+                                              &active_level);
+       if (!NT_STATUS_IS_OK(status)) {
+               dbwrap_transaction_cancel(state.db);
+               DBG_ERR("cluster_level_db_store_active() %s\n",
+                       nt_errstr(status));
+               TALLOC_FREE(frame);
+               return status;
+       }
+
+       req->out.new_level = active_level;
+
+       if (req->in.dry_run) {
+               dbwrap_transaction_cancel(state.db);
+               status = NT_STATUS_NOT_COMMITTED;
+               DBG_DEBUG("active_level: %"PRIu32".%"PRIu32" - %s\n",
+                         active_level.major, active_level.minor,
+                         nt_errstr(status));
+               TALLOC_FREE(frame);
+               return status;
+       }
+
+       ret = dbwrap_transaction_commit(state.db);
+       if (ret != 0) {
+               DBG_ERR("dbwrap_transaction_commit() failed\n");
+               TALLOC_FREE(frame);
+               return NT_STATUS_INTERNAL_DB_ERROR;
+       }
+
+       DBG_DEBUG("active_level: %"PRIu32".%"PRIu32" - %s\n",
+                 active_level.major, active_level.minor,
+                 nt_errstr(status));
+
+       /*
+        * Announce the change and let every process connected
+        * to ctdb notice it.
+        */
+       messaging_send_all(msg_ctx, MSG_CLUSTER_LEVEL_UPGRADED, NULL, 0);
+
+       ctdbd_conn_set_cluster_level(ctdb_conn, &active_level);
+       TALLOC_FREE(frame);
+       return NT_STATUS_OK;
+}
index 423eb8edd9e416d4e10b44c4a5284aefc3a66378..eeb69bb1323a269782aeea1b270ef275533b0671 100644 (file)
@@ -48,4 +48,19 @@ NTSTATUS cluster_level_db_nodes_foreach(struct ctdbd_connection *ctdb_conn,
                                        cluster_level_db_nodes_foreach_cb_t cb,
                                        void *private_data);
 
+struct cluster_level_db_upgrade_req {
+       struct {
+               bool dry_run;
+       } in;
+       struct {
+               struct cluster_level_active old_level;
+               struct cluster_level_active new_level;
+               uint32_t error_vnn;
+       } out;
+};
+
+NTSTATUS cluster_level_db_upgrade(struct ctdbd_connection *ctdb_conn,
+                                 struct messaging_context *msg_ctx,
+                                 struct cluster_level_db_upgrade_req *req);
+
 #endif /* CLUSTER_LEVEL_DB_H */