MXS-1441 Add switchover_script parameter

If a switchover_script parameter is given, its value will be used as
the switchover script. Otherwise the default will be used. Currently
just echo.

The MySQL Monitor now introduces two script variables, CURRENT_MASTER
and NEW_MASTER, that contain information about the current and new
master respectively.

Switchover is performed only if switchover has been enabled and MaxScale
is *not* in passive mode.
This commit is contained in:
Johan Wikman
2017-10-03 11:57:58 +03:00
parent 17c3d1e612
commit 267a45ad63
3 changed files with 168 additions and 54 deletions

View File

@ -70,6 +70,7 @@ static const char* hb_table_name = "maxscale_schema.replication_heartbeat";
static const char CN_FAILOVER[] = "failover";
static const char CN_FAILOVER_TIMEOUT[] = "failover_timeout";
static const char CN_SWITCHOVER[] = "switchover";
static const char CN_SWITCHOVER_SCRIPT[] = "switchover_script";
static const char CN_SWITCHOVER_TIMEOUT[] = "switchover_timeout";
/** Default failover timeout */
@ -77,6 +78,15 @@ static const char CN_SWITCHOVER_TIMEOUT[] = "switchover_timeout";
/** Default switchover timeout */
#define DEFAULT_SWITCHOVER_TIMEOUT "90"
// TODO: Specify the default switchover script.
static const char DEFAULT_SWITCHOVER_SCRIPT[] =
"/usr/bin/echo CURRENT_MASTER=$CURRENT_MASTER NEW_MASTER=$NEW_MASTER "
"INITIATOR=$INITIATOR "
"PARENT=$PARENT CHILDREN=$CHILDREN EVENT=$EVENT "
"CREDENTIALS=$CREDENTIALS NODELIST=$NODELIST "
"LIST=$LIST MASTERLIST=$MASTERLIST "
"SLAVELIST=$SLAVELIST SYNCEDLIST=$SYNCEDLIST";
/**
* Check whether specified current master is acceptable.
*
@ -235,27 +245,51 @@ bool mysql_switchover_perform(MXS_MONITOR* mon,
SERVER* new_master = monitored_new_master->server;
SERVER* current_master = monitored_current_master ? monitored_current_master->server : NULL;
// TODO: Launch actual switchover command.
const char NONE[] = "none";
const char SWITCHOVER_FORMAT[] =
"/usr/bin/echo --from=%s --to=%s "
"INITIATOR=$INITIATOR "
"PARENT=$PARENT CHILDREN=$CHILDREN EVENT=$EVENT "
"CREDENTIALS=$CREDENTIALS NODELIST=$NODELIST "
"LIST=$LIST MASTERLIST=$MASTERLIST "
"SLAVELIST=$SLAVELIST SYNCEDLIST=$SYNCEDLIST";
const char* switchover_script = mysql_mon->switchover_script;
char switchover_cmd[sizeof(SWITCHOVER_FORMAT) +
strlen(new_master->unique_name) +
current_master ? strlen(current_master->unique_name) : sizeof(NONE)];
if (!switchover_script)
{
switchover_script = DEFAULT_SWITCHOVER_SCRIPT;
}
sprintf(switchover_cmd, SWITCHOVER_FORMAT,
current_master ? current_master->unique_name : NONE,
new_master->unique_name);
int rv = -1;
// TODO: We behave as if the specified new master would be the server that causes the
// TODO: event, although that's not really the case.
int rv = monitor_launch_script(mon, monitored_new_master, switchover_cmd, mysql_mon->switchover_timeout);
EXTERNCMD* cmd = externcmd_allocate(switchover_script, mysql_mon->switchover_timeout);
if (cmd)
{
if (externcmd_matches(cmd, "$CURRENT_MASTER"))
{
char address[(current_master ? strlen(current_master->name) : 0) + 24]; // Extra space for port
if (current_master)
{
snprintf(address, sizeof(address), "[%s]:%d", current_master->name, current_master->port);
}
else
{
strcpy(address, "none");
}
externcmd_substitute_arg(cmd, "[$]CURRENT_MASTER", address);
}
if (externcmd_matches(cmd, "$NEW_MASTER"))
{
char address[strlen(new_master->name) + 24]; // Extra space for port
snprintf(address, sizeof(address), "[%s]:%d", new_master->name, new_master->port);
externcmd_substitute_arg(cmd, "[$]NEW_MASTER", address);
}
// TODO: We behave as if the specified new master would be the server that causes the
// TODO: event, although that's not really the case.
rv = monitor_launch_command(mon, monitored_new_master, cmd);
}
else
{
*result = mxs_json_error("Failed to initialize script '%s'. See log-file for the "
"cause of this failure.", switchover_script);
}
return rv == 0 ? true : false;
}
@ -306,7 +340,7 @@ bool mysql_switchover(MXS_MONITOR* mon, SERVER* new_master, SERVER* current_mast
if (rv)
{
MXS_NOTICE("Switchover %s -> %s performed.",
current_master->unique_name ? current_master->unique_name : "(none)",
current_master->unique_name ? current_master->unique_name : "none",
new_master->unique_name);
if (stopped)
@ -326,13 +360,13 @@ bool mysql_switchover(MXS_MONITOR* mon, SERVER* new_master, SERVER* current_mast
monitorAddParameters(mon, &p);
MXS_ALERT("Switchover %s -> %s failed, failover has been disabled.",
current_master->unique_name ? current_master->unique_name : "(none)",
current_master->unique_name ? current_master->unique_name : "none",
new_master->unique_name);
}
else
{
MXS_ERROR("Switchover %s -> %s failed.",
current_master->unique_name ? current_master->unique_name : "(none)",
current_master->unique_name ? current_master->unique_name : "none",
new_master->unique_name);
}
}
@ -369,18 +403,35 @@ bool mysql_handle_switchover(const MODULECMD_ARG* args, json_t** output)
SERVER* new_master = args->argv[1].value.server;
SERVER* current_master = (args->argc == 3) ? args->argv[2].value.server : NULL;
bool rv;
bool rv = false;
if (mysql_mon->switchover)
if (!config_get_global_options()->passive)
{
rv = mysql_switchover(mon, new_master, current_master, output);
if (mysql_mon->switchover)
{
rv = mysql_switchover(mon, new_master, current_master, output);
}
else
{
MXS_WARNING("Attempt to perform switchover %s -> %s, even though "
"switchover is not enabled.",
current_master ? current_master->unique_name : "none",
new_master->unique_name);
*output = mxs_json_error("Switchover %s -> %s not performed, as switchover is not enabled.",
current_master ? current_master->unique_name : "none",
new_master->unique_name);
}
}
else
{
*output = mxs_json_error("Switchover %s -> %s not performed, as switchover is not enabled.",
current_master ? current_master->unique_name : "(none)",
MXS_WARNING("Attempt to perform switchover %s -> %s, even though "
"MaxScale is in passive mode.",
current_master ? current_master->unique_name : "none",
new_master->unique_name);
*output = mxs_json_error("Switchover %s -> %s not performed, as MaxScale is in passive mode.",
current_master ? current_master->unique_name : "none",
new_master->unique_name);
rv = false;
}
return rv;
@ -462,6 +513,12 @@ MXS_MODULE* MXS_CREATE_MODULE()
{CN_FAILOVER, MXS_MODULE_PARAM_BOOL, "false"},
{CN_FAILOVER_TIMEOUT, MXS_MODULE_PARAM_COUNT, DEFAULT_FAILOVER_TIMEOUT},
{CN_SWITCHOVER, MXS_MODULE_PARAM_BOOL, "false"},
{
CN_SWITCHOVER_SCRIPT,
MXS_MODULE_PARAM_PATH,
NULL,
MXS_MODULE_OPT_PATH_X_OK
},
{CN_SWITCHOVER_TIMEOUT, MXS_MODULE_PARAM_COUNT, DEFAULT_SWITCHOVER_TIMEOUT},
{MXS_END_MODULE_PARAMS}
}
@ -574,6 +631,7 @@ startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params)
{
handle->shutdown = 0;
MXS_FREE(handle->script);
MXS_FREE(handle->switchover_script);
}
else
{
@ -613,6 +671,7 @@ startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params)
handle->failover = config_get_bool(params, CN_FAILOVER);
handle->failover_timeout = config_get_integer(params, CN_FAILOVER_TIMEOUT);
handle->switchover = config_get_bool(params, CN_SWITCHOVER);
handle->switchover_script = config_copy_string(params, CN_SWITCHOVER_SCRIPT);
handle->switchover_timeout = config_get_integer(params, CN_SWITCHOVER_TIMEOUT);
bool error = false;
@ -631,6 +690,7 @@ startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params)
if (error)
{
hashtable_free(handle->server_info);
MXS_FREE(handle->switchover_script);
MXS_FREE(handle->script);
MXS_FREE(handle);
handle = NULL;
@ -643,6 +703,7 @@ startMonitor(MXS_MONITOR *monitor, const MXS_CONFIG_PARAMETER* params)
{
MXS_ERROR("Failed to start monitor thread for monitor '%s'.", monitor->name);
hashtable_free(handle->server_info);
MXS_FREE(handle->switchover_script);
MXS_FREE(handle->script);
MXS_FREE(handle);
handle = NULL;
@ -758,6 +819,11 @@ static json_t* diagnostics_json(const MXS_MONITOR *mon)
json_object_set_new(rval, CN_SWITCHOVER, json_boolean(handle->switchover));
json_object_set_new(rval, CN_SWITCHOVER_TIMEOUT, json_integer(handle->switchover_timeout));
if (handle->switchover_script)
{
json_object_set_new(rval, CN_SWITCHOVER_SCRIPT, json_string(handle->script));
}
if (handle->script)
{
json_object_set_new(rval, "script", json_string(handle->script));