mirror of https://github.com/sysown/proxysql
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2436 lines
79 KiB
2436 lines
79 KiB
/**
|
|
* @file galera_simulator-t.cpp
|
|
* @brief Test file for testing Galera behavior using a simulation through SQLiteServer exposed via
|
|
* 'TEST_GALERA'.
|
|
*/
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <tuple>
|
|
|
|
// NOTE: Only needed during testing
|
|
#include <functional>
|
|
|
|
#include <mysql.h>
|
|
#include <mysqld_error.h>
|
|
#include <string>
|
|
#include <unistd.h>
|
|
|
|
#include "ezOptionParser.hpp"
|
|
#include "proxysql_utils.h"
|
|
#include "utils.h"
|
|
#include "command_line.h"
|
|
#include "json.hpp"
|
|
|
|
#include "common_utils.h"
|
|
#include "galera_utils.h"
|
|
#include "readonly_utils.h"
|
|
#include "replicationlag_utils.h"
|
|
#include "grouprep_utils.h"
|
|
#include "aurora_utils.h"
|
|
|
|
using std::string;
|
|
|
|
/**
|
|
* @brief Top-level mode of the simulator: drive the cluster state ('simulate')
|
|
* or verify the current state against the expected one ('verify').
|
|
*/
|
|
enum class operation_mode {
|
|
simulate,
|
|
verify
|
|
};
|
|
/**
|
|
* @brief Command-line options parsed in 'main()' and passed to the simulator drivers.
|
|
*/
|
|
struct simulator_options {
|
|
// command line options variables
|
|
bool verbose_output = false;
|
|
bool stop_on_failure = false;
|
|
operation_mode op_mode = operation_mode::simulate;
|
|
std::string payload_path {};
|
|
};
|
|
|
|
std::pair<int, nlohmann::ordered_json> simulate_galera_cluster_state(
|
|
MYSQL* proxysql_admin,
|
|
MYSQL* proxysql_sqlite,
|
|
const json& j_test_definition,
|
|
const operation_mode& op_mode,
|
|
const bool verbose = false
|
|
) {
|
|
std::pair<int, nlohmann::ordered_json> result {};
|
|
|
|
std::vector<mysql_server_def> mysql_servers {};
|
|
std::vector<galera_hostgroup_config> galera_hostgroups {};
|
|
|
|
std::vector<galera_server_state> galera_init_servers_state {};
|
|
std::vector<galera_server_state> galera_new_servers_state {};
|
|
|
|
// the expected initial cluster servers status
|
|
std::vector<server_status> exp_init_cluster_status {};
|
|
// the expected final cluster servers status
|
|
std::vector<server_status> exp_final_cluster_status {};
|
|
|
|
// validations errors from extracting the required values from the JSON payload
|
|
std::pair<int, std::string> ext_mysql_srvs_err {};
|
|
std::pair<int, std::string> ext_galera_conf_err {};
|
|
std::pair<int, std::string> ext_galera_init_srvs_state_err {};
|
|
std::pair<int, std::string> ext_galera_next_srvs_state_err {};
|
|
std::pair<int, std::string> ext_init_cluster_status_err {};
|
|
std::pair<int, std::string> ext_final_cluster_status_err {};
|
|
|
|
// errors from applying the required configuration to ProxySQL
|
|
std::pair<int, std::string> prep_galera_hostgroups_err {};
|
|
std::pair<int, std::string> prep_galera_init_state_err {};
|
|
std::pair<int, std::string> prep_galera_final_state_err {};
|
|
std::pair<int, std::string> prep_mysql_servers_conf_err {};
|
|
std::pair<int, std::string> set_monitor_tos_err {};
|
|
int load_to_run_err = 0;
|
|
|
|
// ************************** VALIDATION ********************************
|
|
|
|
ext_mysql_srvs_err = extract_mysql_servers(j_test_definition, mysql_servers);
|
|
|
|
if (ext_mysql_srvs_err.first) {
|
|
result = invalid_json_error(
|
|
ext_mysql_srvs_err.second, __FILE__, __LINE__
|
|
);
|
|
|
|
goto exit;
|
|
}
|
|
|
|
ext_galera_conf_err =
|
|
extract_galera_hostgroup_config(j_test_definition, galera_hostgroups);
|
|
|
|
if (ext_galera_conf_err.first) {
|
|
result = invalid_json_error(
|
|
ext_galera_conf_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
ext_galera_init_srvs_state_err =
|
|
extract_galera_servers_state(
|
|
galera_state_id::init_state,
|
|
j_test_definition,
|
|
galera_init_servers_state
|
|
);
|
|
|
|
if (ext_galera_init_srvs_state_err.first) {
|
|
result = invalid_json_error(
|
|
ext_galera_init_srvs_state_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
ext_init_cluster_status_err =
|
|
extract_cluster_status(
|
|
cluster_state::init_state,
|
|
j_test_definition,
|
|
exp_init_cluster_status
|
|
);
|
|
|
|
if (ext_init_cluster_status_err.first) {
|
|
result = invalid_json_error(
|
|
ext_init_cluster_status_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
ext_final_cluster_status_err =
|
|
extract_cluster_status(
|
|
cluster_state::final_state,
|
|
j_test_definition,
|
|
exp_final_cluster_status
|
|
);
|
|
|
|
if (ext_final_cluster_status_err.first) {
|
|
result = invalid_json_error(
|
|
ext_final_cluster_status_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
ext_galera_next_srvs_state_err =
|
|
extract_galera_servers_state(
|
|
galera_state_id::new_state,
|
|
j_test_definition,
|
|
galera_new_servers_state
|
|
);
|
|
|
|
if (ext_galera_next_srvs_state_err.first) {
|
|
result = invalid_json_error(
|
|
ext_galera_next_srvs_state_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
// ************************ CONFIGURATION ********************************
|
|
|
|
prep_mysql_servers_conf_err =
|
|
prepare_mysql_servers_config(proxysql_admin, mysql_servers);
|
|
if (prep_mysql_servers_conf_err.first) {
|
|
result = invalid_config_error(prep_mysql_servers_conf_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
prep_galera_hostgroups_err =
|
|
prepare_mysql_galera_hostgroups(proxysql_admin, galera_hostgroups);
|
|
|
|
if (prep_galera_hostgroups_err.first) {
|
|
result = invalid_config_error(prep_galera_hostgroups_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
prep_galera_init_state_err =
|
|
prepare_galera_cluster_state(proxysql_sqlite, galera_init_servers_state, true);
|
|
|
|
if (prep_galera_init_state_err.first) {
|
|
result = invalid_config_error(prep_galera_init_state_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
// final LOAD after all the configuration has been setup
|
|
load_to_run_err = mysql_query(proxysql_admin, "LOAD MYSQL SERVERS TO RUNTIME");
|
|
if (load_to_run_err) {
|
|
const auto& query_err =
|
|
create_query_error(proxysql_admin, "LOAD MYSQL SERVERS TO RUNTIME", __FILE__, __LINE__);
|
|
result = internal_error(query_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
set_monitor_tos_err = set_galera_monitor_check_times(proxysql_admin);
|
|
if (set_monitor_tos_err.first) {
|
|
result = internal_error(set_monitor_tos_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
// ************************** VERIFICATION *********************************
|
|
{
|
|
int healthcheck_interval = 0;
|
|
int healthcheck_timeout = 0;
|
|
|
|
std::pair<int, std::string> get_monitor_times_err =
|
|
get_galera_monitor_check_times(proxysql_admin, healthcheck_interval, healthcheck_timeout);
|
|
if (get_monitor_times_err.first) {
|
|
result = internal_error(get_monitor_times_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
// map the values to secs
|
|
double healthcheck_interval_s = static_cast<double>(healthcheck_interval) / 1000;
|
|
double healthcheck_timeout_s = static_cast<double>(healthcheck_timeout) / 1000;
|
|
|
|
// proper waiting for monitor timing
|
|
sleep(healthcheck_interval_s + mysql_servers.size()*healthcheck_timeout_s*3 + 1);
|
|
|
|
// check that the current cluster state matches the expected one
|
|
std::vector<server_status> init_cluster_status {};
|
|
const auto& init_cluster_st_err =
|
|
get_current_cluster_status(proxysql_admin, init_cluster_status);
|
|
|
|
if (init_cluster_st_err.first) {
|
|
result = internal_error(
|
|
"'get_current_cluster_status' failed for 'init_state': '" +
|
|
init_cluster_st_err.second + "'",
|
|
__FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
// check the initial cluster status is the expected one
|
|
if (!check_cluster_status(exp_init_cluster_status, init_cluster_status)) {
|
|
result = verification_error(
|
|
"Expected 'proxysql_init_state' doesn't match actual cluster state",
|
|
exp_init_cluster_status,
|
|
init_cluster_status,
|
|
{}
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
// detect the changes that are going to be imposed
|
|
const auto& state_diff =
|
|
galera_servers_state_diff(galera_init_servers_state, galera_new_servers_state);
|
|
|
|
// set the new servers state
|
|
const auto& galera_new_state_to_set =
|
|
galera_update_cluster_state(galera_init_servers_state, galera_new_servers_state);
|
|
prep_galera_final_state_err =
|
|
prepare_galera_cluster_state(proxysql_sqlite, galera_new_servers_state);
|
|
|
|
if (prep_galera_final_state_err.first) {
|
|
result = internal_error(prep_galera_final_state_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
// sleep for monitor ot reconfigure the servers
|
|
sleep(healthcheck_interval_s + mysql_servers.size()*healthcheck_timeout_s*3 + 1);
|
|
|
|
// check that the final cluster state matches the expected one
|
|
std::vector<server_status> final_cluster_status {};
|
|
const auto& final_cluster_st_err =
|
|
get_current_cluster_status(proxysql_admin, final_cluster_status);
|
|
|
|
if (final_cluster_st_err.first) {
|
|
result = internal_error(
|
|
"'get_current_cluster_status' failed for 'final_state': '" +
|
|
final_cluster_st_err.second + "'",
|
|
__FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
// if we are just simulating no further operations are needed
|
|
if (op_mode == operation_mode::simulate) {
|
|
ordered_json j_final_cluster_status = cluster_status_to_json(final_cluster_status);
|
|
std::string init_state_checksum = cluster_status_checksum(init_cluster_status);
|
|
std::string final_state_checksum = cluster_status_checksum(final_cluster_status);
|
|
|
|
result = {
|
|
EXIT_SUCCESS,
|
|
nlohmann::ordered_json {
|
|
{ "err_type", "none" },
|
|
{ "result",
|
|
nlohmann::ordered_json {
|
|
{ "cluster_type", "GALERA" },
|
|
{ "mysql_servers", j_test_definition.at("mysql_servers") },
|
|
{ "mysql_galera_hostgroups", j_test_definition.at("mysql_galera_hostgroups") },
|
|
{ "galera_servers_init_state", j_test_definition.at("galera_servers_init_state") },
|
|
{ "galera_servers_new_state", j_test_definition.at("galera_servers_new_state") },
|
|
{ "proxysql_init_state_checksum", init_state_checksum },
|
|
{ "proxysql_init_state", j_test_definition.at("proxysql_init_state") },
|
|
{ "proxysql_final_state_checksum", final_state_checksum },
|
|
{ "proxysql_final_state", j_final_cluster_status },
|
|
}
|
|
}
|
|
}
|
|
};
|
|
goto exit;
|
|
}
|
|
|
|
if (!check_cluster_status(exp_final_cluster_status, final_cluster_status)) {
|
|
result = verification_error(
|
|
"Expected 'proxysql_final_state' doesn't match actual cluster state",
|
|
exp_final_cluster_status,
|
|
final_cluster_status,
|
|
state_diff
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
if (verbose) {
|
|
ordered_json j_init_cluster_status = cluster_status_to_json(init_cluster_status);
|
|
ordered_json j_final_cluster_status = cluster_status_to_json(final_cluster_status);
|
|
std::string init_state_checksum = cluster_status_checksum(init_cluster_status);
|
|
std::string final_state_checksum = cluster_status_checksum(final_cluster_status);
|
|
|
|
result = {
|
|
EXIT_SUCCESS,
|
|
nlohmann::ordered_json {
|
|
{ "err_type", "none" },
|
|
{ "result",
|
|
nlohmann::ordered_json {
|
|
{ "err_type", "none" },
|
|
{ "err_msg", "" },
|
|
{ "cluster_type", "GALERA" },
|
|
{ "mysql_servers", j_test_definition.at("mysql_servers") },
|
|
{ "mysql_galera_hostgroups", j_test_definition.at("mysql_galera_hostgroups") },
|
|
{ "galera_servers_init_state", j_test_definition.at("galera_servers_init_state") },
|
|
{ "galera_servers_new_state", j_test_definition.at("galera_servers_new_state") },
|
|
{ "proxysql_init_state_checksum", init_state_checksum },
|
|
{ "proxysql_init_state", j_init_cluster_status },
|
|
{ "proxysql_final_state_checksum", final_state_checksum },
|
|
{ "proxysql_final_state", j_final_cluster_status },
|
|
}
|
|
}
|
|
}
|
|
};
|
|
} else {
|
|
result = {
|
|
EXIT_SUCCESS,
|
|
nlohmann::ordered_json { { "err_type", "none" }, { "err_msg", "" } },
|
|
};
|
|
}
|
|
}
|
|
|
|
exit:
|
|
|
|
return result;
|
|
}
|
|
|
|
std::pair<int, nlohmann::ordered_json> simulate_mysql_readonly_cluster_state(
|
|
MYSQL* proxysql_admin,
|
|
MYSQL* proxysql_sqlite,
|
|
const json& j_test_definition,
|
|
const operation_mode& op_mode,
|
|
const bool verbose = false
|
|
) {
|
|
std::pair<int, nlohmann::ordered_json> result {};
|
|
|
|
std::vector<mysql_server_def> mysql_servers {};
|
|
std::vector<replication_hostgroup_config> replication_hostgroups {};
|
|
std::vector<monitor_variable> monitor_variables {};
|
|
std::vector<readonly_server_state> readonly_init_servers_state {};
|
|
std::vector<readonly_server_state> readonly_new_servers_state {};
|
|
|
|
// the expected initial cluster servers status
|
|
std::vector<server_status> exp_init_cluster_status {};
|
|
// the expected final cluster servers status
|
|
std::vector<server_status> exp_final_cluster_status {};
|
|
|
|
// validations errors from extracting the required values from the JSON payload
|
|
std::pair<int, std::string> ext_mysql_srvs_err {};
|
|
std::pair<int, std::string> ext_replication_conf_err {};
|
|
std::pair<int, std::string> ext_monitor_conf_err{};
|
|
std::pair<int, std::string> ext_readonly_init_srvs_state_err {};
|
|
std::pair<int, std::string> ext_readonly_next_srvs_state_err {};
|
|
std::pair<int, std::string> ext_init_cluster_status_err {};
|
|
std::pair<int, std::string> ext_final_cluster_status_err {};
|
|
|
|
// errors from applying the required configuration to ProxySQL
|
|
std::pair<int, std::string> prep_replication_hostgroups_err {};
|
|
std::pair<int, std::string> prep_replication_init_state_err {};
|
|
std::pair<int, std::string> prep_replication_final_state_err {};
|
|
std::pair<int, std::string> prep_mysql_servers_conf_err {};
|
|
std::pair<int, std::string> set_monitor_variables_err{};
|
|
std::pair<int, std::string> set_monitor_tos_err {};
|
|
int load_to_run_err = 0;
|
|
|
|
const std::vector<std::string> rephostgrp_monitor_variables{
|
|
"monitor_writer_is_also_reader"
|
|
};
|
|
|
|
const std::vector<monitor_variable> monitor_defaults_variables{
|
|
{ "monitor_writer_is_also_reader", 1 }
|
|
};
|
|
|
|
// ************************** VALIDATION ********************************
|
|
|
|
ext_mysql_srvs_err = extract_mysql_servers(j_test_definition, mysql_servers);
|
|
|
|
if (ext_mysql_srvs_err.first) {
|
|
result = invalid_json_error(
|
|
ext_mysql_srvs_err.second, __FILE__, __LINE__
|
|
);
|
|
|
|
goto exit;
|
|
}
|
|
|
|
ext_replication_conf_err =
|
|
extract_replication_hostgroup_config(j_test_definition, replication_hostgroups);
|
|
|
|
if (ext_replication_conf_err.first) {
|
|
result = invalid_json_error(
|
|
ext_replication_conf_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
ext_monitor_conf_err =
|
|
extract_monitor_config(j_test_definition, monitor_variables);
|
|
|
|
if (ext_monitor_conf_err.first) {
|
|
result = invalid_json_error(
|
|
ext_monitor_conf_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
ext_readonly_init_srvs_state_err =
|
|
extract_readonly_servers_state(
|
|
readonly_state_id::init_state,
|
|
j_test_definition,
|
|
readonly_init_servers_state
|
|
);
|
|
|
|
if (ext_readonly_init_srvs_state_err.first) {
|
|
result = invalid_json_error(
|
|
ext_readonly_init_srvs_state_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
ext_init_cluster_status_err =
|
|
extract_cluster_status(
|
|
cluster_state::init_state,
|
|
j_test_definition,
|
|
exp_init_cluster_status
|
|
);
|
|
|
|
if (ext_init_cluster_status_err.first) {
|
|
result = invalid_json_error(
|
|
ext_init_cluster_status_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
ext_final_cluster_status_err =
|
|
extract_cluster_status(
|
|
cluster_state::final_state,
|
|
j_test_definition,
|
|
exp_final_cluster_status
|
|
);
|
|
|
|
if (ext_final_cluster_status_err.first) {
|
|
result = invalid_json_error(
|
|
ext_final_cluster_status_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
ext_readonly_next_srvs_state_err =
|
|
extract_readonly_servers_state(
|
|
readonly_state_id::new_state,
|
|
j_test_definition,
|
|
readonly_new_servers_state
|
|
);
|
|
|
|
if (ext_readonly_next_srvs_state_err.first) {
|
|
result = invalid_json_error(
|
|
ext_readonly_next_srvs_state_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
// ************************ CONFIGURATION ********************************
|
|
|
|
prep_mysql_servers_conf_err =
|
|
prepare_mysql_servers_config(proxysql_admin, mysql_servers);
|
|
if (prep_mysql_servers_conf_err.first) {
|
|
result = invalid_config_error(prep_mysql_servers_conf_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
prep_replication_hostgroups_err =
|
|
prepare_mysql_replication_hostgroups(proxysql_admin, replication_hostgroups);
|
|
|
|
if (prep_replication_hostgroups_err.first) {
|
|
result = invalid_config_error(prep_replication_hostgroups_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
/**
|
|
* @brief Change the monitored server state values first for READ_ONLY.
|
|
* @details Due to these two facts, reported servers could not match the right 'exp_init_cluster_status':
|
|
* - The non-convergent nature of READ_ONLY actions due to 'writer_is_also_reader'. When enabled servers
|
|
* are only placed in the 'reader_hostgroup' if they were previously detected there.
|
|
* - Simulator assumes a value 'read_only: 1' for servers that didn't previously receive a value.
|
|
*
|
|
* Due to this conditions, a server could be found in the 'reader_hostgroup', due to
|
|
* 'writer_is_also_reader' even if it wasn't ever placed there by user configuration.
|
|
*/
|
|
{
|
|
prep_replication_init_state_err =
|
|
prepare_readonly_cluster_state(proxysql_sqlite, readonly_init_servers_state, true);
|
|
|
|
if (prep_replication_init_state_err.first) {
|
|
result = invalid_config_error(prep_replication_init_state_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Change the monitor variable values before server loading for READ_ONLY.
|
|
* @details Due to the non-convergent nature of READ_ONLY actions due to 'writer_is_also_reader', servers could
|
|
* not match the right 'exp_init_cluster_status' if the configuration value for 'writer_is_also_reader' changes
|
|
* after previous monitoring actions has been already taken.
|
|
*/
|
|
{
|
|
// prepare monitor variables
|
|
set_monitor_variables_err =
|
|
set_monitor_variables(proxysql_admin, rephostgrp_monitor_variables, monitor_variables);
|
|
|
|
// final LOAD for 'mysql' variables after configuration has been setup
|
|
load_to_run_err = mysql_query(proxysql_admin, "LOAD MYSQL VARIABLES TO RUNTIME");
|
|
if (load_to_run_err) {
|
|
const auto& query_err =
|
|
create_query_error(proxysql_admin, "LOAD MYSQL VARIABLES TO RUNTIME", __FILE__, __LINE__);
|
|
result = internal_error(query_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
// final LOAD after all the configuration has been setup
|
|
load_to_run_err = mysql_query(proxysql_admin, "LOAD MYSQL SERVERS TO RUNTIME");
|
|
if (load_to_run_err) {
|
|
const auto& query_err =
|
|
create_query_error(proxysql_admin, "LOAD MYSQL SERVERS TO RUNTIME", __FILE__, __LINE__);
|
|
result = internal_error(query_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
// give the variables some defaults if they are not present
|
|
monitor_variables = set_monitor_variables_defaults(
|
|
monitor_variables, monitor_defaults_variables
|
|
);
|
|
|
|
set_monitor_tos_err = set_readonly_monitor_check_times(proxysql_admin);
|
|
if (set_monitor_tos_err.first) {
|
|
result = internal_error(set_monitor_tos_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
// ************************** VERIFICATION *********************************
|
|
{
|
|
int readonly_interval = 0;
|
|
int readonly_timeout = 0;
|
|
|
|
std::pair<int, std::string> get_monitor_times_err =
|
|
get_readonly_monitor_check_times(proxysql_admin, readonly_interval, readonly_timeout);
|
|
if (get_monitor_times_err.first) {
|
|
result = internal_error(get_monitor_times_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
// map the values to secs
|
|
double readonly_interval_s = static_cast<double>(readonly_interval) / 1000;
|
|
double readonly_timeout_s = static_cast<double>(readonly_timeout) / 1000;
|
|
|
|
// proper waiting for monitor timing
|
|
double sleep_delay =
|
|
readonly_interval_s + mysql_servers.size()*readonly_timeout_s*0.1 + 1;
|
|
|
|
usleep(sleep_delay * pow(10, 6));
|
|
|
|
// check that the current cluster state matches the expected one
|
|
std::vector<server_status> init_cluster_status {};
|
|
const auto& init_cluster_st_err =
|
|
get_current_cluster_status(proxysql_admin, init_cluster_status);
|
|
|
|
if (init_cluster_st_err.first) {
|
|
result = internal_error(
|
|
"'get_current_cluster_status' failed for 'init_state': '" +
|
|
init_cluster_st_err.second + "'",
|
|
__FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
// check the initial cluster status is the expected one
|
|
if (!check_cluster_status(exp_init_cluster_status, init_cluster_status)) {
|
|
result = verification_error(
|
|
"Expected 'proxysql_init_state' doesn't match actual cluster state",
|
|
exp_init_cluster_status,
|
|
init_cluster_status,
|
|
{}
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
// detect the changes that are going to be imposed
|
|
const auto& state_diff =
|
|
readonly_servers_state_diff(readonly_init_servers_state, readonly_new_servers_state);
|
|
|
|
// set the new servers state
|
|
const auto& replication_new_state_to_set =
|
|
readonly_update_cluster_state(readonly_init_servers_state, readonly_new_servers_state);
|
|
prep_replication_final_state_err =
|
|
prepare_readonly_cluster_state(proxysql_sqlite, readonly_new_servers_state);
|
|
|
|
if (prep_replication_final_state_err.first) {
|
|
result = internal_error(prep_replication_final_state_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
// sleep for monitor ot reconfigure the servers
|
|
usleep(sleep_delay * pow(10, 6));
|
|
|
|
// check that the final cluster state matches the expected one
|
|
std::vector<server_status> final_cluster_status {};
|
|
const auto& final_cluster_st_err =
|
|
get_current_cluster_status(proxysql_admin, final_cluster_status);
|
|
|
|
if (final_cluster_st_err.first) {
|
|
result = internal_error(
|
|
"'get_current_cluster_status' failed for 'final_state': '" +
|
|
final_cluster_st_err.second + "'",
|
|
__FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
// if we are just simulating no further operations are needed
|
|
if (op_mode == operation_mode::simulate) {
|
|
ordered_json j_final_cluster_status = cluster_status_to_json(final_cluster_status);
|
|
std::string init_state_checksum = cluster_status_checksum(init_cluster_status);
|
|
std::string final_state_checksum = cluster_status_checksum(final_cluster_status);
|
|
|
|
result = {
|
|
EXIT_SUCCESS,
|
|
nlohmann::ordered_json {
|
|
{ "err_type", "none" },
|
|
{ "result",
|
|
nlohmann::ordered_json {
|
|
{ "cluster_type", "replication" },
|
|
{ "mysql_servers", j_test_definition.at("mysql_servers") },
|
|
{ "mysql_replication_hostgroups", j_test_definition.at("mysql_replication_hostgroups") },
|
|
{ "readonly_servers_init_state", j_test_definition.at("readonly_servers_init_state") },
|
|
{ "readonly_servers_new_state", j_test_definition.at("readonly_servers_new_state") },
|
|
{ "proxysql_init_state_checksum", init_state_checksum },
|
|
{ "proxysql_init_state", j_test_definition.at("proxysql_init_state") },
|
|
{ "proxysql_final_state_checksum", final_state_checksum },
|
|
{ "proxysql_final_state", j_final_cluster_status },
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
bool has_monitor_config =
|
|
check_present_and_type(j_test_definition, { "mysql_monitor_config" }, json::value_t::array);
|
|
|
|
if (has_monitor_config) {
|
|
result.second["result"]["mysql_monitor_config"] = j_test_definition.at("mysql_monitor_config");
|
|
}
|
|
|
|
goto exit;
|
|
}
|
|
|
|
if (!check_cluster_status(exp_final_cluster_status, final_cluster_status)) {
|
|
result = verification_error(
|
|
"Expected 'proxysql_final_state' doesn't match actual cluster state",
|
|
exp_final_cluster_status,
|
|
final_cluster_status,
|
|
state_diff
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
if (verbose) {
|
|
ordered_json j_init_cluster_status = cluster_status_to_json(init_cluster_status);
|
|
ordered_json j_final_cluster_status = cluster_status_to_json(final_cluster_status);
|
|
std::string init_state_checksum = cluster_status_checksum(init_cluster_status);
|
|
std::string final_state_checksum = cluster_status_checksum(final_cluster_status);
|
|
|
|
result = {
|
|
EXIT_SUCCESS,
|
|
nlohmann::ordered_json {
|
|
{ "err_type", "none" },
|
|
{ "result",
|
|
nlohmann::ordered_json {
|
|
{ "err_type", "none" },
|
|
{ "err_msg", "" },
|
|
{ "cluster_type", "READ_ONLY" },
|
|
{ "mysql_servers", j_test_definition.at("mysql_servers") },
|
|
{ "mysql_replication_hostgroups", j_test_definition.at("mysql_replication_hostgroups") },
|
|
{ "readonly_servers_init_state", j_test_definition.at("readonly_servers_init_state") },
|
|
{ "readonly_servers_new_state", j_test_definition.at("readonly_servers_new_state") },
|
|
{ "proxysql_init_state_checksum", init_state_checksum },
|
|
{ "proxysql_init_state", j_init_cluster_status },
|
|
{ "proxysql_final_state_checksum", final_state_checksum },
|
|
{ "proxysql_final_state", j_final_cluster_status },
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
bool has_monitor_config =
|
|
check_present_and_type(j_test_definition, { "mysql_monitor_config" }, json::value_t::array);
|
|
|
|
if (has_monitor_config) {
|
|
result.second["result"]["mysql_monitor_config"] = j_test_definition.at("mysql_monitor_config");
|
|
}
|
|
|
|
} else {
|
|
result = {
|
|
EXIT_SUCCESS,
|
|
nlohmann::ordered_json { { "err_type", "none" }, { "err_msg", "" } },
|
|
};
|
|
}
|
|
}
|
|
|
|
exit:
|
|
|
|
return result;
|
|
}
|
|
|
|
std::pair<int, nlohmann::ordered_json> simulate_mysql_group_replication_cluster_state(
|
|
MYSQL* proxysql_admin,
|
|
MYSQL* proxysql_sqlite,
|
|
const json& j_test_definition,
|
|
const operation_mode& op_mode,
|
|
const bool verbose = false
|
|
) {
|
|
std::pair<int, nlohmann::ordered_json> result {};
|
|
|
|
std::vector<mysql_server_def> mysql_servers {};
|
|
std::vector<hostgroup_attributes_def> hostgroup_attributes {};
|
|
std::vector<monitor_variable> monitor_variables {};
|
|
std::vector<group_replication_hostgroup_config> group_replication_hostgroups {};
|
|
|
|
std::vector<grouprep_server_state> grouprep_init_servers_state {};
|
|
std::vector<grouprep_server_state> grouprep_new_servers_state {};
|
|
|
|
// the expected initial cluster servers status
|
|
std::vector<server_status> exp_init_cluster_status {};
|
|
// the expected final cluster servers status
|
|
std::vector<server_status> exp_final_cluster_status {};
|
|
|
|
// validations errors from extracting the required values from the JSON payload
|
|
std::pair<int, std::string> ext_mysql_srvs_err {};
|
|
std::pair<int, std::string> ext_hostgroup_attributes_err {};
|
|
std::pair<int, std::string> ext_group_replication_conf_err {};
|
|
std::pair<int, std::string> ext_monitor_conf_err {};
|
|
std::pair<int, std::string> ext_grouprep_init_srvs_state_err {};
|
|
std::pair<int, std::string> ext_grouprep_next_srvs_state_err {};
|
|
std::pair<int, std::string> ext_init_cluster_status_err {};
|
|
std::pair<int, std::string> ext_final_cluster_status_err {};
|
|
|
|
// errors from applying the required configuration to ProxySQL
|
|
std::pair<int, std::string> prep_group_replication_hostgroups_err {};
|
|
std::pair<int, std::string> prep_group_replication_init_state_err {};
|
|
std::pair<int, std::string> prep_group_replication_final_state_err {};
|
|
std::pair<int, std::string> prep_mysql_servers_conf_err {};
|
|
std::pair<int, std::string> prep_mysql_hostgroup_attributes_conf_err {};
|
|
std::pair<int, std::string> set_monitor_tos_err {};
|
|
std::pair<int, std::string> set_monitor_variables_err {};
|
|
int load_to_run_err = 0;
|
|
|
|
string setup_state_timestamp {};
|
|
|
|
const std::vector<std::string> grouprep_monitor_variables {
|
|
"monitor_groupreplication_max_transactions_behind_for_read_only",
|
|
"monitor_groupreplication_healthcheck_interval",
|
|
"monitor_groupreplication_healthcheck_timeout",
|
|
"monitor_groupreplication_max_transactions_behind_count"
|
|
};
|
|
|
|
const std::vector<monitor_variable> monitor_defaults_variables {
|
|
{ "monitor_groupreplication_max_transactions_behind_for_read_only", 1 },
|
|
{ "monitor_groupreplication_healthcheck_interval", 200 },
|
|
{ "monitor_groupreplication_healthcheck_timeout", 100 },
|
|
{ "monitor_groupreplication_max_transactions_behind_count", 3 }
|
|
};
|
|
// ************************** VALIDATION ********************************
|
|
|
|
ext_mysql_srvs_err = extract_mysql_servers(j_test_definition, mysql_servers);
|
|
|
|
if (ext_mysql_srvs_err.first) {
|
|
result = invalid_json_error(
|
|
ext_mysql_srvs_err.second, __FILE__, __LINE__
|
|
);
|
|
|
|
goto exit;
|
|
}
|
|
|
|
ext_hostgroup_attributes_err = extract_mysql_hostgroup_attributes(j_test_definition, hostgroup_attributes);
|
|
if (ext_hostgroup_attributes_err.first) {
|
|
result = invalid_json_error(ext_hostgroup_attributes_err.second, __FILE__, __LINE__);
|
|
|
|
goto exit;
|
|
}
|
|
|
|
ext_group_replication_conf_err =
|
|
extract_group_replication_hostgroup_config(
|
|
j_test_definition, group_replication_hostgroups
|
|
);
|
|
|
|
if (ext_group_replication_conf_err.first) {
|
|
result = invalid_json_error(
|
|
ext_group_replication_conf_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
ext_monitor_conf_err =
|
|
extract_monitor_config(j_test_definition, monitor_variables);
|
|
|
|
if (ext_monitor_conf_err.first) {
|
|
result = invalid_json_error(
|
|
ext_monitor_conf_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
ext_grouprep_init_srvs_state_err =
|
|
extract_grouprep_servers_state(
|
|
grouprep_state_id::init_state,
|
|
j_test_definition,
|
|
grouprep_init_servers_state
|
|
);
|
|
|
|
if (ext_grouprep_init_srvs_state_err.first) {
|
|
result = invalid_json_error(
|
|
ext_grouprep_init_srvs_state_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
ext_init_cluster_status_err =
|
|
extract_cluster_status(
|
|
cluster_state::init_state,
|
|
j_test_definition,
|
|
exp_init_cluster_status
|
|
);
|
|
|
|
if (ext_init_cluster_status_err.first) {
|
|
result = invalid_json_error(
|
|
ext_init_cluster_status_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
ext_final_cluster_status_err =
|
|
extract_cluster_status(
|
|
cluster_state::final_state,
|
|
j_test_definition,
|
|
exp_final_cluster_status
|
|
);
|
|
|
|
if (ext_final_cluster_status_err.first) {
|
|
result = invalid_json_error(
|
|
ext_final_cluster_status_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
ext_grouprep_next_srvs_state_err =
|
|
extract_grouprep_servers_state(
|
|
grouprep_state_id::new_state,
|
|
j_test_definition,
|
|
grouprep_new_servers_state
|
|
);
|
|
|
|
if (ext_grouprep_next_srvs_state_err.first) {
|
|
result = invalid_json_error(
|
|
ext_grouprep_next_srvs_state_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
// ************************ CONFIGURATION ********************************
|
|
|
|
prep_mysql_servers_conf_err =
|
|
prepare_mysql_servers_config(proxysql_admin, mysql_servers);
|
|
if (prep_mysql_servers_conf_err.first) {
|
|
result = invalid_config_error(
|
|
prep_mysql_servers_conf_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
if (!hostgroup_attributes.empty()) {
|
|
prep_mysql_hostgroup_attributes_conf_err =
|
|
prepare_mysql_hostgroup_attributes_config(proxysql_admin, hostgroup_attributes);
|
|
if (prep_mysql_hostgroup_attributes_conf_err.first) {
|
|
result = invalid_config_error(prep_mysql_hostgroup_attributes_conf_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
prep_group_replication_hostgroups_err =
|
|
prepare_mysql_group_replication_hostgroups(
|
|
proxysql_admin, group_replication_hostgroups
|
|
);
|
|
|
|
if (prep_group_replication_hostgroups_err.first) {
|
|
result = invalid_config_error(
|
|
prep_group_replication_hostgroups_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
// give the variables some defaults if they are not present
|
|
monitor_variables = set_monitor_variables_defaults(
|
|
monitor_variables, monitor_defaults_variables
|
|
);
|
|
|
|
// prepare monitor variables
|
|
set_monitor_variables_err =
|
|
set_monitor_variables(
|
|
proxysql_admin, grouprep_monitor_variables, monitor_variables
|
|
);
|
|
|
|
// final LOAD for 'mysql' variables after configuration has been setup
|
|
load_to_run_err = mysql_query(proxysql_admin, "LOAD MYSQL VARIABLES TO RUNTIME");
|
|
if (load_to_run_err) {
|
|
const auto& query_err =
|
|
create_query_error(
|
|
proxysql_admin, "LOAD MYSQL VARIABLES TO RUNTIME", __FILE__, __LINE__
|
|
);
|
|
result = internal_error(query_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
setup_state_timestamp = get_fmt_time();
|
|
|
|
// change the values for the cluster state as a last step
|
|
prep_group_replication_init_state_err =
|
|
prepare_grouprep_cluster_state(
|
|
proxysql_sqlite, grouprep_init_servers_state, hostgroup_attributes.empty() ? 1 : 2
|
|
);
|
|
|
|
if (prep_group_replication_init_state_err.first) {
|
|
result = invalid_config_error(prep_group_replication_init_state_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
// final LOAD after all the configuration has been setup
|
|
load_to_run_err = mysql_query(proxysql_admin, "LOAD MYSQL SERVERS TO RUNTIME");
|
|
if (load_to_run_err) {
|
|
const auto& query_err =
|
|
create_query_error(
|
|
proxysql_admin, "LOAD MYSQL SERVERS TO RUNTIME", __FILE__, __LINE__
|
|
);
|
|
result = internal_error(query_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
// ************************** VERIFICATION *********************************
|
|
{
|
|
int grouprep_interval = 0;
|
|
int grouprep_timeout = 0;
|
|
|
|
std::pair<int, std::string> get_monitor_times_err =
|
|
get_grouprep_monitor_check_times(proxysql_admin, grouprep_interval, grouprep_timeout);
|
|
if (get_monitor_times_err.first) {
|
|
result = internal_error(get_monitor_times_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
// map the values to secs
|
|
double grouprep_interval_s = static_cast<double>(grouprep_interval) / 1000;
|
|
double grouprep_timeout_s = static_cast<double>(grouprep_timeout) / 1000;
|
|
|
|
// proper waiting for monitor timing
|
|
double sleep_delay =
|
|
grouprep_interval_s + mysql_servers.size()*grouprep_timeout_s*0.1 + 1;
|
|
|
|
usleep(sleep_delay * pow(10, 6));
|
|
|
|
string init_state_timestamp { get_fmt_time() };
|
|
|
|
// check that the current cluster state matches the expected one
|
|
std::vector<server_status> init_cluster_status {};
|
|
const auto& init_cluster_st_err =
|
|
get_current_cluster_status(proxysql_admin, init_cluster_status);
|
|
|
|
if (init_cluster_st_err.first) {
|
|
result = internal_error(
|
|
"'get_current_cluster_status' failed for 'init_state': '" +
|
|
init_cluster_st_err.second + "'",
|
|
__FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
// check the initial cluster status is the expected one
|
|
if (!check_cluster_status(exp_init_cluster_status, init_cluster_status)) {
|
|
result = verification_error(
|
|
"Expected 'proxysql_init_state' doesn't match actual cluster state",
|
|
exp_init_cluster_status,
|
|
init_cluster_status,
|
|
{},
|
|
setup_state_timestamp,
|
|
init_state_timestamp
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
// detect the changes that are going to be imposed
|
|
const auto& state_diff =
|
|
grouprep_servers_state_diff(grouprep_init_servers_state, grouprep_new_servers_state);
|
|
|
|
// set the new servers state
|
|
const auto& group_replication_new_state_to_set =
|
|
grouprep_update_cluster_state(grouprep_init_servers_state, grouprep_new_servers_state);
|
|
prep_group_replication_final_state_err =
|
|
prepare_grouprep_cluster_state(proxysql_sqlite, grouprep_new_servers_state);
|
|
|
|
if (prep_group_replication_final_state_err.first) {
|
|
result = internal_error(prep_group_replication_final_state_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
// sleep for monitor ot reconfigure the servers
|
|
usleep(sleep_delay * pow(10, 6));
|
|
|
|
string final_state_timestamp { get_fmt_time() };
|
|
|
|
// check that the final cluster state matches the expected one
|
|
std::vector<server_status> final_cluster_status {};
|
|
const auto& final_cluster_st_err =
|
|
get_current_cluster_status(proxysql_admin, final_cluster_status);
|
|
|
|
if (final_cluster_st_err.first) {
|
|
result = internal_error(
|
|
"'get_current_cluster_status' failed for 'final_state': '" +
|
|
final_cluster_st_err.second + "'",
|
|
__FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
// if we are just simulating no further operations are needed
|
|
if (op_mode == operation_mode::simulate) {
|
|
ordered_json j_final_cluster_status = cluster_status_to_json(final_cluster_status);
|
|
std::string init_state_checksum = cluster_status_checksum(init_cluster_status);
|
|
std::string final_state_checksum = cluster_status_checksum(final_cluster_status);
|
|
|
|
result = {
|
|
EXIT_SUCCESS,
|
|
nlohmann::ordered_json {
|
|
{ "err_type", "none" },
|
|
{ "result", nlohmann::ordered_json {} }
|
|
}
|
|
};
|
|
|
|
bool has_monitor_config =
|
|
check_present_and_type(j_test_definition, {"mysql_monitor_config"}, json::value_t::array);
|
|
|
|
result.second["result"]["cluster_type"] = "GROUP_REPLICATION";
|
|
result.second["result"]["mysql_servers"] = j_test_definition.at("mysql_servers");
|
|
result.second["result"]["mysql_group_replication_hostgroups"] = j_test_definition.at("mysql_group_replication_hostgroups");
|
|
if (has_monitor_config) {
|
|
result.second["result"]["mysql_monitor_config"] = j_test_definition.at("mysql_monitor_config");
|
|
}
|
|
result.second["result"]["grouprep_servers_init_state"] = j_test_definition.at("grouprep_servers_init_state");
|
|
result.second["result"]["grouprep_servers_new_state"] = j_test_definition.at("grouprep_servers_new_state");
|
|
result.second["result"]["proxysql_init_state_checksum"] = init_state_checksum;
|
|
result.second["result"]["proxysql_init_state"] = j_test_definition.at("proxysql_init_state");
|
|
result.second["result"]["proxysql_init_state_timestamp"] = init_state_timestamp;
|
|
result.second["result"]["proxysql_final_state_checksum"] = final_state_checksum;
|
|
result.second["result"]["proxysql_final_state"] = j_final_cluster_status;
|
|
result.second["result"]["proxysql_final_state_timestamp"] = final_state_timestamp;
|
|
|
|
goto exit;
|
|
}
|
|
|
|
if (!check_cluster_status(exp_final_cluster_status, final_cluster_status)) {
|
|
result = verification_error(
|
|
"Expected 'proxysql_final_state' doesn't match actual cluster state",
|
|
exp_final_cluster_status,
|
|
final_cluster_status,
|
|
state_diff,
|
|
init_state_timestamp,
|
|
final_state_timestamp
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
if (verbose) {
|
|
ordered_json j_init_cluster_status = cluster_status_to_json(init_cluster_status);
|
|
ordered_json j_final_cluster_status = cluster_status_to_json(final_cluster_status);
|
|
std::string init_state_checksum = cluster_status_checksum(init_cluster_status);
|
|
std::string final_state_checksum = cluster_status_checksum(final_cluster_status);
|
|
|
|
result = {
|
|
EXIT_SUCCESS,
|
|
nlohmann::ordered_json {
|
|
{ "err_type", "none" },
|
|
{ "result", nlohmann::ordered_json {} }
|
|
}
|
|
};
|
|
|
|
bool has_monitor_config =
|
|
check_present_and_type(j_test_definition, {"mysql_monitor_config"}, json::value_t::array);
|
|
|
|
result.second["result"]["err_type"] = "none";
|
|
result.second["result"]["err_msg"] = "";
|
|
result.second["result"]["cluster_type"] = "GROUP_REPLICATION";
|
|
result.second["result"]["mysql_servers"] = j_test_definition.at("mysql_servers");
|
|
result.second["result"]["mysql_group_replication_hostgroups"] = j_test_definition.at("mysql_group_replication_hostgroups");
|
|
if (has_monitor_config) {
|
|
result.second["result"]["mysql_monitor_config"] = j_test_definition.at("mysql_monitor_config");
|
|
}
|
|
result.second["result"]["grouprep_servers_init_state"] = j_test_definition.at("grouprep_servers_init_state");
|
|
result.second["result"]["grouprep_servers_new_state"] = j_test_definition.at("grouprep_servers_new_state");
|
|
result.second["result"]["proxysql_init_state_timestamp"] = init_state_timestamp;
|
|
result.second["result"]["proxysql_init_state_checksum"] = init_state_checksum;
|
|
result.second["result"]["proxysql_init_state"] = j_init_cluster_status;
|
|
result.second["result"]["proxysql_final_state_checksum"] = final_state_checksum;
|
|
result.second["result"]["proxysql_final_state_timestamp"] = final_state_timestamp;
|
|
result.second["result"]["proxysql_final_state"] = j_final_cluster_status;
|
|
} else {
|
|
result = {
|
|
EXIT_SUCCESS,
|
|
nlohmann::ordered_json { { "err_type", "none" }, { "err_msg", "" } },
|
|
};
|
|
}
|
|
}
|
|
|
|
exit:
|
|
|
|
return result;
|
|
}
|
|
|
|
std::pair<int, nlohmann::ordered_json> simulate_mysql_replicationlag_cluster_state(
|
|
MYSQL* proxysql_admin,
|
|
MYSQL* proxysql_sqlite,
|
|
const json& j_test_definition,
|
|
const operation_mode& op_mode,
|
|
const bool verbose = false
|
|
) {
|
|
std::pair<int, nlohmann::ordered_json> result{};
|
|
|
|
std::vector<mysql_server_def> mysql_servers{};
|
|
std::vector<monitor_variable> monitor_variables{};
|
|
std::vector<replicationlag_server_state> replicationlag_init_servers_state{};
|
|
std::vector<replicationlag_server_state> replicationlag_new_servers_state{};
|
|
std::vector<hostgroup_attributes_def> hostgroup_attributes {};
|
|
|
|
// the expected initial cluster servers status
|
|
std::vector<server_status> exp_init_cluster_status{};
|
|
// the expected final cluster servers status
|
|
std::vector<server_status> exp_final_cluster_status{};
|
|
|
|
// validations errors from extracting the required values from the JSON payload
|
|
std::pair<int, std::string> ext_mysql_srvs_err{};
|
|
std::pair<int, std::string> ext_hostgroup_attributes_err{};
|
|
std::pair<int, std::string> ext_replication_conf_err{};
|
|
std::pair<int, std::string> ext_monitor_conf_err{};
|
|
std::pair<int, std::string> ext_replicationlag_init_srvs_state_err{};
|
|
std::pair<int, std::string> ext_replicationlag_next_srvs_state_err{};
|
|
std::pair<int, std::string> ext_init_cluster_status_err{};
|
|
std::pair<int, std::string> ext_final_cluster_status_err{};
|
|
|
|
// errors from applying the required configuration to ProxySQL
|
|
std::pair<int, std::string> prep_replication_init_state_err{};
|
|
std::pair<int, std::string> prep_replication_final_state_err{};
|
|
std::pair<int, std::string> prep_mysql_servers_conf_err{};
|
|
std::pair<int, std::string> prep_mysql_hostgroup_attributes_conf_err {};
|
|
std::pair<int, std::string> set_monitor_variables_err{};
|
|
std::pair<int, std::string> set_monitor_tos_err{};
|
|
int load_to_run_err = 0;
|
|
|
|
const std::vector<std::string> rephostgrp_monitor_variables{
|
|
"monitor_replication_lag_count",
|
|
"monitor_slave_lag_when_null"
|
|
};
|
|
|
|
const std::vector<monitor_variable> monitor_defaults_variables{
|
|
{ "monitor_replication_lag_count", 1 },
|
|
{ "monitor_slave_lag_when_null", 60 }
|
|
};
|
|
|
|
// ************************** VALIDATION ********************************
|
|
|
|
ext_mysql_srvs_err = extract_mysql_servers(j_test_definition, mysql_servers);
|
|
|
|
if (ext_mysql_srvs_err.first) {
|
|
result = invalid_json_error(
|
|
ext_mysql_srvs_err.second, __FILE__, __LINE__
|
|
);
|
|
|
|
goto exit;
|
|
}
|
|
|
|
ext_hostgroup_attributes_err = extract_mysql_hostgroup_attributes(j_test_definition, hostgroup_attributes);
|
|
if (ext_hostgroup_attributes_err.first) {
|
|
result = invalid_json_error(ext_hostgroup_attributes_err.second, __FILE__, __LINE__);
|
|
|
|
goto exit;
|
|
}
|
|
|
|
ext_monitor_conf_err =
|
|
extract_monitor_config(j_test_definition, monitor_variables);
|
|
|
|
if (ext_monitor_conf_err.first) {
|
|
result = invalid_json_error(
|
|
ext_monitor_conf_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
ext_replicationlag_init_srvs_state_err =
|
|
extract_replicationlag_servers_state(
|
|
replicationlag_state_id::init_state,
|
|
j_test_definition,
|
|
replicationlag_init_servers_state
|
|
);
|
|
|
|
if (ext_replicationlag_init_srvs_state_err.first) {
|
|
result = invalid_json_error(
|
|
ext_replicationlag_init_srvs_state_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
ext_init_cluster_status_err =
|
|
extract_cluster_status(
|
|
cluster_state::init_state,
|
|
j_test_definition,
|
|
exp_init_cluster_status
|
|
);
|
|
|
|
if (ext_init_cluster_status_err.first) {
|
|
result = invalid_json_error(
|
|
ext_init_cluster_status_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
ext_final_cluster_status_err =
|
|
extract_cluster_status(
|
|
cluster_state::final_state,
|
|
j_test_definition,
|
|
exp_final_cluster_status
|
|
);
|
|
|
|
if (ext_final_cluster_status_err.first) {
|
|
result = invalid_json_error(
|
|
ext_final_cluster_status_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
ext_replicationlag_next_srvs_state_err =
|
|
extract_replicationlag_servers_state(
|
|
replicationlag_state_id::new_state,
|
|
j_test_definition,
|
|
replicationlag_new_servers_state
|
|
);
|
|
|
|
if (ext_replicationlag_next_srvs_state_err.first) {
|
|
result = invalid_json_error(
|
|
ext_replicationlag_next_srvs_state_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
// ************************ CONFIGURATION ********************************
|
|
|
|
prep_mysql_servers_conf_err =
|
|
prepare_mysql_servers_config(proxysql_admin, mysql_servers);
|
|
if (prep_mysql_servers_conf_err.first) {
|
|
result = invalid_config_error(prep_mysql_servers_conf_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
if (!hostgroup_attributes.empty()) {
|
|
prep_mysql_hostgroup_attributes_conf_err =
|
|
prepare_mysql_hostgroup_attributes_config(proxysql_admin, hostgroup_attributes);
|
|
if (prep_mysql_hostgroup_attributes_conf_err.first) {
|
|
result = invalid_config_error(prep_mysql_hostgroup_attributes_conf_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
// final LOAD after all the configuration has been setup
|
|
load_to_run_err = mysql_query(proxysql_admin, "LOAD MYSQL SERVERS TO RUNTIME");
|
|
if (load_to_run_err) {
|
|
const auto& query_err =
|
|
create_query_error(proxysql_admin, "LOAD MYSQL SERVERS TO RUNTIME", __FILE__, __LINE__);
|
|
result = internal_error(query_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
// give the variables some defaults if they are not present
|
|
monitor_variables = set_monitor_variables_defaults(
|
|
monitor_variables, monitor_defaults_variables
|
|
);
|
|
|
|
// prepare monitor variables
|
|
set_monitor_variables_err =
|
|
set_monitor_variables(
|
|
proxysql_admin, rephostgrp_monitor_variables, monitor_variables
|
|
);
|
|
|
|
// final LOAD for 'mysql' variables after configuration has been setup
|
|
load_to_run_err = mysql_query(proxysql_admin, "LOAD MYSQL VARIABLES TO RUNTIME");
|
|
if (load_to_run_err) {
|
|
const auto& query_err =
|
|
create_query_error(
|
|
proxysql_admin, "LOAD MYSQL VARIABLES TO RUNTIME", __FILE__, __LINE__
|
|
);
|
|
result = internal_error(query_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
set_monitor_tos_err = set_replicationlag_monitor_check_times(proxysql_admin);
|
|
if (set_monitor_tos_err.first) {
|
|
result = internal_error(set_monitor_tos_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
// change the values for the cluster state as a last step
|
|
prep_replication_init_state_err =
|
|
prepare_replicationlag_cluster_state(proxysql_sqlite, replicationlag_init_servers_state, true);
|
|
|
|
if (prep_replication_init_state_err.first) {
|
|
result = invalid_config_error(prep_replication_init_state_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
// ************************** VERIFICATION *********************************
|
|
{
|
|
int replicationlag_interval = 0;
|
|
int replicationlag_timeout = 0;
|
|
|
|
std::pair<int, std::string> get_monitor_times_err =
|
|
get_replicationlag_monitor_check_times(proxysql_admin, replicationlag_interval, replicationlag_timeout);
|
|
if (get_monitor_times_err.first) {
|
|
result = internal_error(get_monitor_times_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
// map the values to secs
|
|
double replicationlag_interval_s = static_cast<double>(replicationlag_interval) / 1000;
|
|
double replicationlag_timeout_s = static_cast<double>(replicationlag_timeout) / 1000;
|
|
|
|
// proper waiting for monitor timing
|
|
double sleep_delay =
|
|
replicationlag_interval_s + mysql_servers.size() * replicationlag_timeout_s * 0.1 + 1;
|
|
|
|
usleep(sleep_delay * pow(10, 6));
|
|
|
|
// check that the current cluster state matches the expected one
|
|
std::vector<server_status> init_cluster_status{};
|
|
const auto& init_cluster_st_err =
|
|
get_current_cluster_status(proxysql_admin, init_cluster_status);
|
|
|
|
if (init_cluster_st_err.first) {
|
|
result = internal_error(
|
|
"'get_current_cluster_status' failed for 'init_state': '" +
|
|
init_cluster_st_err.second + "'",
|
|
__FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
// check the initial cluster status is the expected one
|
|
if (!check_cluster_status(exp_init_cluster_status, init_cluster_status)) {
|
|
result = verification_error(
|
|
"Expected 'proxysql_init_state' doesn't match actual cluster state",
|
|
exp_init_cluster_status,
|
|
init_cluster_status,
|
|
{}
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
// detect the changes that are going to be imposed
|
|
const auto& state_diff =
|
|
replicationlag_servers_state_diff(replicationlag_init_servers_state, replicationlag_new_servers_state);
|
|
|
|
// set the new servers state
|
|
const auto& replication_new_state_to_set =
|
|
replicationlag_update_cluster_state(replicationlag_init_servers_state, replicationlag_new_servers_state);
|
|
prep_replication_final_state_err =
|
|
prepare_replicationlag_cluster_state(proxysql_sqlite, replicationlag_new_servers_state);
|
|
|
|
if (prep_replication_final_state_err.first) {
|
|
result = internal_error(prep_replication_final_state_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
// sleep for monitor ot reconfigure the servers
|
|
usleep(sleep_delay * pow(10, 6));
|
|
|
|
// check that the final cluster state matches the expected one
|
|
std::vector<server_status> final_cluster_status{};
|
|
const auto& final_cluster_st_err =
|
|
get_current_cluster_status(proxysql_admin, final_cluster_status);
|
|
|
|
if (final_cluster_st_err.first) {
|
|
result = internal_error(
|
|
"'get_current_cluster_status' failed for 'final_state': '" +
|
|
final_cluster_st_err.second + "'",
|
|
__FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
// if we are just simulating no further operations are needed
|
|
if (op_mode == operation_mode::simulate) {
|
|
json j_final_cluster_status = cluster_status_to_json(final_cluster_status);
|
|
std::string init_state_checksum = cluster_status_checksum(init_cluster_status);
|
|
std::string final_state_checksum = cluster_status_checksum(final_cluster_status);
|
|
|
|
result = {
|
|
EXIT_SUCCESS,
|
|
nlohmann::ordered_json {
|
|
{ "err_type", "none" },
|
|
{ "result",
|
|
nlohmann::ordered_json {
|
|
{ "cluster_type", "replicationlag" },
|
|
{ "mysql_servers", j_test_definition.at("mysql_servers") },
|
|
{ "replicationlag_servers_init_state", j_test_definition.at("replicationlag_servers_init_state") },
|
|
{ "replicationlag_servers_new_state", j_test_definition.at("replicationlag_servers_new_state") },
|
|
{ "proxysql_init_state_checksum", init_state_checksum },
|
|
{ "proxysql_init_state", j_test_definition.at("proxysql_init_state") },
|
|
{ "proxysql_final_state_checksum", final_state_checksum },
|
|
{ "proxysql_final_state", j_final_cluster_status },
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
bool has_monitor_config =
|
|
check_present_and_type(j_test_definition, { "mysql_monitor_config" }, json::value_t::array);
|
|
|
|
if (has_monitor_config) {
|
|
result.second["result"]["mysql_monitor_config"] = j_test_definition.at("mysql_monitor_config");
|
|
}
|
|
|
|
bool has_hostgroup_attributes =
|
|
check_present_and_type(j_test_definition, { "mysql_hostgroup_attributes" }, json::value_t::array);
|
|
|
|
if (has_hostgroup_attributes) {
|
|
result.second["result"]["mysql_hostgroup_attributes"] = j_test_definition.at("mysql_hostgroup_attributes");
|
|
}
|
|
|
|
goto exit;
|
|
}
|
|
|
|
if (!check_cluster_status(exp_final_cluster_status, final_cluster_status)) {
|
|
result = verification_error(
|
|
"Expected 'proxysql_final_state' doesn't match actual cluster state",
|
|
exp_final_cluster_status,
|
|
final_cluster_status,
|
|
state_diff
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
if (verbose) {
|
|
json j_init_cluster_status = cluster_status_to_json(init_cluster_status);
|
|
json j_final_cluster_status = cluster_status_to_json(final_cluster_status);
|
|
std::string init_state_checksum = cluster_status_checksum(init_cluster_status);
|
|
std::string final_state_checksum = cluster_status_checksum(final_cluster_status);
|
|
|
|
result = {
|
|
EXIT_SUCCESS,
|
|
nlohmann::ordered_json {
|
|
{ "err_type", "none" },
|
|
{ "result",
|
|
nlohmann::ordered_json {
|
|
{ "err_type", "none" },
|
|
{ "err_msg", "" },
|
|
{ "cluster_type", "REPLICATION_LAG" },
|
|
{ "mysql_servers", j_test_definition.at("mysql_servers") },
|
|
{ "replicationlag_servers_init_state", j_test_definition.at("replicationlag_servers_init_state") },
|
|
{ "replicationlag_servers_new_state", j_test_definition.at("replicationlag_servers_new_state") },
|
|
{ "proxysql_init_state_checksum", init_state_checksum },
|
|
{ "proxysql_init_state", j_init_cluster_status },
|
|
{ "proxysql_final_state_checksum", final_state_checksum },
|
|
{ "proxysql_final_state", j_final_cluster_status },
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
bool has_monitor_config =
|
|
check_present_and_type(j_test_definition, { "mysql_monitor_config" }, json::value_t::array);
|
|
|
|
if (has_monitor_config) {
|
|
result.second["result"]["mysql_monitor_config"] = j_test_definition.at("mysql_monitor_config");
|
|
}
|
|
|
|
bool has_hostgroup_attributes =
|
|
check_present_and_type(j_test_definition, { "mysql_hostgroup_attributes" }, json::value_t::array);
|
|
|
|
if (has_hostgroup_attributes) {
|
|
result.second["result"]["mysql_hostgroup_attributes"] = j_test_definition.at("mysql_hostgroup_attributes");
|
|
}
|
|
|
|
}
|
|
else {
|
|
result = {
|
|
EXIT_SUCCESS,
|
|
nlohmann::ordered_json { { "err_type", "none" }, { "err_msg", "" } },
|
|
};
|
|
}
|
|
}
|
|
|
|
exit:
|
|
|
|
return result;
|
|
}
|
|
|
|
std::pair<int, nlohmann::ordered_json> simulate_aws_aurora_cluster_state(
|
|
MYSQL* proxysql_admin,
|
|
MYSQL* proxysql_sqlite,
|
|
const json& j_test_definition,
|
|
const operation_mode& op_mode,
|
|
const bool verbose = false
|
|
) {
|
|
std::pair<int, nlohmann::ordered_json> result {};
|
|
|
|
std::vector<mysql_server_def> mysql_servers {};
|
|
std::vector<hostgroup_attributes_def> hostgroup_attributes {};
|
|
std::vector<monitor_variable> monitor_variables {};
|
|
std::vector<aurora_hostgroup_config_t> aurora_hostgroups {};
|
|
|
|
std::vector<aurora_server_state_t> aurora_init_servers_state {};
|
|
std::vector<aurora_server_state_t> aurora_new_servers_state {};
|
|
|
|
// the expected initial cluster servers status
|
|
std::vector<server_status> exp_init_cluster_status {};
|
|
// the expected final cluster servers status
|
|
std::vector<server_status> exp_final_cluster_status {};
|
|
|
|
// validations errors from extracting the required values from the JSON payload
|
|
std::pair<int, std::string> ext_mysql_srvs_err {};
|
|
std::pair<int, std::string> ext_hostgroup_attributes_err {};
|
|
std::pair<int, std::string> ext_aurora_conf_err {};
|
|
std::pair<int, std::string> ext_monitor_conf_err {};
|
|
std::pair<int, std::string> ext_aurora_init_srvs_state_err {};
|
|
std::pair<int, std::string> ext_aurora_next_srvs_state_err {};
|
|
std::pair<int, std::string> ext_init_cluster_status_err {};
|
|
std::pair<int, std::string> ext_final_cluster_status_err {};
|
|
|
|
// errors from applying the required configuration to ProxySQL
|
|
std::pair<int, std::string> prep_aurora_hostgroups_err {};
|
|
std::pair<int, std::string> prep_aurora_init_state_err {};
|
|
std::pair<int, std::string> prep_aurora_final_state_err {};
|
|
std::pair<int, std::string> prep_mysql_servers_conf_err {};
|
|
std::pair<int, std::string> prep_mysql_hostgroup_attributes_conf_err {};
|
|
std::pair<int, std::string> set_monitor_tos_err {};
|
|
std::pair<int, std::string> set_monitor_variables_err {};
|
|
int load_to_run_err = 0;
|
|
|
|
string setup_state_timestamp {};
|
|
|
|
const std::vector<std::string> aurora_monitor_variables {
|
|
"mysql-aurora_max_lag_ms_only_read_from_replicas"
|
|
};
|
|
|
|
const std::vector<monitor_variable> monitor_defaults_variables {
|
|
{ "mysql-aurora_max_lag_ms_only_read_from_replicas", 2 }
|
|
};
|
|
// ************************** VALIDATION ********************************
|
|
|
|
ext_mysql_srvs_err = extract_mysql_servers(j_test_definition, mysql_servers);
|
|
|
|
if (ext_mysql_srvs_err.first) {
|
|
result = invalid_json_error(ext_mysql_srvs_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
ext_hostgroup_attributes_err = extract_mysql_hostgroup_attributes(j_test_definition, hostgroup_attributes);
|
|
if (ext_hostgroup_attributes_err.first) {
|
|
result = invalid_json_error(ext_hostgroup_attributes_err.second, __FILE__, __LINE__);
|
|
|
|
goto exit;
|
|
}
|
|
|
|
ext_aurora_conf_err =
|
|
extract_aurora_hostgroup_config(j_test_definition, aurora_hostgroups);
|
|
|
|
if (ext_aurora_conf_err.first) {
|
|
result = invalid_json_error(ext_aurora_conf_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
ext_monitor_conf_err =
|
|
extract_monitor_config(j_test_definition, monitor_variables);
|
|
|
|
if (ext_monitor_conf_err.first) {
|
|
result = invalid_json_error(ext_monitor_conf_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
ext_aurora_init_srvs_state_err =
|
|
extract_aurora_servers_state(
|
|
aurora_state_id::init_state, j_test_definition, aurora_init_servers_state
|
|
);
|
|
|
|
if (ext_aurora_init_srvs_state_err.first) {
|
|
result = invalid_json_error(
|
|
ext_aurora_init_srvs_state_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
ext_init_cluster_status_err =
|
|
extract_cluster_status(
|
|
cluster_state::init_state, j_test_definition, exp_init_cluster_status
|
|
);
|
|
|
|
if (ext_init_cluster_status_err.first) {
|
|
result = invalid_json_error(
|
|
ext_init_cluster_status_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
ext_final_cluster_status_err =
|
|
extract_cluster_status(
|
|
cluster_state::final_state,
|
|
j_test_definition,
|
|
exp_final_cluster_status
|
|
);
|
|
|
|
if (ext_final_cluster_status_err.first) {
|
|
result = invalid_json_error(
|
|
ext_final_cluster_status_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
ext_aurora_next_srvs_state_err =
|
|
extract_aurora_servers_state(
|
|
aurora_state_id::new_state, j_test_definition, aurora_new_servers_state
|
|
);
|
|
|
|
if (ext_aurora_next_srvs_state_err.first) {
|
|
result = invalid_json_error(
|
|
ext_aurora_next_srvs_state_err.second, __FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
// ************************ CONFIGURATION ********************************
|
|
|
|
prep_mysql_servers_conf_err = prepare_mysql_servers_config(proxysql_admin, mysql_servers);
|
|
if (prep_mysql_servers_conf_err.first) {
|
|
result = invalid_config_error(prep_mysql_servers_conf_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
if (!hostgroup_attributes.empty()) {
|
|
prep_mysql_hostgroup_attributes_conf_err =
|
|
prepare_mysql_hostgroup_attributes_config(proxysql_admin, hostgroup_attributes);
|
|
if (prep_mysql_hostgroup_attributes_conf_err.first) {
|
|
result = invalid_config_error(prep_mysql_hostgroup_attributes_conf_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
prep_aurora_hostgroups_err = prepare_mysql_aurora_hostgroups(proxysql_admin, aurora_hostgroups);
|
|
if (prep_aurora_hostgroups_err.first) {
|
|
result = invalid_config_error(prep_aurora_hostgroups_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
// give the variables some defaults if they are not present
|
|
monitor_variables = set_monitor_variables_defaults(
|
|
monitor_variables, monitor_defaults_variables
|
|
);
|
|
|
|
// prepare monitor variables
|
|
set_monitor_variables_err =
|
|
set_monitor_variables(
|
|
proxysql_admin, aurora_monitor_variables, monitor_variables
|
|
);
|
|
|
|
// final LOAD for 'mysql' variables after configuration has been setup
|
|
load_to_run_err = mysql_query(proxysql_admin, "LOAD MYSQL VARIABLES TO RUNTIME");
|
|
if (load_to_run_err) {
|
|
const auto& query_err {
|
|
create_query_error(proxysql_admin, "LOAD MYSQL VARIABLES TO RUNTIME", __FILE__, __LINE__)
|
|
};
|
|
result = internal_error(query_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
setup_state_timestamp = get_fmt_time();
|
|
|
|
// change the values for the cluster state as a last step
|
|
prep_aurora_init_state_err =
|
|
prepare_aurora_cluster_state(
|
|
proxysql_sqlite, aurora_init_servers_state, hostgroup_attributes.empty() ? 1 : 2
|
|
);
|
|
|
|
if (prep_aurora_init_state_err.first) {
|
|
result = invalid_config_error(prep_aurora_init_state_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
// final LOAD after all the configuration has been setup
|
|
load_to_run_err = mysql_query(proxysql_admin, "LOAD MYSQL SERVERS TO RUNTIME");
|
|
if (load_to_run_err) {
|
|
const auto& query_err =
|
|
create_query_error(
|
|
proxysql_admin, "LOAD MYSQL SERVERS TO RUNTIME", __FILE__, __LINE__
|
|
);
|
|
result = internal_error(query_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
// ************************** VERIFICATION *********************************
|
|
{
|
|
int aurora_interval = 0;
|
|
int aurora_timeout = 0;
|
|
|
|
std::pair<int, std::string> get_monitor_times_err =
|
|
get_aurora_monitor_check_times(proxysql_admin, aurora_interval, aurora_timeout);
|
|
if (get_monitor_times_err.first) {
|
|
result = internal_error(get_monitor_times_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
// map the values to secs
|
|
double aurora_interval_s = static_cast<double>(aurora_interval) / 1000;
|
|
double aurora_timeout_s = static_cast<double>(aurora_timeout) / 1000;
|
|
|
|
// proper waiting for monitor timing
|
|
double sleep_delay =
|
|
aurora_interval_s + mysql_servers.size()*aurora_timeout_s*0.1 + 1;
|
|
|
|
usleep(sleep_delay * pow(10, 6));
|
|
|
|
string init_state_timestamp { get_fmt_time() };
|
|
|
|
// check that the current cluster state matches the expected one
|
|
std::vector<server_status> init_cluster_status {};
|
|
const auto& init_cluster_st_err =
|
|
get_current_cluster_status(proxysql_admin, init_cluster_status);
|
|
|
|
if (init_cluster_st_err.first) {
|
|
result = internal_error(
|
|
"'get_current_cluster_status' failed for 'init_state': '" +
|
|
init_cluster_st_err.second + "'",
|
|
__FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
// check the initial cluster status is the expected one
|
|
if (!check_cluster_status(exp_init_cluster_status, init_cluster_status)) {
|
|
result = verification_error(
|
|
"Expected 'proxysql_init_state' doesn't match actual cluster state",
|
|
exp_init_cluster_status,
|
|
init_cluster_status,
|
|
{},
|
|
setup_state_timestamp,
|
|
init_state_timestamp
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
// detect the changes that are going to be imposed
|
|
const auto& state_diff {
|
|
aurora_servers_state_diff(aurora_init_servers_state, aurora_new_servers_state)
|
|
};
|
|
|
|
// set the new servers state
|
|
const auto& aurora_new_state_to_set {
|
|
aurora_update_cluster_state(aurora_init_servers_state, aurora_new_servers_state)
|
|
};
|
|
// cleanup=1 deletes REPLICA_HOST_STATUS rows whose SERVER_ID is not in the new
|
|
// state, so payloads can simulate a server being removed from the topology
|
|
// (e.g. for autopurge_missing_checks coverage). Backwards-compatible: existing
|
|
// payloads keep the full server list in new_state, so nothing extra is purged.
|
|
prep_aurora_final_state_err =
|
|
prepare_aurora_cluster_state(proxysql_sqlite, aurora_new_servers_state, 1);
|
|
|
|
if (prep_aurora_final_state_err.first) {
|
|
result = internal_error(prep_aurora_final_state_err.second, __FILE__, __LINE__);
|
|
goto exit;
|
|
}
|
|
|
|
// sleep for monitor ot reconfigure the servers
|
|
usleep(sleep_delay * pow(10, 6));
|
|
string final_state_timestamp { get_fmt_time() };
|
|
|
|
// check that the final cluster state matches the expected one
|
|
std::vector<server_status> final_cluster_status {};
|
|
const auto& final_cluster_st_err { get_current_cluster_status(proxysql_admin, final_cluster_status) };
|
|
|
|
if (final_cluster_st_err.first) {
|
|
result = internal_error(
|
|
"'get_current_cluster_status' failed for 'final_state': '" +
|
|
final_cluster_st_err.second + "'",
|
|
__FILE__, __LINE__
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
// if we are just simulating no further operations are needed
|
|
if (op_mode == operation_mode::simulate) {
|
|
ordered_json j_final_cluster_status = cluster_status_to_json(final_cluster_status);
|
|
std::string init_state_checksum = cluster_status_checksum(init_cluster_status);
|
|
std::string final_state_checksum = cluster_status_checksum(final_cluster_status);
|
|
|
|
result = {
|
|
EXIT_SUCCESS,
|
|
nlohmann::ordered_json {
|
|
{ "err_type", "none" },
|
|
{ "result", nlohmann::ordered_json {} }
|
|
}
|
|
};
|
|
|
|
bool has_monitor_config {
|
|
check_present_and_type(j_test_definition, {"mysql_monitor_config"}, json::value_t::array)
|
|
};
|
|
|
|
result.second["result"]["cluster_type"] = "AURORA";
|
|
result.second["result"]["mysql_servers"] = j_test_definition.at("mysql_servers");
|
|
result.second["result"]["mysql_aws_aurora_hostgroups"] = j_test_definition.at("mysql_aws_aurora_hostgroups");
|
|
if (has_monitor_config) {
|
|
result.second["result"]["mysql_monitor_config"] = j_test_definition.at("mysql_monitor_config");
|
|
}
|
|
result.second["result"]["aurora_servers_init_state"] = j_test_definition.at("aurora_servers_init_state");
|
|
result.second["result"]["aurora_servers_new_state"] = j_test_definition.at("aurora_servers_new_state");
|
|
result.second["result"]["proxysql_init_state_checksum"] = init_state_checksum;
|
|
result.second["result"]["proxysql_init_state"] = j_test_definition.at("proxysql_init_state");
|
|
result.second["result"]["proxysql_init_state_timestamp"] = init_state_timestamp;
|
|
result.second["result"]["proxysql_final_state_checksum"] = final_state_checksum;
|
|
result.second["result"]["proxysql_final_state"] = j_final_cluster_status;
|
|
result.second["result"]["proxysql_final_state_timestamp"] = final_state_timestamp;
|
|
|
|
goto exit;
|
|
}
|
|
|
|
if (!check_cluster_status(exp_final_cluster_status, final_cluster_status)) {
|
|
result = verification_error(
|
|
"Expected 'proxysql_final_state' doesn't match actual cluster state",
|
|
exp_final_cluster_status,
|
|
final_cluster_status,
|
|
state_diff,
|
|
init_state_timestamp,
|
|
final_state_timestamp
|
|
);
|
|
goto exit;
|
|
}
|
|
|
|
if (verbose) {
|
|
ordered_json j_init_cluster_status = cluster_status_to_json(init_cluster_status);
|
|
ordered_json j_final_cluster_status = cluster_status_to_json(final_cluster_status);
|
|
std::string init_state_checksum = cluster_status_checksum(init_cluster_status);
|
|
std::string final_state_checksum = cluster_status_checksum(final_cluster_status);
|
|
|
|
result = {
|
|
EXIT_SUCCESS,
|
|
nlohmann::ordered_json {
|
|
{ "err_type", "none" },
|
|
{ "result", nlohmann::ordered_json {} }
|
|
}
|
|
};
|
|
|
|
bool has_monitor_config =
|
|
check_present_and_type(j_test_definition, {"mysql_monitor_config"}, json::value_t::array);
|
|
|
|
result.second["result"]["err_type"] = "none";
|
|
result.second["result"]["err_msg"] = "";
|
|
result.second["result"]["cluster_type"] = "AURORA";
|
|
result.second["result"]["mysql_servers"] = j_test_definition.at("mysql_servers");
|
|
result.second["result"]["mysql_aws_aurora_hostgroups"] = j_test_definition.at("mysql_aws_aurora_hostgroups");
|
|
if (has_monitor_config) {
|
|
result.second["result"]["mysql_monitor_config"] = j_test_definition.at("mysql_monitor_config");
|
|
}
|
|
result.second["result"]["aurora_servers_init_state"] = j_test_definition.at("aurora_servers_init_state");
|
|
result.second["result"]["aurora_servers_new_state"] = j_test_definition.at("aurora_servers_new_state");
|
|
result.second["result"]["proxysql_init_state_timestamp"] = init_state_timestamp;
|
|
result.second["result"]["proxysql_init_state_checksum"] = init_state_checksum;
|
|
result.second["result"]["proxysql_init_state"] = j_init_cluster_status;
|
|
result.second["result"]["proxysql_final_state_checksum"] = final_state_checksum;
|
|
result.second["result"]["proxysql_final_state_timestamp"] = final_state_timestamp;
|
|
result.second["result"]["proxysql_final_state"] = j_final_cluster_status;
|
|
} else {
|
|
result = {
|
|
EXIT_SUCCESS,
|
|
nlohmann::ordered_json { { "err_type", "none" }, { "err_msg", "" } },
|
|
};
|
|
}
|
|
}
|
|
|
|
exit:
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @brief Enum holding the types of payloads supported by the
|
|
* simulator.
|
|
*/
|
|
enum class test_def_type {
|
|
UNSUPPORTED = -1,
|
|
GALERA_CLUSTER = 0,
|
|
READONLY_TEST,
|
|
GROUP_REPLICATION,
|
|
REPLICATION_LAG,
|
|
AWS_AURORA
|
|
};
|
|
|
|
/**
|
|
* @brief Retrieve the type of the supplied test file.
|
|
*
|
|
* @param j_test_definition The JSON holding the test definition.
|
|
* @param test_type A reference to the 'test type' to be retrieved.
|
|
*
|
|
* @return A `std::pair` of kind `{ err_code, "err_msg" }`.
|
|
*/
|
|
std::pair<int, std::string> get_test_type(
|
|
const nlohmann::ordered_json& j_test_definition,
|
|
enum test_def_type& test_type
|
|
) {
|
|
std::pair<int, std::string> result { EXIT_SUCCESS, "" };
|
|
|
|
try {
|
|
const std::string cluster_type = j_test_definition.at("cluster_type");
|
|
|
|
if (cluster_type == "GALERA") {
|
|
test_type = test_def_type::GALERA_CLUSTER;
|
|
} else if (cluster_type == "READ_ONLY") {
|
|
test_type = test_def_type::READONLY_TEST;
|
|
} else if (cluster_type == "GROUP_REPLICATION") {
|
|
test_type = test_def_type::GROUP_REPLICATION;
|
|
} else if (cluster_type == "REPLICATION_LAG") {
|
|
test_type = test_def_type::REPLICATION_LAG;
|
|
} else if (cluster_type == "AURORA") {
|
|
test_type = test_def_type::AWS_AURORA;
|
|
} else {
|
|
test_type = test_def_type::UNSUPPORTED;
|
|
// Specify the error
|
|
result = { EXIT_FAILURE, "Unsupported 'cluster_type'" };
|
|
}
|
|
} catch (const std::exception& e) {
|
|
result = {
|
|
EXIT_FAILURE,
|
|
std::string { "Exception trying to access supplied JSON: '" } + e.what() + "'",
|
|
};
|
|
test_type = test_def_type::UNSUPPORTED;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
std::pair<int, std::string> get_cmd_options(
|
|
int argc, const char* argv[], simulator_options& out_sim_options
|
|
) {
|
|
std::pair<int, std::string> err_res { EXIT_SUCCESS, "" };
|
|
|
|
// command line options to extract
|
|
bool enable_verbose_output = false;
|
|
bool stop_on_failure = false;
|
|
operation_mode op_mode = operation_mode::simulate;
|
|
std::string payload_path {};
|
|
|
|
// define the command line options
|
|
ez::ezOptionParser opt {};
|
|
opt.overview = "Generic Cluster Simulator for ProxySQL";
|
|
opt.syntax = "cluster_simulator [OPTIONS]";
|
|
opt.footer = "\n\nHave fun :)";
|
|
|
|
// clang-format off
|
|
opt.add(
|
|
(const char *)"", 0, 0, 0, (const char *)"Display usage instructions.",
|
|
(const char *)"-h", (const char *)"-help", (const char *)"--help", (const char *)"--usage"
|
|
);
|
|
opt.add(
|
|
(const char *)"", 0, 0, 0, (const char *)"Enable verbose output",
|
|
(const char *)"-v", (const char *)"--verbose"
|
|
);
|
|
opt.add(
|
|
(const char *)"simulate", 0, 1, 0, (const char *)"Operation mode",
|
|
(const char *)"-m", (const char *)"--mode"
|
|
);
|
|
opt.add(
|
|
(const char *)"", 0, 0, 0, (const char *)"Stop on failure",
|
|
(const char *)"--stop-on-failure"
|
|
);
|
|
opt.add(
|
|
(const char *)"", 1, 1, 0, (const char *)"Specify input payload file",
|
|
(const char *)"-f", (const char *)"--file"
|
|
);
|
|
// clang-format on
|
|
|
|
// parse the arguments
|
|
opt.parse(argc, argv);
|
|
|
|
// extract command line options
|
|
if (opt.isSet("-h")) {
|
|
std::string usage {};
|
|
opt.getUsage(usage);
|
|
std::cout << usage << std::endl;
|
|
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
if (opt.isSet("-v")) {
|
|
enable_verbose_output = true;
|
|
}
|
|
if (opt.isSet("--stop-on-failure")) {
|
|
stop_on_failure = true;
|
|
}
|
|
if (opt.isSet("-m")) {
|
|
std::string s_op_mode {};
|
|
opt.get("-m")->getString(s_op_mode);
|
|
|
|
if (s_op_mode == "simulate") {
|
|
op_mode = operation_mode::simulate;
|
|
} else if (s_op_mode == "verify") {
|
|
op_mode = operation_mode::verify;
|
|
} else {
|
|
std::string t_err_msg { "File %s, line %d, Error: %s" };
|
|
std::string err_msg {};
|
|
string_format(
|
|
t_err_msg, err_msg, __FILE__, __LINE__,
|
|
"Invalid '--mode' supplied"
|
|
);
|
|
err_res = { EXIT_FAILURE, err_msg };
|
|
}
|
|
}
|
|
if (opt.isSet("-f")) {
|
|
opt.get("-f")->getString(payload_path);
|
|
|
|
if (payload_path.empty()) {
|
|
std::string t_err_msg { "File %s, line %d, Error: %s" };
|
|
std::string err_msg {};
|
|
string_format(
|
|
t_err_msg, err_msg, __FILE__, __LINE__,
|
|
"Empty string supplied to parameter '--file'"
|
|
);
|
|
err_res = { EXIT_FAILURE, err_msg };
|
|
}
|
|
} else {
|
|
std::string t_err_msg { "File %s, line %d, Error: %s" };
|
|
std::string err_msg {};
|
|
string_format(
|
|
t_err_msg, err_msg, __FILE__, __LINE__,
|
|
"Missing required parameter '--file'"
|
|
);
|
|
err_res = { EXIT_FAILURE, err_msg };
|
|
}
|
|
|
|
// check if arguments has been properly extracted
|
|
if (err_res.first == EXIT_SUCCESS) {
|
|
// option struct to be filled and returned
|
|
simulator_options sim_options {};
|
|
|
|
sim_options.verbose_output = enable_verbose_output;
|
|
sim_options.op_mode = op_mode;
|
|
sim_options.payload_path = payload_path;
|
|
sim_options.stop_on_failure = stop_on_failure;
|
|
|
|
// fill the output parameter
|
|
out_sim_options = sim_options;
|
|
}
|
|
|
|
return err_res;
|
|
}
|
|
|
|
// Per-family SQLiteServer endpoints — env var overrides with legacy defaults.
|
|
static std::string env_or(const char* name, const std::string& def) {
|
|
const char* v = std::getenv(name);
|
|
return (v != nullptr && *v != '\0') ? std::string { v } : def;
|
|
}
|
|
static int env_or(const char* name, int def) {
|
|
const char* v = std::getenv(name);
|
|
if (v == nullptr || *v == '\0') { return def; }
|
|
try { return std::stoi(v); } catch (const std::exception&) { return def; }
|
|
}
|
|
|
|
const std::string galera_hostname { env_or("GALERA_HOSTNAME", "127.1.1.11") };
|
|
const std::string galera_username { env_or("GALERA_USERNAME", "galera1") };
|
|
const std::string galera_pass { env_or("GALERA_PASSWORD", "pass1") };
|
|
const int galera_port = env_or("GALERA_PORT", 3306);
|
|
|
|
const std::string readonly_hostname { env_or("READONLY_HOSTNAME", "127.0.0.1") };
|
|
const std::string readonly_username { env_or("READONLY_USERNAME", "root") };
|
|
const std::string readonly_pass { env_or("READONLY_PASSWORD", "root") };
|
|
const int readonly_port = env_or("READONLY_PORT", 3306);
|
|
|
|
const std::string grouprep_hostname { env_or("GROUPREP_HOSTNAME", "127.2.1.1") };
|
|
const std::string grouprep_username { env_or("GROUPREP_USERNAME", "grouprep1") };
|
|
const std::string grouprep_pass { env_or("GROUPREP_PASSWORD", "pass1") };
|
|
const int grouprep_port = env_or("GROUPREP_PORT", 3306);
|
|
|
|
const std::string replicationlag_hostname { env_or("REPL_LAG_HOSTNAME", "127.0.0.1") };
|
|
const std::string replicationlag_username { env_or("REPL_LAG_USERNAME", "root") };
|
|
const std::string replicationlag_pass { env_or("REPL_LAG_PASSWORD", "root") };
|
|
const int replicationlag_port = env_or("REPL_LAG_PORT", 3306);
|
|
|
|
const std::string aws_aurora_hostname { env_or("AURORA_HOSTNAME", "127.0.1.11") };
|
|
const std::string aws_aurora_username { env_or("AURORA_USERNAME", "aurora1") };
|
|
const std::string aws_aurora_pass { env_or("AURORA_PASSWORD", "pass1") };
|
|
const int aws_aurora_port = env_or("AURORA_PORT", 3306);
|
|
|
|
// //////////////////////////////////////////////
|
|
|
|
int main(int argc, const char *argv[]) {
|
|
int err_code = 0;
|
|
CommandLine cl {};
|
|
std::pair<int, nlohmann::ordered_json> result {
|
|
EXIT_SUCCESS,
|
|
{
|
|
{ "err_type", "none" },
|
|
{ "err_msg", "" }
|
|
}
|
|
};
|
|
|
|
MYSQL* proxysql_admin = mysql_init(NULL);
|
|
MYSQL* proxysql_sqlite = nullptr;
|
|
|
|
std::pair<int, std::string> cmd_options_err {};
|
|
simulator_options options {};
|
|
|
|
if(cl.getEnv()) {
|
|
result = invalid_input_error(
|
|
"Unable to get the required 'ENV' variables", __FILE__, __LINE__
|
|
);
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
if (
|
|
!mysql_real_connect(
|
|
proxysql_admin, cl.host, cl.admin_username, cl.admin_password,
|
|
NULL, cl.admin_port, NULL, 0
|
|
)
|
|
) {
|
|
result = invalid_input_error(
|
|
std::string { mysql_error(proxysql_admin) }, __FILE__, __LINE__
|
|
);
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
cmd_options_err = get_cmd_options(argc, argv, options);
|
|
if (cmd_options_err.first) {
|
|
result = invalid_input_error(cmd_options_err.second, __FILE__, __LINE__);
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
{
|
|
nlohmann::ordered_json j_tests_defs {};
|
|
std::vector<nlohmann::ordered_json> simulation_results {};
|
|
|
|
try {
|
|
std::ifstream payload_stream { options.payload_path };
|
|
j_tests_defs = nlohmann::ordered_json::parse(payload_stream);
|
|
} catch (std::exception& e) {
|
|
std::string t_err_msg {
|
|
"Input JSON payload failed to be parsed with error: '%s'"
|
|
};
|
|
std::string err_msg {};
|
|
string_format(t_err_msg, err_msg, e.what());
|
|
result = invalid_input_error(err_msg, __FILE__, __LINE__);
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
// check that the payload has the basic shape to access the test
|
|
// definitions
|
|
if (!j_tests_defs.is_array()) {
|
|
result = invalid_input_error(
|
|
"Test payload must be a JSON array", __FILE__, __LINE__
|
|
);
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
// ********************* CLUSTER SIMULATION **************************
|
|
// *********************************************************************
|
|
|
|
for (const auto& j_test_def : j_tests_defs) {
|
|
// Extract the type of cluster to simulate from the payload
|
|
enum test_def_type test_type { test_def_type::UNSUPPORTED };
|
|
const auto& test_type_res =
|
|
get_test_type(j_test_def, test_type);
|
|
|
|
if (test_type_res.first) {
|
|
result = invalid_input_error(
|
|
"Test payload holds an non-existent, invalid or unsupported"
|
|
" \"cluster_type\"",
|
|
__FILE__, __LINE__
|
|
);
|
|
break;
|
|
}
|
|
|
|
if (test_type == test_def_type::GALERA_CLUSTER) {
|
|
// Connect to the 'SQLite' to change the contents of 'HOST_STATUS_GALERA'
|
|
proxysql_sqlite = mysql_init(NULL);
|
|
|
|
if (
|
|
!mysql_real_connect(
|
|
proxysql_sqlite, galera_hostname.c_str(), galera_username.c_str(),
|
|
galera_pass.c_str(), NULL, galera_port, NULL, 0
|
|
)
|
|
) {
|
|
result = invalid_input_error(
|
|
std::string { mysql_error(proxysql_sqlite) }, __FILE__, __LINE__
|
|
);
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
const std::pair<int, nlohmann::ordered_json> sim_res =
|
|
simulate_galera_cluster_state(
|
|
proxysql_admin,
|
|
proxysql_sqlite,
|
|
j_test_def,
|
|
options.op_mode,
|
|
options.verbose_output
|
|
);
|
|
|
|
simulation_results.push_back(sim_res.second);
|
|
|
|
if (sim_res.first) {
|
|
result.second = {
|
|
{ "err_type", "simulation_error" },
|
|
{ "err_msg", "One or more of the requested simulations have failed" }
|
|
};
|
|
|
|
result.first = EXIT_FAILURE;
|
|
if (options.stop_on_failure) {
|
|
break;
|
|
}
|
|
}
|
|
} else if (test_type == test_def_type::READONLY_TEST) {
|
|
// Connect to the 'SQLite' to change the contents of 'READONLY_STATUS'
|
|
proxysql_sqlite = mysql_init(NULL);
|
|
|
|
if (
|
|
!mysql_real_connect(
|
|
proxysql_sqlite, readonly_hostname.c_str(), readonly_username.c_str(),
|
|
readonly_pass.c_str(), NULL, readonly_port, NULL, 0
|
|
)
|
|
) {
|
|
result = invalid_input_error(
|
|
std::string { mysql_error(proxysql_sqlite) }, __FILE__, __LINE__
|
|
);
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
const std::pair<int, nlohmann::ordered_json> sim_res =
|
|
simulate_mysql_readonly_cluster_state(
|
|
proxysql_admin,
|
|
proxysql_sqlite,
|
|
j_test_def,
|
|
options.op_mode,
|
|
options.verbose_output
|
|
);
|
|
|
|
simulation_results.push_back(sim_res.second);
|
|
|
|
if (sim_res.first) {
|
|
result.second = {
|
|
{ "err_type", "simulation_error" },
|
|
{ "err_msg", "One or more of the requested simulations have failed" }
|
|
};
|
|
|
|
result.first = EXIT_FAILURE;
|
|
if (options.stop_on_failure) {
|
|
break;
|
|
}
|
|
}
|
|
} else if (test_type == test_def_type::GROUP_REPLICATION) {
|
|
// Connect to the 'SQLite' to change the contents of
|
|
// 'GR_MEMBER_ROUTING_CANDIDATE_STATUS'.
|
|
proxysql_sqlite = mysql_init(NULL);
|
|
|
|
if (
|
|
!mysql_real_connect(
|
|
proxysql_sqlite, grouprep_hostname.c_str(), grouprep_username.c_str(),
|
|
grouprep_pass.c_str(), NULL, grouprep_port, NULL, 0
|
|
)
|
|
) {
|
|
result = invalid_input_error(
|
|
std::string { mysql_error(proxysql_sqlite) }, __FILE__, __LINE__
|
|
);
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
const std::pair<int, nlohmann::ordered_json> sim_res =
|
|
simulate_mysql_group_replication_cluster_state(
|
|
proxysql_admin,
|
|
proxysql_sqlite,
|
|
j_test_def,
|
|
options.op_mode,
|
|
options.verbose_output
|
|
);
|
|
|
|
simulation_results.push_back(sim_res.second);
|
|
|
|
if (sim_res.first) {
|
|
result.second = {
|
|
{ "err_type", "simulation_error" },
|
|
{ "err_msg", "One or more of the requested simulations have failed" }
|
|
};
|
|
|
|
result.first = EXIT_FAILURE;
|
|
if (options.stop_on_failure) {
|
|
break;
|
|
}
|
|
}
|
|
} else if (test_type == test_def_type::REPLICATION_LAG) {
|
|
// Connect to the 'SQLite' to change the contents of 'REPLICATIONLAG_HOST_STATUS'
|
|
proxysql_sqlite = mysql_init(NULL);
|
|
|
|
if (
|
|
!mysql_real_connect(
|
|
proxysql_sqlite, replicationlag_hostname.c_str(), replicationlag_username.c_str(),
|
|
replicationlag_pass.c_str(), NULL, replicationlag_port, NULL, 0
|
|
)
|
|
) {
|
|
result = invalid_input_error(
|
|
std::string{ mysql_error(proxysql_sqlite) }, __FILE__, __LINE__
|
|
);
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
const std::pair<int, nlohmann::ordered_json> sim_res =
|
|
simulate_mysql_replicationlag_cluster_state(
|
|
proxysql_admin,
|
|
proxysql_sqlite,
|
|
j_test_def,
|
|
options.op_mode,
|
|
options.verbose_output
|
|
);
|
|
|
|
simulation_results.push_back(sim_res.second);
|
|
|
|
if (sim_res.first) {
|
|
result.second = {
|
|
{ "err_type", "simulation_error" },
|
|
{ "err_msg", "One or more of the requested simulations have failed" }
|
|
};
|
|
|
|
result.first = EXIT_FAILURE;
|
|
if (options.stop_on_failure) {
|
|
break;
|
|
}
|
|
}
|
|
} else if (test_type == test_def_type::AWS_AURORA) {
|
|
// Connect to the 'SQLite' to change the contents of 'HOST_STATUS_aws_aurora'
|
|
proxysql_sqlite = mysql_init(NULL);
|
|
|
|
if (
|
|
!mysql_real_connect(
|
|
proxysql_sqlite, aws_aurora_hostname.c_str(), aws_aurora_username.c_str(),
|
|
aws_aurora_pass.c_str(), NULL, aws_aurora_port, NULL, 0
|
|
)
|
|
) {
|
|
result = invalid_input_error(mysql_error(proxysql_sqlite), __FILE__, __LINE__);
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
const std::pair<int, nlohmann::ordered_json> sim_res =
|
|
simulate_aws_aurora_cluster_state(
|
|
proxysql_admin,
|
|
proxysql_sqlite,
|
|
j_test_def,
|
|
options.op_mode,
|
|
options.verbose_output
|
|
);
|
|
|
|
simulation_results.push_back(sim_res.second);
|
|
|
|
if (sim_res.first) {
|
|
result.second = {
|
|
{ "err_type", "simulation_error" },
|
|
{ "err_msg", "One or more of the requested simulations have failed" }
|
|
};
|
|
|
|
result.first = EXIT_FAILURE;
|
|
if (options.stop_on_failure) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
mysql_close(proxysql_sqlite);
|
|
}
|
|
|
|
// ************************************************************************
|
|
|
|
// set the simulations results in the output
|
|
result.second["results"] = simulation_results;
|
|
}
|
|
|
|
cleanup:
|
|
std::string t_str_res { serialize_result(result.second) };
|
|
std::cout << t_str_res << std::endl;
|
|
|
|
mysql_close(proxysql_admin);
|
|
|
|
return result.first;
|
|
}
|