Merge branch 'MXS-121' into develop
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
if(BUILD_TESTS OR BUILD_TOOLS)
|
||||
add_library(fullcore STATIC adminusers.c atomic.c config.c buffer.c dbusers.c dcb.c filter.c gwbitmask.c gw_utils.c hashtable.c hint.c housekeeper.c load_utils.c memlog.c modutil.c monitor.c poll.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c)
|
||||
add_library(fullcore STATIC adminusers.c atomic.c config.c buffer.c dbusers.c dcb.c filter.c gwbitmask.c gw_utils.c hashtable.c hint.c housekeeper.c load_utils.c memlog.c modutil.c monitor.c poll.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c externcmd.c)
|
||||
if(WITH_JEMALLOC)
|
||||
target_link_libraries(fullcore ${JEMALLOC_LIBRARIES})
|
||||
elseif(WITH_TCMALLOC)
|
||||
@ -12,7 +12,7 @@ add_executable(maxscale atomic.c buffer.c spinlock.c gateway.c
|
||||
gw_utils.c utils.c dcb.c load_utils.c session.c service.c server.c
|
||||
poll.c config.c users.c hashtable.c dbusers.c thread.c gwbitmask.c
|
||||
monitor.c adminusers.c secrets.c filter.c modutil.c hint.c
|
||||
housekeeper.c memlog.c resultset.c)
|
||||
housekeeper.c memlog.c resultset.c externcmd.c)
|
||||
|
||||
if(WITH_JEMALLOC)
|
||||
target_link_libraries(maxscale ${JEMALLOC_LIBRARIES})
|
||||
|
||||
@ -1932,6 +1932,8 @@ static char *monitor_params[] =
|
||||
"servers",
|
||||
"user",
|
||||
"passwd",
|
||||
"script",
|
||||
"events",
|
||||
"monitor_interval",
|
||||
"detect_replication_lag",
|
||||
"detect_stale_master",
|
||||
|
||||
169
server/core/externcmd.c
Normal file
169
server/core/externcmd.c
Normal file
@ -0,0 +1,169 @@
|
||||
#include <externcmd.h>
|
||||
|
||||
/** Defined in log_manager.cc */
|
||||
extern int lm_enabled_logfiles_bitmask;
|
||||
extern size_t log_ses_count[];
|
||||
extern __thread log_info_t tls_log_info;
|
||||
|
||||
/**
|
||||
* Tokenize a string into arguments suitable for a execvp call.
|
||||
* @param args Argument string
|
||||
* @param argv Array of char pointers to be filled with tokenized arguments
|
||||
* @return 0 on success, -1 on error
|
||||
*/
|
||||
int tokenize_arguments(char* args, char** argv)
|
||||
{
|
||||
int i = 0;
|
||||
bool quoted = false;
|
||||
bool read = false;
|
||||
bool escaped = false;
|
||||
char *ptr,*start;
|
||||
char qc;
|
||||
|
||||
start = args;
|
||||
ptr = start;
|
||||
|
||||
while(*ptr != '\0' && i < MAXSCALE_EXTCMD_ARG_MAX)
|
||||
{
|
||||
if(escaped)
|
||||
{
|
||||
escaped = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(*ptr == '\\')
|
||||
{
|
||||
escaped = true;
|
||||
}
|
||||
else if(quoted && !escaped && *ptr == qc) /** End of quoted string */
|
||||
{
|
||||
*ptr = '\0';
|
||||
argv[i++] = strdup(start);
|
||||
read = false;
|
||||
quoted = false;
|
||||
}
|
||||
else if (!quoted)
|
||||
{
|
||||
if(isspace(*ptr))
|
||||
{
|
||||
*ptr = '\0';
|
||||
if(read) /** New token */
|
||||
{
|
||||
argv[i++] = strdup(start);
|
||||
read = false;
|
||||
}
|
||||
}
|
||||
else if( *ptr == '\"' || *ptr == '\'')
|
||||
{
|
||||
/** New quoted token, strip quotes */
|
||||
quoted = true;
|
||||
qc = *ptr;
|
||||
start = ptr + 1;
|
||||
}
|
||||
else if(!read)
|
||||
{
|
||||
start = ptr;
|
||||
read = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
if(read)
|
||||
argv[i++] = strdup(start);
|
||||
argv[i] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate a new external command.
|
||||
* The name and parameters are copied into the external command structure so
|
||||
* the original memory can be freed if needed.
|
||||
* @param command Command to execute with the parameters
|
||||
* @return Pointer to new external command struct or NULL if an error occurred
|
||||
*/
|
||||
EXTERNCMD* externcmd_allocate(char* argstr)
|
||||
{
|
||||
EXTERNCMD* cmd;
|
||||
|
||||
if(argstr == NULL)
|
||||
return NULL;
|
||||
|
||||
if((cmd = (EXTERNCMD*)malloc(sizeof(EXTERNCMD))) != NULL)
|
||||
{
|
||||
if(tokenize_arguments(argstr,cmd->parameters) == -1)
|
||||
{
|
||||
free(cmd);
|
||||
return NULL;
|
||||
}
|
||||
if(access(cmd->parameters[0],F_OK) != 0)
|
||||
{
|
||||
skygw_log_write(LE,
|
||||
"Error: Cannot find file: %s",
|
||||
cmd->parameters[0]);
|
||||
externcmd_free(cmd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(access(cmd->parameters[0],X_OK) != 0)
|
||||
{
|
||||
skygw_log_write(LE,
|
||||
"Error: Cannot execute file: %s",
|
||||
cmd->parameters[0]);
|
||||
externcmd_free(cmd);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Free a previously allocated external command.
|
||||
* @param cmd Command to free
|
||||
*/
|
||||
void externcmd_free(EXTERNCMD* cmd)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0;cmd->parameters[i] != NULL;i++)
|
||||
{
|
||||
free(cmd->parameters[i]);
|
||||
}
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
*Execute a command in a separate process.
|
||||
*@param cmd Command to execute
|
||||
*@return 0 on success, -1 on error.
|
||||
*/
|
||||
int externcmd_execute(EXTERNCMD* cmd)
|
||||
{
|
||||
int rval = 0;
|
||||
pid_t pid;
|
||||
|
||||
pid = fork();
|
||||
|
||||
if(pid < 0)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"Error: Failed to execute command '%s', fork failed: [%d] %s",
|
||||
cmd->parameters[0],errno,strerror(errno));
|
||||
rval = -1;
|
||||
}
|
||||
else if(pid == 0)
|
||||
{
|
||||
/** Child process, execute command */
|
||||
execvp(cmd->parameters[0],cmd->parameters);
|
||||
_exit(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd->child = pid;
|
||||
cmd->n_exec++;
|
||||
LOGIF(LD,skygw_log_write(LD,"[monitor_exec_cmd] Forked child process %d : %s.",pid,cmd));
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
* and monitor id
|
||||
* 30/10/14 Massimiliano Pinto Addition of disable_master_failback parameter
|
||||
* 07/11/14 Massimiliano Pinto Addition of monitor network timeouts
|
||||
* 08/05/15 Markus Makela Moved common monitor variables to MONITOR struct
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
@ -65,8 +66,6 @@ MONITOR *mon;
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
mon->state = MONITOR_STATE_ALLOC;
|
||||
mon->name = strdup(name);
|
||||
|
||||
if ((mon->module = load_module(module, MODULE_MONITOR)) == NULL)
|
||||
{
|
||||
@ -78,9 +77,16 @@ MONITOR *mon;
|
||||
free(mon);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mon->state = MONITOR_STATE_ALLOC;
|
||||
mon->name = strdup(name);
|
||||
mon->handle = NULL;
|
||||
|
||||
mon->databases = NULL;
|
||||
mon->password = NULL;
|
||||
mon->read_timeout = DEFAULT_READ_TIMEOUT;
|
||||
mon->write_timeout = DEFAULT_WRITE_TIMEOUT;
|
||||
mon->connect_timeout = DEFAULT_CONNECT_TIMEOUT;
|
||||
mon->interval = MONITOR_INTERVAL;
|
||||
spinlock_init(&mon->lock);
|
||||
spinlock_acquire(&monLock);
|
||||
mon->next = allMonitors;
|
||||
allMonitors = mon;
|
||||
@ -100,7 +106,7 @@ monitor_free(MONITOR *mon)
|
||||
{
|
||||
MONITOR *ptr;
|
||||
|
||||
mon->module->stopMonitor(mon->handle);
|
||||
mon->module->stopMonitor(mon);
|
||||
mon->state = MONITOR_STATE_FREED;
|
||||
spinlock_acquire(&monLock);
|
||||
if (allMonitors == mon)
|
||||
@ -127,8 +133,10 @@ MONITOR *ptr;
|
||||
void
|
||||
monitorStart(MONITOR *monitor, void* params)
|
||||
{
|
||||
monitor->handle = (*monitor->module->startMonitor)(monitor->handle,params);
|
||||
spinlock_acquire(&monitor->lock);
|
||||
monitor->handle = (*monitor->module->startMonitor)(monitor,params);
|
||||
monitor->state = MONITOR_STATE_RUNNING;
|
||||
spinlock_release(&monitor->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -142,7 +150,7 @@ monitorStop(MONITOR *monitor)
|
||||
if(monitor->state != MONITOR_STATE_STOPPED)
|
||||
{
|
||||
monitor->state = MONITOR_STATE_STOPPING;
|
||||
monitor->module->stopMonitor(monitor->handle);
|
||||
monitor->module->stopMonitor(monitor);
|
||||
monitor->state = MONITOR_STATE_STOPPED;
|
||||
}
|
||||
}
|
||||
@ -175,7 +183,31 @@ MONITOR *ptr;
|
||||
void
|
||||
monitorAddServer(MONITOR *mon, SERVER *server)
|
||||
{
|
||||
mon->module->registerServer(mon->handle, server);
|
||||
MONITOR_SERVERS *ptr, *db;
|
||||
|
||||
if ((db = (MONITOR_SERVERS *)malloc(sizeof(MONITOR_SERVERS))) == NULL)
|
||||
return;
|
||||
db->server = server;
|
||||
db->con = NULL;
|
||||
db->next = NULL;
|
||||
db->mon_err_count = 0;
|
||||
/** Server status is uninitialized */
|
||||
db->mon_prev_status = -1;
|
||||
/* pending status is updated by get_replication_tree */
|
||||
db->pending_status = 0;
|
||||
|
||||
spinlock_acquire(&mon->lock);
|
||||
|
||||
if (mon->databases == NULL)
|
||||
mon->databases = db;
|
||||
else
|
||||
{
|
||||
ptr = mon->databases;
|
||||
while (ptr->next != NULL)
|
||||
ptr = ptr->next;
|
||||
ptr->next = db;
|
||||
}
|
||||
spinlock_release(&mon->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -189,7 +221,8 @@ monitorAddServer(MONITOR *mon, SERVER *server)
|
||||
void
|
||||
monitorAddUser(MONITOR *mon, char *user, char *passwd)
|
||||
{
|
||||
mon->module->defaultUser(mon->handle, user, passwd);
|
||||
mon->user = strdup(user);
|
||||
mon->password = strdup(passwd);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -209,7 +242,7 @@ MONITOR *ptr;
|
||||
dcb_printf(dcb, "Monitor: %p\n", ptr);
|
||||
dcb_printf(dcb, "\tName: %s\n", ptr->name);
|
||||
if (ptr->module->diagnostics)
|
||||
ptr->module->diagnostics(dcb, ptr->handle);
|
||||
ptr->module->diagnostics(dcb, ptr);
|
||||
ptr = ptr->next;
|
||||
}
|
||||
spinlock_release(&monLock);
|
||||
@ -227,7 +260,7 @@ monitorShow(DCB *dcb, MONITOR *monitor)
|
||||
dcb_printf(dcb, "Monitor: %p\n", monitor);
|
||||
dcb_printf(dcb, "\tName: %s\n", monitor->name);
|
||||
if (monitor->module->diagnostics)
|
||||
monitor->module->diagnostics(dcb, monitor->handle);
|
||||
monitor->module->diagnostics(dcb, monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -288,10 +321,7 @@ MONITOR *ptr;
|
||||
void
|
||||
monitorSetInterval (MONITOR *mon, unsigned long interval)
|
||||
{
|
||||
if (mon->module->setInterval != NULL) {
|
||||
mon->interval = interval;
|
||||
mon->module->setInterval(mon->handle, interval);
|
||||
}
|
||||
mon->interval = interval;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -303,9 +333,55 @@ monitorSetInterval (MONITOR *mon, unsigned long interval)
|
||||
*/
|
||||
void
|
||||
monitorSetNetworkTimeout(MONITOR *mon, int type, int value) {
|
||||
if (mon->module->setNetworkTimeout != NULL) {
|
||||
mon->module->setNetworkTimeout(mon->handle, type, value);
|
||||
|
||||
int max_timeout = (int)(mon->interval/1000);
|
||||
int new_timeout = max_timeout -1;
|
||||
|
||||
if (new_timeout <= 0)
|
||||
new_timeout = DEFAULT_CONNECT_TIMEOUT;
|
||||
|
||||
switch(type) {
|
||||
case MONITOR_CONNECT_TIMEOUT:
|
||||
if (value < max_timeout) {
|
||||
memcpy(&mon->connect_timeout, &value, sizeof(int));
|
||||
} else {
|
||||
memcpy(&mon->connect_timeout, &new_timeout, sizeof(int));
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"warning : Monitor Connect Timeout %i is greater than monitor interval ~%i seconds"
|
||||
", lowering to %i seconds", value, max_timeout, new_timeout)));
|
||||
}
|
||||
break;
|
||||
|
||||
case MONITOR_READ_TIMEOUT:
|
||||
if (value < max_timeout) {
|
||||
memcpy(&mon->read_timeout, &value, sizeof(int));
|
||||
} else {
|
||||
memcpy(&mon->read_timeout, &new_timeout, sizeof(int));
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"warning : Monitor Read Timeout %i is greater than monitor interval ~%i seconds"
|
||||
", lowering to %i seconds", value, max_timeout, new_timeout)));
|
||||
}
|
||||
break;
|
||||
|
||||
case MONITOR_WRITE_TIMEOUT:
|
||||
if (value < max_timeout) {
|
||||
memcpy(&mon->write_timeout, &value, sizeof(int));
|
||||
} else {
|
||||
memcpy(&mon->write_timeout, &new_timeout, sizeof(int));
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"warning : Monitor Write Timeout %i is greater than monitor interval ~%i seconds"
|
||||
", lowering to %i seconds", value, max_timeout, new_timeout)));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Monitor setNetworkTimeout received an unsupported action type %i", type)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
20
server/include/externcmd.h
Normal file
20
server/include/externcmd.h
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef _EXTERN_CMD_HG
|
||||
#define _EXTERN_CMD_HG
|
||||
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
#define MAXSCALE_EXTCMD_ARG_MAX 256
|
||||
|
||||
typedef struct extern_cmd_t{
|
||||
char* parameters[MAXSCALE_EXTCMD_ARG_MAX]; /*< Command arguments */
|
||||
int n_exec; /*< Number of times executed */
|
||||
pid_t child; /*< PID of the child process */
|
||||
}EXTERNCMD;
|
||||
|
||||
EXTERNCMD* externcmd_allocate(char* argstr);
|
||||
void externcmd_free(EXTERNCMD* cmd);
|
||||
int externcmd_execute(EXTERNCMD* cmd);
|
||||
#endif
|
||||
@ -17,6 +17,7 @@
|
||||
*
|
||||
* Copyright MariaDB Corporation Ab 2013-2014
|
||||
*/
|
||||
#include <mysql.h>
|
||||
#include <server.h>
|
||||
#include <dcb.h>
|
||||
#include <resultset.h>
|
||||
@ -69,19 +70,14 @@
|
||||
typedef struct {
|
||||
void *(*startMonitor)(void *, void*);
|
||||
void (*stopMonitor)(void *);
|
||||
void (*registerServer)(void *, SERVER *);
|
||||
void (*unregisterServer)(void *, SERVER *);
|
||||
void (*defaultUser)(void *, char *, char *);
|
||||
void (*diagnostics)(DCB *, void *);
|
||||
void (*setInterval)(void *, size_t);
|
||||
void (*setNetworkTimeout)(void *, int, int);
|
||||
} MONITOR_OBJECT;
|
||||
|
||||
/**
|
||||
* The monitor API version number. Any change to the monitor module API
|
||||
* must change these versions usign the rules defined in modinfo.h
|
||||
*/
|
||||
#define MONITOR_VERSION {2, 0, 0}
|
||||
#define MONITOR_VERSION {3, 0, 0}
|
||||
|
||||
/** Monitor's poll frequency */
|
||||
#define MON_BASE_INTERVAL_MS 100
|
||||
@ -112,12 +108,46 @@ typedef enum
|
||||
#define DEFAULT_READ_TIMEOUT 1
|
||||
#define DEFAULT_WRITE_TIMEOUT 2
|
||||
|
||||
|
||||
#define MONITOR_RUNNING 1
|
||||
#define MONITOR_STOPPING 2
|
||||
#define MONITOR_STOPPED 3
|
||||
|
||||
#define MONITOR_INTERVAL 10000 // in milliseconds
|
||||
#define MONITOR_DEFAULT_ID 1UL // unsigned long value
|
||||
#define MONITOR_MAX_NUM_SLAVES 20 //number of MySQL slave servers associated to a MySQL master server
|
||||
|
||||
|
||||
/**
|
||||
* The linked list of servers that are being monitored by the monitor module.
|
||||
*/
|
||||
typedef struct monitor_servers {
|
||||
SERVER *server; /**< The server being monitored */
|
||||
MYSQL *con; /**< The MySQL connection */
|
||||
int mon_err_count;
|
||||
unsigned int mon_prev_status;
|
||||
unsigned int pending_status; /**< Pending Status flag bitmap */
|
||||
struct monitor_servers
|
||||
*next; /**< The next server in the list */
|
||||
} MONITOR_SERVERS;
|
||||
|
||||
/**
|
||||
* Representation of the running monitor.
|
||||
*/
|
||||
typedef struct monitor {
|
||||
char *name; /**< The name of the monitor module */
|
||||
char* user; /*< Monitor username */
|
||||
char* password; /*< Monitor password */
|
||||
SPINLOCK lock;
|
||||
MONITOR_SERVERS* databases; /*< List of databases the monitor monitors */
|
||||
monitor_state_t state; /**< The state of the monitor */
|
||||
int connect_timeout; /**< Connect timeout in seconds for mysql_real_connect */
|
||||
int read_timeout; /**< Timeout in seconds to read from the server.
|
||||
* There are retries and the total effective timeout value is three times the option value.
|
||||
*/
|
||||
int write_timeout; /**< Timeout in seconds for each attempt to write to the server.
|
||||
* There are retries and the total effective timeout value is two times the option value.
|
||||
*/
|
||||
MONITOR_OBJECT *module; /**< The "monitor object" */
|
||||
void *handle; /**< Handle returned from startMonitor */
|
||||
size_t interval; /**< The monitor interval */
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
add_library(mysqlmon SHARED mysql_mon.c)
|
||||
add_library(mysqlmon SHARED mysql_mon.c monitor_common.c)
|
||||
target_link_libraries(mysqlmon log_manager utils)
|
||||
install(TARGETS mysqlmon DESTINATION modules)
|
||||
|
||||
add_library(galeramon SHARED galera_mon.c)
|
||||
add_library(galeramon SHARED galeramon.c monitor_common.c)
|
||||
target_link_libraries(galeramon log_manager utils)
|
||||
install(TARGETS galeramon DESTINATION modules)
|
||||
|
||||
add_library(ndbclustermon SHARED ndbcluster_mon.c)
|
||||
add_library(ndbclustermon SHARED ndbclustermon.c monitor_common.c)
|
||||
target_link_libraries(ndbclustermon log_manager utils)
|
||||
install(TARGETS ndbclustermon DESTINATION modules)
|
||||
if(BUILD_MMMON)
|
||||
add_library(mmmon SHARED mm_mon.c)
|
||||
add_library(mmmon SHARED mmmon.c monitor_common.c)
|
||||
target_link_libraries(mmmon log_manager utils)
|
||||
install(TARGETS mmmon DESTINATION modules)
|
||||
endif()
|
||||
|
||||
@ -34,24 +34,13 @@
|
||||
* 10/11/14 Massimiliano Pinto Added setNetworkTimeout for connect,read,write
|
||||
* 20/04/15 Guillaume Lefranc Added availableWhenDonor feature
|
||||
* 22/04/15 Martin Brampton Addition of disableMasterRoleSetting
|
||||
* 08/05/15 Markus Makela Addition of launchable scripts
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <monitor.h>
|
||||
#include <mysqlmon.h>
|
||||
#include <thread.h>
|
||||
#include <mysql.h>
|
||||
#include <mysqld_error.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
#include <secrets.h>
|
||||
#include <dcb.h>
|
||||
#include <modinfo.h>
|
||||
#include <maxconfig.h>
|
||||
|
||||
#include <galeramon.h>
|
||||
|
||||
/** Defined in log_manager.cc */
|
||||
extern int lm_enabled_logfiles_bitmask;
|
||||
@ -60,7 +49,7 @@ extern __thread log_info_t tls_log_info;
|
||||
|
||||
static void monitorMain(void *);
|
||||
|
||||
static char *version_str = "V1.4.0";
|
||||
static char *version_str = "V2.0.0";
|
||||
|
||||
MODULE_INFO info = {
|
||||
MODULE_API_MONITOR,
|
||||
@ -71,27 +60,16 @@ MODULE_INFO info = {
|
||||
|
||||
static void *startMonitor(void *,void*);
|
||||
static void stopMonitor(void *);
|
||||
static void registerServer(void *, SERVER *);
|
||||
static void unregisterServer(void *, SERVER *);
|
||||
static void defaultUsers(void *, char *, char *);
|
||||
static void diagnostics(DCB *, void *);
|
||||
static void setInterval(void *, size_t);
|
||||
static MONITOR_SERVERS *get_candidate_master(MONITOR_SERVERS *);
|
||||
static MONITOR_SERVERS *set_cluster_master(MONITOR_SERVERS *, MONITOR_SERVERS *, int);
|
||||
static void disableMasterFailback(void *, int);
|
||||
static void setNetworkTimeout(void *arg, int type, int value);
|
||||
static bool mon_status_changed(MONITOR_SERVERS* mon_srv);
|
||||
static bool mon_print_fail_status(MONITOR_SERVERS* mon_srv);
|
||||
bool isGaleraEvent(monitor_event_t event);
|
||||
|
||||
static MONITOR_OBJECT MyObject = {
|
||||
startMonitor,
|
||||
stopMonitor,
|
||||
registerServer,
|
||||
unregisterServer,
|
||||
defaultUsers,
|
||||
diagnostics,
|
||||
setInterval,
|
||||
setNetworkTimeout
|
||||
stopMonitor,
|
||||
diagnostics
|
||||
};
|
||||
|
||||
/**
|
||||
@ -142,47 +120,80 @@ GetModuleObject()
|
||||
static void *
|
||||
startMonitor(void *arg,void* opt)
|
||||
{
|
||||
MYSQL_MONITOR *handle;
|
||||
CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt;
|
||||
if (arg != NULL)
|
||||
{
|
||||
handle = (MYSQL_MONITOR *)arg;
|
||||
handle->shutdown = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((handle = (MYSQL_MONITOR *)malloc(sizeof(MYSQL_MONITOR))) == NULL)
|
||||
return NULL;
|
||||
handle->databases = NULL;
|
||||
handle->shutdown = 0;
|
||||
handle->defaultUser = NULL;
|
||||
handle->defaultPasswd = NULL;
|
||||
handle->id = MONITOR_DEFAULT_ID;
|
||||
handle->interval = MONITOR_INTERVAL;
|
||||
handle->disableMasterFailback = 0;
|
||||
handle->availableWhenDonor = 0;
|
||||
handle->disableMasterRoleSetting = 0;
|
||||
handle->master = NULL;
|
||||
handle->connect_timeout=DEFAULT_CONNECT_TIMEOUT;
|
||||
handle->read_timeout=DEFAULT_READ_TIMEOUT;
|
||||
handle->write_timeout=DEFAULT_WRITE_TIMEOUT;
|
||||
spinlock_init(&handle->lock);
|
||||
}
|
||||
MONITOR* mon = arg;
|
||||
GALERA_MONITOR *handle = mon->handle;
|
||||
CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt;
|
||||
bool have_events = false;
|
||||
if (handle != NULL)
|
||||
{
|
||||
handle->shutdown = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((handle = (GALERA_MONITOR *)malloc(sizeof(GALERA_MONITOR))) == NULL)
|
||||
return NULL;
|
||||
handle->shutdown = 0;
|
||||
handle->id = MONITOR_DEFAULT_ID;
|
||||
handle->disableMasterFailback = 0;
|
||||
handle->availableWhenDonor = 0;
|
||||
handle->disableMasterRoleSetting = 0;
|
||||
handle->master = NULL;
|
||||
handle->script = NULL;
|
||||
memset(handle->events,false,sizeof(handle->events));
|
||||
spinlock_init(&handle->lock);
|
||||
}
|
||||
|
||||
|
||||
while(params)
|
||||
while(params)
|
||||
{
|
||||
if(!strcmp(params->name,"disable_master_failback"))
|
||||
handle->disableMasterFailback = config_truth_value(params->value);
|
||||
else if(!strcmp(params->name,"available_when_donor"))
|
||||
handle->availableWhenDonor = config_truth_value(params->value);
|
||||
else if(!strcmp(params->name,"disable_master_role_setting"))
|
||||
handle->disableMasterRoleSetting = config_truth_value(params->value);
|
||||
else if(!strcmp(params->name,"script"))
|
||||
{
|
||||
if(!strcmp(params->name,"disable_master_failback"))
|
||||
handle->disableMasterFailback = config_truth_value(params->value);
|
||||
else if(!strcmp(params->name,"available_when_donor"))
|
||||
handle->availableWhenDonor = config_truth_value(params->value);
|
||||
else if(!strcmp(params->name,"disable_master_role_setting"))
|
||||
handle->disableMasterRoleSetting = config_truth_value(params->value);
|
||||
params = params->next;
|
||||
}
|
||||
if(handle->script)
|
||||
free(handle->script);
|
||||
|
||||
handle->tid = (THREAD)thread_start(monitorMain, handle);
|
||||
return handle;
|
||||
if(access(params->value,X_OK) == 0)
|
||||
{
|
||||
handle->script = strdup(params->value);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(access(params->value,F_OK) == 0)
|
||||
{
|
||||
skygw_log_write(LE,
|
||||
"Error: The file cannot be executed: %s",
|
||||
params->value);
|
||||
}
|
||||
else
|
||||
{
|
||||
skygw_log_write(LE,
|
||||
"Error: The file cannot be found: %s",
|
||||
params->value);
|
||||
}
|
||||
handle->script = NULL;
|
||||
}
|
||||
}
|
||||
else if(!strcmp(params->name,"events"))
|
||||
{
|
||||
mon_parse_event_string((bool*)&handle->events,sizeof(handle->events),params->value);
|
||||
have_events = true;
|
||||
}
|
||||
params = params->next;
|
||||
}
|
||||
|
||||
/** If no specific events are given, enable them all */
|
||||
if(!have_events)
|
||||
{
|
||||
memset(handle->events,true,sizeof(handle->events));
|
||||
}
|
||||
|
||||
handle->tid = (THREAD)thread_start(monitorMain, mon);
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -193,82 +204,13 @@ CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt;
|
||||
static void
|
||||
stopMonitor(void *arg)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR* mon = (MONITOR*)arg;
|
||||
GALERA_MONITOR *handle = (GALERA_MONITOR *)mon->handle;
|
||||
|
||||
handle->shutdown = 1;
|
||||
thread_wait((void *)handle->tid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a server that must be added to the monitored servers for
|
||||
* a monitoring module.
|
||||
*
|
||||
* @param arg A handle on the running monitor module
|
||||
* @param server The server to add
|
||||
*/
|
||||
static void
|
||||
registerServer(void *arg, SERVER *server)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR_SERVERS *ptr, *db;
|
||||
|
||||
if ((db = (MONITOR_SERVERS *)malloc(sizeof(MONITOR_SERVERS))) == NULL)
|
||||
return;
|
||||
db->server = server;
|
||||
db->con = NULL;
|
||||
db->next = NULL;
|
||||
spinlock_acquire(&handle->lock);
|
||||
if (handle->databases == NULL)
|
||||
handle->databases = db;
|
||||
else
|
||||
{
|
||||
ptr = handle->databases;
|
||||
while (ptr->next != NULL)
|
||||
ptr = ptr->next;
|
||||
ptr->next = db;
|
||||
}
|
||||
spinlock_release(&handle->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a server from those being monitored by a monitoring module
|
||||
*
|
||||
* @param arg A handle on the running monitor module
|
||||
* @param server The server to remove
|
||||
*/
|
||||
static void
|
||||
unregisterServer(void *arg, SERVER *server)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR_SERVERS *ptr, *lptr;
|
||||
|
||||
spinlock_acquire(&handle->lock);
|
||||
if (handle->databases == NULL)
|
||||
{
|
||||
spinlock_release(&handle->lock);
|
||||
return;
|
||||
}
|
||||
if (handle->databases->server == server)
|
||||
{
|
||||
ptr = handle->databases;
|
||||
handle->databases = handle->databases->next;
|
||||
free(ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = handle->databases;
|
||||
while (ptr->next != NULL && ptr->next->server != server)
|
||||
ptr = ptr->next;
|
||||
if (ptr->next)
|
||||
{
|
||||
lptr = ptr->next;
|
||||
ptr->next = ptr->next->next;
|
||||
free(lptr);
|
||||
}
|
||||
}
|
||||
spinlock_release(&handle->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Diagnostic interface
|
||||
*
|
||||
@ -278,7 +220,8 @@ MONITOR_SERVERS *ptr, *lptr;
|
||||
static void
|
||||
diagnostics(DCB *dcb, void *arg)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR* mon = (MONITOR*)arg;
|
||||
GALERA_MONITOR *handle = (GALERA_MONITOR *)mon->handle;
|
||||
MONITOR_SERVERS *db;
|
||||
char *sep;
|
||||
|
||||
@ -295,16 +238,16 @@ char *sep;
|
||||
break;
|
||||
}
|
||||
|
||||
dcb_printf(dcb,"\tSampling interval:\t%lu milliseconds\n", handle->interval);
|
||||
dcb_printf(dcb,"\tSampling interval:\t%lu milliseconds\n", mon->interval);
|
||||
dcb_printf(dcb,"\tMaster Failback:\t%s\n", (handle->disableMasterFailback == 1) ? "off" : "on");
|
||||
dcb_printf(dcb,"\tAvailable when Donor:\t%s\n", (handle->availableWhenDonor == 1) ? "on" : "off");
|
||||
dcb_printf(dcb,"\tMaster Role Setting Disabled:\t%s\n", (handle->disableMasterRoleSetting == 1) ? "on" : "off");
|
||||
dcb_printf(dcb,"\tConnect Timeout:\t%i seconds\n", handle->connect_timeout);
|
||||
dcb_printf(dcb,"\tRead Timeout:\t\t%i seconds\n", handle->read_timeout);
|
||||
dcb_printf(dcb,"\tWrite Timeout:\t\t%i seconds\n", handle->write_timeout);
|
||||
dcb_printf(dcb,"\tConnect Timeout:\t%i seconds\n", mon->connect_timeout);
|
||||
dcb_printf(dcb,"\tRead Timeout:\t\t%i seconds\n", mon->read_timeout);
|
||||
dcb_printf(dcb,"\tWrite Timeout:\t\t%i seconds\n", mon->write_timeout);
|
||||
dcb_printf(dcb, "\tMonitored servers: ");
|
||||
|
||||
db = handle->databases;
|
||||
db = mon->databases;
|
||||
sep = "";
|
||||
while (db)
|
||||
{
|
||||
@ -315,27 +258,6 @@ char *sep;
|
||||
dcb_printf(dcb, "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default username and password to use to monitor if the server does not
|
||||
* override this.
|
||||
*
|
||||
* @param arg The handle allocated by startMonitor
|
||||
* @param uname The default user name
|
||||
* @param passwd The default password
|
||||
*/
|
||||
static void
|
||||
defaultUsers(void *arg, char *uname, char *passwd)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
|
||||
if (handle->defaultUser)
|
||||
free(handle->defaultUser);
|
||||
if (handle->defaultPasswd)
|
||||
free(handle->defaultPasswd);
|
||||
handle->defaultUser = strdup(uname);
|
||||
handle->defaultPasswd = strdup(passwd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitor an individual server
|
||||
*
|
||||
@ -343,14 +265,14 @@ MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
* @param database The database to probe
|
||||
*/
|
||||
static void
|
||||
monitorDatabase(MYSQL_MONITOR *handle, MONITOR_SERVERS *database)
|
||||
monitorDatabase(MONITOR *mon, MONITOR_SERVERS *database)
|
||||
{
|
||||
GALERA_MONITOR* handle = (GALERA_MONITOR*)mon->handle;
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES *result;
|
||||
int num_fields;
|
||||
int isjoined = 0;
|
||||
char *uname = handle->defaultUser;
|
||||
char *passwd = handle->defaultPasswd;
|
||||
char *uname = mon->user;
|
||||
char *passwd = mon->password;
|
||||
unsigned long int server_version = 0;
|
||||
char *server_string;
|
||||
|
||||
@ -372,18 +294,17 @@ char *server_string;
|
||||
if (database->con == NULL || mysql_ping(database->con) != 0)
|
||||
{
|
||||
char *dpwd = decryptPassword(passwd);
|
||||
int rc;
|
||||
int connect_timeout = handle->connect_timeout;
|
||||
int read_timeout = handle->read_timeout;
|
||||
int write_timeout = handle->write_timeout;
|
||||
int connect_timeout = mon->connect_timeout;
|
||||
int read_timeout = mon->read_timeout;
|
||||
int write_timeout = mon->write_timeout;
|
||||
|
||||
if(database->con)
|
||||
mysql_close(database->con);
|
||||
database->con = mysql_init(NULL);
|
||||
|
||||
rc = mysql_options(database->con, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&connect_timeout);
|
||||
rc = mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout);
|
||||
rc = mysql_options(database->con, MYSQL_OPT_WRITE_TIMEOUT, (void *)&write_timeout);
|
||||
mysql_options(database->con, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&connect_timeout);
|
||||
mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout);
|
||||
mysql_options(database->con, MYSQL_OPT_WRITE_TIMEOUT, (void *)&write_timeout);
|
||||
|
||||
if (mysql_real_connect(database->con, database->server->name,
|
||||
uname, dpwd, NULL, database->server->port, NULL, 0) == NULL)
|
||||
@ -428,9 +349,6 @@ char *server_string;
|
||||
/* If we get this far then we have a working connection */
|
||||
server_set_status(database->server, SERVER_RUNNING);
|
||||
|
||||
/* get server version from current server */
|
||||
server_version = mysql_get_server_version(database->con);
|
||||
|
||||
/* get server version string */
|
||||
server_string = (char *)mysql_get_server_info(database->con);
|
||||
if (server_string) {
|
||||
@ -443,7 +361,6 @@ char *server_string;
|
||||
if (mysql_query(database->con, "SHOW STATUS LIKE 'wsrep_local_state'") == 0
|
||||
&& (result = mysql_store_result(database->con)) != NULL)
|
||||
{
|
||||
num_fields = mysql_num_fields(result);
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
if (strcmp(row[1], "4") == 0)
|
||||
@ -454,7 +371,6 @@ char *server_string;
|
||||
if (mysql_query(database->con, "SHOW VARIABLES LIKE 'wsrep_sst_method'") == 0
|
||||
&& (result = mysql_store_result(database->con)) != NULL)
|
||||
{
|
||||
num_fields = mysql_num_fields(result);
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
if (strncmp(row[1], "xtrabackup", 10) == 0)
|
||||
@ -471,7 +387,6 @@ char *server_string;
|
||||
&& (result = mysql_store_result(database->con)) != NULL)
|
||||
{
|
||||
long local_index = -1;
|
||||
num_fields = mysql_num_fields(result);
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
local_index = strtol(row[1], NULL, 10);
|
||||
@ -499,14 +414,20 @@ char *server_string;
|
||||
static void
|
||||
monitorMain(void *arg)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR* mon = (MONITOR*)arg;
|
||||
GALERA_MONITOR *handle;
|
||||
MONITOR_SERVERS *ptr;
|
||||
size_t nrounds = 0;
|
||||
MONITOR_SERVERS *candidate_master = NULL;
|
||||
int master_stickiness = handle->disableMasterFailback;
|
||||
int master_stickiness;
|
||||
int is_cluster=0;
|
||||
int log_no_members = 1;
|
||||
monitor_event_t evtype;
|
||||
|
||||
spinlock_acquire(&mon->lock);
|
||||
handle = (GALERA_MONITOR *)mon->handle;
|
||||
spinlock_release(&mon->lock);
|
||||
master_stickiness = handle->disableMasterFailback;
|
||||
if (mysql_thread_init())
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
@ -535,7 +456,7 @@ int log_no_members = 1;
|
||||
* round.
|
||||
*/
|
||||
|
||||
if (nrounds != 0 && ((nrounds*MON_BASE_INTERVAL_MS)%handle->interval) >= MON_BASE_INTERVAL_MS)
|
||||
if (nrounds != 0 && ((nrounds*MON_BASE_INTERVAL_MS)%mon->interval) >= MON_BASE_INTERVAL_MS)
|
||||
{
|
||||
nrounds += 1;
|
||||
continue;
|
||||
@ -546,11 +467,13 @@ int log_no_members = 1;
|
||||
/* reset cluster members counter */
|
||||
is_cluster=0;
|
||||
|
||||
ptr = handle->databases;
|
||||
ptr = mon->databases;
|
||||
|
||||
while (ptr)
|
||||
{
|
||||
monitorDatabase(handle, ptr);
|
||||
ptr->mon_prev_status = ptr->server->status;
|
||||
|
||||
monitorDatabase(mon, ptr);
|
||||
|
||||
/* clear bits for non member nodes */
|
||||
if ( ! SERVER_IN_MAINT(ptr->server) && (ptr->server->node_id < 0 || ! SERVER_IS_JOINED(ptr->server))) {
|
||||
@ -586,6 +509,7 @@ int log_no_members = 1;
|
||||
/** Increase this server'e error count */
|
||||
dcb_call_foreach(ptr->server,DCB_REASON_NOT_RESPONDING);
|
||||
ptr->mon_err_count += 1;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -604,7 +528,7 @@ int log_no_members = 1;
|
||||
*/
|
||||
|
||||
/* get the candidate master, following MIN(node_id) rule */
|
||||
candidate_master = get_candidate_master(handle->databases);
|
||||
candidate_master = get_candidate_master(mon->databases);
|
||||
|
||||
/* Select the master, based on master_stickiness */
|
||||
if (1 == handle->disableMasterRoleSetting) {
|
||||
@ -614,7 +538,7 @@ int log_no_members = 1;
|
||||
handle->master = set_cluster_master(handle->master, candidate_master, master_stickiness);
|
||||
}
|
||||
|
||||
ptr = handle->databases;
|
||||
ptr = mon->databases;
|
||||
|
||||
while (ptr) {
|
||||
if (!SERVER_IS_JOINED(ptr->server) || SERVER_IN_MAINT(ptr->server)) {
|
||||
@ -663,20 +587,32 @@ int log_no_members = 1;
|
||||
log_no_members = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the monitor sampling interval.
|
||||
*
|
||||
* @param arg The handle allocated by startMonitor
|
||||
* @param interval The interval to set in monitor struct, in milliseconds
|
||||
*/
|
||||
static void
|
||||
setInterval(void *arg, size_t interval)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
memcpy(&handle->interval, &interval, sizeof(unsigned long));
|
||||
|
||||
ptr = mon->databases;
|
||||
|
||||
while(ptr)
|
||||
{
|
||||
|
||||
/** Execute monitor script if a server state has changed */
|
||||
if(mon_status_changed(ptr))
|
||||
{
|
||||
evtype = mon_get_event_type(ptr);
|
||||
if(isGaleraEvent(evtype))
|
||||
{
|
||||
skygw_log_write(LOGFILE_TRACE,"Server changed state: %s[%s:%u]: %s",
|
||||
ptr->server->unique_name,
|
||||
ptr->server->name,ptr->server->port,
|
||||
mon_get_event_name(ptr));
|
||||
if(handle->script && handle->events[evtype])
|
||||
{
|
||||
monitor_launch_script(mon,ptr,handle->script);
|
||||
}
|
||||
}
|
||||
}
|
||||
ptr = ptr->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -761,7 +697,7 @@ static MONITOR_SERVERS *set_cluster_master(MONITOR_SERVERS *current_master, MONI
|
||||
static void
|
||||
disableMasterFailback(void *arg, int disable)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
GALERA_MONITOR *handle = (GALERA_MONITOR *)arg;
|
||||
memcpy(&handle->disableMasterFailback, &disable, sizeof(int));
|
||||
}
|
||||
|
||||
@ -777,115 +713,43 @@ MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
static void
|
||||
availableWhenDonor(void *arg, int disable)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
GALERA_MONITOR *handle = (GALERA_MONITOR *)arg;
|
||||
memcpy(&handle->availableWhenDonor, &disable, sizeof(int));
|
||||
}
|
||||
|
||||
static monitor_event_t galera_events[] = {
|
||||
MASTER_DOWN_EVENT,
|
||||
MASTER_UP_EVENT,
|
||||
SLAVE_DOWN_EVENT,
|
||||
SLAVE_UP_EVENT,
|
||||
SERVER_DOWN_EVENT,
|
||||
SERVER_UP_EVENT,
|
||||
SYNCED_DOWN_EVENT,
|
||||
SYNCED_UP_EVENT,
|
||||
DONOR_DOWN_EVENT,
|
||||
DONOR_UP_EVENT,
|
||||
LOST_MASTER_EVENT,
|
||||
LOST_SLAVE_EVENT,
|
||||
LOST_SYNCED_EVENT,
|
||||
LOST_DONOR_EVENT,
|
||||
NEW_MASTER_EVENT,
|
||||
NEW_SLAVE_EVENT,
|
||||
NEW_SYNCED_EVENT,
|
||||
NEW_DONOR_EVENT,
|
||||
MAX_MONITOR_EVENT
|
||||
};
|
||||
/**
|
||||
* Set the timeouts to use in the monitor.
|
||||
*
|
||||
* @param arg The handle allocated by startMonitor
|
||||
* @param type The connect timeout type
|
||||
* @param value The timeout value to set
|
||||
*/
|
||||
static void
|
||||
setNetworkTimeout(void *arg, int type, int value)
|
||||
* Check if the Galera monitor is monitoring this event type.
|
||||
* @param event Event to check
|
||||
* @return True if the event is monitored, false if it is not
|
||||
* */
|
||||
bool isGaleraEvent(monitor_event_t event)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
int max_timeout = (int)(handle->interval/1000);
|
||||
int new_timeout = max_timeout -1;
|
||||
|
||||
if (new_timeout <= 0)
|
||||
new_timeout = DEFAULT_CONNECT_TIMEOUT;
|
||||
|
||||
switch(type) {
|
||||
case MONITOR_CONNECT_TIMEOUT:
|
||||
if (value < max_timeout) {
|
||||
memcpy(&handle->connect_timeout, &value, sizeof(int));
|
||||
} else {
|
||||
memcpy(&handle->connect_timeout, &new_timeout, sizeof(int));
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"warning : Monitor Connect Timeout %i is greater than monitor interval ~%i seconds"
|
||||
", lowering to %i seconds", value, max_timeout, new_timeout)));
|
||||
}
|
||||
break;
|
||||
|
||||
case MONITOR_READ_TIMEOUT:
|
||||
if (value < max_timeout) {
|
||||
memcpy(&handle->read_timeout, &value, sizeof(int));
|
||||
} else {
|
||||
memcpy(&handle->read_timeout, &new_timeout, sizeof(int));
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"warning : Monitor Read Timeout %i is greater than monitor interval ~%i seconds"
|
||||
", lowering to %i seconds", value, max_timeout, new_timeout)));
|
||||
}
|
||||
break;
|
||||
|
||||
case MONITOR_WRITE_TIMEOUT:
|
||||
if (value < max_timeout) {
|
||||
memcpy(&handle->write_timeout, &value, sizeof(int));
|
||||
} else {
|
||||
memcpy(&handle->write_timeout, &new_timeout, sizeof(int));
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"warning : Monitor Write Timeout %i is greater than monitor interval ~%i seconds"
|
||||
", lowering to %i seconds", value, max_timeout, new_timeout)));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Monitor setNetworkTimeout received an unsupported action type %i", type)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current monitored server status has changed
|
||||
*
|
||||
* @param mon_srv The monitored server
|
||||
* @return true if status has changed or false
|
||||
*/
|
||||
static bool mon_status_changed(
|
||||
MONITOR_SERVERS* mon_srv)
|
||||
{
|
||||
bool succp;
|
||||
|
||||
if (mon_srv->mon_prev_status != mon_srv->server->status)
|
||||
{
|
||||
succp = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
succp = false;
|
||||
}
|
||||
return succp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current monitored server has a loggable failure status
|
||||
*
|
||||
* @param mon_srv The monitored server
|
||||
* @return true if failed status can be logged or false
|
||||
*/
|
||||
static bool mon_print_fail_status(
|
||||
MONITOR_SERVERS* mon_srv)
|
||||
{
|
||||
bool succp;
|
||||
int errcount = mon_srv->mon_err_count;
|
||||
uint8_t modval;
|
||||
|
||||
modval = 1<<(MIN(errcount/10, 7));
|
||||
|
||||
if (SERVER_IS_DOWN(mon_srv->server) && errcount == 0)
|
||||
{
|
||||
succp = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
succp = false;
|
||||
}
|
||||
return succp;
|
||||
}
|
||||
int i;
|
||||
for(i = 0;galera_events[i] != MAX_MONITOR_EVENT;i++)
|
||||
{
|
||||
if(event == galera_events[i])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
67
server/modules/monitor/galeramon.h
Normal file
67
server/modules/monitor/galeramon.h
Normal file
@ -0,0 +1,67 @@
|
||||
#ifndef _GALERAMON_H
|
||||
#define _GALERAMON_H
|
||||
/*
|
||||
* This file is distributed as part of the MariaDB Corporation MaxScale. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright MariaDB Corporation Ab 2013-2014
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <monitor.h>
|
||||
#include <monitor_common.h>
|
||||
#include <spinlock.h>
|
||||
#include <externcmd.h>
|
||||
#include <thread.h>
|
||||
#include <mysql.h>
|
||||
#include <mysqld_error.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
#include <secrets.h>
|
||||
#include <dcb.h>
|
||||
#include <modinfo.h>
|
||||
#include <maxconfig.h>
|
||||
|
||||
/**
|
||||
* @file galeramon.h - The Galera cluster monitor
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 07/05/15 Markus Makela Initial Implementation of galeramon.h
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* The handle for an instance of a MySQL Monitor module
|
||||
*/
|
||||
typedef struct {
|
||||
SPINLOCK lock; /**< The monitor spinlock */
|
||||
pthread_t tid; /**< id of monitor thread */
|
||||
int shutdown; /**< Flag to shutdown the monitor thread */
|
||||
int status; /**< Monitor status */
|
||||
unsigned long id; /**< Monitor ID */
|
||||
int disableMasterFailback; /**< Monitor flag for Galera Cluster Master failback */
|
||||
int availableWhenDonor; /**< Monitor flag for Galera Cluster Donor availability */
|
||||
int disableMasterRoleSetting; /**< Monitor flag to disable setting master role */
|
||||
MONITOR_SERVERS *master; /**< Master server for MySQL Master/Slave replication */
|
||||
char* script;
|
||||
bool events[MAX_MONITOR_EVENT]; /*< enabled events */
|
||||
} GALERA_MONITOR;
|
||||
|
||||
#endif
|
||||
@ -17,31 +17,21 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file mysql_mon.c - A MySQL Multi Muster cluster monitor
|
||||
* @file mm_mon.c - A Multi-Master Multi Muster cluster monitor
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 08/09/14 Massimiliano Pinto Initial implementation
|
||||
* 08/05/15 Markus Makela Addition of launchable scripts
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <monitor.h>
|
||||
#include <mysqlmon.h>
|
||||
#include <thread.h>
|
||||
#include <mysql.h>
|
||||
#include <mysqld_error.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
#include <secrets.h>
|
||||
#include <dcb.h>
|
||||
#include <modinfo.h>
|
||||
#include <maxconfig.h>
|
||||
|
||||
#include <mmmon.h>
|
||||
|
||||
/** Defined in log_manager.cc */
|
||||
extern int lm_enabled_logfiles_bitmask;
|
||||
extern size_t log_ses_count[];
|
||||
@ -49,37 +39,26 @@ extern __thread log_info_t tls_log_info;
|
||||
|
||||
static void monitorMain(void *);
|
||||
|
||||
static char *version_str = "V1.0.1";
|
||||
static char *version_str = "V1.1.1";
|
||||
|
||||
MODULE_INFO info = {
|
||||
MODULE_API_MONITOR,
|
||||
MODULE_BETA_RELEASE,
|
||||
MONITOR_VERSION,
|
||||
"A MySQL Multi Master monitor"
|
||||
"A Multi-Master Multi Master monitor"
|
||||
};
|
||||
|
||||
static void *startMonitor(void *,void*);
|
||||
static void stopMonitor(void *);
|
||||
static void registerServer(void *, SERVER *);
|
||||
static void unregisterServer(void *, SERVER *);
|
||||
static void defaultUser(void *, char *, char *);
|
||||
static void diagnostics(DCB *, void *);
|
||||
static void setInterval(void *, size_t);
|
||||
static void detectStaleMaster(void *, int);
|
||||
static bool mon_status_changed(MONITOR_SERVERS* mon_srv);
|
||||
static bool mon_print_fail_status(MONITOR_SERVERS* mon_srv);
|
||||
static MONITOR_SERVERS *get_current_master(MYSQL_MONITOR *);
|
||||
static void monitor_set_pending_status(MONITOR_SERVERS *, int);
|
||||
static void monitor_clear_pending_status(MONITOR_SERVERS *, int);
|
||||
static MONITOR_SERVERS *get_current_master(MONITOR *);
|
||||
bool isMySQLEvent(monitor_event_t event);
|
||||
|
||||
static MONITOR_OBJECT MyObject = {
|
||||
startMonitor,
|
||||
stopMonitor,
|
||||
registerServer,
|
||||
unregisterServer,
|
||||
defaultUser,
|
||||
diagnostics,
|
||||
setInterval
|
||||
diagnostics
|
||||
};
|
||||
|
||||
/**
|
||||
@ -102,7 +81,7 @@ ModuleInit()
|
||||
{
|
||||
LOGIF(LM, (skygw_log_write(
|
||||
LOGFILE_MESSAGE,
|
||||
"Initialise the MySQL Monitor module %s.",
|
||||
"Initialise the Multi-Master Monitor module %s.",
|
||||
version_str)));
|
||||
}
|
||||
|
||||
@ -131,38 +110,73 @@ GetModuleObject()
|
||||
static void *
|
||||
startMonitor(void *arg,void* opt)
|
||||
{
|
||||
MYSQL_MONITOR *handle;
|
||||
CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt;
|
||||
if (arg)
|
||||
{
|
||||
handle = arg; /* Must be a restart */
|
||||
handle->shutdown = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((handle = (MYSQL_MONITOR *)malloc(sizeof(MYSQL_MONITOR))) == NULL)
|
||||
return NULL;
|
||||
handle->databases = NULL;
|
||||
handle->shutdown = 0;
|
||||
handle->defaultUser = NULL;
|
||||
handle->defaultPasswd = NULL;
|
||||
handle->id = MONITOR_DEFAULT_ID;
|
||||
handle->interval = MONITOR_INTERVAL;
|
||||
handle->replicationHeartbeat = 0;
|
||||
handle->detectStaleMaster = 0;
|
||||
handle->master = NULL;
|
||||
spinlock_init(&handle->lock);
|
||||
}
|
||||
MONITOR* mon = (MONITOR*)arg;
|
||||
MM_MONITOR *handle = mon->handle;
|
||||
CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt;
|
||||
bool have_events = false;
|
||||
|
||||
while(params)
|
||||
if (handle)
|
||||
{
|
||||
handle->shutdown = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((handle = (MM_MONITOR *)malloc(sizeof(MM_MONITOR))) == NULL)
|
||||
return NULL;
|
||||
handle->shutdown = 0;
|
||||
handle->id = MONITOR_DEFAULT_ID;
|
||||
handle->master = NULL;
|
||||
memset(handle->events,false,sizeof(handle->events));
|
||||
spinlock_init(&handle->lock);
|
||||
}
|
||||
|
||||
while(params)
|
||||
{
|
||||
if(!strcmp(params->name,"detect_stale_master"))
|
||||
{
|
||||
if(!strcmp(params->name,"detect_stale_master"))
|
||||
handle->detectStaleMaster = config_truth_value(params->value);
|
||||
params = params->next;
|
||||
handle->detectStaleMaster = config_truth_value(params->value);
|
||||
}
|
||||
|
||||
handle->tid = (THREAD)thread_start(monitorMain, handle);
|
||||
return handle;
|
||||
else if(!strcmp(params->name,"script"))
|
||||
{
|
||||
if(handle->script)
|
||||
{
|
||||
free(handle->script);
|
||||
}
|
||||
if(access(params->value,X_OK) == 0)
|
||||
{
|
||||
handle->script = strdup(params->value);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(access(params->value,F_OK) == 0)
|
||||
{
|
||||
skygw_log_write(LE,
|
||||
"Error: The file cannot be executed: %s",
|
||||
params->value);
|
||||
}
|
||||
else
|
||||
{
|
||||
skygw_log_write(LE,
|
||||
"Error: The file cannot be found: %s",
|
||||
params->value);
|
||||
}
|
||||
handle->script = NULL;
|
||||
}
|
||||
}
|
||||
else if(!strcmp(params->name,"events"))
|
||||
{
|
||||
mon_parse_event_string((bool*)&handle->events,sizeof(handle->events),params->value);
|
||||
have_events = true;
|
||||
}
|
||||
params = params->next;
|
||||
}
|
||||
/** If no specific events are given, enable them all */
|
||||
if(!have_events)
|
||||
{
|
||||
memset(handle->events,true,sizeof(handle->events));
|
||||
}
|
||||
handle->tid = (THREAD)thread_start(monitorMain, mon);
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -173,109 +187,12 @@ CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt;
|
||||
static void
|
||||
stopMonitor(void *arg)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MM_MONITOR *handle = (MM_MONITOR *)arg;
|
||||
|
||||
handle->shutdown = 1;
|
||||
thread_wait((void *)handle->tid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a server that must be added to the monitored servers for
|
||||
* a monitoring module.
|
||||
*
|
||||
* @param arg A handle on the running monitor module
|
||||
* @param server The server to add
|
||||
*/
|
||||
static void
|
||||
registerServer(void *arg, SERVER *server)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR_SERVERS *ptr, *db;
|
||||
|
||||
if ((db = (MONITOR_SERVERS *)malloc(sizeof(MONITOR_SERVERS))) == NULL)
|
||||
return;
|
||||
db->server = server;
|
||||
db->con = NULL;
|
||||
db->next = NULL;
|
||||
db->mon_err_count = 0;
|
||||
db->mon_prev_status = 0;
|
||||
/* pending status is updated by monitorMain */
|
||||
db->pending_status = 0;
|
||||
|
||||
spinlock_acquire(&handle->lock);
|
||||
|
||||
if (handle->databases == NULL)
|
||||
handle->databases = db;
|
||||
else
|
||||
{
|
||||
ptr = handle->databases;
|
||||
while (ptr->next != NULL)
|
||||
ptr = ptr->next;
|
||||
ptr->next = db;
|
||||
}
|
||||
spinlock_release(&handle->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a server from those being monitored by a monitoring module
|
||||
*
|
||||
* @param arg A handle on the running monitor module
|
||||
* @param server The server to remove
|
||||
*/
|
||||
static void
|
||||
unregisterServer(void *arg, SERVER *server)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR_SERVERS *ptr, *lptr;
|
||||
|
||||
spinlock_acquire(&handle->lock);
|
||||
if (handle->databases == NULL)
|
||||
{
|
||||
spinlock_release(&handle->lock);
|
||||
return;
|
||||
}
|
||||
if (handle->databases->server == server)
|
||||
{
|
||||
ptr = handle->databases;
|
||||
handle->databases = handle->databases->next;
|
||||
free(ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = handle->databases;
|
||||
while (ptr->next != NULL && ptr->next->server != server)
|
||||
ptr = ptr->next;
|
||||
if (ptr->next)
|
||||
{
|
||||
lptr = ptr->next;
|
||||
ptr->next = ptr->next->next;
|
||||
free(lptr);
|
||||
}
|
||||
}
|
||||
spinlock_release(&handle->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default username and password to use to monitor if the server does not
|
||||
* override this.
|
||||
*
|
||||
* @param arg The handle allocated by startMonitor
|
||||
* @param uname The default user name
|
||||
* @param passwd The default password
|
||||
*/
|
||||
static void
|
||||
defaultUser(void *arg, char *uname, char *passwd)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
|
||||
if (handle->defaultUser)
|
||||
free(handle->defaultUser);
|
||||
if (handle->defaultPasswd)
|
||||
free(handle->defaultPasswd);
|
||||
handle->defaultUser = strdup(uname);
|
||||
handle->defaultPasswd = strdup(passwd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Daignostic interface
|
||||
*
|
||||
@ -284,7 +201,8 @@ MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
*/
|
||||
static void diagnostics(DCB *dcb, void *arg)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR* mon = (MONITOR*)arg;
|
||||
MM_MONITOR *handle = (MM_MONITOR *)mon->handle;
|
||||
MONITOR_SERVERS *db;
|
||||
char *sep;
|
||||
|
||||
@ -301,11 +219,11 @@ char *sep;
|
||||
break;
|
||||
}
|
||||
|
||||
dcb_printf(dcb,"\tSampling interval:\t%lu milliseconds\n", handle->interval);
|
||||
dcb_printf(dcb,"\tSampling interval:\t%lu milliseconds\n", mon->interval);
|
||||
dcb_printf(dcb,"\tDetect Stale Master:\t%s\n", (handle->detectStaleMaster == 1) ? "enabled" : "disabled");
|
||||
dcb_printf(dcb, "\tMonitored servers: ");
|
||||
|
||||
db = handle->databases;
|
||||
db = mon->databases;
|
||||
sep = "";
|
||||
while (db)
|
||||
{
|
||||
@ -327,15 +245,15 @@ char *sep;
|
||||
* @param database The database to probe
|
||||
*/
|
||||
static void
|
||||
monitorDatabase(MYSQL_MONITOR *handle, MONITOR_SERVERS *database)
|
||||
monitorDatabase(MONITOR* mon, MONITOR_SERVERS *database)
|
||||
{
|
||||
MM_MONITOR *handle = mon->handle;
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES *result;
|
||||
int num_fields;
|
||||
int isslave = 0;
|
||||
int ismaster = 0;
|
||||
char *uname = handle->defaultUser;
|
||||
char *passwd = handle->defaultPasswd;
|
||||
char *uname = mon->user;
|
||||
char *passwd = mon->password;
|
||||
unsigned long int server_version = 0;
|
||||
char *server_string;
|
||||
|
||||
@ -352,19 +270,18 @@ char *server_string;
|
||||
if (SERVER_IN_MAINT(database->server))
|
||||
return;
|
||||
|
||||
/** Store prevous status */
|
||||
/** Store previous status */
|
||||
database->mon_prev_status = database->server->status;
|
||||
|
||||
if (database->con == NULL || mysql_ping(database->con) != 0)
|
||||
{
|
||||
char *dpwd = decryptPassword(passwd);
|
||||
int rc;
|
||||
int read_timeout = 1;
|
||||
if(database->con)
|
||||
mysql_close(database->con);
|
||||
database->con = mysql_init(NULL);
|
||||
|
||||
rc = mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout);
|
||||
mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout);
|
||||
|
||||
if (mysql_real_connect(database->con,
|
||||
database->server->name,
|
||||
@ -438,7 +355,7 @@ char *server_string;
|
||||
&& (result = mysql_store_result(database->con)) != NULL)
|
||||
{
|
||||
long server_id = -1;
|
||||
num_fields = mysql_num_fields(result);
|
||||
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
server_id = strtol(row[0], NULL, 10);
|
||||
@ -464,7 +381,7 @@ char *server_string;
|
||||
{
|
||||
int i = 0;
|
||||
long master_id = -1;
|
||||
num_fields = mysql_num_fields(result);
|
||||
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
/* get Slave_IO_Running and Slave_SQL_Running values*/
|
||||
@ -503,7 +420,7 @@ char *server_string;
|
||||
&& (result = mysql_store_result(database->con)) != NULL)
|
||||
{
|
||||
long master_id = -1;
|
||||
num_fields = mysql_num_fields(result);
|
||||
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
/* get Slave_IO_Running and Slave_SQL_Running values*/
|
||||
@ -535,7 +452,7 @@ char *server_string;
|
||||
if (mysql_query(database->con, "SHOW GLOBAL VARIABLES LIKE 'read_only'") == 0
|
||||
&& (result = mysql_store_result(database->con)) != NULL)
|
||||
{
|
||||
num_fields = mysql_num_fields(result);
|
||||
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
if (strncasecmp(row[1], "OFF", 3) == 0) {
|
||||
@ -585,12 +502,18 @@ char *server_string;
|
||||
static void
|
||||
monitorMain(void *arg)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR* mon = (MONITOR*)arg;
|
||||
MM_MONITOR *handle;
|
||||
MONITOR_SERVERS *ptr;
|
||||
int detect_stale_master = handle->detectStaleMaster;
|
||||
int detect_stale_master;
|
||||
MONITOR_SERVERS *root_master;
|
||||
size_t nrounds = 0;
|
||||
|
||||
spinlock_acquire(&mon->lock);
|
||||
handle = (MM_MONITOR *)mon->handle;
|
||||
spinlock_release(&mon->lock);
|
||||
detect_stale_master = handle->detectStaleMaster;
|
||||
|
||||
if (mysql_thread_init())
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
@ -620,7 +543,7 @@ size_t nrounds = 0;
|
||||
* round.
|
||||
*/
|
||||
if (nrounds != 0 &&
|
||||
((nrounds*MON_BASE_INTERVAL_MS)%handle->interval) >=
|
||||
((nrounds*MON_BASE_INTERVAL_MS)%mon->interval) >=
|
||||
MON_BASE_INTERVAL_MS)
|
||||
{
|
||||
nrounds += 1;
|
||||
@ -629,7 +552,7 @@ size_t nrounds = 0;
|
||||
nrounds += 1;
|
||||
|
||||
/* start from the first server in the list */
|
||||
ptr = handle->databases;
|
||||
ptr = mon->databases;
|
||||
|
||||
while (ptr)
|
||||
{
|
||||
@ -637,7 +560,7 @@ size_t nrounds = 0;
|
||||
ptr->pending_status = ptr->server->status;
|
||||
|
||||
/* monitor current node */
|
||||
monitorDatabase(handle, ptr);
|
||||
monitorDatabase(mon, ptr);
|
||||
|
||||
if (mon_status_changed(ptr))
|
||||
{
|
||||
@ -669,11 +592,11 @@ size_t nrounds = 0;
|
||||
}
|
||||
|
||||
/* Get Master server pointer */
|
||||
root_master = get_current_master(handle);
|
||||
root_master = get_current_master(mon);
|
||||
|
||||
/* Update server status from monitor pending status on that server*/
|
||||
|
||||
ptr = handle->databases;
|
||||
ptr = mon->databases;
|
||||
while (ptr)
|
||||
{
|
||||
if (! SERVER_IN_MAINT(ptr->server)) {
|
||||
@ -690,21 +613,30 @@ size_t nrounds = 0;
|
||||
}
|
||||
ptr = ptr->next;
|
||||
}
|
||||
|
||||
ptr = mon->databases;
|
||||
monitor_event_t evtype;
|
||||
while(ptr)
|
||||
{
|
||||
if(mon_status_changed(ptr))
|
||||
{
|
||||
evtype = mon_get_event_type(ptr);
|
||||
if(isMySQLEvent(evtype))
|
||||
{
|
||||
skygw_log_write(LOGFILE_TRACE,"Server changed state: %s[%s:%u]: %s",
|
||||
ptr->server->unique_name,
|
||||
ptr->server->name,ptr->server->port,
|
||||
mon_get_event_name(ptr));
|
||||
if(handle->script && handle->events[evtype])
|
||||
{
|
||||
monitor_launch_script(mon,ptr,handle->script);
|
||||
}
|
||||
}
|
||||
}
|
||||
ptr = ptr->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the monitor sampling interval.
|
||||
*
|
||||
* @param arg The handle allocated by startMonitor
|
||||
* @param interval The interval to set in monitor struct, in milliseconds
|
||||
*/
|
||||
static void
|
||||
setInterval(void *arg, size_t interval)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
memcpy(&handle->interval, &interval, sizeof(unsigned long));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/Disable the MySQL Replication Stale Master dectection, allowing a previouvsly detected master to still act as a Master.
|
||||
@ -717,70 +649,11 @@ MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
static void
|
||||
detectStaleMaster(void *arg, int enable)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR* mon = (MONITOR*)arg;
|
||||
MM_MONITOR *handle = (MM_MONITOR *)mon->handle;
|
||||
memcpy(&handle->detectStaleMaster, &enable, sizeof(int));
|
||||
}
|
||||
|
||||
static bool mon_status_changed(
|
||||
MONITOR_SERVERS* mon_srv)
|
||||
{
|
||||
bool succp;
|
||||
|
||||
if (mon_srv->mon_prev_status != mon_srv->server->status)
|
||||
{
|
||||
succp = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
succp = false;
|
||||
}
|
||||
return succp;
|
||||
}
|
||||
|
||||
static bool mon_print_fail_status(
|
||||
MONITOR_SERVERS* mon_srv)
|
||||
{
|
||||
bool succp;
|
||||
int errcount = mon_srv->mon_err_count;
|
||||
uint8_t modval;
|
||||
|
||||
modval = 1<<(MIN(errcount/10, 7));
|
||||
|
||||
if (SERVER_IS_DOWN(mon_srv->server) && errcount%modval == 0)
|
||||
{
|
||||
succp = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
succp = false;
|
||||
}
|
||||
return succp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a pending status bit in the monior server
|
||||
*
|
||||
* @param server The server to update
|
||||
* @param bit The bit to clear for the server
|
||||
*/
|
||||
static void
|
||||
monitor_set_pending_status(MONITOR_SERVERS *ptr, int bit)
|
||||
{
|
||||
ptr->pending_status |= bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear a pending status bit in the monior server
|
||||
*
|
||||
* @param server The server to update
|
||||
* @param bit The bit to clear for the server
|
||||
*/
|
||||
static void
|
||||
monitor_clear_pending_status(MONITOR_SERVERS *ptr, int bit)
|
||||
{
|
||||
ptr->pending_status &= ~bit;
|
||||
}
|
||||
|
||||
/*******
|
||||
* This function returns the master server
|
||||
* from a set of MySQL Multi Master monitored servers
|
||||
@ -791,10 +664,11 @@ monitor_clear_pending_status(MONITOR_SERVERS *ptr, int bit)
|
||||
* @return The server at root level with SERVER_MASTER bit
|
||||
*/
|
||||
|
||||
static MONITOR_SERVERS *get_current_master(MYSQL_MONITOR *handle) {
|
||||
static MONITOR_SERVERS *get_current_master(MONITOR *mon) {
|
||||
MM_MONITOR* handle = mon->handle;
|
||||
MONITOR_SERVERS *ptr;
|
||||
|
||||
ptr = handle->databases;
|
||||
ptr = mon->databases;
|
||||
|
||||
while (ptr)
|
||||
{
|
||||
@ -831,3 +705,32 @@ MONITOR_SERVERS *ptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static monitor_event_t mysql_events[] = {
|
||||
MASTER_DOWN_EVENT,
|
||||
MASTER_UP_EVENT,
|
||||
SLAVE_DOWN_EVENT,
|
||||
SLAVE_UP_EVENT,
|
||||
SERVER_DOWN_EVENT,
|
||||
SERVER_UP_EVENT,
|
||||
LOST_MASTER_EVENT,
|
||||
LOST_SLAVE_EVENT,
|
||||
NEW_MASTER_EVENT,
|
||||
NEW_SLAVE_EVENT,
|
||||
MAX_MONITOR_EVENT
|
||||
};
|
||||
/**
|
||||
* Check if the MM monitor is monitoring this event type.
|
||||
* @param event Event to check
|
||||
* @return True if the event is monitored, false if it is not
|
||||
* */
|
||||
bool isMySQLEvent(monitor_event_t event)
|
||||
{
|
||||
int i;
|
||||
for(i = 0;mysql_events[i] != MAX_MONITOR_EVENT;i++)
|
||||
{
|
||||
if(event == mysql_events[i])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
52
server/modules/monitor/mmmon.h
Normal file
52
server/modules/monitor/mmmon.h
Normal file
@ -0,0 +1,52 @@
|
||||
#ifndef _MMMON_H
|
||||
#define _MMMON_H
|
||||
/*
|
||||
* This file is distributed as part of the MariaDB Corporation MaxScale. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright MariaDB Corporation Ab 2015
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <monitor.h>
|
||||
#include <spinlock.h>
|
||||
#include <thread.h>
|
||||
#include <mysql.h>
|
||||
#include <mysqld_error.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
#include <secrets.h>
|
||||
#include <dcb.h>
|
||||
#include <modinfo.h>
|
||||
#include <maxconfig.h>
|
||||
#include <monitor_common.h>
|
||||
#include <externcmd.h>
|
||||
|
||||
/**
|
||||
* The handle for an instance of a Multi-Master Monitor module
|
||||
*/
|
||||
typedef struct {
|
||||
SPINLOCK lock; /**< The monitor spinlock */
|
||||
pthread_t tid; /**< id of monitor thread */
|
||||
int shutdown; /**< Flag to shutdown the monitor thread */
|
||||
int status; /**< Monitor status */
|
||||
unsigned long id; /**< Monitor ID */
|
||||
int detectStaleMaster; /**< Monitor flag for Stale Master detection */
|
||||
MONITOR_SERVERS *master; /**< Master server for Master/Slave replication */
|
||||
char* script; /*< Script to call when state changes occur on servers */
|
||||
bool events[MAX_MONITOR_EVENT]; /*< enabled events */
|
||||
} MM_MONITOR;
|
||||
|
||||
#endif
|
||||
402
server/modules/monitor/monitor_common.c
Normal file
402
server/modules/monitor/monitor_common.c
Normal file
@ -0,0 +1,402 @@
|
||||
/*
|
||||
* This file is distributed as part of the MariaDB Corporation MaxScale. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright MariaDB Corporation Ab 2013-2015
|
||||
*/
|
||||
|
||||
#include <monitor_common.h>
|
||||
|
||||
monitor_event_t mon_name_to_event(char* tok);
|
||||
|
||||
/**
|
||||
* Set a pending status bit in the monitor server
|
||||
*
|
||||
* @param server The server to update
|
||||
* @param bit The bit to clear for the server
|
||||
*/
|
||||
void monitor_set_pending_status(MONITOR_SERVERS *ptr, int bit)
|
||||
{
|
||||
ptr->pending_status |= bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear a pending status bit in the monitor server
|
||||
*
|
||||
* @param server The server to update
|
||||
* @param bit The bit to clear for the server
|
||||
*/
|
||||
void monitor_clear_pending_status(MONITOR_SERVERS *ptr, int bit)
|
||||
{
|
||||
ptr->pending_status &= ~bit;
|
||||
}
|
||||
|
||||
|
||||
monitor_event_t mon_get_event_type(MONITOR_SERVERS* node)
|
||||
{
|
||||
unsigned int prev = node->mon_prev_status;
|
||||
|
||||
if((prev & (SERVER_MASTER|SERVER_RUNNING)) == (SERVER_MASTER|SERVER_RUNNING) &&
|
||||
SERVER_IS_DOWN(node->server))
|
||||
{
|
||||
return MASTER_DOWN_EVENT;
|
||||
}
|
||||
if((prev & (SERVER_RUNNING)) == 0 &&
|
||||
SERVER_IS_RUNNING(node->server) && SERVER_IS_MASTER(node->server))
|
||||
{
|
||||
return MASTER_UP_EVENT;
|
||||
}
|
||||
if((prev & (SERVER_SLAVE|SERVER_RUNNING)) == (SERVER_SLAVE|SERVER_RUNNING) &&
|
||||
SERVER_IS_DOWN(node->server))
|
||||
{
|
||||
return SLAVE_DOWN_EVENT;
|
||||
}
|
||||
if((prev & (SERVER_RUNNING)) == 0 &&
|
||||
SERVER_IS_RUNNING(node->server) && SERVER_IS_SLAVE(node->server))
|
||||
{
|
||||
return SLAVE_UP_EVENT;
|
||||
}
|
||||
|
||||
/** Galera specific events */
|
||||
if((prev & (SERVER_JOINED|SERVER_RUNNING)) == (SERVER_JOINED|SERVER_RUNNING) &&
|
||||
SERVER_IS_DOWN(node->server))
|
||||
{
|
||||
return SYNCED_DOWN_EVENT;
|
||||
}
|
||||
if((prev & (SERVER_RUNNING)) == 0 &&
|
||||
SERVER_IS_RUNNING(node->server) && SERVER_IS_JOINED(node->server))
|
||||
{
|
||||
return SYNCED_UP_EVENT;
|
||||
}
|
||||
|
||||
/** NDB events*/
|
||||
if((prev & (SERVER_NDB|SERVER_RUNNING)) == (SERVER_NDB|SERVER_RUNNING) &&
|
||||
SERVER_IS_DOWN(node->server))
|
||||
{
|
||||
return NDB_DOWN_EVENT;
|
||||
}
|
||||
if((prev & (SERVER_RUNNING)) == 0 &&
|
||||
SERVER_IS_RUNNING(node->server) && SERVER_IS_NDB(node->server))
|
||||
{
|
||||
return NDB_UP_EVENT;
|
||||
}
|
||||
|
||||
if((prev & (SERVER_RUNNING)) == SERVER_RUNNING &&
|
||||
SERVER_IS_RUNNING(node->server) && SERVER_IS_MASTER(node->server))
|
||||
{
|
||||
return NEW_MASTER_EVENT;
|
||||
}
|
||||
if((prev & (SERVER_RUNNING)) == SERVER_RUNNING &&
|
||||
SERVER_IS_RUNNING(node->server) && SERVER_IS_SLAVE(node->server))
|
||||
{
|
||||
return NEW_SLAVE_EVENT;
|
||||
}
|
||||
|
||||
/** Status loss events */
|
||||
if((prev & (SERVER_RUNNING|SERVER_MASTER)) == (SERVER_RUNNING|SERVER_MASTER) &&
|
||||
SERVER_IS_RUNNING(node->server) && !SERVER_IS_MASTER(node->server))
|
||||
{
|
||||
return LOST_MASTER_EVENT;
|
||||
}
|
||||
if((prev & (SERVER_RUNNING|SERVER_SLAVE)) == (SERVER_RUNNING|SERVER_SLAVE) &&
|
||||
SERVER_IS_RUNNING(node->server) && !SERVER_IS_SLAVE(node->server))
|
||||
{
|
||||
return LOST_SLAVE_EVENT;
|
||||
}
|
||||
if((prev & (SERVER_RUNNING|SERVER_JOINED)) == (SERVER_RUNNING|SERVER_JOINED) &&
|
||||
SERVER_IS_RUNNING(node->server) && !SERVER_IS_JOINED(node->server))
|
||||
{
|
||||
return LOST_SYNCED_EVENT;
|
||||
}
|
||||
if((prev & (SERVER_RUNNING|SERVER_NDB)) == (SERVER_RUNNING|SERVER_NDB) &&
|
||||
SERVER_IS_RUNNING(node->server) && !SERVER_IS_NDB(node->server))
|
||||
{
|
||||
return LOST_NDB_EVENT;
|
||||
}
|
||||
|
||||
|
||||
/** Generic server failure */
|
||||
if((prev & SERVER_RUNNING) == 0 &&
|
||||
SERVER_IS_RUNNING(node->server))
|
||||
{
|
||||
return SERVER_UP_EVENT;
|
||||
}
|
||||
if((prev & SERVER_RUNNING) == SERVER_RUNNING &&
|
||||
SERVER_IS_DOWN(node->server))
|
||||
{
|
||||
return SERVER_DOWN_EVENT;
|
||||
}
|
||||
|
||||
/** Something else, most likely a state that does not matter.
|
||||
* For example SERVER_DOWN -> SERVER_MASTER|SERVER_DOWN still results in a
|
||||
* server state equal to not running.*/
|
||||
return UNDEFINED_MONITOR_EVENT;
|
||||
}
|
||||
|
||||
char* mon_get_event_name(MONITOR_SERVERS* node)
|
||||
{
|
||||
switch(mon_get_event_type(node))
|
||||
{
|
||||
case UNDEFINED_MONITOR_EVENT:
|
||||
return "undefined";
|
||||
|
||||
case MASTER_DOWN_EVENT:
|
||||
return "master_down";
|
||||
|
||||
case MASTER_UP_EVENT:
|
||||
return "master_up";
|
||||
|
||||
case SLAVE_DOWN_EVENT:
|
||||
return "slave_down";
|
||||
|
||||
case SLAVE_UP_EVENT:
|
||||
return "slave_up";
|
||||
|
||||
case SERVER_DOWN_EVENT:
|
||||
return "server_down";
|
||||
|
||||
case SERVER_UP_EVENT:
|
||||
return "server_up";
|
||||
|
||||
case SYNCED_DOWN_EVENT:
|
||||
return "synced_down";
|
||||
|
||||
case SYNCED_UP_EVENT:
|
||||
return "synced_up";
|
||||
|
||||
case DONOR_DOWN_EVENT:
|
||||
return "donor_down";
|
||||
|
||||
case DONOR_UP_EVENT:
|
||||
return "donor_up";
|
||||
|
||||
case NDB_DOWN_EVENT:
|
||||
return "ndb_down";
|
||||
|
||||
case NDB_UP_EVENT:
|
||||
return "ndb_up";
|
||||
|
||||
case LOST_MASTER_EVENT:
|
||||
return "lost_master";
|
||||
|
||||
case LOST_SLAVE_EVENT:
|
||||
return "lost_slave";
|
||||
|
||||
case LOST_SYNCED_EVENT:
|
||||
return "lost_synced";
|
||||
|
||||
case LOST_DONOR_EVENT:
|
||||
return "lost_donor";
|
||||
|
||||
case LOST_NDB_EVENT:
|
||||
return "lost_ndb";
|
||||
|
||||
case NEW_MASTER_EVENT:
|
||||
return "new_master";
|
||||
|
||||
case NEW_SLAVE_EVENT:
|
||||
return "new_slave";
|
||||
|
||||
case NEW_SYNCED_EVENT:
|
||||
return "new_synced";
|
||||
|
||||
case NEW_DONOR_EVENT:
|
||||
return "new_donor";
|
||||
|
||||
case NEW_NDB_EVENT:
|
||||
return "new_ndb";
|
||||
|
||||
default:
|
||||
return "MONITOR_EVENT_FAILURE";
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void mon_append_node_names(MONITOR_SERVERS* start,char* str, int len)
|
||||
{
|
||||
MONITOR_SERVERS* ptr = start;
|
||||
bool first = true;
|
||||
|
||||
while(ptr)
|
||||
{
|
||||
if(!first)
|
||||
{
|
||||
strncat(str,",",len);
|
||||
}
|
||||
first = false;
|
||||
strncat(str,ptr->server->unique_name,len);
|
||||
ptr = ptr->next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current monitored server status has changed
|
||||
*
|
||||
* @param mon_srv The monitored server
|
||||
* @return true if status has changed or false
|
||||
*/
|
||||
bool mon_status_changed(
|
||||
MONITOR_SERVERS* mon_srv)
|
||||
{
|
||||
bool succp;
|
||||
|
||||
/** This is the first time the server was set with a status*/
|
||||
if (mon_srv->mon_prev_status == -1)
|
||||
return false;
|
||||
|
||||
if (mon_srv->mon_prev_status != mon_srv->server->status)
|
||||
{
|
||||
succp = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
succp = false;
|
||||
}
|
||||
return succp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current monitored server has a loggable failure status
|
||||
*
|
||||
* @param mon_srv The monitored server
|
||||
* @return true if failed status can be logged or false
|
||||
*/
|
||||
bool mon_print_fail_status(
|
||||
MONITOR_SERVERS* mon_srv)
|
||||
{
|
||||
bool succp;
|
||||
int errcount = mon_srv->mon_err_count;
|
||||
|
||||
if (SERVER_IS_DOWN(mon_srv->server) && errcount == 0)
|
||||
{
|
||||
succp = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
succp = false;
|
||||
}
|
||||
return succp;
|
||||
}
|
||||
|
||||
void monitor_launch_script(MONITOR* mon,MONITOR_SERVERS* ptr, char* script)
|
||||
{
|
||||
char argstr[PATH_MAX + MON_ARG_MAX + 1];
|
||||
EXTERNCMD* cmd;
|
||||
|
||||
snprintf(argstr,PATH_MAX + MON_ARG_MAX,
|
||||
"%s --event=%s --initiator=%s --nodelist=",
|
||||
script,
|
||||
mon_get_event_name(ptr),
|
||||
ptr->server->unique_name);
|
||||
|
||||
mon_append_node_names(mon->databases,argstr,PATH_MAX + MON_ARG_MAX + 1);
|
||||
if((cmd = externcmd_allocate(argstr)) == NULL)
|
||||
{
|
||||
skygw_log_write(LE,"Failed to execute script: %s",script);
|
||||
return;
|
||||
}
|
||||
|
||||
if(externcmd_execute(cmd))
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,
|
||||
"Error: Failed to execute script "
|
||||
"'%s' on server state change event %s.",
|
||||
script,mon_get_event_type(ptr));
|
||||
}
|
||||
externcmd_free(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a string of event names to an array with enabled events.
|
||||
* @param events Pointer to an array of boolean values
|
||||
* @param count Size of the array
|
||||
* @param string String to parse
|
||||
* @return 0 on success. 1 when an error has occurred or an unexpected event was
|
||||
* found.
|
||||
*/
|
||||
int mon_parse_event_string(bool* events, size_t count,char* string)
|
||||
{
|
||||
char *tok,*saved;
|
||||
monitor_event_t event;
|
||||
|
||||
tok = strtok_r(string,",| ",&saved);
|
||||
|
||||
if(tok == NULL)
|
||||
return -1;
|
||||
|
||||
while(tok)
|
||||
{
|
||||
event = mon_name_to_event(tok);
|
||||
if(event == UNDEFINED_MONITOR_EVENT)
|
||||
return -1;
|
||||
events[event] = true;
|
||||
tok = strtok_r(NULL,",| ",&saved);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
monitor_event_t mon_name_to_event(char* tok)
|
||||
{
|
||||
if(!strcasecmp("master_down",tok))
|
||||
return MASTER_DOWN_EVENT;
|
||||
else if(!strcasecmp("master_up",tok))
|
||||
return MASTER_UP_EVENT;
|
||||
else if(!strcasecmp("slave_down",tok))
|
||||
return SLAVE_DOWN_EVENT;
|
||||
else if(!strcasecmp("slave_up",tok))
|
||||
return SLAVE_UP_EVENT;
|
||||
else if(!strcasecmp("server_down",tok))
|
||||
return SERVER_DOWN_EVENT;
|
||||
else if(!strcasecmp("server_up",tok))
|
||||
return SERVER_UP_EVENT;
|
||||
else if(!strcasecmp("synced_down",tok))
|
||||
return SYNCED_DOWN_EVENT;
|
||||
else if(!strcasecmp("synced_up",tok))
|
||||
return SYNCED_UP_EVENT;
|
||||
else if(!strcasecmp("donor_down",tok))
|
||||
return DONOR_DOWN_EVENT;
|
||||
else if(!strcasecmp("donor_up",tok))
|
||||
return DONOR_UP_EVENT;
|
||||
else if(!strcasecmp("ndb_down",tok))
|
||||
return NDB_DOWN_EVENT;
|
||||
else if(!strcasecmp("ndb_up",tok))
|
||||
return NDB_UP_EVENT;
|
||||
else if(!strcasecmp("lost_master",tok))
|
||||
return LOST_MASTER_EVENT;
|
||||
else if(!strcasecmp("lost_slave",tok))
|
||||
return LOST_SLAVE_EVENT;
|
||||
else if(!strcasecmp("lost_synced",tok))
|
||||
return LOST_SYNCED_EVENT;
|
||||
else if(!strcasecmp("lost_donor",tok))
|
||||
return LOST_DONOR_EVENT;
|
||||
else if(!strcasecmp("lost_ndb",tok))
|
||||
return LOST_NDB_EVENT;
|
||||
else if(!strcasecmp("new_master",tok))
|
||||
return NEW_MASTER_EVENT;
|
||||
else if(!strcasecmp("new_slave",tok))
|
||||
return NEW_SLAVE_EVENT;
|
||||
else if(!strcasecmp("new_synced",tok))
|
||||
return NEW_SYNCED_EVENT;
|
||||
else if(!strcasecmp("new_donor",tok))
|
||||
return NEW_DONOR_EVENT;
|
||||
else if(!strcasecmp("new_ndb",tok))
|
||||
return NEW_NDB_EVENT;
|
||||
else
|
||||
return UNDEFINED_MONITOR_EVENT;
|
||||
|
||||
}
|
||||
|
||||
75
server/modules/monitor/monitor_common.h
Normal file
75
server/modules/monitor/monitor_common.h
Normal file
@ -0,0 +1,75 @@
|
||||
#ifndef _MONITOR_COMMON_HG
|
||||
#define _MONITOR_COMMON_HG
|
||||
/*
|
||||
* This file is distributed as part of the MariaDB Corporation MaxScale. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright MariaDB Corporation Ab 2013-2015
|
||||
*/
|
||||
|
||||
#include <server.h>
|
||||
#include <mysql.h>
|
||||
#include <monitor.h>
|
||||
#include <log_manager.h>
|
||||
#include <externcmd.h>
|
||||
/**
|
||||
* @file monitor_common.h - The generic monitor structures all monitors use
|
||||
*
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 07/05/15 Markus Makela Initial Implementation
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
#define MON_ARG_MAX 8192
|
||||
|
||||
/** Monitor events that are caused by servers moving from
|
||||
* one state to another.*/
|
||||
typedef enum {
|
||||
UNDEFINED_MONITOR_EVENT,
|
||||
MASTER_DOWN_EVENT,
|
||||
MASTER_UP_EVENT,
|
||||
SLAVE_DOWN_EVENT,
|
||||
SLAVE_UP_EVENT,
|
||||
SERVER_DOWN_EVENT,
|
||||
SERVER_UP_EVENT,
|
||||
SYNCED_DOWN_EVENT,
|
||||
SYNCED_UP_EVENT,
|
||||
DONOR_DOWN_EVENT,
|
||||
DONOR_UP_EVENT,
|
||||
NDB_DOWN_EVENT,
|
||||
NDB_UP_EVENT,
|
||||
LOST_MASTER_EVENT,
|
||||
LOST_SLAVE_EVENT,
|
||||
LOST_SYNCED_EVENT,
|
||||
LOST_DONOR_EVENT,
|
||||
LOST_NDB_EVENT,
|
||||
NEW_MASTER_EVENT,
|
||||
NEW_SLAVE_EVENT,
|
||||
NEW_SYNCED_EVENT,
|
||||
NEW_DONOR_EVENT,
|
||||
NEW_NDB_EVENT,
|
||||
MAX_MONITOR_EVENT
|
||||
}monitor_event_t;
|
||||
void mon_append_node_names(MONITOR_SERVERS* start,char* str, int len);
|
||||
monitor_event_t mon_get_event_type(MONITOR_SERVERS* node);
|
||||
char* mon_get_event_name(MONITOR_SERVERS* node);
|
||||
void monitor_clear_pending_status(MONITOR_SERVERS *ptr, int bit);
|
||||
void monitor_set_pending_status(MONITOR_SERVERS *ptr, int bit);
|
||||
bool mon_status_changed(MONITOR_SERVERS* mon_srv);
|
||||
bool mon_print_fail_status(MONITOR_SERVERS* mon_srv);
|
||||
void monitor_launch_script(MONITOR* mon,MONITOR_SERVERS* ptr, char* script);
|
||||
int mon_parse_event_string(bool* events, size_t count,char* string);
|
||||
#endif
|
||||
@ -46,24 +46,13 @@
|
||||
* 10/11/14 Massimiliano Pinto Addition of setNetworkTimeout for connect, read, write
|
||||
* 18/11/14 Massimiliano Pinto One server only in configuration becomes master.
|
||||
* servers=server1 must be present in mysql_mon and in router sections as well.
|
||||
* 08/05/15 Markus Makela Added launchable scripts
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <monitor.h>
|
||||
|
||||
#include <mysqlmon.h>
|
||||
#include <thread.h>
|
||||
#include <mysql.h>
|
||||
#include <mysqld_error.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
#include <secrets.h>
|
||||
#include <dcb.h>
|
||||
#include <modinfo.h>
|
||||
#include <maxconfig.h>
|
||||
|
||||
/** Defined in log_manager.cc */
|
||||
extern int lm_enabled_logfiles_bitmask;
|
||||
@ -83,33 +72,19 @@ MODULE_INFO info = {
|
||||
|
||||
static void *startMonitor(void *,void*);
|
||||
static void stopMonitor(void *);
|
||||
static void registerServer(void *, SERVER *);
|
||||
static void unregisterServer(void *, SERVER *);
|
||||
static void defaultUser(void *, char *, char *);
|
||||
static void diagnostics(DCB *, void *);
|
||||
static void setInterval(void *, size_t);
|
||||
static void defaultId(void *, unsigned long);
|
||||
static void setNetworkTimeout(void *, int, int);
|
||||
static bool mon_status_changed(MONITOR_SERVERS* mon_srv);
|
||||
static bool mon_print_fail_status(MONITOR_SERVERS* mon_srv);
|
||||
static MONITOR_SERVERS *getServerByNodeId(MONITOR_SERVERS *, long);
|
||||
static MONITOR_SERVERS *getSlaveOfNodeId(MONITOR_SERVERS *, long);
|
||||
static MONITOR_SERVERS *get_replication_tree(MYSQL_MONITOR *, int);
|
||||
static MONITOR_SERVERS *get_replication_tree(MONITOR *, int);
|
||||
static void set_master_heartbeat(MYSQL_MONITOR *, MONITOR_SERVERS *);
|
||||
static void set_slave_heartbeat(MYSQL_MONITOR *, MONITOR_SERVERS *);
|
||||
static void set_slave_heartbeat(MONITOR *, MONITOR_SERVERS *);
|
||||
static int add_slave_to_master(long *, int, long);
|
||||
static void monitor_set_pending_status(MONITOR_SERVERS *, int);
|
||||
static void monitor_clear_pending_status(MONITOR_SERVERS *, int);
|
||||
|
||||
bool isMySQLEvent(monitor_event_t event);
|
||||
static MONITOR_OBJECT MyObject = {
|
||||
startMonitor,
|
||||
stopMonitor,
|
||||
registerServer,
|
||||
unregisterServer,
|
||||
defaultUser,
|
||||
diagnostics,
|
||||
setInterval,
|
||||
setNetworkTimeout
|
||||
diagnostics
|
||||
};
|
||||
|
||||
/**
|
||||
@ -156,48 +131,80 @@ GetModuleObject()
|
||||
* This function creates a thread to execute the actual monitoring.
|
||||
*
|
||||
* @param arg The current handle - NULL if first start
|
||||
* @param opt Configuration parameters
|
||||
* @return A handle to use when interacting with the monitor
|
||||
*/
|
||||
static void *
|
||||
startMonitor(void *arg, void* opt)
|
||||
{
|
||||
MYSQL_MONITOR *handle;
|
||||
CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt;
|
||||
if (arg)
|
||||
{
|
||||
handle = arg; /* Must be a restart */
|
||||
handle->shutdown = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((handle = (MYSQL_MONITOR *)malloc(sizeof(MYSQL_MONITOR))) == NULL)
|
||||
return NULL;
|
||||
handle->databases = NULL;
|
||||
handle->shutdown = 0;
|
||||
handle->defaultUser = NULL;
|
||||
handle->defaultPasswd = NULL;
|
||||
handle->id = config_get_gateway_id();
|
||||
handle->interval = MONITOR_INTERVAL;
|
||||
handle->replicationHeartbeat = 0;
|
||||
handle->detectStaleMaster = 0;
|
||||
handle->master = NULL;
|
||||
handle->connect_timeout=DEFAULT_CONNECT_TIMEOUT;
|
||||
handle->read_timeout=DEFAULT_READ_TIMEOUT;
|
||||
handle->write_timeout=DEFAULT_WRITE_TIMEOUT;
|
||||
spinlock_init(&handle->lock);
|
||||
}
|
||||
MONITOR* monitor = (MONITOR*)arg;
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR*)monitor->handle;
|
||||
CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt;
|
||||
bool have_events = false;
|
||||
|
||||
while(params)
|
||||
if (handle)
|
||||
{
|
||||
handle->shutdown = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((handle = (MYSQL_MONITOR *)malloc(sizeof(MYSQL_MONITOR))) == NULL)
|
||||
return NULL;
|
||||
handle->shutdown = 0;
|
||||
handle->id = config_get_gateway_id();
|
||||
handle->replicationHeartbeat = 0;
|
||||
handle->detectStaleMaster = 0;
|
||||
handle->master = NULL;
|
||||
handle->script = NULL;
|
||||
memset(handle->events,false,sizeof(handle->events));
|
||||
spinlock_init(&handle->lock);
|
||||
}
|
||||
|
||||
while(params)
|
||||
{
|
||||
if(!strcmp(params->name,"detect_stale_master"))
|
||||
handle->detectStaleMaster = config_truth_value(params->value);
|
||||
else if(!strcmp(params->name,"detect_replication_lag"))
|
||||
handle->replicationHeartbeat = config_truth_value(params->value);
|
||||
else if(!strcmp(params->name,"script"))
|
||||
{
|
||||
if(!strcmp(params->name,"detect_stale_master"))
|
||||
handle->detectStaleMaster = config_truth_value(params->value);
|
||||
else if(!strcmp(params->name,"detect_replication_lag"))
|
||||
handle->replicationHeartbeat = config_truth_value(params->value);
|
||||
params = params->next;
|
||||
if(handle->script)
|
||||
free(handle->script);
|
||||
if(access(params->value,X_OK) == 0)
|
||||
{
|
||||
handle->script = strdup(params->value);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(access(params->value,F_OK) == 0)
|
||||
{
|
||||
skygw_log_write(LE,
|
||||
"Error: The file cannot be executed: %s",
|
||||
params->value);
|
||||
}
|
||||
else
|
||||
{
|
||||
skygw_log_write(LE,
|
||||
"Error: The file cannot be found: %s",
|
||||
params->value);
|
||||
}
|
||||
handle->script = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
handle->tid = (THREAD)thread_start(monitorMain, handle);
|
||||
return handle;
|
||||
else if(!strcmp(params->name,"events"))
|
||||
{
|
||||
mon_parse_event_string(handle->events,sizeof(handle->events),params->value);
|
||||
have_events = true;
|
||||
}
|
||||
params = params->next;
|
||||
}
|
||||
/** If no specific events are given, enable them all */
|
||||
if(!have_events)
|
||||
{
|
||||
memset(handle->events,true,sizeof(handle->events));
|
||||
}
|
||||
handle->tid = (THREAD)thread_start(monitorMain, monitor);
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -208,107 +215,11 @@ CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt;
|
||||
static void
|
||||
stopMonitor(void *arg)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR* mon = (MONITOR*)arg;
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)mon->handle;
|
||||
|
||||
handle->shutdown = 1;
|
||||
thread_wait((void *)handle->tid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a server that must be added to the monitored servers for
|
||||
* a monitoring module.
|
||||
*
|
||||
* @param arg A handle on the running monitor module
|
||||
* @param server The server to add
|
||||
*/
|
||||
static void
|
||||
registerServer(void *arg, SERVER *server)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR_SERVERS *ptr, *db;
|
||||
|
||||
if ((db = (MONITOR_SERVERS *)malloc(sizeof(MONITOR_SERVERS))) == NULL)
|
||||
return;
|
||||
db->server = server;
|
||||
db->con = NULL;
|
||||
db->next = NULL;
|
||||
db->mon_err_count = 0;
|
||||
db->mon_prev_status = 0;
|
||||
/* pending status is updated by get_replication_tree */
|
||||
db->pending_status = 0;
|
||||
|
||||
spinlock_acquire(&handle->lock);
|
||||
|
||||
if (handle->databases == NULL)
|
||||
handle->databases = db;
|
||||
else
|
||||
{
|
||||
ptr = handle->databases;
|
||||
while (ptr->next != NULL)
|
||||
ptr = ptr->next;
|
||||
ptr->next = db;
|
||||
}
|
||||
spinlock_release(&handle->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a server from those being monitored by a monitoring module
|
||||
*
|
||||
* @param arg A handle on the running monitor module
|
||||
* @param server The server to remove
|
||||
*/
|
||||
static void
|
||||
unregisterServer(void *arg, SERVER *server)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR_SERVERS *ptr, *lptr;
|
||||
|
||||
spinlock_acquire(&handle->lock);
|
||||
if (handle->databases == NULL)
|
||||
{
|
||||
spinlock_release(&handle->lock);
|
||||
return;
|
||||
}
|
||||
if (handle->databases->server == server)
|
||||
{
|
||||
ptr = handle->databases;
|
||||
handle->databases = handle->databases->next;
|
||||
free(ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = handle->databases;
|
||||
while (ptr->next != NULL && ptr->next->server != server)
|
||||
ptr = ptr->next;
|
||||
if (ptr->next)
|
||||
{
|
||||
lptr = ptr->next;
|
||||
ptr->next = ptr->next->next;
|
||||
free(lptr);
|
||||
}
|
||||
}
|
||||
spinlock_release(&handle->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default username and password to use to monitor if the server does not
|
||||
* override this.
|
||||
*
|
||||
* @param arg The handle allocated by startMonitor
|
||||
* @param uname The default user name
|
||||
* @param passwd The default password
|
||||
*/
|
||||
static void
|
||||
defaultUser(void *arg, char *uname, char *passwd)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
|
||||
if (handle->defaultUser)
|
||||
free(handle->defaultUser);
|
||||
if (handle->defaultPasswd)
|
||||
free(handle->defaultPasswd);
|
||||
handle->defaultUser = strdup(uname);
|
||||
handle->defaultPasswd = strdup(passwd);
|
||||
handle->shutdown = 1;
|
||||
thread_wait((void *)handle->tid);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -319,45 +230,46 @@ MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
*/
|
||||
static void diagnostics(DCB *dcb, void *arg)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR_SERVERS *db;
|
||||
char *sep;
|
||||
MONITOR* mon = (MONITOR*)arg;
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)mon->handle;
|
||||
MONITOR_SERVERS *db;
|
||||
char *sep;
|
||||
|
||||
switch (handle->status)
|
||||
{
|
||||
case MONITOR_RUNNING:
|
||||
dcb_printf(dcb, "\tMonitor running\n");
|
||||
break;
|
||||
case MONITOR_STOPPING:
|
||||
dcb_printf(dcb, "\tMonitor stopping\n");
|
||||
break;
|
||||
case MONITOR_STOPPED:
|
||||
dcb_printf(dcb, "\tMonitor stopped\n");
|
||||
break;
|
||||
}
|
||||
switch (handle->status)
|
||||
{
|
||||
case MONITOR_RUNNING:
|
||||
dcb_printf(dcb, "\tMonitor running\n");
|
||||
break;
|
||||
case MONITOR_STOPPING:
|
||||
dcb_printf(dcb, "\tMonitor stopping\n");
|
||||
break;
|
||||
case MONITOR_STOPPED:
|
||||
dcb_printf(dcb, "\tMonitor stopped\n");
|
||||
break;
|
||||
}
|
||||
|
||||
dcb_printf(dcb,"\tSampling interval:\t%lu milliseconds\n", handle->interval);
|
||||
dcb_printf(dcb,"\tMaxScale MonitorId:\t%lu\n", handle->id);
|
||||
dcb_printf(dcb,"\tReplication lag:\t%s\n", (handle->replicationHeartbeat == 1) ? "enabled" : "disabled");
|
||||
dcb_printf(dcb,"\tDetect Stale Master:\t%s\n", (handle->detectStaleMaster == 1) ? "enabled" : "disabled");
|
||||
dcb_printf(dcb,"\tConnect Timeout:\t%i seconds\n", handle->connect_timeout);
|
||||
dcb_printf(dcb,"\tRead Timeout:\t\t%i seconds\n", handle->read_timeout);
|
||||
dcb_printf(dcb,"\tWrite Timeout:\t\t%i seconds\n", handle->write_timeout);
|
||||
dcb_printf(dcb, "\tMonitored servers: ");
|
||||
|
||||
db = handle->databases;
|
||||
sep = "";
|
||||
while (db)
|
||||
{
|
||||
dcb_printf(dcb,
|
||||
"%s%s:%d",
|
||||
sep,
|
||||
db->server->name,
|
||||
db->server->port);
|
||||
sep = ", ";
|
||||
db = db->next;
|
||||
}
|
||||
dcb_printf(dcb, "\n");
|
||||
dcb_printf(dcb,"\tSampling interval:\t%lu milliseconds\n", mon->interval);
|
||||
dcb_printf(dcb,"\tMaxScale MonitorId:\t%lu\n", handle->id);
|
||||
dcb_printf(dcb,"\tReplication lag:\t%s\n", (handle->replicationHeartbeat == 1) ? "enabled" : "disabled");
|
||||
dcb_printf(dcb,"\tDetect Stale Master:\t%s\n", (handle->detectStaleMaster == 1) ? "enabled" : "disabled");
|
||||
dcb_printf(dcb,"\tConnect Timeout:\t%i seconds\n", mon->connect_timeout);
|
||||
dcb_printf(dcb,"\tRead Timeout:\t\t%i seconds\n", mon->read_timeout);
|
||||
dcb_printf(dcb,"\tWrite Timeout:\t\t%i seconds\n", mon->write_timeout);
|
||||
dcb_printf(dcb, "\tMonitored servers: ");
|
||||
|
||||
db = mon->databases;
|
||||
sep = "";
|
||||
while (db)
|
||||
{
|
||||
dcb_printf(dcb,
|
||||
"%s%s:%d",
|
||||
sep,
|
||||
db->server->name,
|
||||
db->server->port);
|
||||
sep = ", ";
|
||||
db = db->next;
|
||||
}
|
||||
dcb_printf(dcb, "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -367,14 +279,14 @@ char *sep;
|
||||
* @param database The database to probe
|
||||
*/
|
||||
static void
|
||||
monitorDatabase(MYSQL_MONITOR *handle, MONITOR_SERVERS *database)
|
||||
monitorDatabase(MONITOR *mon, MONITOR_SERVERS *database)
|
||||
{
|
||||
MYSQL_MONITOR* handle = mon->handle;
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES *result;
|
||||
int num_fields;
|
||||
int isslave = 0;
|
||||
char *uname = handle->defaultUser;
|
||||
char *passwd = handle->defaultPasswd;
|
||||
char *uname = mon->user;
|
||||
char *passwd = mon->password;
|
||||
unsigned long int server_version = 0;
|
||||
char *server_string;
|
||||
|
||||
@ -397,17 +309,15 @@ char *server_string;
|
||||
if (database->con == NULL || mysql_ping(database->con) != 0)
|
||||
{
|
||||
char *dpwd = decryptPassword(passwd);
|
||||
int rc;
|
||||
int connect_timeout = handle->connect_timeout;
|
||||
int read_timeout = handle->read_timeout;
|
||||
int write_timeout = handle->write_timeout;
|
||||
if(database->con)
|
||||
mysql_close(database->con);
|
||||
int connect_timeout = mon->connect_timeout;
|
||||
int read_timeout = mon->read_timeout;
|
||||
int write_timeout = mon->write_timeout;
|
||||
|
||||
database->con = mysql_init(NULL);
|
||||
|
||||
rc = mysql_options(database->con, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&connect_timeout);
|
||||
rc = mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout);
|
||||
rc = mysql_options(database->con, MYSQL_OPT_WRITE_TIMEOUT, (void *)&write_timeout);
|
||||
mysql_options(database->con, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&connect_timeout);
|
||||
mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout);
|
||||
mysql_options(database->con, MYSQL_OPT_WRITE_TIMEOUT, (void *)&write_timeout);
|
||||
|
||||
if (mysql_real_connect(database->con,
|
||||
database->server->name,
|
||||
@ -486,7 +396,6 @@ char *server_string;
|
||||
&& (result = mysql_store_result(database->con)) != NULL)
|
||||
{
|
||||
long server_id = -1;
|
||||
num_fields = mysql_num_fields(result);
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
server_id = strtol(row[0], NULL, 10);
|
||||
@ -512,7 +421,6 @@ char *server_string;
|
||||
{
|
||||
int i = 0;
|
||||
long master_id = -1;
|
||||
num_fields = mysql_num_fields(result);
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
/* get Slave_IO_Running and Slave_SQL_Running values*/
|
||||
@ -551,7 +459,6 @@ char *server_string;
|
||||
&& (result = mysql_store_result(database->con)) != NULL)
|
||||
{
|
||||
long master_id = -1;
|
||||
num_fields = mysql_num_fields(result);
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
/* get Slave_IO_Running and Slave_SQL_Running values*/
|
||||
@ -608,15 +515,22 @@ char *server_string;
|
||||
static void
|
||||
monitorMain(void *arg)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR* mon = (MONITOR*) arg;
|
||||
MYSQL_MONITOR *handle;
|
||||
MONITOR_SERVERS *ptr;
|
||||
int replication_heartbeat = handle->replicationHeartbeat;
|
||||
int detect_stale_master = handle->detectStaleMaster;
|
||||
int replication_heartbeat;
|
||||
int detect_stale_master;
|
||||
int num_servers=0;
|
||||
MONITOR_SERVERS *root_master = NULL;
|
||||
size_t nrounds = 0;
|
||||
int log_no_master = 1;
|
||||
|
||||
spinlock_acquire(&mon->lock);
|
||||
handle = (MYSQL_MONITOR *)mon->handle;
|
||||
spinlock_release(&mon->lock);
|
||||
replication_heartbeat = handle->replicationHeartbeat;
|
||||
detect_stale_master = handle->detectStaleMaster;
|
||||
|
||||
if (mysql_thread_init())
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
@ -645,7 +559,7 @@ int log_no_master = 1;
|
||||
* round.
|
||||
*/
|
||||
if (nrounds != 0 &&
|
||||
((nrounds*MON_BASE_INTERVAL_MS)%handle->interval) >=
|
||||
((nrounds*MON_BASE_INTERVAL_MS)%mon->interval) >=
|
||||
MON_BASE_INTERVAL_MS)
|
||||
{
|
||||
nrounds += 1;
|
||||
@ -656,15 +570,17 @@ int log_no_master = 1;
|
||||
num_servers = 0;
|
||||
|
||||
/* start from the first server in the list */
|
||||
ptr = handle->databases;
|
||||
ptr = mon->databases;
|
||||
|
||||
while (ptr)
|
||||
{
|
||||
ptr->mon_prev_status = ptr->server->status;
|
||||
|
||||
/* copy server status into monitor pending_status */
|
||||
ptr->pending_status = ptr->server->status;
|
||||
|
||||
/* monitor current node */
|
||||
monitorDatabase(handle, ptr);
|
||||
monitorDatabase(mon, ptr);
|
||||
|
||||
/* reset the slave list of current node */
|
||||
if (ptr->server->slaves) {
|
||||
@ -697,7 +613,12 @@ int log_no_master = 1;
|
||||
!(SERVER_IS_IN_CLUSTER(ptr->server)))
|
||||
{
|
||||
dcb_call_foreach(ptr->server,DCB_REASON_NOT_RESPONDING);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
if (mon_status_changed(ptr))
|
||||
@ -733,7 +654,7 @@ int log_no_master = 1;
|
||||
ptr = ptr->next;
|
||||
}
|
||||
|
||||
ptr = handle->databases;
|
||||
ptr = mon->databases;
|
||||
/* if only one server is configured, that's is Master */
|
||||
if (num_servers == 1) {
|
||||
if (SERVER_IS_RUNNING(ptr->server)) {
|
||||
@ -750,12 +671,12 @@ int log_no_master = 1;
|
||||
}
|
||||
} else {
|
||||
/* Compute the replication tree */
|
||||
root_master = get_replication_tree(handle, num_servers);
|
||||
root_master = get_replication_tree(mon, num_servers);
|
||||
}
|
||||
|
||||
/* Update server status from monitor pending status on that server*/
|
||||
|
||||
ptr = handle->databases;
|
||||
ptr = mon->databases;
|
||||
while (ptr)
|
||||
{
|
||||
if (! SERVER_IN_MAINT(ptr->server)) {
|
||||
@ -792,7 +713,30 @@ int log_no_master = 1;
|
||||
ptr = ptr->next;
|
||||
}
|
||||
|
||||
/* log master detection failure od first master becomes available after failure */
|
||||
ptr = mon->databases;
|
||||
monitor_event_t evtype;
|
||||
while(ptr)
|
||||
{
|
||||
/** Execute monitor script if a server state has changed */
|
||||
if(mon_status_changed(ptr))
|
||||
{
|
||||
evtype = mon_get_event_type(ptr);
|
||||
if(isMySQLEvent(evtype))
|
||||
{
|
||||
skygw_log_write(LOGFILE_TRACE,"Server changed state: %s[%s:%u]: %s",
|
||||
ptr->server->unique_name,
|
||||
ptr->server->name,ptr->server->port,
|
||||
mon_get_event_name(ptr));
|
||||
if(handle->script && handle->events[evtype])
|
||||
{
|
||||
monitor_launch_script(mon,ptr,handle->script);
|
||||
}
|
||||
}
|
||||
}
|
||||
ptr = ptr->next;
|
||||
}
|
||||
|
||||
/* log master detection failure of first master becomes available after failure */
|
||||
if (root_master &&
|
||||
mon_status_changed(root_master) &&
|
||||
!(root_master->server->status & SERVER_STALE_STATUS))
|
||||
@ -832,7 +776,7 @@ int log_no_master = 1;
|
||||
SERVER_IS_RELAY_SERVER(root_master->server)))
|
||||
{
|
||||
set_master_heartbeat(handle, root_master);
|
||||
ptr = handle->databases;
|
||||
ptr = mon->databases;
|
||||
|
||||
while (ptr) {
|
||||
if( (! SERVER_IN_MAINT(ptr->server)) && SERVER_IS_RUNNING(ptr->server))
|
||||
@ -841,7 +785,7 @@ int log_no_master = 1;
|
||||
(SERVER_IS_SLAVE(ptr->server) ||
|
||||
SERVER_IS_RELAY_SERVER(ptr->server)))
|
||||
{
|
||||
set_slave_heartbeat(handle, ptr);
|
||||
set_slave_heartbeat(mon, ptr);
|
||||
}
|
||||
}
|
||||
ptr = ptr->next;
|
||||
@ -863,19 +807,6 @@ MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
memcpy(&handle->id, &id, sizeof(unsigned long));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the monitor sampling interval.
|
||||
*
|
||||
* @param arg The handle allocated by startMonitor
|
||||
* @param interval The interval to set in monitor struct, in milliseconds
|
||||
*/
|
||||
static void
|
||||
setInterval(void *arg, size_t interval)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
memcpy(&handle->interval, &interval, sizeof(unsigned long));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/Disable the MySQL Replication hearbeat, detecting slave lag behind master.
|
||||
*
|
||||
@ -904,54 +835,6 @@ MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
memcpy(&handle->detectStaleMaster, &enable, sizeof(int));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current monitored server status has changed
|
||||
*
|
||||
* @param mon_srv The monitored server
|
||||
* @return true if status has changed or false
|
||||
*/
|
||||
static bool mon_status_changed(
|
||||
MONITOR_SERVERS* mon_srv)
|
||||
{
|
||||
bool succp;
|
||||
|
||||
if (mon_srv->mon_prev_status != mon_srv->server->status)
|
||||
{
|
||||
succp = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
succp = false;
|
||||
}
|
||||
return succp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current monitored server has a loggable failure status
|
||||
*
|
||||
* @param mon_srv The monitored server
|
||||
* @return true if failed status can be logged or false
|
||||
*/
|
||||
static bool mon_print_fail_status(
|
||||
MONITOR_SERVERS* mon_srv)
|
||||
{
|
||||
bool succp;
|
||||
int errcount = mon_srv->mon_err_count;
|
||||
uint8_t modval;
|
||||
|
||||
modval = 1<<(MIN(errcount/10, 7));
|
||||
|
||||
if (SERVER_IS_DOWN(mon_srv->server) && errcount == 0)
|
||||
{
|
||||
succp = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
succp = false;
|
||||
}
|
||||
return succp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch a MySQL node by node_id
|
||||
*
|
||||
@ -1113,13 +996,13 @@ static void set_master_heartbeat(MYSQL_MONITOR *handle, MONITOR_SERVERS *databas
|
||||
* @param handle The monitor handle
|
||||
* @param database The number database server
|
||||
*/
|
||||
static void set_slave_heartbeat(MYSQL_MONITOR *handle, MONITOR_SERVERS *database) {
|
||||
static void set_slave_heartbeat(MONITOR* mon, MONITOR_SERVERS *database) {
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR*)mon->handle;
|
||||
unsigned long id = handle->id;
|
||||
time_t heartbeat;
|
||||
char select_heartbeat_query[256] = "";
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES *result;
|
||||
int num_fields;
|
||||
|
||||
if (handle->master == NULL) {
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
@ -1139,7 +1022,6 @@ static void set_slave_heartbeat(MYSQL_MONITOR *handle, MONITOR_SERVERS *database
|
||||
if (handle->master !=NULL && (mysql_query(database->con, select_heartbeat_query) == 0
|
||||
&& (result = mysql_store_result(database->con)) != NULL)) {
|
||||
int rows_found = 0;
|
||||
num_fields = mysql_num_fields(result);
|
||||
|
||||
while ((row = mysql_fetch_row(result))) {
|
||||
int rlag = -1;
|
||||
@ -1164,7 +1046,7 @@ static void set_slave_heartbeat(MYSQL_MONITOR *handle, MONITOR_SERVERS *database
|
||||
|
||||
if (rlag >= 0) {
|
||||
/* store rlag only if greater than monitor sampling interval */
|
||||
database->server->rlag = (rlag > (handle->interval / 1000)) ? rlag : 0;
|
||||
database->server->rlag = (rlag > (mon->interval / 1000)) ? rlag : 0;
|
||||
} else {
|
||||
database->server->rlag = -1;
|
||||
}
|
||||
@ -1218,7 +1100,8 @@ static void set_slave_heartbeat(MYSQL_MONITOR *handle, MONITOR_SERVERS *database
|
||||
* @return The server at root level with SERVER_MASTER bit
|
||||
*/
|
||||
|
||||
static MONITOR_SERVERS *get_replication_tree(MYSQL_MONITOR *handle, int num_servers) {
|
||||
static MONITOR_SERVERS *get_replication_tree(MONITOR *mon, int num_servers) {
|
||||
MYSQL_MONITOR* handle = (MYSQL_MONITOR*)mon->handle;
|
||||
MONITOR_SERVERS *ptr;
|
||||
MONITOR_SERVERS *backend;
|
||||
SERVER *current;
|
||||
@ -1226,7 +1109,7 @@ static MONITOR_SERVERS *get_replication_tree(MYSQL_MONITOR *handle, int num_serv
|
||||
long node_id;
|
||||
int root_level;
|
||||
|
||||
ptr = handle->databases;
|
||||
ptr = mon->databases;
|
||||
root_level = num_servers;
|
||||
|
||||
while (ptr)
|
||||
@ -1245,7 +1128,7 @@ static MONITOR_SERVERS *get_replication_tree(MYSQL_MONITOR *handle, int num_serv
|
||||
node_id = current->master_id;
|
||||
if (node_id < 1) {
|
||||
MONITOR_SERVERS *find_slave;
|
||||
find_slave = getSlaveOfNodeId(handle->databases, current->node_id);
|
||||
find_slave = getSlaveOfNodeId(mon->databases, current->node_id);
|
||||
|
||||
if (find_slave == NULL) {
|
||||
current->depth = -1;
|
||||
@ -1265,7 +1148,7 @@ static MONITOR_SERVERS *get_replication_tree(MYSQL_MONITOR *handle, int num_serv
|
||||
root_level = current->depth;
|
||||
handle->master = ptr;
|
||||
}
|
||||
backend = getServerByNodeId(handle->databases, node_id);
|
||||
backend = getServerByNodeId(mon->databases, node_id);
|
||||
|
||||
if (backend) {
|
||||
node_id = backend->server->master_id;
|
||||
@ -1281,7 +1164,7 @@ static MONITOR_SERVERS *get_replication_tree(MYSQL_MONITOR *handle, int num_serv
|
||||
MONITOR_SERVERS *master;
|
||||
current->depth = depth;
|
||||
|
||||
master = getServerByNodeId(handle->databases, current->master_id);
|
||||
master = getServerByNodeId(mon->databases, current->master_id);
|
||||
if (master && master->server && master->server->node_id > 0) {
|
||||
add_slave_to_master(master->server->slaves, MONITOR_MAX_NUM_SLAVES, current->node_id);
|
||||
master->server->depth = current->depth -1;
|
||||
@ -1340,88 +1223,31 @@ static int add_slave_to_master(long *slaves_list, int list_size, long node_id) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static monitor_event_t mysql_events[] = {
|
||||
MASTER_DOWN_EVENT,
|
||||
MASTER_UP_EVENT,
|
||||
SLAVE_DOWN_EVENT,
|
||||
SLAVE_UP_EVENT,
|
||||
SERVER_DOWN_EVENT,
|
||||
SERVER_UP_EVENT,
|
||||
LOST_MASTER_EVENT,
|
||||
LOST_SLAVE_EVENT,
|
||||
NEW_MASTER_EVENT,
|
||||
NEW_SLAVE_EVENT,
|
||||
MAX_MONITOR_EVENT
|
||||
};
|
||||
/**
|
||||
* Set a pending status bit in the monior server
|
||||
*
|
||||
* @param server The server to update
|
||||
* @param bit The bit to clear for the server
|
||||
*/
|
||||
static void
|
||||
monitor_set_pending_status(MONITOR_SERVERS *ptr, int bit)
|
||||
* Check if the MySQL monitor is monitoring this event type.
|
||||
* @param event Event to check
|
||||
* @return True if the event is monitored, false if it is not
|
||||
* */
|
||||
bool isMySQLEvent(monitor_event_t event)
|
||||
{
|
||||
ptr->pending_status |= bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear a pending status bit in the monior server
|
||||
*
|
||||
* @param server The server to update
|
||||
* @param bit The bit to clear for the server
|
||||
*/
|
||||
static void
|
||||
monitor_clear_pending_status(MONITOR_SERVERS *ptr, int bit)
|
||||
{
|
||||
ptr->pending_status &= ~bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default id to use in the monitor.
|
||||
*
|
||||
* @param arg The handle allocated by startMonitor
|
||||
* @param type The connect timeout type
|
||||
* @param value The timeout value to set
|
||||
*/
|
||||
static void
|
||||
setNetworkTimeout(void *arg, int type, int value)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
int max_timeout = (int)(handle->interval/1000);
|
||||
int new_timeout = max_timeout -1;
|
||||
|
||||
if (new_timeout <= 0)
|
||||
new_timeout = DEFAULT_CONNECT_TIMEOUT;
|
||||
|
||||
switch(type) {
|
||||
case MONITOR_CONNECT_TIMEOUT:
|
||||
if (value < max_timeout) {
|
||||
memcpy(&handle->connect_timeout, &value, sizeof(int));
|
||||
} else {
|
||||
memcpy(&handle->connect_timeout, &new_timeout, sizeof(int));
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"warning : Monitor Connect Timeout %i is greater than monitor interval ~%i seconds"
|
||||
", lowering to %i seconds", value, max_timeout, new_timeout)));
|
||||
}
|
||||
break;
|
||||
|
||||
case MONITOR_READ_TIMEOUT:
|
||||
if (value < max_timeout) {
|
||||
memcpy(&handle->read_timeout, &value, sizeof(int));
|
||||
} else {
|
||||
memcpy(&handle->read_timeout, &new_timeout, sizeof(int));
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"warning : Monitor Read Timeout %i is greater than monitor interval ~%i seconds"
|
||||
", lowering to %i seconds", value, max_timeout, new_timeout)));
|
||||
}
|
||||
break;
|
||||
|
||||
case MONITOR_WRITE_TIMEOUT:
|
||||
if (value < max_timeout) {
|
||||
memcpy(&handle->write_timeout, &value, sizeof(int));
|
||||
} else {
|
||||
memcpy(&handle->write_timeout, &new_timeout, sizeof(int));
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"warning : Monitor Write Timeout %i is greater than monitor interval ~%i seconds"
|
||||
", lowering to %i seconds", value, max_timeout, new_timeout)));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Monitor setNetworkTimeout received an unsupported action type %i", type)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int i;
|
||||
for(i = 0;mysql_events[i] != MAX_MONITOR_EVENT;i++)
|
||||
{
|
||||
if(event == mysql_events[i])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -17,10 +17,22 @@
|
||||
*
|
||||
* Copyright MariaDB Corporation Ab 2013-2014
|
||||
*/
|
||||
#include <server.h>
|
||||
#include <spinlock.h>
|
||||
#include <mysql.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <monitor.h>
|
||||
#include <spinlock.h>
|
||||
#include <thread.h>
|
||||
#include <mysql.h>
|
||||
#include <mysqld_error.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
#include <secrets.h>
|
||||
#include <dcb.h>
|
||||
#include <modinfo.h>
|
||||
#include <maxconfig.h>
|
||||
#include <monitor_common.h>
|
||||
#include <externcmd.h>
|
||||
/**
|
||||
* @file mysqlmon.h - The MySQL monitor functionality within the gateway
|
||||
*
|
||||
@ -37,24 +49,10 @@
|
||||
* 07/11/14 Massimiliano Pinto Addition of NetworkTimeout: connect, read, write
|
||||
* 20/04/15 Guillaume Lefranc Addition of availableWhenDonor
|
||||
* 22/04/15 Martin Brampton Addition of disableMasterRoleSetting
|
||||
*
|
||||
* 07/05/15 Markus Makela Addition of command execution on Master server failure
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
/**
|
||||
* The linked list of servers that are being monitored by the MySQL
|
||||
* Monitor module.
|
||||
*/
|
||||
typedef struct monitor_servers {
|
||||
SERVER *server; /**< The server being monitored */
|
||||
MYSQL *con; /**< The MySQL connection */
|
||||
int mon_err_count;
|
||||
unsigned int mon_prev_status;
|
||||
unsigned int pending_status; /**< Pending Status flag bitmap */
|
||||
struct monitor_servers
|
||||
*next; /**< The next server in the list */
|
||||
} MONITOR_SERVERS;
|
||||
|
||||
/**
|
||||
* The handle for an instance of a MySQL Monitor module
|
||||
*/
|
||||
@ -63,9 +61,6 @@ typedef struct {
|
||||
pthread_t tid; /**< id of monitor thread */
|
||||
int shutdown; /**< Flag to shutdown the monitor thread */
|
||||
int status; /**< Monitor status */
|
||||
char *defaultUser; /**< Default username for monitoring */
|
||||
char *defaultPasswd; /**< Default password for monitoring */
|
||||
unsigned long interval; /**< Monitor sampling interval */
|
||||
unsigned long id; /**< Monitor ID */
|
||||
int replicationHeartbeat; /**< Monitor flag for MySQL replication heartbeat */
|
||||
int detectStaleMaster; /**< Monitor flag for MySQL replication Stale Master detection */
|
||||
@ -73,22 +68,8 @@ typedef struct {
|
||||
int availableWhenDonor; /**< Monitor flag for Galera Cluster Donor availability */
|
||||
int disableMasterRoleSetting; /**< Monitor flag to disable setting master role */
|
||||
MONITOR_SERVERS *master; /**< Master server for MySQL Master/Slave replication */
|
||||
MONITOR_SERVERS *databases; /**< Linked list of servers to monitor */
|
||||
int connect_timeout; /**< Connect timeout in seconds for mysql_real_connect */
|
||||
int read_timeout; /**< Timeout in seconds to read from the server.
|
||||
* There are retries and the total effective timeout value is three times the option value.
|
||||
*/
|
||||
int write_timeout; /**< Timeout in seconds for each attempt to write to the server.
|
||||
* There are retries and the total effective timeout value is two times the option value.
|
||||
*/
|
||||
char* script; /*< Script to call when state changes occur on servers */
|
||||
bool events[MAX_MONITOR_EVENT]; /*< enabled events */
|
||||
} MYSQL_MONITOR;
|
||||
|
||||
#define MONITOR_RUNNING 1
|
||||
#define MONITOR_STOPPING 2
|
||||
#define MONITOR_STOPPED 3
|
||||
|
||||
#define MONITOR_INTERVAL 10000 // in milliseconds
|
||||
#define MONITOR_DEFAULT_ID 1UL // unsigned long value
|
||||
#define MONITOR_MAX_NUM_SLAVES 20 //number of MySQL slave servers associated to a MySQL master server
|
||||
|
||||
#endif
|
||||
|
||||
@ -25,24 +25,14 @@
|
||||
* Date Who Description
|
||||
* 25/07/14 Massimiliano Pinto Initial implementation
|
||||
* 10/11/14 Massimiliano Pinto Added setNetworkTimeout for connect,read,write
|
||||
* 08/05/15 Markus Makela Addition of launchable scripts
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <monitor.h>
|
||||
|
||||
#include <mysqlmon.h>
|
||||
#include <thread.h>
|
||||
#include <mysql.h>
|
||||
#include <mysqld_error.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
#include <secrets.h>
|
||||
#include <dcb.h>
|
||||
#include <modinfo.h>
|
||||
#include <maxconfig.h>
|
||||
|
||||
/** Defined in log_manager.cc */
|
||||
extern int lm_enabled_logfiles_bitmask;
|
||||
extern size_t log_ses_count[];
|
||||
@ -50,7 +40,7 @@ extern __thread log_info_t tls_log_info;
|
||||
|
||||
static void monitorMain(void *);
|
||||
|
||||
static char *version_str = "V1.1.0";
|
||||
static char *version_str = "V2.1.0";
|
||||
|
||||
MODULE_INFO info = {
|
||||
MODULE_API_MONITOR,
|
||||
@ -61,22 +51,13 @@ MODULE_INFO info = {
|
||||
|
||||
static void *startMonitor(void *,void*);
|
||||
static void stopMonitor(void *);
|
||||
static void registerServer(void *, SERVER *);
|
||||
static void unregisterServer(void *, SERVER *);
|
||||
static void defaultUsers(void *, char *, char *);
|
||||
static void diagnostics(DCB *, void *);
|
||||
static void setInterval(void *, size_t);
|
||||
static void setNetworkTimeout(void *arg, int type, int value);
|
||||
bool isNdbEvent(monitor_event_t event);
|
||||
|
||||
static MONITOR_OBJECT MyObject = {
|
||||
startMonitor,
|
||||
stopMonitor,
|
||||
registerServer,
|
||||
unregisterServer,
|
||||
defaultUsers,
|
||||
diagnostics,
|
||||
setInterval,
|
||||
setNetworkTimeout
|
||||
stopMonitor,
|
||||
diagnostics
|
||||
};
|
||||
|
||||
/**
|
||||
@ -127,31 +108,65 @@ GetModuleObject()
|
||||
static void *
|
||||
startMonitor(void *arg,void* opt)
|
||||
{
|
||||
MYSQL_MONITOR *handle;
|
||||
CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt;
|
||||
if (arg != NULL)
|
||||
{
|
||||
handle = (MYSQL_MONITOR *)arg;
|
||||
handle->shutdown = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((handle = (MYSQL_MONITOR *)malloc(sizeof(MYSQL_MONITOR))) == NULL)
|
||||
return NULL;
|
||||
handle->databases = NULL;
|
||||
handle->shutdown = 0;
|
||||
handle->defaultUser = NULL;
|
||||
handle->defaultPasswd = NULL;
|
||||
handle->id = MONITOR_DEFAULT_ID;
|
||||
handle->interval = MONITOR_INTERVAL;
|
||||
handle->connect_timeout=DEFAULT_CONNECT_TIMEOUT;
|
||||
handle->read_timeout=DEFAULT_READ_TIMEOUT;
|
||||
handle->write_timeout=DEFAULT_WRITE_TIMEOUT;
|
||||
spinlock_init(&handle->lock);
|
||||
}
|
||||
MONITOR* mon = (MONITOR*)arg;
|
||||
MYSQL_MONITOR *handle = mon->handle;
|
||||
CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt;
|
||||
bool have_events = false;
|
||||
|
||||
handle->tid = (THREAD)thread_start(monitorMain, handle);
|
||||
return handle;
|
||||
if (handle != NULL)
|
||||
{
|
||||
handle->shutdown = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((handle = (MYSQL_MONITOR *)malloc(sizeof(MYSQL_MONITOR))) == NULL)
|
||||
return NULL;
|
||||
handle->shutdown = 0;
|
||||
handle->id = MONITOR_DEFAULT_ID;
|
||||
memset(handle->events,false,sizeof(handle->events));
|
||||
spinlock_init(&handle->lock);
|
||||
}
|
||||
while(params)
|
||||
{
|
||||
if(!strcmp(params->name,"script"))
|
||||
{
|
||||
if(handle->script)
|
||||
free(handle->script);
|
||||
if(access(params->value,X_OK) == 0)
|
||||
{
|
||||
handle->script = strdup(params->value);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(access(params->value,F_OK) == 0)
|
||||
{
|
||||
skygw_log_write(LE,
|
||||
"Error: The file cannot be executed: %s",
|
||||
params->value);
|
||||
}
|
||||
else
|
||||
{
|
||||
skygw_log_write(LE,
|
||||
"Error: The file cannot be found: %s",
|
||||
params->value);
|
||||
}
|
||||
handle->script = NULL;
|
||||
}
|
||||
}
|
||||
else if(!strcmp(params->name,"events"))
|
||||
{
|
||||
mon_parse_event_string(&handle->events,sizeof(handle->events),params->value);
|
||||
have_events = true;
|
||||
}
|
||||
params = params->next;
|
||||
}
|
||||
/** If no specific events are given, enable them all */
|
||||
if(!have_events)
|
||||
{
|
||||
memset(handle->events,true,sizeof(handle->events));
|
||||
}
|
||||
handle->tid = (THREAD)thread_start(monitorMain, mon);
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -162,82 +177,13 @@ CONFIG_PARAMETER* params = (CONFIG_PARAMETER*)opt;
|
||||
static void
|
||||
stopMonitor(void *arg)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR* mon = (MONITOR*)arg;
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)mon->handle;
|
||||
|
||||
handle->shutdown = 1;
|
||||
thread_wait((void *)handle->tid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a server that must be added to the monitored servers for
|
||||
* a monitoring module.
|
||||
*
|
||||
* @param arg A handle on the running monitor module
|
||||
* @param server The server to add
|
||||
*/
|
||||
static void
|
||||
registerServer(void *arg, SERVER *server)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR_SERVERS *ptr, *db;
|
||||
|
||||
if ((db = (MONITOR_SERVERS *)malloc(sizeof(MONITOR_SERVERS))) == NULL)
|
||||
return;
|
||||
db->server = server;
|
||||
db->con = NULL;
|
||||
db->next = NULL;
|
||||
spinlock_acquire(&handle->lock);
|
||||
if (handle->databases == NULL)
|
||||
handle->databases = db;
|
||||
else
|
||||
{
|
||||
ptr = handle->databases;
|
||||
while (ptr->next != NULL)
|
||||
ptr = ptr->next;
|
||||
ptr->next = db;
|
||||
}
|
||||
spinlock_release(&handle->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a server from those being monitored by a monitoring module
|
||||
*
|
||||
* @param arg A handle on the running monitor module
|
||||
* @param server The server to remove
|
||||
*/
|
||||
static void
|
||||
unregisterServer(void *arg, SERVER *server)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR_SERVERS *ptr, *lptr;
|
||||
|
||||
spinlock_acquire(&handle->lock);
|
||||
if (handle->databases == NULL)
|
||||
{
|
||||
spinlock_release(&handle->lock);
|
||||
return;
|
||||
}
|
||||
if (handle->databases->server == server)
|
||||
{
|
||||
ptr = handle->databases;
|
||||
handle->databases = handle->databases->next;
|
||||
free(ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = handle->databases;
|
||||
while (ptr->next != NULL && ptr->next->server != server)
|
||||
ptr = ptr->next;
|
||||
if (ptr->next)
|
||||
{
|
||||
lptr = ptr->next;
|
||||
ptr->next = ptr->next->next;
|
||||
free(lptr);
|
||||
}
|
||||
}
|
||||
spinlock_release(&handle->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Diagnostic interface
|
||||
*
|
||||
@ -247,7 +193,8 @@ MONITOR_SERVERS *ptr, *lptr;
|
||||
static void
|
||||
diagnostics(DCB *dcb, void *arg)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR* mon = arg;
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)mon->handle;
|
||||
MONITOR_SERVERS *db;
|
||||
char *sep;
|
||||
|
||||
@ -264,13 +211,13 @@ char *sep;
|
||||
break;
|
||||
}
|
||||
|
||||
dcb_printf(dcb,"\tSampling interval:\t%lu milliseconds\n", handle->interval);
|
||||
dcb_printf(dcb,"\tConnect Timeout:\t%i seconds\n", handle->connect_timeout);
|
||||
dcb_printf(dcb,"\tRead Timeout:\t\t%i seconds\n", handle->read_timeout);
|
||||
dcb_printf(dcb,"\tWrite Timeout:\t\t%i seconds\n", handle->write_timeout);
|
||||
dcb_printf(dcb,"\tSampling interval:\t%lu milliseconds\n", mon->interval);
|
||||
dcb_printf(dcb,"\tConnect Timeout:\t%i seconds\n", mon->connect_timeout);
|
||||
dcb_printf(dcb,"\tRead Timeout:\t\t%i seconds\n", mon->read_timeout);
|
||||
dcb_printf(dcb,"\tWrite Timeout:\t\t%i seconds\n", mon->write_timeout);
|
||||
dcb_printf(dcb, "\tMonitored servers: ");
|
||||
|
||||
db = handle->databases;
|
||||
db = mon->databases;
|
||||
sep = "";
|
||||
while (db)
|
||||
{
|
||||
@ -281,41 +228,19 @@ char *sep;
|
||||
dcb_printf(dcb, "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default username and password to use to monitor if the server does not
|
||||
* override this.
|
||||
*
|
||||
* @param arg The handle allocated by startMonitor
|
||||
* @param uname The default user name
|
||||
* @param passwd The default password
|
||||
*/
|
||||
static void
|
||||
defaultUsers(void *arg, char *uname, char *passwd)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
|
||||
if (handle->defaultUser)
|
||||
free(handle->defaultUser);
|
||||
if (handle->defaultPasswd)
|
||||
free(handle->defaultPasswd);
|
||||
handle->defaultUser = strdup(uname);
|
||||
handle->defaultPasswd = strdup(passwd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitor an individual server
|
||||
*
|
||||
* @param database The database to probe
|
||||
*/
|
||||
static void
|
||||
monitorDatabase(MONITOR_SERVERS *database, char *defaultUser, char *defaultPasswd, MYSQL_MONITOR *handle)
|
||||
monitorDatabase(MONITOR_SERVERS *database, char *defaultUser, char *defaultPasswd, MONITOR *mon)
|
||||
{
|
||||
MYSQL_MONITOR* handle = mon->handle;
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES *result;
|
||||
int num_fields;
|
||||
int isjoined = 0;
|
||||
char *uname = defaultUser, *passwd = defaultPasswd;
|
||||
unsigned long int server_version = 0;
|
||||
char *server_string;
|
||||
|
||||
if (database->server->monuser != NULL)
|
||||
@ -333,18 +258,17 @@ char *server_string;
|
||||
if (database->con == NULL || mysql_ping(database->con) != 0)
|
||||
{
|
||||
char *dpwd = decryptPassword(passwd);
|
||||
int rc;
|
||||
int connect_timeout = handle->connect_timeout;
|
||||
int read_timeout = handle->read_timeout;
|
||||
int write_timeout = handle->write_timeout;
|
||||
int connect_timeout = mon->connect_timeout;
|
||||
int read_timeout = mon->read_timeout;
|
||||
int write_timeout = mon->write_timeout;
|
||||
|
||||
if(database->con)
|
||||
mysql_close(database->con);
|
||||
database->con = mysql_init(NULL);
|
||||
|
||||
rc = mysql_options(database->con, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&connect_timeout);
|
||||
rc = mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout);
|
||||
rc = mysql_options(database->con, MYSQL_OPT_WRITE_TIMEOUT, (void *)&write_timeout);
|
||||
mysql_options(database->con, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&connect_timeout);
|
||||
mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout);
|
||||
mysql_options(database->con, MYSQL_OPT_WRITE_TIMEOUT, (void *)&write_timeout);
|
||||
|
||||
if (mysql_real_connect(database->con, database->server->name,
|
||||
uname, dpwd, NULL, database->server->port, NULL, 0) == NULL)
|
||||
@ -375,9 +299,6 @@ char *server_string;
|
||||
/* If we get this far then we have a working connection */
|
||||
server_set_status(database->server, SERVER_RUNNING);
|
||||
|
||||
/* get server version from current server */
|
||||
server_version = mysql_get_server_version(database->con);
|
||||
|
||||
/* get server version string */
|
||||
server_string = (char *)mysql_get_server_info(database->con);
|
||||
if (server_string) {
|
||||
@ -390,7 +311,6 @@ char *server_string;
|
||||
if (mysql_query(database->con, "SHOW STATUS LIKE 'Ndb_number_of_ready_data_nodes'") == 0
|
||||
&& (result = mysql_store_result(database->con)) != NULL)
|
||||
{
|
||||
num_fields = mysql_num_fields(result);
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
if (atoi(row[1]) > 0)
|
||||
@ -404,7 +324,6 @@ char *server_string;
|
||||
&& (result = mysql_store_result(database->con)) != NULL)
|
||||
{
|
||||
long cluster_node_id = -1;
|
||||
num_fields = mysql_num_fields(result);
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
cluster_node_id = strtol(row[1], NULL, 10);
|
||||
@ -435,11 +354,15 @@ char *server_string;
|
||||
static void
|
||||
monitorMain(void *arg)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR* mon = arg;
|
||||
MYSQL_MONITOR *handle;
|
||||
MONITOR_SERVERS *ptr;
|
||||
long master_id;
|
||||
size_t nrounds = 0;
|
||||
|
||||
spinlock_acquire(&mon->lock);
|
||||
handle = (MYSQL_MONITOR *)mon->handle;
|
||||
spinlock_release(&mon->lock);
|
||||
|
||||
if (mysql_thread_init())
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
@ -469,22 +392,21 @@ size_t nrounds = 0;
|
||||
* round.
|
||||
*/
|
||||
if (nrounds != 0 &&
|
||||
((nrounds*MON_BASE_INTERVAL_MS)%handle->interval) >=
|
||||
((nrounds*MON_BASE_INTERVAL_MS)%mon->interval) >=
|
||||
MON_BASE_INTERVAL_MS)
|
||||
{
|
||||
nrounds += 1;
|
||||
continue;
|
||||
}
|
||||
nrounds += 1;
|
||||
master_id = -1;
|
||||
ptr = handle->databases;
|
||||
ptr = mon->databases;
|
||||
|
||||
while (ptr)
|
||||
{
|
||||
unsigned int prev_status = ptr->server->status;
|
||||
monitorDatabase(ptr, handle->defaultUser, handle->defaultPasswd,handle);
|
||||
ptr->mon_prev_status = ptr->server->status;
|
||||
monitorDatabase(ptr, mon->user, mon->password,mon);
|
||||
|
||||
if (ptr->server->status != prev_status ||
|
||||
if (ptr->server->status != ptr->mon_prev_status ||
|
||||
SERVER_IS_DOWN(ptr->server))
|
||||
{
|
||||
LOGIF(LD, (skygw_log_write_flush(
|
||||
@ -497,80 +419,64 @@ size_t nrounds = 0;
|
||||
|
||||
ptr = ptr->next;
|
||||
}
|
||||
|
||||
ptr = mon->databases;
|
||||
monitor_event_t evtype;
|
||||
|
||||
while(ptr)
|
||||
{
|
||||
/** Execute monitor script if a server state has changed */
|
||||
if(mon_status_changed(ptr))
|
||||
{
|
||||
evtype = mon_get_event_type(ptr);
|
||||
if(isNdbEvent(evtype))
|
||||
{
|
||||
skygw_log_write(LOGFILE_TRACE,"Server changed state: %s[%s:%u]: %s",
|
||||
ptr->server->unique_name,
|
||||
ptr->server->name,ptr->server->port,
|
||||
mon_get_event_name(ptr));
|
||||
if(handle->script && handle->events[evtype])
|
||||
{
|
||||
monitor_launch_script(mon,ptr,handle->script);
|
||||
}
|
||||
}
|
||||
}
|
||||
ptr = ptr->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the monitor sampling interval.
|
||||
*
|
||||
* @param arg The handle allocated by startMonitor
|
||||
* @param interval The interval to set in monitor struct, in milliseconds
|
||||
*/
|
||||
static void
|
||||
setInterval(void *arg, size_t interval)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
memcpy(&handle->interval, &interval, sizeof(unsigned long));
|
||||
}
|
||||
|
||||
static monitor_event_t ndb_events[] = {
|
||||
MASTER_DOWN_EVENT,
|
||||
MASTER_UP_EVENT,
|
||||
SLAVE_DOWN_EVENT,
|
||||
SLAVE_UP_EVENT,
|
||||
SERVER_DOWN_EVENT,
|
||||
SERVER_UP_EVENT,
|
||||
NDB_UP_EVENT,
|
||||
NDB_DOWN_EVENT,
|
||||
LOST_MASTER_EVENT,
|
||||
LOST_SLAVE_EVENT,
|
||||
LOST_NDB_EVENT,
|
||||
NEW_MASTER_EVENT,
|
||||
NEW_SLAVE_EVENT,
|
||||
NEW_NDB_EVENT,
|
||||
MAX_MONITOR_EVENT
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the timeouts to use in the monitor.
|
||||
*
|
||||
* @param arg The handle allocated by startMonitor
|
||||
* @param type The connect timeout type
|
||||
* @param value The timeout value to set
|
||||
* Check if the event type is one the ndbcustermonitor is interested in.
|
||||
* @param event Event to check
|
||||
* @return True if the event is monitored, false if it is not
|
||||
*/
|
||||
static void
|
||||
setNetworkTimeout(void *arg, int type, int value)
|
||||
bool isNdbEvent(monitor_event_t event)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
int max_timeout = (int)(handle->interval/1000);
|
||||
int new_timeout = max_timeout -1;
|
||||
|
||||
if (new_timeout <= 0)
|
||||
new_timeout = DEFAULT_CONNECT_TIMEOUT;
|
||||
|
||||
switch(type) {
|
||||
case MONITOR_CONNECT_TIMEOUT:
|
||||
if (value < max_timeout) {
|
||||
memcpy(&handle->connect_timeout, &value, sizeof(int));
|
||||
} else {
|
||||
memcpy(&handle->connect_timeout, &new_timeout, sizeof(int));
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"warning : Monitor Connect Timeout %i is greater than monitor interval ~%i seconds"
|
||||
", lowering to %i seconds", value, max_timeout, new_timeout)));
|
||||
}
|
||||
break;
|
||||
|
||||
case MONITOR_READ_TIMEOUT:
|
||||
if (value < max_timeout) {
|
||||
memcpy(&handle->read_timeout, &value, sizeof(int));
|
||||
} else {
|
||||
memcpy(&handle->read_timeout, &new_timeout, sizeof(int));
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"warning : Monitor Read Timeout %i is greater than monitor interval ~%i seconds"
|
||||
", lowering to %i seconds", value, max_timeout, new_timeout)));
|
||||
}
|
||||
break;
|
||||
|
||||
case MONITOR_WRITE_TIMEOUT:
|
||||
if (value < max_timeout) {
|
||||
memcpy(&handle->write_timeout, &value, sizeof(int));
|
||||
} else {
|
||||
memcpy(&handle->write_timeout, &new_timeout, sizeof(int));
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"warning : Monitor Write Timeout %i is greater than monitor interval ~%i seconds"
|
||||
", lowering to %i seconds", value, max_timeout, new_timeout)));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Monitor setNetworkTimeout received an unsupported action type %i", type)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int i;
|
||||
for(i = 0;ndb_events[i] != MAX_MONITOR_EVENT;i++)
|
||||
{
|
||||
if(event == ndb_events[i])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
56
server/modules/monitor/ndbclustermon.h
Normal file
56
server/modules/monitor/ndbclustermon.h
Normal file
@ -0,0 +1,56 @@
|
||||
#ifndef _MYSQLMON_H
|
||||
#define _MYSQLMON_H
|
||||
/*
|
||||
* This file is distributed as part of the MariaDB Corporation MaxScale. It is free
|
||||
* software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation,
|
||||
* version 2.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc., 51
|
||||
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Copyright MariaDB Corporation Ab 2013-2014
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <monitor.h>
|
||||
#include <spinlock.h>
|
||||
#include <thread.h>
|
||||
#include <mysql.h>
|
||||
#include <mysqld_error.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
#include <secrets.h>
|
||||
#include <dcb.h>
|
||||
#include <modinfo.h>
|
||||
#include <maxconfig.h>
|
||||
#include <monitor_common.h>
|
||||
#include <externcmd.h>
|
||||
|
||||
/**
|
||||
* @file ndbclustermon.h - The NDB Cluster monitor
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* The handle for an instance of a NDB Cluster Monitor module
|
||||
*/
|
||||
typedef struct {
|
||||
SPINLOCK lock; /**< The monitor spinlock */
|
||||
pthread_t tid; /**< id of monitor thread */
|
||||
int shutdown; /**< Flag to shutdown the monitor thread */
|
||||
int status; /**< Monitor status */
|
||||
unsigned long id; /**< Monitor ID */
|
||||
MONITOR_SERVERS *master; /**< Master server for MySQL Master/Slave replication */
|
||||
char* script; /*< Script to call when state changes occur on servers */
|
||||
bool events[MAX_MONITOR_EVENT]; /*< enabled events */
|
||||
} MYSQL_MONITOR;
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user