451 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			451 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * @file monitor.c  - The monitor module management routines
 | |
|  *
 | |
|  * @verbatim
 | |
|  * Revision History
 | |
|  *
 | |
|  * Date		Who			Description
 | |
|  * 08/07/13	Mark Riddoch		Initial implementation
 | |
|  * 23/05/14	Massimiliano Pinto	Addition of monitor_interval parameter
 | |
|  * 					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
 | |
|  */
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <monitor.h>
 | |
| #include <spinlock.h>
 | |
| #include <modules.h>
 | |
| #include <skygw_utils.h>
 | |
| #include <log_manager.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;
 | |
| 
 | |
| static MONITOR	*allMonitors = NULL;
 | |
| static SPINLOCK	monLock = SPINLOCK_INIT;
 | |
| 
 | |
| /**
 | |
|  * Allocate a new monitor, load the associated module for the monitor
 | |
|  * and start execution on the monitor.
 | |
|  *
 | |
|  * @param name		The name of the monitor module to load
 | |
|  * @param module	The module to load
 | |
|  * @return 	The newly created monitor
 | |
|  */
 | |
| MONITOR *
 | |
| monitor_alloc(char *name, char *module)
 | |
| {
 | |
| MONITOR	*mon;
 | |
| 
 | |
| 	if ((mon = (MONITOR *)malloc(sizeof(MONITOR))) == NULL)
 | |
| 	{
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	if ((mon->module = load_module(module, MODULE_MONITOR)) == NULL)
 | |
| 	{
 | |
| 		LOGIF(LE, (skygw_log_write_flush(
 | |
|                                    LOGFILE_ERROR,
 | |
|                                    "Error : Unable to load monitor module '%s'.",
 | |
|                                    name)));
 | |
| 		free(mon);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	mon->state = MONITOR_STATE_ALLOC;
 | |
| 	mon->name = strdup(name);
 | |
| 	mon->handle = NULL;
 | |
| 	mon->databases = NULL;
 | |
| 	mon->password = NULL;
 | |
| 	mon->user = 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;
 | |
| 	spinlock_release(&monLock);
 | |
| 
 | |
| 	return mon;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Free a monitor, first stop the monitor and then remove the monitor from
 | |
|  * the chain of monitors and free the memory.
 | |
|  *
 | |
|  * @param mon	The monitor to free
 | |
|  */
 | |
| void
 | |
| monitor_free(MONITOR *mon)
 | |
| {
 | |
| MONITOR	*ptr;
 | |
| 
 | |
| 	mon->module->stopMonitor(mon);
 | |
| 	mon->state = MONITOR_STATE_FREED;
 | |
| 	spinlock_acquire(&monLock);
 | |
| 	if (allMonitors == mon)
 | |
| 		allMonitors = mon->next;
 | |
| 	else
 | |
| 	{
 | |
| 		ptr = allMonitors;
 | |
| 		while (ptr->next && ptr->next != mon)
 | |
| 			ptr = ptr->next;
 | |
| 		if (ptr->next)
 | |
| 			ptr->next = mon->next;
 | |
| 	}
 | |
| 	spinlock_release(&monLock);
 | |
| 	free(mon->name);
 | |
| 	free(mon);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Start an individual monitor that has previoulsy been stopped.
 | |
|  *
 | |
|  * @param monitor The Monitor that should be started
 | |
|  */
 | |
| void
 | |
| monitorStart(MONITOR *monitor, void* params)
 | |
| {
 | |
| 	spinlock_acquire(&monitor->lock);
 | |
| 	monitor->handle = (*monitor->module->startMonitor)(monitor,params);
 | |
| 	monitor->state = MONITOR_STATE_RUNNING;
 | |
| 	spinlock_release(&monitor->lock);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Stop a given monitor
 | |
|  *
 | |
|  * @param monitor	The monitor to stop
 | |
|  */
 | |
| void
 | |
| monitorStop(MONITOR *monitor)
 | |
| {
 | |
|     if(monitor->state != MONITOR_STATE_STOPPED)
 | |
|     {
 | |
| 	monitor->state = MONITOR_STATE_STOPPING;
 | |
| 	monitor->module->stopMonitor(monitor);
 | |
| 	monitor->state = MONITOR_STATE_STOPPED;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Shutdown all running monitors
 | |
|  */
 | |
| void
 | |
| monitorStopAll()
 | |
| {
 | |
| MONITOR	*ptr;
 | |
| 
 | |
| 	spinlock_acquire(&monLock);
 | |
| 	ptr = allMonitors;
 | |
| 	while (ptr)
 | |
| 	{
 | |
| 		monitorStop(ptr);
 | |
| 		ptr = ptr->next;
 | |
| 	}
 | |
| 	spinlock_release(&monLock);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Add a server to a monitor. Simply register the server that needs to be
 | |
|  * monitored to the running monitor module.
 | |
|  *
 | |
|  * @param mon		The Monitor instance
 | |
|  * @param server	The Server to add to the monitoring
 | |
|  */
 | |
| void
 | |
| monitorAddServer(MONITOR *mon, SERVER *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;
 | |
| 	db->log_version_err = true;
 | |
| 	/** 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);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Add a default user to the monitor. This user is used to connect to the
 | |
|  * monitored databases but may be overriden on a per server basis.
 | |
|  *
 | |
|  * @param mon		The monitor instance
 | |
|  * @param user		The default username to use when connecting
 | |
|  * @param passwd	The default password associated to the default user.
 | |
|  */
 | |
| void
 | |
| monitorAddUser(MONITOR *mon, char *user, char *passwd)
 | |
| {
 | |
|     mon->user = strdup(user);
 | |
|     mon->password = strdup(passwd);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Show all monitors
 | |
|  *
 | |
|  * @param dcb	DCB for printing output
 | |
|  */
 | |
| void
 | |
| monitorShowAll(DCB *dcb)
 | |
| {
 | |
| MONITOR	*ptr;
 | |
| 
 | |
| 	spinlock_acquire(&monLock);
 | |
| 	ptr = allMonitors;
 | |
| 	while (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);
 | |
| 		ptr = ptr->next;
 | |
| 	}
 | |
| 	spinlock_release(&monLock);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Show a single monitor
 | |
|  *
 | |
|  * @param dcb	DCB for printing output
 | |
|  */
 | |
| void
 | |
| 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);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * List all the monitors
 | |
|  *
 | |
|  * @param dcb	DCB for printing output
 | |
|  */
 | |
| void
 | |
| monitorList(DCB *dcb)
 | |
| {
 | |
| MONITOR	*ptr;
 | |
| 
 | |
| 	spinlock_acquire(&monLock);
 | |
| 	ptr = allMonitors;
 | |
| 	dcb_printf(dcb, "---------------------+---------------------\n");
 | |
| 	dcb_printf(dcb, "%-20s | Status\n", "Monitor");
 | |
| 	dcb_printf(dcb, "---------------------+---------------------\n");
 | |
| 	while (ptr)
 | |
| 	{
 | |
| 		dcb_printf(dcb, "%-20s | %s\n", ptr->name,
 | |
| 			ptr->state & MONITOR_STATE_RUNNING
 | |
| 					? "Running" : "Stopped");
 | |
| 		ptr = ptr->next;
 | |
| 	}
 | |
| 	dcb_printf(dcb, "---------------------+---------------------\n");
 | |
| 	spinlock_release(&monLock);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Find a monitor by name
 | |
|  *
 | |
|  * @param	name	The name of the monitor
 | |
|  * @return	Pointer to the monitor or NULL
 | |
|  */
 | |
| MONITOR *
 | |
| monitor_find(char *name)
 | |
| {
 | |
| MONITOR	*ptr;
 | |
| 
 | |
| 	spinlock_acquire(&monLock);
 | |
| 	ptr = allMonitors;
 | |
| 	while (ptr)
 | |
| 	{
 | |
| 		if (!strcmp(ptr->name, name))
 | |
| 			break;
 | |
| 		ptr = ptr->next;
 | |
| 	}
 | |
| 	spinlock_release(&monLock);
 | |
| 	return ptr;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Set the monitor sampling interval.
 | |
|  *
 | |
|  * @param mon		The monitor instance
 | |
|  * @param interval	The sampling interval in milliseconds
 | |
|  */
 | |
| void
 | |
| monitorSetInterval (MONITOR *mon, unsigned long interval)
 | |
| {
 | |
|     mon->interval = interval;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Set Monitor timeouts for connect/read/write
 | |
|  *
 | |
|  * @param mon		The monitor instance
 | |
|  * @param type		The timeout handling type
 | |
|  * @param value		The timeout to set
 | |
|  */
 | |
| void
 | |
| monitorSetNetworkTimeout(MONITOR *mon, int type, int 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;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Provide a row to the result set that defines the set of monitors
 | |
|  *
 | |
|  * @param set	The result set
 | |
|  * @param data	The index of the row to send
 | |
|  * @return The next row or NULL
 | |
|  */
 | |
| static RESULT_ROW *
 | |
| monitorRowCallback(RESULTSET *set, void *data)
 | |
| {
 | |
| int		*rowno = (int *)data;
 | |
| int		i = 0;;
 | |
| char		buf[20];
 | |
| RESULT_ROW	*row;
 | |
| MONITOR		*ptr;
 | |
| 
 | |
| 	spinlock_acquire(&monLock);
 | |
| 	ptr = allMonitors;
 | |
| 	while (i < *rowno && ptr)
 | |
| 	{
 | |
| 		i++;
 | |
| 		ptr = ptr->next;
 | |
| 	}
 | |
| 	if (ptr == NULL)
 | |
| 	{
 | |
| 		spinlock_release(&monLock);
 | |
| 		free(data);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	(*rowno)++;
 | |
| 	row = resultset_make_row(set);
 | |
| 	resultset_row_set(row, 0, ptr->name);
 | |
| 	resultset_row_set(row, 1, ptr->state & MONITOR_STATE_RUNNING
 | |
|                                         ? "Running" : "Stopped");
 | |
| 	spinlock_release(&monLock);
 | |
| 	return row;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Return a resultset that has the current set of monitors in it
 | |
|  *
 | |
|  * @return A Result set
 | |
|  */
 | |
| RESULTSET *
 | |
| monitorGetList()
 | |
| {
 | |
| RESULTSET	*set;
 | |
| int		*data;
 | |
| 
 | |
| 	if ((data = (int *)malloc(sizeof(int))) == NULL)
 | |
| 		return NULL;
 | |
| 	*data = 0;
 | |
| 	if ((set = resultset_create(monitorRowCallback, data)) == NULL)
 | |
| 	{
 | |
| 		free(data);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	resultset_add_column(set, "Monitor", 20, COL_TYPE_VARCHAR);
 | |
| 	resultset_add_column(set, "Status", 10, COL_TYPE_VARCHAR);
 | |
| 
 | |
| 	return set;
 | |
| }
 | 
