Merge branch 'develop' into MAX-11

Conflicts:
	server/include/server.h
	server/modules/monitor/mysql_mon.c
	server/modules/protocol/mysql_client.c
	server/modules/routing/readwritesplit/readwritesplit.c
	utils/skygw_debug.h
This commit is contained in:
VilhoRaatikka
2014-06-07 00:50:08 +03:00
47 changed files with 2759 additions and 128 deletions

View File

@ -0,0 +1,86 @@
# This file is distributed as part of MaxScale form SkySQL. 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 2014
#
# Revision History
# Date Who Description
# 29/05/14 Mark Riddoch Initial module development
include ../../../build_gateway.inc
LOGPATH := $(ROOT_PATH)/log_manager
UTILSPATH := $(ROOT_PATH)/utils
CC=cc
CFLAGS=-c -fPIC -I/usr/include -I../include -I../../include -I$(LOGPATH) \
-I$(UTILSPATH) -Wall -g
include ../../../makefile.inc
LDFLAGS=-shared -L$(LOGPATH) -Wl,-rpath,$(DEST)/lib \
-Wl,-rpath,$(LOGPATH) -Wl,-rpath,$(UTILSPATH)
TESTSRCS=testfilter.c
TESTOBJ=$(TESTSRCS:.c=.o)
QLASRCS=qlafilter.c
QLAOBJ=$(QLASRCS:.c=.o)
REGEXSRCS=regexfilter.c
REGEXOBJ=$(REGEXSRCS:.c=.o)
SRCS=$(TESTSRCS) $(QLASRCS) $(REGEXSRCS)
OBJ=$(SRCS:.c=.o)
LIBS=$(UTILSPATH)/skygw_utils.o -lssl -llog_manager
MODULES= libtestfilter.so libqlafilter.so libregexfilter.so
all: $(MODULES)
libtestfilter.so: $(TESTOBJ)
$(CC) $(LDFLAGS) $(TESTOBJ) $(LIBS) -o $@
libqlafilter.so: $(QLAOBJ)
$(CC) $(LDFLAGS) $(QLAOBJ) $(LIBS) -o $@
libregexfilter.so: $(REGEXOBJ)
$(CC) $(LDFLAGS) $(REGEXOBJ) $(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 $(MODULES) $(DEST)/MaxScale/modules
cleantests:
$(MAKE) -C test cleantests
buildtests:
$(MAKE) -C test DEBUG=Y buildtests
runtests:
$(MAKE) -C test runtests
testall:
$(MAKE) -C test testall
include depend.mk

View File

@ -0,0 +1,295 @@
/*
* This file is distributed as part of MaxScale by SkySQL. 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 2014
*/
/**
* QLA Filter - Query Log All. A primitive query logging filter, simply
* used to verify the filter mechanism for downstream filters. All queries
* that are passed through the filter will be written to file.
*
* The filter makes no attempt to deal with query packets that do not fit
* in a single GWBUF.
*
* A single option may be passed to the filter, this is the name of the
* file to which the queries are logged. A serial number is appended to this
* name in order that each session logs to a different file.
*/
#include <stdio.h>
#include <fcntl.h>
#include <filter.h>
#include <modinfo.h>
#include <modutil.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
MODULE_INFO info = {
MODULE_API_FILTER,
MODULE_ALPHA_RELEASE,
FILTER_VERSION,
"A simple query logging filter"
};
static char *version_str = "V1.0.0";
/*
* The filter entry points
*/
static FILTER *createInstance(char **options, FILTER_PARAMETER **);
static void *newSession(FILTER *instance, SESSION *session);
static void closeSession(FILTER *instance, void *session);
static void freeSession(FILTER *instance, void *session);
static void setDownstream(FILTER *instance, void *fsession, DOWNSTREAM *downstream);
static int routeQuery(FILTER *instance, void *fsession, GWBUF *queue);
static void diagnostic(FILTER *instance, void *fsession, DCB *dcb);
static FILTER_OBJECT MyObject = {
createInstance,
newSession,
closeSession,
freeSession,
setDownstream,
routeQuery,
diagnostic,
};
/**
* A instance structure, the assumption is that the option passed
* to the filter is simply a base for the filename to which the queries
* are logged.
*
* To this base a session number is attached such that each session will
* have a nique name.
*/
typedef struct {
int sessions;
char *filebase;
} QLA_INSTANCE;
/**
* The session structure for this QLA filter.
* This stores the downstream filter information, such that the
* filter is able to pass the query on to the next filter (or router)
* in the chain.
*
* It also holds the file descriptor to which queries are written.
*/
typedef struct {
DOWNSTREAM down;
char *filename;
int fd;
} QLA_SESSION;
/**
* 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()
{
}
/**
* 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
*/
FILTER_OBJECT *
GetModuleObject()
{
return &MyObject;
}
/**
* Create an instance of the filter for a particular service
* within MaxScale.
*
* @param options The options for this filter
*
* @return The instance data for this new instance
*/
static FILTER *
createInstance(char **options, FILTER_PARAMETER **params)
{
QLA_INSTANCE *my_instance;
if ((my_instance = calloc(1, sizeof(QLA_INSTANCE))) != NULL)
{
if (options)
my_instance->filebase = strdup(options[0]);
else
my_instance->filebase = strdup("qla");
my_instance->sessions = 0;
}
return (FILTER *)my_instance;
}
/**
* Associate a new session with this instance of the filter.
*
* Create the file to log to and open it.
*
* @param instance The filter instance data
* @param session The session itself
* @return Session specific data for this session
*/
static void *
newSession(FILTER *instance, SESSION *session)
{
QLA_INSTANCE *my_instance = (QLA_INSTANCE *)instance;
QLA_SESSION *my_session;
if ((my_session = calloc(1, sizeof(QLA_SESSION))) != NULL)
{
if ((my_session->filename =
(char *)malloc(strlen(my_instance->filebase) + 20))
== NULL)
{
free(my_session);
return NULL;
}
sprintf(my_session->filename, "%s.%d", my_instance->filebase,
my_instance->sessions);
my_instance->sessions++;
my_session->fd = open(my_session->filename,
O_WRONLY|O_CREAT|O_TRUNC, 0666);
}
return my_session;
}
/**
* Close a session with the filter, this is the mechanism
* by which a filter may cleanup data structure etc.
* In the case of the QLA filter we simple close the file descriptor.
*
* @param instance The filter instance data
* @param session The session being closed
*/
static void
closeSession(FILTER *instance, void *session)
{
QLA_SESSION *my_session = (QLA_SESSION *)session;
close(my_session->fd);
}
/**
* Free the memory associated with the session
*
* @param instance The filter instance
* @param session The filter session
*/
static void
freeSession(FILTER *instance, void *session)
{
QLA_SESSION *my_session = (QLA_SESSION *)session;
free(my_session->filename);
free(session);
return;
}
/**
* Set the downstream filter or router to which queries will be
* passed from this filter.
*
* @param instance The filter instance data
* @param session The filter session
* @param downstream The downstream filter or router.
*/
static void
setDownstream(FILTER *instance, void *session, DOWNSTREAM *downstream)
{
QLA_SESSION *my_session = (QLA_SESSION *)session;
my_session->down = *downstream;
}
/**
* The routeQuery entry point. This is passed the query buffer
* to which the filter should be applied. Once applied the
* query should normally be passed to the downstream component
* (filter or router) in the filter chain.
*
* @param instance The filter instance data
* @param session The filter session
* @param queue The query data
*/
static int
routeQuery(FILTER *instance, void *session, GWBUF *queue)
{
QLA_SESSION *my_session = (QLA_SESSION *)session;
char *ptr, t_buf[40];
int length;
struct tm t;
struct timeval tv;
if (modutil_extract_SQL(queue, &ptr, &length))
{
gettimeofday(&tv, NULL);
localtime_r(&tv.tv_sec, &t);
sprintf(t_buf, "%02d:%02d:%02d.%-3d %d/%02d/%d, ",
t.tm_hour, t.tm_min, t.tm_sec, (int)(tv.tv_usec / 1000),
t.tm_mday, t.tm_mon + 1, 1900 + t.tm_year);
write(my_session->fd, t_buf, strlen(t_buf));
write(my_session->fd, ptr, length);
write(my_session->fd, "\n", 1);
}
/* Pass the query downstream */
return my_session->down.routeQuery(my_session->down.instance,
my_session->down.session, queue);
}
/**
* Diagnostics routine
*
* If fsession is NULL then print diagnostics on the filter
* instance as a whole, otherwise print diagnostics for the
* particular session.
*
* @param instance The filter instance
* @param fsession Filter session, may be NULL
* @param dcb The DCB for diagnostic output
*/
static void
diagnostic(FILTER *instance, void *fsession, DCB *dcb)
{
QLA_SESSION *my_session = (QLA_SESSION *)fsession;
if (my_session)
{
dcb_printf(dcb, "\t\tLogging to file %s.\n",
my_session->filename);
}
}

View File

@ -0,0 +1,355 @@
/*
* This file is distributed as part of MaxScale by SkySQL. 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 2014
*/
#include <stdio.h>
#include <filter.h>
#include <modinfo.h>
#include <modutil.h>
#include <string.h>
#include <regex.h>
/**
* regexfilter.c - a very simple regular expression rewrite filter.
*
* A simple regular expression query rewrite filter.
* Two parameters should be defined in the filter configuration
* match=<regular expression>
* replace=<replacement text>
*/
MODULE_INFO info = {
MODULE_API_FILTER,
MODULE_ALPHA_RELEASE,
FILTER_VERSION,
"A query rewrite filter that uses regular expressions to rewite queries"
};
static char *version_str = "V1.0.0";
static FILTER *createInstance(char **options, FILTER_PARAMETER **params);
static void *newSession(FILTER *instance, SESSION *session);
static void closeSession(FILTER *instance, void *session);
static void freeSession(FILTER *instance, void *session);
static void setDownstream(FILTER *instance, void *fsession, DOWNSTREAM *downstream);
static int routeQuery(FILTER *instance, void *fsession, GWBUF *queue);
static void diagnostic(FILTER *instance, void *fsession, DCB *dcb);
static char *regex_replace(char *sql, int length, regex_t *re, char *replace);
static FILTER_OBJECT MyObject = {
createInstance,
newSession,
closeSession,
freeSession,
setDownstream,
routeQuery,
diagnostic,
};
/**
* Instance structure
*/
typedef struct {
char *match; /* Regular expression to match */
char *replace; /* Replacement text */
regex_t re; /* Compiled regex text */
} REGEX_INSTANCE;
/**
* The session structure for this regex filter
*/
typedef struct {
DOWNSTREAM down;
int no_change;
int replacements;
} REGEX_SESSION;
/**
* 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()
{
}
/**
* 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
*/
FILTER_OBJECT *
GetModuleObject()
{
return &MyObject;
}
/**
* Create an instance of the filter for a particular service
* within MaxScale.
*
* @param options The options for this filter
*
* @return The instance data for this new instance
*/
static FILTER *
createInstance(char **options, FILTER_PARAMETER **params)
{
REGEX_INSTANCE *my_instance;
int i;
if ((my_instance = calloc(1, sizeof(REGEX_INSTANCE))) != NULL)
{
my_instance->match = NULL;
my_instance->replace = NULL;
for (i = 0; params[i]; i++)
{
if (!strcmp(params[i]->name, "match"))
my_instance->match = strdup(params[i]->value);
if (!strcmp(params[i]->name, "replace"))
my_instance->replace = strdup(params[i]->value);
}
if (my_instance->match == NULL || my_instance->replace == NULL)
{
return NULL;
}
if (regcomp(&my_instance->re, my_instance->match, REG_ICASE))
{
free(my_instance->match);
free(my_instance->replace);
free(my_instance);
return NULL;
}
}
return (FILTER *)my_instance;
}
/**
* Associate a new session with this instance of the filter.
*
* @param instance The filter instance data
* @param session The session itself
* @return Session specific data for this session
*/
static void *
newSession(FILTER *instance, SESSION *session)
{
REGEX_SESSION *my_session;
if ((my_session = calloc(1, sizeof(REGEX_SESSION))) != NULL)
{
my_session->no_change = 0;
my_session->replacements = 0;
}
return my_session;
}
/**
* Close a session with the filter, this is the mechanism
* by which a filter may cleanup data structure etc.
*
* @param instance The filter instance data
* @param session The session being closed
*/
static void
closeSession(FILTER *instance, void *session)
{
}
/**
* Free the memory associated with this filter session.
*
* @param instance The filter instance data
* @param session The session being closed
*/
static void
freeSession(FILTER *instance, void *session)
{
free(session);
return;
}
/**
* Set the downstream component for this filter.
*
* @param instance The filter instance data
* @param session The session being closed
* @param downstream The downstream filter or router
*/
static void
setDownstream(FILTER *instance, void *session, DOWNSTREAM *downstream)
{
REGEX_SESSION *my_session = (REGEX_SESSION *)session;
my_session->down = *downstream;
}
/**
* The routeQuery entry point. This is passed the query buffer
* to which the filter should be applied. Once applied the
* query shoudl normally be passed to the downstream component
* (filter or router) in the filter chain.
*
* @param instance The filter instance data
* @param session The filter session
* @param queue The query data
*/
static int
routeQuery(FILTER *instance, void *session, GWBUF *queue)
{
REGEX_INSTANCE *my_instance = (REGEX_INSTANCE *)instance;
REGEX_SESSION *my_session = (REGEX_SESSION *)session;
char *sql, *newsql;
int length;
if (modutil_is_SQL(queue))
{
modutil_extract_SQL(queue, &sql, &length);
newsql = regex_replace(sql, length, &my_instance->re,
my_instance->replace);
if (newsql)
{
queue = modutil_replace_SQL(queue, newsql);
free(newsql);
my_session->replacements++;
}
else
my_session->no_change++;
}
return my_session->down.routeQuery(my_session->down.instance,
my_session->down.session, queue);
}
/**
* Diagnostics routine
*
* If fsession is NULL then print diagnostics on the filter
* instance as a whole, otherwise print diagnostics for the
* particular session.
*
* @param instance The filter instance
* @param fsession Filter session, may be NULL
* @param dcb The DCB for diagnostic output
*/
static void
diagnostic(FILTER *instance, void *fsession, DCB *dcb)
{
REGEX_INSTANCE *my_instance = (REGEX_INSTANCE *)instance;
REGEX_SESSION *my_session = (REGEX_SESSION *)fsession;
dcb_printf(dcb, "\t\tSearch and replace: s/%s/%s/\n",
my_instance->match, my_instance->replace);
if (my_session)
{
dcb_printf(dcb, "\t\tNo. of queries unaltered by filter: %d\n",
my_session->no_change);
dcb_printf(dcb, "\t\tNo. of queries altered by filter: %d\n",
my_session->replacements);
}
}
/**
* Perform a regular expression match and subsititution on the SQL
*
* @param sql The original SQL text
* @param length The length of the SQL text
* @param re The compiled regular expression
* @param replace The replacement text
* @return The replaced text or NULL if no replacement was done.
*/
static char *
regex_replace(char *sql, int length, regex_t *re, char *replace)
{
char *orig, *result, *ptr;
int i, res_size, res_length, rep_length;
int last_match;
regmatch_t match[10];
orig = strndup(sql, length);
if (regexec(re, orig, 10, match, 0))
{
free(orig);
return NULL;
}
res_size = 2 * length;
result = (char *)malloc(res_size);
res_length = 0;
rep_length = strlen(replace);
last_match = 0;
for (i = 0; i < 10; i++)
{
if (match[i].rm_so != -1)
{
ptr = &result[res_length];
if (last_match < match[i].rm_so)
{
int to_copy = match[i].rm_so - last_match;
if (last_match + to_copy > res_size)
{
res_size = last_match + to_copy + length;
result = (char *)realloc(result, res_size);
}
memcpy(ptr, &sql[last_match], to_copy);
res_length += to_copy;
}
last_match = match[i].rm_eo;
if (res_length + rep_length > res_size)
{
res_size += rep_length;
result = (char *)realloc(result, res_size);
}
ptr = &result[res_length];
memcpy(ptr, replace, rep_length);
res_length += rep_length;
}
}
if (last_match < length)
{
int to_copy = length - last_match;
if (last_match + to_copy > res_size)
{
res_size = last_match + to_copy + 1;
result = (char *)realloc(result, res_size);
}
ptr = &result[res_length];
memcpy(ptr, &sql[last_match], to_copy);
res_length += to_copy;
}
result[res_length] = 0;
return result;
}

View File

@ -0,0 +1,234 @@
/*
* This file is distributed as part of MaxScale by SkySQL. 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 2014
*/
#include <stdio.h>
#include <filter.h>
#include <modinfo.h>
#include <modutil.h>
/**
* testfilter.c - a very simple test filter.
*
* This filter is a very simple example used to test the filter API,
* it merely counts the number of statements that flow through the
* filter pipeline.
*
* Reporting is done via the diagnostics print routine.
*/
MODULE_INFO info = {
MODULE_API_FILTER,
MODULE_ALPHA_RELEASE,
FILTER_VERSION,
"A simple query counting filter"
};
static char *version_str = "V1.0.0";
static FILTER *createInstance(char **options, FILTER_PARAMETER **params);
static void *newSession(FILTER *instance, SESSION *session);
static void closeSession(FILTER *instance, void *session);
static void freeSession(FILTER *instance, void *session);
static void setDownstream(FILTER *instance, void *fsession, DOWNSTREAM *downstream);
static int routeQuery(FILTER *instance, void *fsession, GWBUF *queue);
static void diagnostic(FILTER *instance, void *fsession, DCB *dcb);
static FILTER_OBJECT MyObject = {
createInstance,
newSession,
closeSession,
freeSession,
setDownstream,
routeQuery,
diagnostic,
};
/**
* A dummy instance structure
*/
typedef struct {
int sessions;
} TEST_INSTANCE;
/**
* A dummy session structure for this test filter
*/
typedef struct {
DOWNSTREAM down;
int count;
} TEST_SESSION;
/**
* 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()
{
}
/**
* 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
*/
FILTER_OBJECT *
GetModuleObject()
{
return &MyObject;
}
/**
* Create an instance of the filter for a particular service
* within MaxScale.
*
* @param options The options for this filter
*
* @return The instance data for this new instance
*/
static FILTER *
createInstance(char **options, FILTER_PARAMETER **params)
{
TEST_INSTANCE *my_instance;
if ((my_instance = calloc(1, sizeof(TEST_INSTANCE))) != NULL)
my_instance->sessions = 0;
return (FILTER *)my_instance;
}
/**
* Associate a new session with this instance of the filter.
*
* @param instance The filter instance data
* @param session The session itself
* @return Session specific data for this session
*/
static void *
newSession(FILTER *instance, SESSION *session)
{
TEST_INSTANCE *my_instance = (TEST_INSTANCE *)instance;
TEST_SESSION *my_session;
if ((my_session = calloc(1, sizeof(TEST_SESSION))) != NULL)
{
my_instance->sessions++;
my_session->count = 0;
}
return my_session;
}
/**
* Close a session with the filter, this is the mechanism
* by which a filter may cleanup data structure etc.
*
* @param instance The filter instance data
* @param session The session being closed
*/
static void
closeSession(FILTER *instance, void *session)
{
}
/**
* Free the memory associated with this filter session.
*
* @param instance The filter instance data
* @param session The session being closed
*/
static void
freeSession(FILTER *instance, void *session)
{
free(session);
return;
}
/**
* Set the downstream component for this filter.
*
* @param instance The filter instance data
* @param session The session being closed
* @param downstream The downstream filter or router
*/
static void
setDownstream(FILTER *instance, void *session, DOWNSTREAM *downstream)
{
TEST_SESSION *my_session = (TEST_SESSION *)session;
my_session->down = *downstream;
}
/**
* The routeQuery entry point. This is passed the query buffer
* to which the filter should be applied. Once applied the
* query shoudl normally be passed to the downstream component
* (filter or router) in the filter chain.
*
* @param instance The filter instance data
* @param session The filter session
* @param queue The query data
*/
static int
routeQuery(FILTER *instance, void *session, GWBUF *queue)
{
TEST_SESSION *my_session = (TEST_SESSION *)session;
if (modutil_is_SQL(queue))
my_session->count++;
return my_session->down.routeQuery(my_session->down.instance,
my_session->down.session, queue);
}
/**
* Diagnostics routine
*
* If fsession is NULL then print diagnostics on the filter
* instance as a whole, otherwise print diagnostics for the
* particular session.
*
* @param instance The filter instance
* @param fsession Filter session, may be NULL
* @param dcb The DCB for diagnostic output
*/
static void
diagnostic(FILTER *instance, void *fsession, DCB *dcb)
{
TEST_INSTANCE *my_instance = (TEST_INSTANCE *)instance;
TEST_SESSION *my_session = (TEST_SESSION *)fsession;
if (my_session)
dcb_printf(dcb, "\t\tNo. of queries routed by filter: %d\n",
my_session->count);
else
dcb_printf(dcb, "\t\tNo. of sessions created: %d\n",
my_instance->sessions);
}

View File

@ -236,7 +236,6 @@ typedef struct router_instance {
} ROUTER_INSTANCE;
#define BACKEND_TYPE(b) (SERVER_IS_MASTER((b)->backend_server) ? BE_MASTER : \
(SERVER_IS_SLAVE((b)->backend_server) ? BE_SLAVE : \
(SERVER_IS_JOINED((b)->backend_server) ? BE_JOINED : BE_UNDEFINED)));
(SERVER_IS_SLAVE((b)->backend_server) ? BE_SLAVE : BE_UNDEFINED));
#endif /*< _RWSPLITROUTER_H */

View File

@ -26,6 +26,9 @@
* 22/07/13 Mark Riddoch Initial implementation
* 21/05/14 Massimiliano Pinto Monitor sets a master server
* that has the lowest value of wsrep_local_index
* 23/05/14 Massimiliano Pinto Added 1 configuration option (setInterval).
* Interval is printed in diagnostics.
* 03/06/14 Mark Riddoch Add support for maintenance mode
*
* @endverbatim
*/
@ -42,12 +45,20 @@
#include <log_manager.h>
#include <secrets.h>
#include <dcb.h>
#include <modinfo.h>
extern int lm_enabled_logfiles_bitmask;
static void monitorMain(void *);
static char *version_str = "V1.1.0";
static char *version_str = "V1.2.0";
MODULE_INFO info = {
MODULE_API_MONITOR,
MODULE_ALPHA_RELEASE,
MONITOR_VERSION,
"A Galera cluster monitor"
};
static void *startMonitor(void *);
static void stopMonitor(void *);
@ -55,8 +66,9 @@ 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 *, unsigned long);
static MONITOR_OBJECT MyObject = { startMonitor, stopMonitor, registerServer, unregisterServer, defaultUsers, diagnostics };
static MONITOR_OBJECT MyObject = { startMonitor, stopMonitor, registerServer, unregisterServer, defaultUsers, diagnostics, setInterval, NULL, NULL };
/**
* Implementation of the mandatory version entry point
@ -121,6 +133,8 @@ MYSQL_MONITOR *handle;
handle->shutdown = 0;
handle->defaultUser = NULL;
handle->defaultPasswd = NULL;
handle->id = MONITOR_DEFAULT_ID;
handle->interval = MONITOR_INTERVAL;
spinlock_init(&handle->lock);
}
handle->tid = (THREAD)thread_start(monitorMain, handle);
@ -236,7 +250,10 @@ char *sep;
dcb_printf(dcb, "\tMonitor stopped\n");
break;
}
dcb_printf(dcb,"\tSampling interval:\t%lu milliseconds\n", handle->interval);
dcb_printf(dcb, "\tMonitored servers: ");
db = handle->databases;
sep = "";
while (db)
@ -293,13 +310,29 @@ char *server_string;
if (uname == NULL)
return;
/* Don't even probe server flagged as in maintenance */
if (SERVER_IN_MAINT(database->server))
return;
if (database->con == NULL || mysql_ping(database->con) != 0)
{
char *dpwd = decryptPassword(passwd);
int rc;
int read_timeout = 1;
database->con = mysql_init(NULL);
rc = mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout);
if (mysql_real_connect(database->con, database->server->name,
uname, dpwd, NULL, database->server->port, NULL, 0) == NULL)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Monitor was unable to connect to "
"server %s:%d : \"%s\"",
database->server->name,
database->server->port,
mysql_error(database->con))));
server_clear_status(database->server, SERVER_RUNNING);
database->server->node_id = -1;
free(dpwd);
@ -395,11 +428,12 @@ long master_id;
while (ptr)
{
unsigned int prev_status = ptr->server->status;
monitorDatabase(ptr, handle->defaultUser, handle->defaultPasswd);
/* set master_id to the lowest value of ptr->server->node_id */
if (ptr->server->node_id >= 0 && SERVER_IS_JOINED(ptr->server)) {
if ((! SERVER_IN_MAINT(ptr->server)) && ptr->server->node_id >= 0 && SERVER_IS_JOINED(ptr->server)) {
if (ptr->server->node_id < master_id && master_id >= 0) {
master_id = ptr->server->node_id;
} else {
@ -407,11 +441,21 @@ long master_id;
master_id = ptr->server->node_id;
}
}
} else {
} else if (!SERVER_IN_MAINT(ptr->server)) {
/* clear M/S status */
server_clear_status(ptr->server, SERVER_SLAVE);
server_clear_status(ptr->server, SERVER_MASTER);
}
if (ptr->server->status != prev_status ||
SERVER_IS_DOWN(ptr->server))
{
LOGIF(LM, (skygw_log_write_flush(
LOGFILE_MESSAGE,
"Backend server %s:%d state : %s",
ptr->server->name,
ptr->server->port,
STRSRVSTATUS(ptr->server))));
}
ptr = ptr->next;
}
@ -420,7 +464,7 @@ long master_id;
/* this server loop sets Master and Slave roles */
while (ptr)
{
if (ptr->server->node_id >= 0 && master_id >= 0) {
if ((! SERVER_IN_MAINT(ptr->server)) && ptr->server->node_id >= 0 && master_id >= 0) {
/* set the Master role */
if (SERVER_IS_JOINED(ptr->server) && (ptr->server->node_id == master_id)) {
server_set_status(ptr->server, SERVER_MASTER);
@ -434,6 +478,19 @@ long master_id;
ptr = ptr->next;
}
thread_millisleep(MONITOR_INTERVAL);
thread_millisleep(handle->interval);
}
}
/**
* 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, unsigned long interval)
{
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
memcpy(&handle->interval, &interval, sizeof(unsigned long));
}

View File

@ -30,6 +30,9 @@
* diagnostic interface
* 20/05/14 Massimiliano Pinto Addition of support for MariadDB multimaster replication setup.
* New server field version_string is updated.
* 28/05/14 Massimiliano Pinto Added set Id and configuration options (setInverval)
* Parameters are now printed in diagnostics
* 03/06/14 Mark Ridoch Add support for maintenance mode
*
* @endverbatim
*/
@ -46,12 +49,20 @@
#include <log_manager.h>
#include <secrets.h>
#include <dcb.h>
#include <modinfo.h>
extern int lm_enabled_logfiles_bitmask;
static void monitorMain(void *);
static char *version_str = "V1.1.0";
static char *version_str = "V1.2.0";
MODULE_INFO info = {
MODULE_API_MONITOR,
MODULE_ALPHA_RELEASE,
MONITOR_VERSION,
"A MySQL Master/Slave replication monitor"
};
static void *startMonitor(void *);
static void stopMonitor(void *);
@ -59,8 +70,11 @@ 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 *, unsigned long);
static void defaultId(void *, unsigned long);
static void replicationHeartbeat(void *, int);
static MONITOR_OBJECT MyObject = { startMonitor, stopMonitor, registerServer, unregisterServer, defaultUser, diagnostics };
static MONITOR_OBJECT MyObject = { startMonitor, stopMonitor, registerServer, unregisterServer, defaultUser, diagnostics, setInterval, defaultId, replicationHeartbeat };
/**
* Implementation of the mandatory version entry point
@ -126,6 +140,8 @@ MYSQL_MONITOR *handle;
handle->shutdown = 0;
handle->defaultUser = NULL;
handle->defaultPasswd = NULL;
handle->id = MONITOR_DEFAULT_ID;
handle->interval = MONITOR_INTERVAL;
spinlock_init(&handle->lock);
}
handle->tid = (THREAD)thread_start(monitorMain, handle);
@ -261,7 +277,12 @@ char *sep;
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, "\tMonitored servers: ");
db = handle->databases;
sep = "";
while (db)
@ -280,13 +301,13 @@ char *sep;
/**
* Monitor an individual server
*
* @param handle The MySQL Monitor object
* @param database The database to probe
* @param defaultUser Default username for the monitor
* @param defaultPasswd Default password for the monitor
*/
static void
monitorDatabase(MONITOR_SERVERS *database, char *defaultUser, char *defaultPasswd)
monitorDatabase(MYSQL_MONITOR *handle, MONITOR_SERVERS *database)
{
<<<<<<< HEAD
MYSQL_ROW row;
MYSQL_RES *result;
int num_fields;
@ -298,6 +319,21 @@ static int conn_err_count;
static int modval = 10;
if (database->server->monuser != NULL)
=======
MYSQL_ROW row;
MYSQL_RES *result;
int num_fields;
int ismaster = 0, isslave = 0;
char *uname = handle->defaultUser, *passwd = handle->defaultPasswd;
unsigned long int server_version = 0;
char *server_string;
unsigned long id = handle->id;
int replication_heartbeat = handle->replicationHeartbeat;
static int conn_err_count;
static int modval = 10;
if (database->server->monuser != NULL)
>>>>>>> develop
{
uname = database->server->monuser;
passwd = database->server->monpw;
@ -306,13 +342,24 @@ static int modval = 10;
if (uname == NULL)
return;
<<<<<<< HEAD
=======
/* Don't probe servers in maintenance mode */
if (SERVER_IN_MAINT(database->server))
return;
>>>>>>> develop
if (database->con == NULL || mysql_ping(database->con) != 0)
{
char *dpwd = decryptPassword(passwd);
int rc;
int read_timeout = 1;
<<<<<<< HEAD
database->con = mysql_init(NULL);
=======
database->con = mysql_init(NULL);
>>>>>>> develop
rc = mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout);
if (mysql_real_connect(database->con,
@ -324,6 +371,7 @@ static int modval = 10;
NULL,
0) == NULL)
{
<<<<<<< HEAD
if (conn_err_count%modval == 0)
{
LOGIF(LE, (skygw_log_write_flush(
@ -340,6 +388,16 @@ static int modval = 10;
{
conn_err_count += 1;
}
=======
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Monitor was unable to connect to "
"server %s:%d : \"%s\"",
database->server->name,
database->server->port,
mysql_error(database->con))));
>>>>>>> develop
free(dpwd);
server_clear_status(database->server, SERVER_RUNNING);
@ -386,15 +444,112 @@ static int modval = 10;
{
/* Log lack of permission */
}
}
else if ((result = mysql_store_result(database->con)) != NULL)
{
database->server->rlag = -1;
} else if ((result = mysql_store_result(database->con)) != NULL) {
num_fields = mysql_num_fields(result);
while ((row = mysql_fetch_row(result)))
{
ismaster = 1;
}
mysql_free_result(result);
if (ismaster && replication_heartbeat == 1) {
time_t heartbeat;
time_t purge_time;
char heartbeat_insert_query[128]="";
char heartbeat_purge_query[128]="";
handle->master_id = database->server->node_id;
/* create the maxscale_schema database */
if (mysql_query(database->con, "CREATE DATABASE IF NOT EXISTS maxscale_schema")) {
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"[mysql_mon]: Error creating maxscale_schema database in Master server"
": %s", mysql_error(database->con))));
database->server->rlag = -1;
}
/* create repl_heartbeat table in maxscale_schema database */
if (mysql_query(database->con, "CREATE TABLE IF NOT EXISTS "
"maxscale_schema.replication_heartbeat "
"(maxscale_id INT NOT NULL, "
"master_server_id INT NOT NULL, "
"master_timestamp INT UNSIGNED NOT NULL, "
"PRIMARY KEY ( master_server_id, maxscale_id ) ) "
"ENGINE=MYISAM DEFAULT CHARSET=latin1")) {
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"[mysql_mon]: Error creating maxscale_schema.replication_heartbeat table in Master server"
": %s", mysql_error(database->con))));
database->server->rlag = -1;
}
/* auto purge old values after 48 hours*/
purge_time = time(0) - (3600 * 48);
sprintf(heartbeat_purge_query, "DELETE FROM maxscale_schema.replication_heartbeat WHERE master_timestamp < %lu", purge_time);
if (mysql_query(database->con, heartbeat_purge_query)) {
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"[mysql_mon]: Error deleting from maxscale_schema.replication_heartbeat table: [%s], %s",
heartbeat_purge_query,
mysql_error(database->con))));
}
heartbeat = time(0);
/* set node_ts for master as time(0) */
database->server->node_ts = heartbeat;
sprintf(heartbeat_insert_query, "UPDATE maxscale_schema.replication_heartbeat SET master_timestamp = %lu WHERE master_server_id = %i AND maxscale_id = %lu", heartbeat, handle->master_id, id);
/* Try to insert MaxScale timestamp into master */
if (mysql_query(database->con, heartbeat_insert_query)) {
database->server->rlag = -1;
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"[mysql_mon]: Error updating maxscale_schema.replication_heartbeat table: [%s], %s",
heartbeat_insert_query,
mysql_error(database->con))));
} else {
if (mysql_affected_rows(database->con) == 0) {
heartbeat = time(0);
sprintf(heartbeat_insert_query, "REPLACE INTO maxscale_schema.replication_heartbeat (master_server_id, maxscale_id, master_timestamp ) VALUES ( %i, %lu, %lu)", handle->master_id, id, heartbeat);
if (mysql_query(database->con, heartbeat_insert_query)) {
database->server->rlag = -1;
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"[mysql_mon]: Error inserting into maxscale_schema.replication_heartbeat table: [%s], %s",
heartbeat_insert_query,
mysql_error(database->con))));
} else {
/* Set replication lag to 0 for the master */
database->server->rlag = 0;
LOGIF(LD, (skygw_log_write_flush(
LOGFILE_DEBUG,
"[mysql_mon]: heartbeat table inserted data for %s:%i", database->server->name, database->server->port)));
}
} else {
/* Set replication lag as 0 for the master */
database->server->rlag = 0;
LOGIF(LD, (skygw_log_write_flush(
LOGFILE_DEBUG,
"[mysql_mon]: heartbeat table updated for %s:%i", database->server->name, database->server->port)));
}
}
}
}
/* Check if the Slave_SQL_Running and Slave_IO_Running status is
@ -439,6 +594,80 @@ static int modval = 10;
}
}
/* Get the master_timestamp value from maxscale_schema.replication_heartbeat table */
if (isslave && replication_heartbeat == 1) {
time_t heartbeat;
char select_heartbeat_query[256] = "";
sprintf(select_heartbeat_query, "SELECT master_timestamp "
"FROM maxscale_schema.replication_heartbeat "
"WHERE maxscale_id = %lu AND master_server_id = %i",
id, handle->master_id);
/* if there is a master then send the query to the slave with master_id*/
if (handle->master_id >= 0 && (mysql_query(database->con, select_heartbeat_query) == 0
&& (result = mysql_store_result(database->con)) != NULL)) {
num_fields = mysql_num_fields(result);
while ((row = mysql_fetch_row(result))) {
int rlag = -1;
time_t slave_read;
heartbeat = time(0);
slave_read = strtoul(row[0], NULL, 10);
if ((errno == ERANGE && (slave_read == LONG_MAX || slave_read == LONG_MIN)) || (errno != 0 && slave_read == 0)) {
slave_read = 0;
}
if (slave_read) {
/* set the replication lag */
rlag = heartbeat - slave_read;
}
/* set this node_ts as master_timestamp read from replication_heartbeat table */
database->server->node_ts = slave_read;
if (rlag >= 0) {
/* store rlag only if greater than monitor sampling interval */
database->server->rlag = (rlag > (handle->interval / 1000)) ? rlag : 0;
} else {
database->server->rlag = -1;
}
LOGIF(LD, (skygw_log_write_flush(
LOGFILE_DEBUG,
"[mysql_mon]: replication heartbeat: "
"server %s:%i is %i seconds behind master",
database->server->name,
database->server->port,
database->server->rlag)));
}
mysql_free_result(result);
} else {
database->server->rlag = -1;
database->server->node_ts = 0;
if (handle->master_id < 0) {
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"[mysql_mon]: error: replication heartbeat: "
"master_server_id NOT available for %s:%i",
database->server->name,
database->server->port)));
} else {
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"[mysql_mon]: error: replication heartbeat: "
"failed selecting from hearthbeat table of %s:%i : [%s], %s",
database->server->name,
database->server->port,
select_heartbeat_query,
mysql_error(database->con))));
}
}
}
if (ismaster)
{
server_set_status(database->server, SERVER_MASTER);
@ -492,11 +721,18 @@ static int modval = 10;
{
unsigned int prev_status = ptr->server->status;
<<<<<<< HEAD
monitorDatabase(ptr, handle->defaultUser, handle->defaultPasswd);
if (ptr->server->status != prev_status ||
(SERVER_IS_DOWN(ptr->server) &&
err_count%modval == 0))
=======
monitorDatabase(handle, ptr);
if (ptr->server->status != prev_status ||
SERVER_IS_DOWN(ptr->server))
>>>>>>> develop
{
LOGIF(LM, (skygw_log_write_flush(
LOGFILE_MESSAGE,
@ -504,6 +740,7 @@ static int modval = 10;
ptr->server->name,
ptr->server->port,
STRSRVSTATUS(ptr->server))));
<<<<<<< HEAD
err_count = 0;
modval += 1;
}
@ -511,8 +748,51 @@ static int modval = 10;
{
err_count += 1;
}
=======
}
>>>>>>> develop
ptr = ptr->next;
}
thread_millisleep(10000);
thread_millisleep(handle->interval);
}
}
/**
* Set the default id to use in the monitor.
*
* @param arg The handle allocated by startMonitor
* @param id The id to set in monitor struct
*/
static void
defaultId(void *arg, unsigned long id)
{
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, unsigned long 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.
*
* @param arg The handle allocated by startMonitor
* @param replicationHeartbeat To enable it 1, disable it with 0
*/
static void
replicationHeartbeat(void *arg, int replicationHeartbeat)
{
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
memcpy(&handle->replicationHeartbeat, &replicationHeartbeat, sizeof(int));
}

View File

@ -30,6 +30,7 @@
* Date Who Description
* 08/07/13 Mark Riddoch Initial implementation
* 26/05/14 Massimiliano Pinto Default values for MONITOR_INTERVAL
* 28/05/14 Massimiliano Pinto Addition of new fields in MYSQL_MONITOR struct
*
* @endverbatim
*/
@ -55,6 +56,10 @@ typedef struct {
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 master_id; /**< Master server-id for MySQL Master/Slave replication */
MONITOR_SERVERS *databases; /**< Linked list of servers to monitor */
} MYSQL_MONITOR;
@ -63,5 +68,6 @@ typedef struct {
#define MONITOR_STOPPED 3
#define MONITOR_INTERVAL 10000 // in milliseconds
#define MONITOR_DEFAULT_ID 1UL // unsigned long value
#endif

View File

@ -39,6 +39,14 @@
#include <httpd.h>
#include <gw.h>
#include <modinfo.h>
MODULE_INFO info = {
MODULE_API_PROTOCOL,
MODULE_IN_DEVELOPMENT,
GWPROTOCOL_VERSION,
"An experimental HTTPD implementation for use in admnistration"
};
#define ISspace(x) isspace((int)(x))
#define HTTP_SERVER_STRING "Gateway(c) v.1.0.0"

View File

@ -43,6 +43,14 @@
* 27/09/2013 Massimiliano Pinto Changed in gw_read_backend_event the check for dcb_read(), now is if rc < 0
*
*/
#include <modinfo.h>
MODULE_INFO info = {
MODULE_API_PROTOCOL,
MODULE_ALPHA_RELEASE,
GWPROTOCOL_VERSION,
"The MySQL to backend server protocol"
};
extern int lm_enabled_logfiles_bitmask;
@ -1016,4 +1024,4 @@ static int gw_session(DCB *backend_dcb, void *data) {
return 1;
}
*/
*/

View File

@ -40,6 +40,14 @@
#include <log_manager.h>
#include <mysql_client_server_protocol.h>
#include <gw.h>
#include <modinfo.h>
MODULE_INFO info = {
MODULE_API_PROTOCOL,
MODULE_ALPHA_RELEASE,
GWPROTOCOL_VERSION,
"The client to MaxScale MySQL protocol implementation"
};
extern int lm_enabled_logfiles_bitmask;
@ -57,11 +65,7 @@ static int gw_client_hangup_event(DCB *dcb);
int mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char* mysql_message);
int MySQLSendHandshake(DCB* dcb);
static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue);
static int route_by_statement(
ROUTER* router_instance,
ROUTER_OBJECT* router,
void* rsession,
GWBUF* read_buf);
static int route_by_statement(SESSION *, GWBUF *);
/*
* The "module object" for the mysqld client protocol module.
@ -791,6 +795,7 @@ int gw_read_client_event(DCB* dcb) {
/** Route COM_QUIT to backend */
if (mysql_command == '\x01') {
<<<<<<< HEAD
#if defined(ERRHANDLE)
/**
* Close router session and that closes
@ -799,7 +804,11 @@ int gw_read_client_event(DCB* dcb) {
*/
dcb_close(dcb);
#else
router->routeQuery(router_instance, rsession, read_buffer);
SESSION_ROUTE_QUERY(session, read_buffer);
// router->routeQuery(router_instance, rsession, read_buffer);
=======
SESSION_ROUTE_QUERY(session, read_buffer);
>>>>>>> develop
LOGIF(LD, (skygw_log_write_flush(
LOGFILE_DEBUG,
"%lu [gw_read_client_event] Routed COM_QUIT to "
@ -818,10 +827,7 @@ int gw_read_client_event(DCB* dcb) {
* Feed each statement completely and separately
* to router.
*/
rc = route_by_statement(router_instance,
router,
rsession,
read_buffer);
rc = route_by_statement(session, read_buffer);
if (read_buffer != NULL)
{
/** add incomplete mysql packet to read queue */
@ -831,9 +837,7 @@ int gw_read_client_event(DCB* dcb) {
else
{
/** Feed whole packet to router */
rc = router->routeQuery(router_instance,
rsession,
read_buffer);
rc = SESSION_ROUTE_QUERY(session, read_buffer);
}
/** succeed */
@ -1436,11 +1440,7 @@ gw_client_hangup_event(DCB *dcb)
* Return 1 in success. If the last packet is incomplete return success but
* leave incomplete packet to readbuf.
*/
static int route_by_statement(
ROUTER* router_instance,
ROUTER_OBJECT* router,
void* rsession,
GWBUF* readbuf)
static int route_by_statement(SESSION *session, GWBUF *readbuf)
{
int rc = -1;
GWBUF* packetbuf;
@ -1452,7 +1452,7 @@ static int route_by_statement(
if (packetbuf != NULL)
{
CHK_GWBUF(packetbuf);
rc = router->routeQuery(router_instance, rsession, packetbuf);
rc = SESSION_ROUTE_QUERY(session, packetbuf);
}
else
{

View File

@ -36,6 +36,14 @@
#include <adminusers.h>
#include <skygw_utils.h>
#include <log_manager.h>
#include <modinfo.h>
MODULE_INFO info = {
MODULE_API_PROTOCOL,
MODULE_ALPHA_RELEASE,
GWPROTOCOL_VERSION,
"A telnet deamon protocol for simple administration interface"
};
extern int lm_enabled_logfiles_bitmask;
@ -140,9 +148,6 @@ telnetd_read_event(DCB* dcb)
int n;
GWBUF *head = NULL;
SESSION *session = dcb->session;
ROUTER_OBJECT *router = session->service->router;
ROUTER *router_instance = session->service->router_instance;
void *rsession = session->router_session;
TELNETD *telnetd = (TELNETD *)dcb->protocol;
char *password, *t;
@ -196,7 +201,7 @@ char *password, *t;
free(password);
break;
case TELNETD_STATE_DATA:
router->routeQuery(router_instance, rsession, head);
SESSION_ROUTE_QUERY(session, head);
break;
}
}

View File

@ -35,6 +35,7 @@
#include <session.h>
#include <router.h>
#include <modules.h>
#include <modinfo.h>
#include <atomic.h>
#include <spinlock.h>
#include <dcb.h>
@ -43,6 +44,14 @@
#include <skygw_utils.h>
#include <log_manager.h>
MODULE_INFO info = {
MODULE_API_ROUTER,
MODULE_ALPHA_RELEASE,
ROUTER_VERSION,
"The debug user interface"
};
extern int lm_enabled_logfiles_bitmask;
static char *version_str = "V1.1.1";

View File

@ -40,6 +40,7 @@
* 20/05/14 Mark Riddoch Added ability to give server and service names rather
* than simply addresses
* 23/05/14 Mark Riddoch Added support for developer and user modes
* 29/05/14 Mark Riddoch Add Filter support
*
* @endverbatim
*/
@ -50,6 +51,7 @@
#include <service.h>
#include <session.h>
#include <router.h>
#include <filter.h>
#include <modules.h>
#include <atomic.h>
#include <server.h>
@ -77,6 +79,7 @@
#define ARG_TYPE_SESSION 6
#define ARG_TYPE_DCB 7
#define ARG_TYPE_MONITOR 8
#define ARG_TYPE_FILTER 9
/**
* The subcommand structure
@ -113,6 +116,14 @@ struct subcommand showoptions[] = {
"Show the poll statistics",
"Show the poll statistics",
{0, 0, 0} },
{ "filter", 0, dprintFilter,
"Show details of a filter, called with a filter name",
"Show details of a filter, called with the address of a filter",
{ARG_TYPE_FILTER, 0, 0} },
{ "filters", 0, dprintAllFilters,
"Show all filters",
"Show all filters",
{0, 0, 0} },
{ "modules", 0, dprintAllModules,
"Show all currently loaded modules",
"Show all currently loaded modules",
@ -157,6 +168,10 @@ struct subcommand showoptions[] = {
* The subcommands of the list command
*/
struct subcommand listoptions[] = {
{ "filters", 0, dListFilters,
"List all the filters defined within MaxScale",
"List all the filters defined within MaxScale",
{0, 0, 0} },
{ "listeners", 0, dListListeners,
"List all the listeners defined within MaxScale",
"List all the listeners defined within MaxScale",
@ -493,6 +508,10 @@ SERVICE *service;
if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0)
rval = (unsigned long)monitor_find(arg);
return rval;
case ARG_TYPE_FILTER:
if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0)
rval = (unsigned long)filter_find(arg);
return rval;
}
return 0;
}
@ -516,7 +535,7 @@ execute_cmd(CLI_SESSION *cli)
{
DCB *dcb = cli->session->client;
int argc, i, j, found = 0;
char *args[MAXARGS];
char *args[MAXARGS + 1];
unsigned long arg1, arg2, arg3;
int in_quotes = 0, escape_next = 0;
char *ptr, *lptr;
@ -754,11 +773,13 @@ static struct {
char *str;
unsigned int bit;
} ServerBits[] = {
{ "running", SERVER_RUNNING },
{ "master", SERVER_MASTER },
{ "slave", SERVER_SLAVE },
{ "synced", SERVER_JOINED },
{ NULL, 0 }
{ "running", SERVER_RUNNING },
{ "master", SERVER_MASTER },
{ "slave", SERVER_SLAVE },
{ "synced", SERVER_JOINED },
{ "maintenance", SERVER_MAINT },
{ "maint", SERVER_MAINT },
{ NULL, 0 }
};
/**
* Map the server status bit

View File

@ -79,6 +79,7 @@
#include <readconnection.h>
#include <dcb.h>
#include <spinlock.h>
#include <modinfo.h>
#include <skygw_types.h>
#include <skygw_utils.h>
@ -88,6 +89,13 @@
extern int lm_enabled_logfiles_bitmask;
MODULE_INFO info = {
MODULE_API_ROUTER,
MODULE_ALPHA_RELEASE,
ROUTER_VERSION,
"A connection based router to load balance based on connections"
};
static char *version_str = "V1.0.2";
/* The router entry points */
@ -345,6 +353,9 @@ int master_host = -1;
inst->bitmask)));
}
if (SERVER_IN_MAINT(inst->servers[i]->server))
continue;
/*
* If router_options=slave, get the running master
* It will be used if there are no running slaves at all

View File

@ -30,10 +30,19 @@
#include <query_classifier.h>
#include <dcb.h>
#include <spinlock.h>
#include <modinfo.h>
MODULE_INFO info = {
MODULE_API_ROUTER,
MODULE_ALPHA_RELEASE,
ROUTER_VERSION,
"A Read/Write splitting router for enhancement read scalability"
};
#if defined(SS_DEBUG)
# include <mysql_client_server_protocol.h>
#endif
extern int lm_enabled_logfiles_bitmask;
/**
@ -829,8 +838,7 @@ static bool get_dcb(
BACKEND* b = backend_ref[i].bref_backend;
if (backend_ref[i].bref_state == BREF_IN_USE &&
(SERVER_IS_MASTER(b->backend_server) ||
SERVER_IS_JOINED(b->backend_server)))
(SERVER_IS_MASTER(b->backend_server)))
{
*p_dcb = backend_ref[i].bref_dcb;
succp = true;
@ -1544,8 +1552,7 @@ static bool select_connect_backend_servers(
}
}
else if (!master_connected &&
(SERVER_IS_MASTER(b->backend_server) ||
SERVER_IS_JOINED(b->backend_server)))
(SERVER_IS_MASTER(b->backend_server)))
{
master_found = true;
@ -1659,8 +1666,7 @@ static bool select_connect_backend_servers(
"Selected %s in \t%s:%d",
(btype == BE_MASTER ? "master" :
(btype == BE_SLAVE ? "slave" :
(btype == BE_JOINED ? "galera node" :
"unknown node type"))),
"unknown node type")),
b->backend_server->name,
b->backend_server->port)));
}

View File

@ -17,9 +17,17 @@
*/
#include <stdio.h>
#include <router.h>
#include <modinfo.h>
static char *version_str = "V1.0.0";
MODULE_INFO info = {
MODULE_API_ROUTER,
MODULE_IN_DEVELOPMENT,
ROUTER_VERSION,
"A test router - not for use in real systems"
};
static ROUTER *createInstance(SERVICE *service, char **options);
static void *newSession(ROUTER *instance, SESSION *session);
static void closeSession(ROUTER *instance, void *session);
@ -145,4 +153,4 @@ static uint8_t getCapabilities(
void* router_session)
{
return 0;
}
}