From: Stefan Metzmacher Date: Fri, 31 Jul 2026 08:07:58 +0000 (+0200) Subject: s3:cluster_level_db: add cluster_level_db_upgrade() X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3b964f8ac7fcf580d2d50128e37dffcaeb243160;p=thirdparty%2Fsamba.git s3:cluster_level_db: add cluster_level_db_upgrade() This verifies all nodes are supporting the same ranges and then upgrades to the highest possible level. Signed-off-by: Stefan Metzmacher Reviewed-by: Ralph Boehme --- diff --git a/source3/lib/cluster_level_db.c b/source3/lib/cluster_level_db.c index 18169bbcbfb..9637700459a 100644 --- a/source3/lib/cluster_level_db.c +++ b/source3/lib/cluster_level_db.c @@ -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; +} diff --git a/source3/lib/cluster_level_db.h b/source3/lib/cluster_level_db.h index 423eb8edd9e..eeb69bb1323 100644 --- a/source3/lib/cluster_level_db.h +++ b/source3/lib/cluster_level_db.h @@ -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 */