/** * @file mariadb_nodes.cpp - backend nodes routines * * @verbatim * Revision History * * Date Who Description * 17/11/14 Timofey Turenko Initial implementation * * @endverbatim */ #include "mariadb_nodes.h" #include "sql_const.h" #include #include #include #include #include namespace { static bool g_require_gtid = false; } void Mariadb_nodes::require_gtid(bool value) { g_require_gtid = value; } Mariadb_nodes::Mariadb_nodes(const char *pref, const char *test_cwd, bool verbose): v51(false) { use_ipv6 = false; strcpy(prefix, pref); memset(this->nodes, 0, sizeof(this->nodes)); memset(blocked, 0, sizeof(blocked)); no_set_pos = false; this->verbose = verbose; strcpy(test_dir, test_cwd); read_env(); truncate_mariadb_logs(); flush_hosts(); close_active_connections(); } Mariadb_nodes::~Mariadb_nodes() { for (int i = 0; i < N; i++) { if (blocked[i]) { unblock_node(i); } } } int Mariadb_nodes::connect(int i) { if (nodes[i] == NULL || mysql_ping(nodes[i]) != 0) { if (nodes[i]) { mysql_close(nodes[i]); } nodes[i] = open_conn_db_timeout(port[i], IP[i], "test", user_name, password, 50, ssl); } if ((nodes[i] != NULL) && (mysql_errno(nodes[i]) != 0)) { return 1; } else { return 0; } } int Mariadb_nodes::connect() { int res = 0; for (int i = 0; i < N; i++) { res += connect(i); } return res; } void Mariadb_nodes::close_connections() { for (int i = 0; i < N; i++) { if (nodes[i] != NULL) { mysql_close(nodes[i]); nodes[i] = NULL; } } } void Mariadb_nodes::read_env() { char * env; char env_name[64]; read_basic_env(); sprintf(env_name, "%s_user", prefix); env = getenv(env_name); if (env != NULL) { sscanf(env, "%s", user_name); } else { sprintf(user_name, "skysql"); } sprintf(env_name, "%s_password", prefix); env = getenv(env_name); if (env != NULL) { sscanf(env, "%s", password); } else { sprintf(password, "skysql"); } ssl = false; sprintf(env_name, "%s_ssl", prefix); env = getenv(env_name); if ((env != NULL) && ((strcasecmp(env, "yes") == 0) || (strcasecmp(env, "true") == 0) )) { ssl = true; } if ((N > 0) && (N < 255)) { for (int i = 0; i < N; i++) { //reading ports sprintf(env_name, "%s_%03d_port", prefix, i); env = getenv(env_name); if (env != NULL) { sscanf(env, "%d", &port[i]); } else { port[i] = 3306; } //reading sockets sprintf(env_name, "%s_%03d_socket", prefix, i); env = getenv(env_name); if (env != NULL) { sprintf(socket[i], "%s", env); sprintf(socket_cmd[i], "--socket=%s", env); } else { sprintf(socket[i], " "); sprintf(socket_cmd[i], " "); } //reading start_db_command sprintf(env_name, "%s_%03d_start_db_command", prefix, i); env = getenv(env_name); if (env != NULL) { sprintf(start_db_command[i], "%s", env); } else { sprintf(start_db_command[i], "%s", "service mysql start"); } //reading stop_db_command sprintf(env_name, "%s_%03d_stop_db_command", prefix, i); env = getenv(env_name); if (env != NULL) { sprintf(stop_db_command[i], "%s", env); } else { sprintf(stop_db_command[i], "%s", "service mysql stop"); } //reading cleanup_db_command sprintf(env_name, "%s_%03d_cleanup_db_command", prefix, i); env = getenv(env_name); if (env != NULL) { sprintf(cleanup_db_command[i], "%s", env); } else { sprintf(cleanup_db_command[i], "rm -rf /var/lib/mysql/*; killall -9 mysqld"); } } } } void Mariadb_nodes::print_env() { for (int i = 0; i < N; i++) { printf("%s node %d \t%s\tPort=%d\n", prefix, i, IP[i], port[i]); printf("%s Access user %s\n", prefix, access_user[i]); } printf("%s User name %s\n", prefix, user_name); printf("%s Password %s\n", prefix, password); } int Mariadb_nodes::find_master() { char str[255]; char master_IP[256]; int i = 0; int found = 0; int master_node = 255; while ((found == 0) && (i < N)) { if (find_field( nodes[i], (char *) "show slave status;", (char *) "Master_Host", &str[0] ) == 0 ) { found = 1; strcpy(master_IP, str); } i++; } if (found == 1) { found = 0; i = 0; while ((found == 0) && (i < N)) { if (strcmp(IP[i], master_IP) == 0) { found = 1; master_node = i; } i++; } } return master_node; } void Mariadb_nodes::change_master(int NewMaster, int OldMaster) { for (int i = 0; i < N; i++) { if (mysql_ping(nodes[i]) == 0) { execute_query(nodes[i], "STOP SLAVE"); } } execute_query(nodes[NewMaster], "RESET SLAVE ALL"); execute_query(nodes[NewMaster], create_repl_user); if (mysql_ping(nodes[OldMaster]) == 0) { execute_query(nodes[OldMaster], "RESET MASTER"); } char log_file[256]; char log_pos[256]; find_field(nodes[NewMaster], "show master status", "File", &log_file[0]); find_field(nodes[NewMaster], "show master status", "Position", &log_pos[0]); for (int i = 0; i < N; i++) { if (i != NewMaster && mysql_ping(nodes[i]) == 0) { char str[1024]; sprintf(str, setup_slave, IP[NewMaster], log_file, log_pos, port[NewMaster]); execute_query(nodes[i], str); } } } int Mariadb_nodes::stop_node(int node) { return ssh_node(node, stop_db_command[node], true); } int Mariadb_nodes::start_node(int node, const char* param) { char cmd[1024]; if (v51) { sprintf(cmd, "%s %s --report-host", start_db_command[node], param); } else { sprintf(cmd, "%s %s", start_db_command[node], param); } return ssh_node(node, cmd, true); } int Mariadb_nodes::stop_nodes() { int i; int local_result = 0; connect(); for (i = 0; i < N; i++) { printf("Stopping node %d\n", i); fflush(stdout); local_result += execute_query(nodes[i], "stop slave;"); local_result += stop_node(i); local_result += ssh_node_f(i, true, "rm -f /var/lib/mysql/*master*.info"); } return local_result; } int Mariadb_nodes::stop_slaves() { int i; int global_result = 0; connect(); for (i = 0; i < N; i++) { printf("Stopping slave %d\n", i); fflush(stdout); global_result += execute_query(nodes[i], (char *) "stop slave;"); } close_connections(); return global_result; } int Mariadb_nodes::cleanup_db_node(int node) { return ssh_node(node, cleanup_db_command[node], true); } int Mariadb_nodes::cleanup_db_nodes() { int i; int local_result = 0; for (i = 0; i < N; i++) { printf("Cleaning node %d\n", i); fflush(stdout); local_result += cleanup_db_node(i); fflush(stdout); } return local_result; } int Mariadb_nodes::start_replication() { char str[1024]; char dtr[1024]; int local_result = 0; // Start all nodes for (int i = 0; i < N; i++) { if (start_node(i, (char *) "")) { printf("Start of node %d failed, trying to cleanup and re-initialize node\n", i); cleanup_db_node(i); prepare_server(i); local_result += start_node(i, (char *) ""); } ssh_node_f(i, true, "sudo rm -f /etc/my.cnf.d/kerb.cnf"); // Create users for replication as well as the users that are used by the tests sprintf(str, "%s/create_user.sh", test_dir); sprintf(dtr, "%s", access_homedir[i]); copy_to_node(i, str, dtr); ssh_node_f(i, false, "export node_user=\"%s\"; export node_password=\"%s\"; %s/create_user.sh %s", user_name, password, access_homedir[0], socket_cmd[0]); } connect(); for (int i = 0; i < N; i++) { execute_query(nodes[i], "SET GLOBAL read_only=OFF"); execute_query(nodes[i], "STOP SLAVE;"); if (g_require_gtid) { execute_query(nodes[i], "SET GLOBAL gtid_slave_pos='0-1-0'"); } if (i != 0) { // TODO: Reuse the code in sync_slaves() to get the actual file name and position execute_query(nodes[i], "CHANGE MASTER TO " "MASTER_HOST='%s', MASTER_PORT=%d, " "MASTER_USER='repl', MASTER_PASSWORD='repl', " "%s", IP_private[0], port[0], g_require_gtid ? "MASTER_USE_GTID=slave_pos" : "MASTER_LOG_FILE='mar-bin.000001', MASTER_LOG_POS=4"); execute_query(nodes[i], "START SLAVE"); } } return local_result; } int Galera_nodes::start_galera() { char str[1024]; char sys1[1024]; int i; int local_result = 0; local_result += stop_nodes(); // Remove the grastate.dat file ssh_node(0, "rm -f /var/lib/mysql/grastate.dat", true); printf("Starting new Galera cluster\n"); fflush(stdout); ssh_node(0, "echo [mysqld] > cluster_address.cnf", false); ssh_node(0, "echo wsrep_cluster_address=gcomm:// >> cluster_address.cnf", false); ssh_node(0, "cp cluster_address.cnf /etc/my.cnf.d/", true); if (start_node(0, (char *) " --wsrep-cluster-address=gcomm://") != 0) { cleanup_db_node(0); prepare_server(0); local_result += start_node(0, (char *) " --wsrep-cluster-address=gcomm://"); } sprintf(str, "%s/create_user_galera.sh", test_dir); copy_to_node_legacy(str, "~/", 0); sprintf(str, "export galera_user=\"%s\"; export galera_password=\"%s\"; ./create_user_galera.sh %s", user_name, password, socket_cmd[0]); ssh_node(0, str, false); for (i = 1; i < N; i++) { printf("Starting node %d\n", i); fflush(stdout); ssh_node(i, "echo [mysqld] > cluster_address.cnf", true); sprintf(str, "echo wsrep_cluster_address=gcomm://%s >> cluster_address.cnf", IP_private[0]); ssh_node(i, str, true); ssh_node(i, "cp cluster_address.cnf /etc/my.cnf.d/", true); sprintf(&sys1[0], " --wsrep-cluster-address=gcomm://%s", IP_private[0]); if (this->verbose) { printf("%s\n", sys1); fflush(stdout); } local_result += start_node(i, sys1); fflush(stdout); } sleep(5); local_result += connect(); local_result += execute_query(nodes[0], create_repl_user); close_connections(); return local_result; } int Mariadb_nodes::clean_iptables(int node) { char sys1[1024]; int local_result = 0; local_result += ssh_node(node, (char *) "echo \"#!/bin/bash\" > clean_iptables.sh", false); sprintf(sys1, "echo \"while [ \\\"\\$(iptables -n -L INPUT 1|grep '%d')\\\" != \\\"\\\" ]; do iptables -D INPUT 1; done\" >> clean_iptables.sh", port[node]); local_result += ssh_node(node, (char *) sys1, false); sprintf(sys1, "echo \"while [ \\\"\\$(ip6tables -n -L INPUT 1|grep '%d')\\\" != \\\"\\\" ]; do ip6tables -D INPUT 1; done\" >> clean_iptables.sh", port[node]); local_result += ssh_node(node, (char *) sys1, false); local_result += ssh_node(node, (char *) "chmod a+x clean_iptables.sh", false); local_result += ssh_node(node, (char *) "./clean_iptables.sh", true); return local_result; } int Mariadb_nodes::block_node(int node) { char sys1[1024]; int local_result = 0; local_result += clean_iptables(node); sprintf(&sys1[0], "iptables -I INPUT -p tcp --dport %d -j REJECT", port[node]); if (this->verbose) { printf("%s\n", sys1); fflush(stdout); } local_result += ssh_node(node, sys1, true); sprintf(&sys1[0], "ip6tables -I INPUT -p tcp --dport %d -j REJECT", port[node]); if (this->verbose) { printf("%s\n", sys1); fflush(stdout); } local_result += ssh_node(node, sys1, true); blocked[node] = true; return local_result; } int Mariadb_nodes::unblock_node(int node) { char sys1[1024]; int local_result = 0; local_result += clean_iptables(node); sprintf(&sys1[0], "iptables -I INPUT -p tcp --dport %d -j ACCEPT", port[node]); if (this->verbose) { printf("%s\n", sys1); fflush(stdout); } local_result += ssh_node(node, sys1, true); sprintf(&sys1[0], "ip6tables -I INPUT -p tcp --dport %d -j ACCEPT", port[node]); if (this->verbose) { printf("%s\n", sys1); fflush(stdout); } local_result += ssh_node(node, sys1, true); blocked[node] = false; return local_result; } int Mariadb_nodes::unblock_all_nodes() { int rval = 0; for (int i = 0; i < this->N; i++) { rval += this->unblock_node(i); } return rval; } bool is_readonly(MYSQL* conn) { bool rval = false; char output[512]; find_field(conn, "SHOW VARIABLES LIKE 'read_only'", "Value", output); if (strcasecmp(output, "OFF") != 0) { rval = true; } return rval; } bool Mariadb_nodes::check_master_node(MYSQL *conn) { bool rval = true; if (mysql_query(conn, "SHOW SLAVE HOSTS")) { printf("%s\n", mysql_error(conn)); rval = false; } else { MYSQL_RES *res = mysql_store_result(conn); if (res) { int rows = mysql_num_rows(res); if (rows != N - 1) { if (!v51) { printf("Number of slave hosts is %d when it should be %d\n", rows, N - 1); rval = false; } } } mysql_free_result(res); } if (mysql_query(conn, "SHOW SLAVE STATUS")) { printf("%s\n", mysql_error(conn)); rval = false; } else { MYSQL_RES *res = mysql_store_result(conn); if (res) { if (mysql_num_rows(res) > 0) { printf("The master is configured as a slave\n"); rval = false; } mysql_free_result(res); } } if (is_readonly(conn)) { printf("The master is in read-only mode\n"); rval = false; } return rval; } /** * @brief bad_slave_thread_status Check if field in the slave status outpur is not 'yes' * @param conn MYSQL struct (connection have to be open) * @param field Field to check * @param node Node index * @return false if requested field is 'Yes' */ bool Mariadb_nodes::bad_slave_thread_status(MYSQL *conn, const char *field, int node) { char str[1024] = ""; bool rval = false; // Doing 3 attempts to check status for (int i = 0; i < 2; i++) { if (find_field(conn, "SHOW SLAVE STATUS;", field, str) != 0) { printf("Node %d: %s not found in SHOW SLAVE STATUS\n", node, field); break; } if (verbose) { printf("Node %d: field %s is %s\n", node, field, str); } if (strcmp(str, "Yes") == 0 || strcmp(str, "No") == 0) { break; } /** Any other state is transient and we should try again */ sleep(1); } if (strcmp(str, "Yes") != 0) { if (verbose) { printf("Node %d: %s is '%s'\n", node, field, str); } rval = true; } return rval; } static bool wrong_replication_type(MYSQL *conn) { bool rval = true; for (int i = 0; i < 2; i++) { char str[1024] = ""; if (find_field(conn, "SHOW SLAVE STATUS", "Gtid_IO_Pos", str) == 0) { // If the test requires GTID based replication, Gtid_IO_Pos must not be empty if ((rval = (*str != '\0') != g_require_gtid)) { printf("Wrong value for 'Gtid_IO_Pos' (%s), expected it to be %s.\n", str, g_require_gtid ? "not empty" : "empty"); } else { break; } } sleep(1); } return rval; } /** * @brief multi_source_replication Check if slave is connected to more then one master * @param conn MYSQL struct (have to be open) * @param node Node index * @return false if multisource replication is not detected */ static bool multi_source_replication(MYSQL *conn, int node) { bool rval = true; MYSQL_RES *res; if (mysql_query(conn, "SHOW ALL SLAVES STATUS") == 0 && (res = mysql_store_result(conn))) { if (mysql_num_rows(res) == 1) { rval = false; } else { printf("Node %d: More than one configured slave\n", node); fflush(stdout); } } else { printf("Node %d does not support SHOW ALL SLAVE STATUS, ignoring multi source replication check\n", node); fflush(stdout); rval = false; } return rval; } int Mariadb_nodes::check_replication() { int master = 0; int res = 0; if (verbose) { printf("Checking Master/Slave setup\n"); fflush(stdout); } if (this->connect()) { return 1; } res = get_versions(); for (int i = 0; i < N && res == 0; i++) { if (i == master) { if (!check_master_node(nodes[i])) { res = 1; if (verbose) { printf("Master node check failed for node %d\n", i); } } } else if (bad_slave_thread_status(nodes[i], "Slave_IO_Running", i) || bad_slave_thread_status(nodes[i], "Slave_SQL_Running", i) || wrong_replication_type(nodes[i]) || multi_source_replication(nodes[i], i) || is_readonly(nodes[i])) { res = 1; if (verbose) { printf("Slave %d check failed\n", i); } } } if (verbose) { printf("Replication check for %s gave code %d\n", prefix, res); } return res; } bool Mariadb_nodes::fix_replication() { if (check_replication()) { unblock_all_nodes(); if (check_nodes()) { printf("****** VMS ARE BROKEN! Exiting *****\n"); return false; } int attempts = 2; int attempts_with_cleanup = 1; int attempts_with_revert = 1; while (check_replication() && attempts > 0) { if (attempts != 2) { stop_nodes(); } start_replication(); close_connections(); check_replication(); attempts--; if (attempts == 0 && check_replication()) { if (attempts_with_cleanup > 0) { printf("****** BACKEND IS STILL BROKEN! Trying to cleanup all nodes *****\n"); stop_nodes(); cleanup_db_nodes(); prepare_servers(); attempts_with_cleanup--; attempts = 2; sleep(10); start_replication(); sleep(10); } else { if (attempts_with_revert > 0) { printf("****** BACKEND IS STILL BROKEN! Trying to revert all nodes from snapshot *****\n"); revert_nodes_snapshot(); attempts_with_cleanup = 1; attempts = 2; } else { printf("****** BACKEND IS STILL BROKEN! Exiting *****\n"); return false; } } } } flush_hosts(); } return true; } bool Mariadb_nodes::revert_nodes_snapshot() { char str[1024]; bool rval = true; for (int i = 0; i < N; i++) { sprintf(str, "%s clean --node-name %s_%03d", revert_snapshot_command, prefix, i); if (system(str)) { rval = false; } ssh_node_f(i, true, "pkill -9 mysqld"); } return rval; } int Galera_nodes::check_galera() { int res1 = 0; if (verbose) { printf("Checking Galera\n"); fflush(stdout); } if (this->nodes[0] == NULL) { this->connect(); } res1 = get_versions(); for (int i = 0; i < N; i++) { MYSQL *conn = open_conn(port[i], IP[i], user_name, password, ssl); if (conn == NULL || mysql_errno(conn) != 0) { printf("Error connectiong node %d: %s\n", i, mysql_error(conn)); res1 = 1; } else { char str[1024] = ""; if (find_field(conn, (char *) "SHOW STATUS WHERE Variable_name='wsrep_cluster_size';", (char *) "Value", str) != 0) { printf("wsrep_cluster_size is not found in SHOW STATUS LIKE 'wsrep%%' results\n"); fflush(stdout); res1 = 1; } else { int cluster_size; sscanf(str, "%d", &cluster_size); if (cluster_size != N) { printf("wsrep_cluster_size is not %d, it is %d\n", N, cluster_size); fflush(stdout); res1 = 1; } } } mysql_close(conn); } return res1; } int Mariadb_nodes::set_slave(MYSQL * conn, char master_host[], int master_port, char log_file[], char log_pos[]) { char str[1024]; sprintf(str, setup_slave, master_host, log_file, log_pos, master_port); if (no_set_pos) { sprintf(str, setup_slave_no_pos, master_host, master_port); } if (this->verbose) { printf("Setup slave SQL: %s\n", str); } return execute_query(conn, str); } int Mariadb_nodes::set_repl_user() { int global_result = 0; global_result += connect(); for (int i = 0; i < N; i++) { global_result += execute_query(nodes[i], create_repl_user); } close_connections(); return global_result; } int Mariadb_nodes::get_server_id(int index) { int id = -1; char str[1024]; if (find_field(this->nodes[index], "SELECT @@server_id", "@@server_id", (char*) str) == 0) { id = atoi(str); } else { printf("find_field failed for %s:%d\n", this->IP[index], this->port[index]); } return id; } std::string Mariadb_nodes::get_server_id_str(int index) { std::stringstream ss; ss << get_server_id(index); return ss.str(); } int Mariadb_nodes::flush_hosts() { if (this->nodes[0] == NULL && this->connect()) { return 1; } int local_result = 0; for (int i = 0; i < N; i++) { if (mysql_query(nodes[i], "FLUSH HOSTS")) { local_result++; } if (mysql_query(nodes[i], "SET GLOBAL max_connections=10000")) { local_result++; } if (mysql_query(nodes[i], "SELECT CONCAT('\\'', user, '\\'@\\'', host, '\\'') FROM mysql.user WHERE user = ''") == 0) { MYSQL_RES *res = mysql_store_result(nodes[i]); if (res) { std::vector users; MYSQL_ROW row; while ((row = mysql_fetch_row(res))) { users.push_back(row[0]); } mysql_free_result(res); if (users.size() > 0) { printf("Detected anonymous users, dropping them.\n"); for (auto& s : users) { std::string query = "DROP USER "; query += s; printf("%s\n", query.c_str()); mysql_query(nodes[i], query.c_str()); } } } } else { printf("Failed to query for anonymous users: %s\n", mysql_error(nodes[i])); local_result++; } } return local_result; } int Mariadb_nodes::execute_query_all_nodes(const char* sql) { int local_result = 0; connect(); for (int i = 0; i < N; i++) { local_result += execute_query(nodes[i], sql); } close_connections(); return local_result; } int Mariadb_nodes::get_version(int i) { char * str; int ec; int local_result = 0; if (find_field(nodes[i], "SELECT @@version", "@@version", version[i])) { printf("Failed to get version: %s, trying ssh node and use MariaDB client\n", mysql_error(nodes[i])); str = ssh_node_output(i, "mysql --batch --silent -e \"select @@version\"", true, &ec); if (ec) { local_result++; printf("Failed to get version, node %d is broken\n", i); } else { strcpy(version[i], str); free(str); } } strcpy(version_number[i], version[i]); str = strchr(version_number[i], '-'); if (str != NULL) { str[0] = 0; } strcpy(version_major[i], version_number[i]); if (strstr(version_major[i], "5.") == version_major[i]) { version_major[i][3] = 0; } if (strstr(version_major[i], "10.") == version_major[i]) { version_major[i][4] = 0; } if (verbose) { printf("Node %s%d: %s\t %s \t %s\n", prefix, i, version[i], version_number[i], version_major[i]); } return local_result; } int Mariadb_nodes::get_versions() { int local_result = 0; v51 = false; for (int i = 0; i < N; i++) { local_result += get_version(i); } for (int i = 0; i < N; i++) { if (strcmp(version_major[i], "5.1") == 0) { v51 = true; } } return local_result; } std::string Mariadb_nodes::get_lowest_version() { std::string rval; get_versions(); int lowest = INT_MAX; for (int i = 0; i < N; i++) { int int_version = get_int_version(version[i]); if (lowest > int_version) { rval = version[i]; lowest = int_version; } } return rval; } int Mariadb_nodes::truncate_mariadb_logs() { int local_result = 0; for (int node = 0; node < N; node++) { char sys[1024]; if (strcmp(IP[node], "127.0.0.1") != 0) { sprintf(sys, "ssh -i %s -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=quiet %s@%s 'sudo truncate /var/lib/mysql/*.err --size 0;sudo rm -f /etc/my.cnf.d/binlog_enc*\' &", sshkey[node], access_user[node], IP[node]); local_result += system(sys); } } return local_result; } int Mariadb_nodes::configure_ssl(bool require) { int local_result = 0; char str[1024]; this->ssl = 1; for (int i = 0; i < N; i++) { printf("Node %d\n", i); stop_node(i); sprintf(str, "%s/ssl-cert", test_dir); local_result += copy_to_node_legacy(str, (char *) "~/", i); sprintf(str, "%s/ssl.cnf", test_dir); local_result += copy_to_node_legacy(str, (char *) "~/", i); local_result += ssh_node(i, (char *) "cp ~/ssl.cnf /etc/my.cnf.d/", true); local_result += ssh_node(i, (char *) "cp -r ~/ssl-cert /etc/", true); local_result += ssh_node(i, (char *) "chown mysql:mysql -R /etc/ssl-cert", true); start_node(i, (char *) ""); } if (require) { // Create DB user on first node printf("Set user to require ssl: %s\n", str); sprintf(str, "%s/create_user_ssl.sh", test_dir); copy_to_node_legacy(str, (char *) "~/", 0); sprintf(str, "export node_user=\"%s\"; export node_password=\"%s\"; ./create_user_ssl.sh %s", user_name, password, socket_cmd[0]); printf("cmd: %s\n", str); ssh_node(0, str, false); } return local_result; } int Mariadb_nodes::disable_ssl() { int local_result = 0; char str[1024]; local_result += connect(); sprintf(str, "DROP USER %s; grant all privileges on *.* to '%s'@'%%' identified by '%s';", user_name, user_name, password); local_result += execute_query(nodes[0], (char *) ""); close_connections(); for (int i = 0; i < N; i++) { stop_node(i); local_result += ssh_node(i, (char *) "rm -f /etc/my.cnf.d/ssl.cnf", true); start_node(i, (char *) ""); } return local_result; } static void wait_until_pos(MYSQL *mysql, int filenum, int pos) { int slave_filenum = 0; int slave_pos = 0; do { if (mysql_query(mysql, "SHOW SLAVE STATUS")) { printf("Failed to execute SHOW SLAVE STATUS: %s", mysql_error(mysql)); break; } MYSQL_RES *res = mysql_store_result(mysql); if (res) { MYSQL_ROW row = mysql_fetch_row(res); if (row && row[5] && strchr(row[5], '.') && row[21]) { char *file_suffix = strchr(row[5], '.') + 1; slave_filenum = atoi(file_suffix); slave_pos = atoi(row[21]); } mysql_free_result(res); } } while (slave_filenum < filenum || slave_pos < pos); } void Mariadb_nodes::sync_slaves(int node) { if (this->nodes[node] == NULL) { this->connect(); } if (mysql_query(this->nodes[node], "SHOW MASTER STATUS")) { printf("Failed to execute SHOW MASTER STATUS: %s", mysql_error(this->nodes[node])); } else { MYSQL_RES *res = mysql_store_result(this->nodes[node]); if (res) { MYSQL_ROW row = mysql_fetch_row(res); if (row && row[node] && row[1]) { const char* file_suffix = strchr(row[node], '.') + 1; int filenum = atoi(file_suffix); int pos = atoi(row[1]); for (int i = 0; i < this->N; i++) { if (i != node) { wait_until_pos(this->nodes[i], filenum, pos); } } } mysql_free_result(res); } } } void Mariadb_nodes::close_active_connections() { if (this->nodes[0] == NULL) { this->connect(); } const char *sql = "select id from information_schema.processlist where id != @@pseudo_thread_id and user not in ('system user', 'repl')"; for (int i = 0; i < N; i++) { if (!mysql_query(nodes[i], sql)) { MYSQL_RES *res = mysql_store_result(nodes[i]); if (res) { MYSQL_ROW row; while ((row = mysql_fetch_row(res))) { std::string q("KILL "); q += row[0]; execute_query_silent(nodes[i], q.c_str()); } mysql_free_result(res); } } } } void Mariadb_nodes::stash_server_settings(int node) { ssh_node(node, "sudo rm -rf /etc/my.cnf.d.backup/", true); ssh_node(node, "sudo mkdir /etc/my.cnf.d.backup/", true); ssh_node(node, "sudo cp -r /etc/my.cnf.d/* /etc/my.cnf.d.backup/", true); } void Mariadb_nodes::restore_server_settings(int node) { ssh_node(node, "sudo mv -f /etc/my.cnf.d.backup/* /etc/my.cnf.d/", true); } void Mariadb_nodes::disable_server_setting(int node, const char* setting) { ssh_node_f(node, true, "sudo sed -i 's/%s/#%s/' /etc/my.cnf.d/*", setting, setting); } void Mariadb_nodes::add_server_setting(int node, const char* setting) { ssh_node_f(node, true, "sudo sed -i '$a [server]' /etc/my.cnf.d/server*.cnf", setting); ssh_node_f(node, true, "sudo sed -i '$a %s' /etc/my.cnf.d/server*.cnf", setting); } void Mariadb_nodes::reset_server_settings(int node) { std::stringstream ss; ss << test_dir << "/mdbci/cnf/server" << node + 1 << ".cnf"; // Note: This is a CentOS specific path ssh_node(node, "sudo rm -rf /etc/my.cnf.d/*", true); copy_to_node(node, ss.str().c_str(), "~/"); ssh_node_f(node, false, "sudo mv ~/server%d.cnf /etc/my.cnf.d/", node + 1); } void Mariadb_nodes::reset_server_settings() { for (int i = 0; i < N; i++) { reset_server_settings(i); } } /** * @brief extract_version_from_string Tries to find MariaDB server version number in the output of 'mysqld --version' * Function does not allocate any memory * @param version String returned by 'mysqld --version' * @return pointer to the string with version number */ char * extract_version_from_string(char * version) { int pos1 = 0; int pos2 = 0; int l = strlen(version); while ((! isdigit(version[pos1])) && (pos1 < l)) { pos1++; } pos2 = pos1; while (((isdigit(version[pos2]) || version[pos2] == '.')) && (pos2 < l)) { pos2++; } version[pos2] = '\0'; return &version[pos1]; } int Mariadb_nodes::prepare_server(int i) { int ec; char * version; char * version_digits; char * tmp_pass; char str1[1024]; char str2[1024]; ssh_node_f(i, true, "%s", stop_db_command[i]); sleep(5); ssh_node(i, "sed -i \"s/bind-address/#bind-address/g\" /etc/mysql/my.cnf.d/*.cnf", true); ssh_node(i, "ln -s /etc/apparmor.d/usr.sbin.mysqld /etc/apparmor.d/disable/usr.sbin.mysqld; sudo service apparmor restart", true); version = ssh_node_output(i, "/usr/sbin/mysqld --version", false, &ec); if (ec == 0) { version_digits = extract_version_from_string(version); printf("Detected server version on node %d is %s\n", i, version_digits); if (memcmp(version_digits, "5.", 2) == 0) { ssh_node(i, "sed -i \"s/binlog_row_image=full//\" /etc/my.cnf.d/*.cnf", true); } if (memcmp(version_digits, "5.7", 3) == 0) { // Disable 'validate_password' plugin, searach for random temporal // password in the log and reseting passord to empty string ssh_node(i, "/usr/sbin/mysqld --initialize; sudo chown -R mysql:mysql /var/lib/mysql", true); ssh_node(i, start_db_command[i], true); tmp_pass = ssh_node_output(i, "cat /var/log/mysqld.log | grep \"temporary password\" | sed -n -e 's/^.*: //p'", true, &ec); ssh_node_f(i, true, "mysqladmin -uroot -p'%s' password '%s'", tmp_pass, tmp_pass); ssh_node_f(i, false, "echo \"UNINSTALL PLUGIN validate_password\" | sudo mysql -uroot -p'%s'", tmp_pass); ssh_node(i, stop_db_command[i], true); ssh_node(i, start_db_command[i], true); ssh_node_f(i, true, "mysqladmin -uroot -p'%s' password ''", tmp_pass); } else { printf("Executing mysql_install_db on node %d\n", i); ssh_node(i, "mysql_install_db; sudo chown -R mysql:mysql /var/lib/mysql", true); printf("Starting server on node %d\n", i); if (ssh_node(i, start_db_command[i], true)) { printf("Server start on node %d failed\n", i); } } sleep(15); sprintf(str1, "%s/mdbci/backend/create_*_user.sql", test_dir); sprintf(str2, "%s/", access_homedir[i]); copy_to_node(i, str1, str2); sprintf(str1, "mysql < %s/create_repl_user.sql", access_homedir[i]); ssh_node(i, str1, true); sprintf(str1, "mysql < %s/create_skysql_user.sql", access_homedir[i]); ssh_node(i, str1, true); free(version); return 0; } else { return 1; } } int Mariadb_nodes::prepare_servers() { int rval = 0; for (int i; i < N; i++) { if (prepare_server(i)) { rval = 1; } } return rval; } void Mariadb_nodes::replicate_from(int slave, int master, const char* type) { std::stringstream change_master; change_master << "CHANGE MASTER TO MASTER_HOST = '" << IP[master] << "', MASTER_PORT = " << port[master] << ", MASTER_USE_GTID = " << type << ", " "MASTER_USER='repl', MASTER_PASSWORD='repl';"; if (verbose) { std::cout << "Server " << slave + 1 << " starting to replicate from server " << master + 1 << std::endl; std::cout << "Query is '" << change_master.str() << "'" << std::endl; } execute_query(nodes[slave], "STOP SLAVE;"); execute_query(nodes[slave], change_master.str().c_str()); execute_query(nodes[slave], "START SLAVE;"); }