#include "mariadbmon.hh" #include #include /* * Copyright (c) 2018 MariaDB Corporation Ab * * Use of this software is governed by the Business Source License included * in the LICENSE.TXT file and at www.mariadb.com/bsl11. * * Change Date: 2020-01-01 * * On the date above, in accordance with the Business Source License, use * of this software will be governed by version 2 or later of the General * Public License. */ /** * Generate a CHANGE MASTER TO-query. * * @param master_host Master hostname/address * @param master_port Master port * @return Generated query */ string MariaDBMonitor::generate_change_master_cmd(const string& master_host, int master_port) { std::stringstream change_cmd; change_cmd << "CHANGE MASTER TO MASTER_HOST = '" << master_host << "', "; change_cmd << "MASTER_PORT = " << master_port << ", "; change_cmd << "MASTER_USE_GTID = current_pos, "; change_cmd << "MASTER_USER = '" << m_replication_user << "', "; const char MASTER_PW[] = "MASTER_PASSWORD = '"; const char END[] = "';"; #if defined(SS_DEBUG) std::stringstream change_cmd_nopw; change_cmd_nopw << change_cmd.str(); change_cmd_nopw << MASTER_PW << "******" << END;; MXS_DEBUG("Change master command is '%s'.", change_cmd_nopw.str().c_str()); #endif change_cmd << MASTER_PW << m_replication_password << END; return change_cmd.str(); } /** * Redirects slaves to replicate from another master server. * * @param slaves An array of slaves * @param new_master The replication master * @param redirected_slaves A vector where to insert successfully redirected slaves. * @return The number of slaves successfully redirected. */ int MariaDBMonitor::redirect_slaves(MXS_MONITORED_SERVER* new_master, const ServerVector& slaves, ServerVector* redirected_slaves) { ss_dassert(redirected_slaves != NULL); MXS_NOTICE("Redirecting slaves to new master."); string change_cmd = generate_change_master_cmd(new_master->server->name, new_master->server->port); int successes = 0; for (ServerVector::const_iterator iter = slaves.begin(); iter != slaves.end(); iter++) { if (redirect_one_slave(*iter, change_cmd.c_str())) { successes++; redirected_slaves->push_back(*iter); } } return successes; } /** * Set the new master to replicate from the cluster external master. * * @param new_master The server being promoted * @param err_out Error output * @return True if new master accepted commands */ bool MariaDBMonitor::start_external_replication(MXS_MONITORED_SERVER* new_master, json_t** err_out) { bool rval = false; string change_cmd = generate_change_master_cmd(external_master_host, external_master_port); if (mxs_mysql_query(new_master->con, change_cmd.c_str()) == 0 && mxs_mysql_query(new_master->con, "START SLAVE;") == 0) { MXS_NOTICE("New master starting replication from external master %s:%d.", external_master_host.c_str(), external_master_port); rval = true; } else { PRINT_MXS_JSON_ERROR(err_out, "Could not start replication from external master: '%s'.", mysql_error(new_master->con)); } return rval; } /** * Starts a new slave connection on a server. Should be used on a demoted master server. * * @param old_master The server which will start replication * @param new_master Replication target * @return True if commands were accepted. This does not guarantee that replication proceeds * successfully. */ bool MariaDBMonitor::switchover_start_slave(MXS_MONITORED_SERVER* old_master, SERVER* new_master) { bool rval = false; string change_cmd = generate_change_master_cmd(new_master->name, new_master->port); if (mxs_mysql_query(old_master->con, change_cmd.c_str()) == 0 && mxs_mysql_query(old_master->con, "START SLAVE;") == 0) { MXS_NOTICE("Old master '%s' starting replication from '%s'.", old_master->server->unique_name, new_master->unique_name); rval = true; } else { MXS_ERROR("Old master '%s' could not start replication: '%s'.", old_master->server->unique_name, mysql_error(old_master->con)); } return rval; } /** * Redirect one slave server to another master * * @param slave Server to redirect * @param change_cmd Change master command, usually generated by generate_change_master_cmd() * @return True if slave accepted all commands */ bool MariaDBMonitor::redirect_one_slave(MXS_MONITORED_SERVER* slave, const char* change_cmd) { bool rval = false; if (mxs_mysql_query(slave->con, "STOP SLAVE;") == 0 && mxs_mysql_query(slave->con, "RESET SLAVE;") == 0 && // To erase any old I/O or SQL errors mxs_mysql_query(slave->con, change_cmd) == 0 && mxs_mysql_query(slave->con, "START SLAVE;") == 0) { rval = true; MXS_NOTICE("Slave '%s' redirected to new master.", slave->server->unique_name); } else { MXS_WARNING("Slave '%s' redirection failed: '%s'.", slave->server->unique_name, mysql_error(slave->con)); } return rval; } uint32_t MariaDBMonitor::do_rejoin(const ServerVector& joinable_servers) { SERVER* master = this->master->server; uint32_t servers_joined = 0; if (!joinable_servers.empty()) { string change_cmd = generate_change_master_cmd(master->name, master->port); for (ServerVector::const_iterator iter = joinable_servers.begin(); iter != joinable_servers.end(); iter++) { MXS_MONITORED_SERVER* joinable = *iter; const char* name = joinable->server->unique_name; const char* master_name = master->unique_name; MySqlServerInfo* redir_info = get_server_info(this, joinable); bool op_success; if (redir_info->n_slaves_configured == 0) { MXS_NOTICE("Directing standalone server '%s' to replicate from '%s'.", name, master_name); op_success = join_cluster(joinable, change_cmd.c_str()); } else { MXS_NOTICE("Server '%s' is replicating from a server other than '%s', " "redirecting it to '%s'.", name, master_name, master_name); op_success = redirect_one_slave(joinable, change_cmd.c_str()); } if (op_success) { servers_joined++; } } } return servers_joined; } bool MariaDBMonitor::cluster_can_be_joined() { return (master != NULL && SERVER_IS_MASTER(master->server) && master_gtid_domain >= 0); }