
MXS-2236 Add own long test and possibility to run tests under Valgrind Long test executes INSERT queries, transactions, prepared statements in parallel to create weird load on Maxscale to catch crashes and leaks Test is not included into ctest scope. Test should be executed manually. For BuildBot (and also for run_test.sh) 'test_set' should be set 'NAME# ./long_test' Time to run test is defined by 'long_test_time' variable (in seconds) Possibility to run Maxscale under Valgrind is also added. To run Maxscale under Vaslgrind 'use_valgrind=yes' variable have to be defined
534 lines
12 KiB
C++
534 lines
12 KiB
C++
#include "maxscales.h"
|
|
#include <sstream>
|
|
#include <unordered_map>
|
|
|
|
Maxscales::Maxscales(const char *pref, const char *test_cwd, bool verbose, bool use_valgrind)
|
|
{
|
|
strcpy(prefix, pref);
|
|
this->verbose = verbose;
|
|
this->use_valgrind = use_valgrind;
|
|
valgring_log_num = 0;
|
|
strcpy(test_dir, test_cwd);
|
|
read_env();
|
|
if (use_valgrind)
|
|
{
|
|
for (int i = 0; i < N; i++)
|
|
{
|
|
ssh_node_f(i, true, "yum install -y valgrind gdb 2>&1", maxscale_log_dir[i]);
|
|
ssh_node_f(i, true, "apt install -y --force-yes valgrind gdb 2>&1", maxscale_log_dir[i]);
|
|
ssh_node_f(i, true, "zypper -n install valgrind gdb 2>&1", maxscale_log_dir[i]);
|
|
ssh_node_f(i, true, "rm -rf /var/cache/maxscale/maxscale.lock");
|
|
}
|
|
}
|
|
}
|
|
|
|
int Maxscales::read_env()
|
|
{
|
|
char * env;
|
|
char env_name[64];
|
|
|
|
read_basic_env();
|
|
|
|
if ((N > 0) && (N < 255))
|
|
{
|
|
for (int i = 0; i < N; i++)
|
|
{
|
|
sprintf(env_name, "%s_%03d_cnf", prefix, i);
|
|
env = getenv(env_name);
|
|
if (env == NULL)
|
|
{
|
|
sprintf(env_name, "%s_cnf", prefix);
|
|
env = getenv(env_name);
|
|
}
|
|
if (env != NULL)
|
|
{
|
|
sprintf(maxscale_cnf[i], "%s", env);
|
|
}
|
|
else
|
|
{
|
|
sprintf(maxscale_cnf[i], "/etc/maxscale.cnf");
|
|
}
|
|
|
|
sprintf(env_name, "%s_%03d_log_dir", prefix, i);
|
|
env = getenv(env_name);
|
|
if (env == NULL)
|
|
{
|
|
sprintf(env_name, "%s_log_dir", prefix);
|
|
env = getenv(env_name);
|
|
}
|
|
|
|
if (env != NULL)
|
|
{
|
|
sprintf(maxscale_log_dir[i], "%s", env);
|
|
}
|
|
else
|
|
{
|
|
sprintf(maxscale_log_dir[i], "/var/log/maxscale/");
|
|
}
|
|
|
|
sprintf(env_name, "%s_%03d_binlog_dir", prefix, i);
|
|
env = getenv(env_name);
|
|
if (env == NULL)
|
|
{
|
|
sprintf(env_name, "%s_binlog_dir", prefix);
|
|
env = getenv(env_name);
|
|
}
|
|
if (env != NULL)
|
|
{
|
|
sprintf(maxscale_binlog_dir[i], "%s", env);
|
|
}
|
|
else
|
|
{
|
|
sprintf(maxscale_binlog_dir[i], "/var/lib/maxscale/Binlog_Service/");
|
|
}
|
|
|
|
sprintf(env_name, "%s_%03d_maxadmin_password", prefix, i);
|
|
env = getenv(env_name);
|
|
if (env == NULL)
|
|
{
|
|
sprintf(env_name, "%s_maxadmin_password", prefix);
|
|
env = getenv(env_name);
|
|
}
|
|
if (env != NULL)
|
|
{
|
|
sprintf(maxadmin_password[i], "%s", env);
|
|
}
|
|
else
|
|
{
|
|
sprintf(maxadmin_password[i], "mariadb");
|
|
}
|
|
|
|
rwsplit_port[i] = 4006;
|
|
readconn_master_port[i] = 4008;
|
|
readconn_slave_port[i] = 4009;
|
|
binlog_port[i] = 5306;
|
|
|
|
ports[i][0] = rwsplit_port[i];
|
|
ports[i][1] = readconn_master_port[i];
|
|
ports[i][2] = readconn_slave_port[i];
|
|
|
|
N_ports[0] = 3;
|
|
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int Maxscales::connect_rwsplit(int m)
|
|
{
|
|
if (use_ipv6)
|
|
{
|
|
conn_rwsplit[m] = open_conn(rwsplit_port[m], IP6[m], user_name,
|
|
password, ssl);
|
|
}
|
|
else
|
|
{
|
|
conn_rwsplit[m] = open_conn(rwsplit_port[m], IP[m], user_name,
|
|
password, ssl);
|
|
}
|
|
routers[m][0] = conn_rwsplit[m];
|
|
|
|
int rc = 0;
|
|
int my_errno = mysql_errno(conn_rwsplit[m]);
|
|
|
|
if (my_errno)
|
|
{
|
|
if (verbose)
|
|
{
|
|
printf("Failed to connect to readwritesplit: %d, %s", my_errno, mysql_error(conn_rwsplit[m]));
|
|
}
|
|
rc = my_errno;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
int Maxscales::connect_readconn_master(int m)
|
|
{
|
|
if (use_ipv6)
|
|
{
|
|
conn_master[m] = open_conn(readconn_master_port[m], IP6[m],
|
|
user_name, password, ssl);
|
|
}
|
|
else
|
|
{
|
|
conn_master[m] = open_conn(readconn_master_port[m], IP[m],
|
|
user_name, password, ssl);
|
|
}
|
|
routers[m][1] = conn_master[m];
|
|
|
|
int rc = 0;
|
|
int my_errno = mysql_errno(conn_master[m]);
|
|
|
|
if (my_errno)
|
|
{
|
|
if (verbose)
|
|
{
|
|
printf("Failed to connect to readwritesplit: %d, %s", my_errno, mysql_error(conn_master[m]));
|
|
}
|
|
rc = my_errno;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
int Maxscales::connect_readconn_slave(int m)
|
|
{
|
|
if (use_ipv6)
|
|
{
|
|
conn_slave[m] = open_conn(readconn_slave_port[m], IP6[m],
|
|
user_name, password, ssl);
|
|
}
|
|
else
|
|
{
|
|
conn_slave[m] = open_conn(readconn_slave_port[m], IP[m],
|
|
user_name, password, ssl);
|
|
}
|
|
routers[m][2] = conn_slave[m];
|
|
|
|
int rc = 0;
|
|
int my_errno = mysql_errno(conn_slave[m]);
|
|
|
|
if (my_errno)
|
|
{
|
|
if (verbose)
|
|
{
|
|
printf("Failed to connect to readwritesplit: %d, %s", my_errno, mysql_error(conn_slave[m]));
|
|
}
|
|
rc = my_errno;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
int Maxscales::connect_maxscale(int m)
|
|
{
|
|
return connect_rwsplit(m) +
|
|
connect_readconn_master(m) +
|
|
connect_readconn_slave(m);
|
|
}
|
|
|
|
int Maxscales::close_maxscale_connections(int m)
|
|
{
|
|
mysql_close(conn_master[m]);
|
|
mysql_close(conn_slave[m]);
|
|
mysql_close(conn_rwsplit[m]);
|
|
return 0;
|
|
}
|
|
|
|
int Maxscales::restart_maxscale(int m)
|
|
{
|
|
int res;
|
|
if (use_valgrind)
|
|
{
|
|
res = stop_maxscale(m);
|
|
res += start_maxscale(m);
|
|
}
|
|
else
|
|
{
|
|
res =ssh_node(m, "service maxscale restart", true);
|
|
}
|
|
fflush(stdout);
|
|
return res;
|
|
}
|
|
|
|
int Maxscales::start_maxscale(int m)
|
|
{
|
|
int res;
|
|
if (use_valgrind)
|
|
{
|
|
res = ssh_node_f(m, false,
|
|
"sudo --user=maxscale valgrind --leak-check=full --show-leak-kinds=all "
|
|
"--log-file=/%s/valgrind%02d.log --trace-children=yes "
|
|
"--track-origins=yes /usr/bin/maxscale", maxscale_log_dir[m], valgring_log_num);
|
|
valgring_log_num++;
|
|
}
|
|
else
|
|
{
|
|
res = ssh_node(m, "service maxscale start", true);
|
|
}
|
|
fflush(stdout);
|
|
return res;
|
|
}
|
|
|
|
int Maxscales::stop_maxscale(int m)
|
|
{
|
|
int res;
|
|
if (use_valgrind)
|
|
{
|
|
res = ssh_node_f(m, true, "sudo kill $(pidof valgrind) 2>&1 > /dev/null");
|
|
if ((res != 0) || atoi(ssh_node_output(m, "pidof valgrind", true, &res)) > 0)
|
|
{
|
|
res = ssh_node_f(m, true, "sudo kill -9 $(pidof valgrind) 2>&1 > /dev/null");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
res = ssh_node(m, "service maxscale stop", true);
|
|
}
|
|
fflush(stdout);
|
|
return res;
|
|
}
|
|
|
|
|
|
int Maxscales::execute_maxadmin_command(int m, const char * cmd)
|
|
{
|
|
return ssh_node_f(m, true, "maxadmin %s", cmd);
|
|
}
|
|
int Maxscales::execute_maxadmin_command_print(int m, const char * cmd)
|
|
{
|
|
int exit_code;
|
|
printf("%s\n", ssh_node_output_f(m, true, &exit_code, "maxadmin %s", cmd));
|
|
return exit_code;
|
|
}
|
|
|
|
int Maxscales::check_maxadmin_param(int m, const char *command, const char *param, const char *value)
|
|
{
|
|
char result[1024];
|
|
int rval = 1;
|
|
|
|
if (get_maxadmin_param(m, (char*)command, (char*)param, (char*)result) == 0)
|
|
{
|
|
char *end = strchr(result, '\0') - 1;
|
|
|
|
while (isspace(*end))
|
|
{
|
|
*end-- = '\0';
|
|
}
|
|
|
|
char *start = result;
|
|
|
|
while (isspace(*start))
|
|
{
|
|
start++;
|
|
}
|
|
|
|
if (strcmp(start, value) == 0)
|
|
{
|
|
rval = 0;
|
|
}
|
|
else
|
|
{
|
|
printf("Expected %s, got %s\n", value, start);
|
|
}
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
int Maxscales::get_maxadmin_param(int m, const char *command, const char *param, char *result)
|
|
{
|
|
char * buf;
|
|
int exit_code;
|
|
|
|
buf = ssh_node_output_f(m, true, &exit_code, "maxadmin %s", command);
|
|
|
|
//printf("%s\n", buf);
|
|
|
|
char *x = strstr(buf, param);
|
|
|
|
if (x == NULL)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
x += strlen(param);
|
|
|
|
// Skip any trailing parts of the parameter name
|
|
while (!isspace(*x))
|
|
{
|
|
x++;
|
|
}
|
|
|
|
// Trim leading whitespace
|
|
while (!isspace(*x))
|
|
{
|
|
x++;
|
|
}
|
|
|
|
char *end = strchr(x, '\n');
|
|
|
|
// Trim trailing whitespace
|
|
while (isspace(*end))
|
|
{
|
|
*end-- = '\0';
|
|
}
|
|
|
|
strcpy(result, x);
|
|
|
|
return exit_code;
|
|
}
|
|
|
|
|
|
long unsigned Maxscales::get_maxscale_memsize(int m)
|
|
{
|
|
int exit_code;
|
|
char * ps_out = ssh_node_output(m, "ps -e -o pid,vsz,comm= | grep maxscale", false, &exit_code);
|
|
long unsigned mem = 0;
|
|
pid_t pid;
|
|
sscanf(ps_out, "%d %lu", &pid, &mem);
|
|
return mem;
|
|
}
|
|
|
|
|
|
int Maxscales::find_master_maxadmin(Mariadb_nodes * nodes, int m)
|
|
{
|
|
bool found = false;
|
|
int master = -1;
|
|
|
|
for (int i = 0; i < nodes->N; i++)
|
|
{
|
|
char show_server[256];
|
|
char res[256];
|
|
sprintf(show_server, "show server server%d", i + 1);
|
|
get_maxadmin_param(m, show_server, (char *) "Status", res);
|
|
|
|
if (strstr(res, "Master"))
|
|
{
|
|
if (found)
|
|
{
|
|
master = -1;
|
|
}
|
|
else
|
|
{
|
|
master = i;
|
|
found = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return master;
|
|
}
|
|
|
|
int Maxscales::find_slave_maxadmin(Mariadb_nodes * nodes, int m)
|
|
{
|
|
int slave = -1;
|
|
|
|
for (int i = 0; i < nodes->N; i++)
|
|
{
|
|
char show_server[256];
|
|
char res[256];
|
|
sprintf(show_server, "show server server%d", i + 1);
|
|
get_maxadmin_param(m, show_server, (char *) "Status", res);
|
|
|
|
if (strstr(res, "Slave"))
|
|
{
|
|
slave = i;
|
|
}
|
|
}
|
|
|
|
return slave;
|
|
}
|
|
|
|
StringSet Maxscales::get_server_status(const char* name, int m)
|
|
{
|
|
std::set<std::string> rval;
|
|
int exit_code;
|
|
char* res = ssh_node_output_f(m, true, &exit_code, "maxadmin list servers|grep \'%s\'", name);
|
|
char* pipe = strrchr(res, '|');
|
|
|
|
if (res && pipe)
|
|
{
|
|
pipe++;
|
|
char* tok = strtok(pipe, ",");
|
|
|
|
while (tok)
|
|
{
|
|
char* p = tok;
|
|
char *end = strchr(tok, '\n');
|
|
if (!end)
|
|
{
|
|
end = strchr(tok, '\0');
|
|
}
|
|
|
|
// Trim leading whitespace
|
|
while (p < end && isspace(*p))
|
|
{
|
|
p++;
|
|
}
|
|
|
|
// Trim trailing whitespace
|
|
while (end > tok && isspace(*end))
|
|
{
|
|
*end-- = '\0';
|
|
}
|
|
|
|
rval.insert(p);
|
|
tok = strtok(NULL, ",\n");
|
|
}
|
|
|
|
free(res);
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
int Maxscales::port(enum service type, int m) const
|
|
{
|
|
switch (type)
|
|
{
|
|
case RWSPLIT:
|
|
return rwsplit_port[m];
|
|
case READCONN_MASTER:
|
|
return readconn_master_port[m];
|
|
case READCONN_SLAVE:
|
|
return readconn_slave_port[m];
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void Maxscales::wait_for_monitor(int intervals, int m)
|
|
{
|
|
//Helper for getting number of monitor ticks
|
|
auto get_ticks = [&](std::string name)
|
|
{
|
|
int rc;
|
|
char* ticks = ssh_node_output_f(m, false, &rc, "maxctrl api get monitors/%s data.attributes.ticks", name.c_str());
|
|
char* ptr;
|
|
int rval = strtol(ticks, &ptr, 10);
|
|
|
|
if (ptr == ticks || (*ptr != '\0' && !isspace(*ptr)))
|
|
{
|
|
printf("ERROR, invalid monitor tick value: %s\n", ticks);
|
|
rval = -1;
|
|
}
|
|
|
|
free(ticks);
|
|
return rval;
|
|
};
|
|
|
|
int rc = 0;
|
|
|
|
// Get a list of monitor names that are running
|
|
char* monitors = ssh_node_output_f(m, false, &rc, "maxctrl --tsv list monitors|grep Running|cut -f 1");
|
|
std::istringstream is;
|
|
is.str(monitors);
|
|
free(monitors);
|
|
std::string name;
|
|
std::unordered_map<std::string, int> ticks;
|
|
|
|
// For each monitor, store the current monitor tick
|
|
while (std::getline(is, name))
|
|
{
|
|
ticks[name] = get_ticks(name);
|
|
}
|
|
|
|
for (auto a: ticks)
|
|
{
|
|
// Wait a maximum of 60 seconds for a single monitor interval
|
|
for (int i = 0; i < 60; i++)
|
|
{
|
|
int start = a.second;
|
|
int end = get_ticks(a.first);
|
|
|
|
if (start == -1 || end == -1 || end - start >= intervals)
|
|
{
|
|
break;
|
|
}
|
|
sleep(1);
|
|
}
|
|
}
|
|
}
|