diff --git a/Makefile b/Makefile index a814a2628..0f157d3b5 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,7 @@ # targets # 18/06/13 Mark Riddoch Addition of install target # 21/06/13 Mark Riddoch Addition of inih +# 08/07/13 Mark Riddoch Addition of monitor modules DEST=/usr/local/skysql @@ -29,17 +30,20 @@ all: (cd core; make) (cd modules/routing; make) (cd modules/protocol; make) + (cd modules/monitor; make) clean: (cd Documentation; rm -rf html) (cd core; make clean) (cd modules/routing; make clean) (cd modules/protocol; make clean) + (cd modules/monitor; make clean) depend: (cd core; make depend) (cd modules/routing; make depend) (cd modules/protocol; make depend) + (cd modules/monitor; make depend) documentation: doxygen doxygate @@ -48,3 +52,4 @@ install: (cd core; make DEST=$(DEST) install) (cd modules/routing; make DEST=$(DEST) install) (cd modules/protocol; make DEST=$(DEST) install) + (cd modules/moinitor; make DEST=$(DEST) install) diff --git a/core/Makefile b/core/Makefile index 89418dccb..39e80aac9 100644 --- a/core/Makefile +++ b/core/Makefile @@ -55,7 +55,8 @@ LDFLAGS=-rdynamic -L$(LOGPATH) \ SRCS= atomic.c buffer.c spinlock.c gateway.c gateway_mysql_protocol.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 + poll.c config.c users.c hashtable.c dbusers.c thread.c gwbitmask.c \ + monitor.c HDRS= ../include/atomic.h ../include/buffer.h ../include/dcb.h \ ../include/gateway_mysql.h ../include/gw.h ../include/mysql_protocol.h \ diff --git a/core/config.c b/core/config.c index 524d27fd6..894de5834 100644 --- a/core/config.c +++ b/core/config.c @@ -24,6 +24,7 @@ * * Date Who Description * 21/06/13 Mark Riddoch Initial implementation + * 08/07/13 mark Riddoch Addition on monitor module support * * @endverbatim */ @@ -35,6 +36,7 @@ #include #include #include +#include static int process_config_context(CONFIG_CONTEXT *); static void free_config_context(CONFIG_CONTEXT *); @@ -155,8 +157,12 @@ CONFIG_CONTEXT *obj; char *address = config_get_value(obj->parameters, "address"); char *port = config_get_value(obj->parameters, "port"); char *protocol = config_get_value(obj->parameters, "protocol"); + char *monuser = config_get_value(obj->parameters, "monitoruser"); + char *monpw = config_get_value(obj->parameters, "monitorpw"); if (address && port && protocol) obj->element = server_alloc(address, protocol, atoi(port)); + if (obj->element && monuser && monpw) + serverAddMonUser(obj->element, monuser, monpw); } obj = obj->next; @@ -215,6 +221,30 @@ CONFIG_CONTEXT *obj; serviceAddProtocol(ptr->element, protocol, atoi(port)); } } + else if (!strcmp(type, "monitor")) + { + char *module = config_get_value(obj->parameters, "module"); + char *servers = config_get_value(obj->parameters, "servers"); + if (module) + { + obj->element = monitor_alloc(obj->object, module); + if (servers && obj->element) + { + char *s = strtok(servers, ","); + while (s) + { + CONFIG_CONTEXT *obj1 = context; + while (obj1) + { + if (strcmp(s, obj1->object) == 0 && obj->element && obj1->element) + monitorAddServer(obj->element, obj1->element); + obj1 = obj1->next; + } + s = strtok(NULL, ","); + } + } + } + } obj = obj->next; } diff --git a/core/monitor.c b/core/monitor.c new file mode 100644 index 000000000..ac9d04368 --- /dev/null +++ b/core/monitor.c @@ -0,0 +1,116 @@ +/* + * This file is distributed as part of the SkySQL Gateway. 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 SkySQL Ab 2013 + */ + +/** + * @file monitor.c - The monitor module management routines + * + * @verbatim + * Revision History + * + * Date Who Description + * 08/07/13 Mark Riddoch Initial implementation + * + * @endverbatim + */ +#include +#include +#include +#include +#include +#include + + +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 + * @return The newly created monitor + */ +MONITOR * +monitor_alloc(char *name, char *module) +{ +MONITOR *mon; + + if ((mon = (MONITOR *)malloc(sizeof(MONITOR))) == NULL) + { + return NULL; + } + + mon->name = strdup(name); + mon->module = strdup(module); + if ((mon->module = load_module(module, MODULE_MONITOR)) == NULL) + { + fprintf(stderr, "Unable to load monitor module '%s'\n", name); + free(mon->name); + free(mon); + return NULL; + } + mon->handle = (*mon->module->startMonitor)(); + + 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->handle); + 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); +} + +/** + * 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) +{ + mon->module->registerServer(mon->handle, server); +} diff --git a/core/server.c b/core/server.c index 18d725692..50655a771 100644 --- a/core/server.c +++ b/core/server.c @@ -238,3 +238,18 @@ server_clear_status(SERVER *server, int bit) { server->status &= ~bit; } + +/** + * Add a user name and password to use for monitoring the + * state of the server. + * + * @param server The server to update + * @param user The user name to use + * @param passwd The password of the user + */ +void +serverAddMonUser(SERVER *server, char *user, char *passwd) +{ + server->monuser = strdup(user); + server->monpw = strdup(passwd); +} diff --git a/core/thread.c b/core/thread.c index f8581abd7..dded79507 100644 --- a/core/thread.c +++ b/core/thread.c @@ -61,3 +61,18 @@ void *rval; pthread_join((pthread_t)thd, &rval); } + +/** + * Put the thread to sleep for a number of milliseconds + * + * @param ms Number of milliseconds to sleep + */ +void +thread_millisleep(int ms) +{ +struct timespec req; + + req.tv_sec = ms / 1000; + req.tv_nsec = (ms % 1000) * 1000000; + nanosleep(&req, NULL); +} diff --git a/gateway.cnf b/gateway.cnf index 662c52374..8482f5939 100644 --- a/gateway.cnf +++ b/gateway.cnf @@ -18,18 +18,24 @@ type=server address=127.0.0.1 port=3306 protocol=MySQLBackend +monitoruser=massi +monitorpw=massi [server2] type=server address=127.0.0.1 port=3307 protocol=MySQLBackend +monitoruser=massi +monitorpw=massi [server3] type=server address=127.0.0.1 port=3308 protocol=MySQLBackend +monitoruser=massi +monitorpw=massi [Debug Service] type=service @@ -46,3 +52,8 @@ type=listener service=Test Service protocol=MySQLClient port=4006 + +[MySQL Monitor] +type=monitor +module=mysqlmon +servers=server1,server2,server3 diff --git a/include/modules.h b/include/modules.h index cb967f3a0..f3ec14f07 100644 --- a/include/modules.h +++ b/include/modules.h @@ -29,6 +29,7 @@ * * Date Who Description * 13/06/13 Mark Riddoch Initial implementation + * 08/07/13 Mark Riddoch Addition of monitor modules * @endverbatim */ @@ -47,6 +48,7 @@ typedef struct modules { */ #define MODULE_PROTOCOL "Protocol" /**< A protocol module type */ #define MODULE_ROUTER "Router" /**< A router module type */ +#define MODULE_MONITOR "Monitor" /**< A database monitor module type */ extern void *load_module(const char *module, const char *type); diff --git a/include/monitor.h b/include/monitor.h new file mode 100644 index 000000000..eaed0a912 --- /dev/null +++ b/include/monitor.h @@ -0,0 +1,79 @@ +#ifndef _MONITOR_H +#define _MONITOR_H +/* + * This file is distributed as part of the SkySQL Gateway. 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 SkySQL Ab 2013 + */ +#include + +/** + * @file monitor.h The interface to the monitor module + * + * @verbatim + * Revision History + * + * Date Who Description + * 07/07/13 Mark Riddoch Initial implementation + * + * @endverbatim + */ + +/** + * The "Module Object" for a monitor module. + * + * The monitor modules are designed to monitor the backend databases that the gateway + * connects to and provide information regarding the status of the databases that + * is used in the routing decisions. + * + * startMonitor is called to start the monitoring process, it is called on the main + * thread of the gateway and is responsible for creating a thread for the monitor + * itself to run on. This should use the entry points defined in the thread.h + * header file rather than make direct calls to the operating system thrading libraries. + * The return from startMonitor is a void * handle that will be passed to all other monitor + * API calls. + * + * stopMonitor is responsible for shuting down and destroying a monitor, it is called + * with the void * handle that was returned by startMonitor. + * + * registerServer is called to register a server that must be monitored with a running + * monitor. this will be called with the handle returned from the startMonitor call and + * the SERVER structure that the monitor must update and monitor. The SERVER structure + * contains the information required to connect to the monitored server. + * + * unregisterServer is called to remove a server from the set of servers that need to be + * monitored. + */ +typedef struct { + void *(*startMonitor)(); + void (*stopMonitor)(void *); + void (*registerServer)(void *, SERVER *); + void (*unregisterServer)(void *, SERVER *); +} MONITOR_OBJECT; + +/** + * Representation of the running monitor. + */ +typedef struct monitor { + char *name; /**< The name of the monitor module */ + MONITOR_OBJECT *module; /**< The "monitor object" */ + void *handle; /**< Handle returned from startMonitor */ + struct monitor *next; /**< Next monitor in the linked list */ +} MONITOR; + +extern MONITOR *monitor_alloc(char *, char *); +extern void monitor_free(MONITOR *); +extern void monitorAddServer(MONITOR *, SERVER *); +#endif diff --git a/include/server.h b/include/server.h index 2e3f9d6f5..beebb510b 100644 --- a/include/server.h +++ b/include/server.h @@ -54,6 +54,8 @@ typedef struct server { unsigned short port; /**< Port to listen on */ char *protocol; /**< Protocol module to use */ unsigned int status; /**< Status flag bitmap for the server */ + char *monuser; /**< User name to use to monitor the db */ + char *monpw; /**< Password to use to monitor the db */ SERVER_STATS stats; /**< The server statistics */ struct server *next; /**< Next server */ struct server *nextdb; /**< Next server in list attached to a service */ @@ -100,4 +102,5 @@ extern void dprintServer(DCB *, SERVER *); extern char *server_status(SERVER *); extern void server_set_status(SERVER *, int); extern void server_clear_status(SERVER *, int); +extern void serverAddMonUser(SERVER *, char *, char *); #endif diff --git a/include/thread.h b/include/thread.h index ec2f2a03d..e3ea6f0dc 100644 --- a/include/thread.h +++ b/include/thread.h @@ -33,5 +33,6 @@ extern void *thread_start(void (*entry)(void *), void *arg); extern void thread_wait(void *thd); +extern void thread_millisleep(int ms); #endif diff --git a/modules/monitor/Makefile b/modules/monitor/Makefile new file mode 100644 index 000000000..7f1149956 --- /dev/null +++ b/modules/monitor/Makefile @@ -0,0 +1,59 @@ +# This file is distributed as part of the SkySQL Gateway. 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 SkySQL Ab 2013 +# +# Revision History +# Date Who Description +# 08/07/13 Mark Riddoch Initial implementation + +include ../../../build_gateway.inc +LOGPATH := $(ROOT_PATH)/log_manager +UTILSPATH := $(ROOT_PATH)/utils + +CC=cc +CFLAGS=-c -fPIC -I. -I/usr/include -I../include -I../../include -I$(LOGPATH) \ + -I$(UTILSPATH) -I/usr/include/mysql -Wall -g +LDFLAGS=-shared -L$(LOGPATH) -Wl,-rpath,$(LOGPATH) -Wl,-rpath,$(UTILSPATH) +MYSQLSRCS=mysql_mon.c +MYSQLOBJ=$(MYSQLSRCS:.c=.o) +SRCS=$(MYSQLSRCS) +OBJ=$(SRCS:.c=.o) +LIBS=$(UTILSPATH)/skygw_utils.o -llog_manager \ + -L/packages/mariadb-5.5.25/libmysql -lmysqlclient +MODULES=libmysqlmon.so + + +all: $(MODULES) + +libmysqlmon.so: $(MYSQLOBJ) + $(CC) $(LDFLAGS) $(MYSQLOBJ) $(LIBS) -o $@ + +.c.o: + $(CC) $(CFLAGS) $< -o $@ + +clean: + rm -f $(OBJ) $(MODULES) + +tags: + ctags $(SRCS) $(HDRS) + +depend: + @rm -f depend.mk + cc -M $(CFLAGS) $(SRCS) > depend.mk + +install: $(MODULES) + install -D $< $(DEST)/gateway/modules + +include depend.mk diff --git a/modules/monitor/depend.mk b/modules/monitor/depend.mk new file mode 100644 index 000000000..058a7b421 --- /dev/null +++ b/modules/monitor/depend.mk @@ -0,0 +1,16 @@ +mysql_mon.o: mysql_mon.c /usr/include/stdio.h /usr/include/features.h \ + /usr/include/sys/cdefs.h /usr/include/bits/wordsize.h \ + /usr/include/gnu/stubs.h /usr/include/gnu/stubs-64.h \ + /usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stddef.h \ + /usr/include/bits/types.h /usr/include/bits/typesizes.h \ + /usr/include/libio.h /usr/include/_G_config.h /usr/include/wchar.h \ + /usr/lib/gcc/x86_64-redhat-linux/4.4.6/include/stdarg.h \ + /usr/include/bits/stdio_lim.h /usr/include/bits/sys_errlist.h \ + ../../include/monitor.h mysqlmon.h ../../include/server.h \ + ../../include/dcb.h ../../include/spinlock.h ../../include/thread.h \ + /usr/include/pthread.h /usr/include/endian.h /usr/include/bits/endian.h \ + /usr/include/bits/byteswap.h /usr/include/sched.h /usr/include/time.h \ + /usr/include/bits/sched.h /usr/include/bits/time.h \ + /usr/include/xlocale.h /usr/include/bits/pthreadtypes.h \ + /usr/include/bits/setjmp.h ../../include/buffer.h \ + ../../include/gwbitmask.h diff --git a/modules/monitor/mysql_mon.c b/modules/monitor/mysql_mon.c new file mode 100644 index 000000000..54ad6e44f --- /dev/null +++ b/modules/monitor/mysql_mon.c @@ -0,0 +1,223 @@ +/* + * This file is distributed as part of the SkySQL Gateway. 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 SkySQL Ab 2013 + */ +#include +#include +#include +#include +#include + +static void monitorMain(void *); + +static char *version_str = "V1.0.0"; + +static void *startMonitor(); +static void stopMonitor(void *); +static void registerServer(void *, SERVER *); +static void unregisterServer(void *, SERVER *); + +static MONITOR_OBJECT MyObject = { startMonitor, stopMonitor, registerServer, unregisterServer }; + +/** + * Implementation of the mandatory version entry point + * + * @return version string of the module + */ +char * +version() +{ + return version_str; +} + +/** + * The module initialisation routine, called when the module + * is first loaded. + */ +void +ModuleInit() +{ + fprintf(stderr, "Initialise the MySQL Monitor module.\n"); +} + +/** + * The module entry point routine. It is this routine that + * must populate the structure that is referred to as the + * "module object", this is a structure with the set of + * external entry points for this module. + * + * @return The module object + */ +MONITOR_OBJECT * +GetModuleObject() +{ + return &MyObject; +} + +/** + * Start the instance of the monitor, returning a handle on the monitor. + * + * This function creates a thread to execute the actual monitoring. + * + * @return A handle to use when interacting with the monitor + */ +static void * +startMonitor() +{ +MYSQL_MONITOR *handle; + + if ((handle = (MYSQL_MONITOR *)malloc(sizeof(MYSQL_MONITOR))) == NULL) + return NULL; + handle->databases = NULL; + handle->shutdown = 0; + spinlock_init(&handle->lock); + thread_start(monitorMain, handle); + return handle; +} + +/** + * Stop a running monitor + * + * @param handle Handle on thr running monior + */ +static void +stopMonitor(void *arg) +{ +MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; + + handle->shutdown = 1; +} + +/** + * Register a server that must be added to the monitored servers for + * a monitoring module. + * + * @param handle 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 handle 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); +} + +/** + * Monitor an individual server + * + * @param database The database to probe + */ +static void +monitorDatabase(MONITOR_SERVERS *database) +{ + if (database->con == NULL || mysql_ping(database->con) != 0) + { + database->con = mysql_init(NULL); + if (mysql_real_connect(database->con, database->server->name, + database->server->monuser, database->server->monpw, + NULL, database->server->port, NULL, 0) == NULL) + { + server_clear_status(database->server, SERVER_RUNNING); + return; + } + } + + // If we get this far then we have a workign connection + server_set_status(database->server, SERVER_RUNNING); + +} + +/** + * The entry point for the monitoring module thread + * + * @param arg The handle of the monitor + */ +static void +monitorMain(void *arg) +{ +MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg; +MONITOR_SERVERS *ptr; + + while (1) + { + thread_millisleep(1000); + + if (handle->shutdown) + return; + ptr = handle->databases; + while (ptr) + { + monitorDatabase(ptr); + ptr = ptr->next; + } + } +} diff --git a/modules/monitor/mysqlmon.h b/modules/monitor/mysqlmon.h new file mode 100644 index 000000000..da8d42960 --- /dev/null +++ b/modules/monitor/mysqlmon.h @@ -0,0 +1,55 @@ +#ifndef _MYSQLMON_H +#define _MYSQLMON_H +/* + * This file is distributed as part of the SkySQL Gateway. 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 SkySQL Ab 2013 + */ +#include +#include +#include + +/** + * @file mysqlmon.h - The MySQL monitor functionality within the gateway + * + * @verbatim + * Revision History + * + * Date Who Description + * 08/07/13 Mark Riddoch Initial implementation + * + * @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 */ + struct monitor_servers + *next; /**< The next server in the list */ +} MONITOR_SERVERS; + +/** + * The handle for an instance of a MySQL Monitor module + */ +typedef struct { + SPINLOCK lock; /**< The monitor spinlock */ + int shutdown; /**< Flag to shutdown the monitor thread */ + MONITOR_SERVERS *databases; /**< Linked list of servers to monitor */ +} MYSQL_MONITOR; +#endif