From 3b76ed43c43aa0bc457389c66ea54f0634ad4b9a Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Tue, 3 Mar 2015 13:31:12 +0200 Subject: [PATCH 01/10] Added tee filter multi-statement support. --- server/core/modutil.c | 96 ++++++++++++++++++++++++++++++++- server/include/modutil.h | 3 ++ server/modules/filter/tee.c | 102 ++++++++++++++++++++++++++++++++---- 3 files changed, 189 insertions(+), 12 deletions(-) diff --git a/server/core/modutil.c b/server/core/modutil.c index 9ba57bdf6..5fe936e6a 100644 --- a/server/core/modutil.c +++ b/server/core/modutil.c @@ -568,7 +568,7 @@ modutil_count_signal_packets(GWBUF *reply, int use_ok, int n_found) } } - if((ptr + pktlen) > end) + if((ptr + pktlen) > end || (eof + n_found) >= 2) { ptr = prev; break; @@ -676,3 +676,97 @@ static void modutil_reply_routing_error( poll_add_epollin_event_to_dcb(backend_dcb, buf); return; } + +/** + * Find the first occurrence of a character in a string. This function ignores + * escaped characters and all characters that are enclosed in single or double quotes. + * @param ptr Pointer to area of memory to inspect + * @param c Character to search for + * @param len Size of the memory area + * @return Pointer to the first non-escaped, non-quoted occurrence of the character. + * If the character is not found, NULL is returned. + */ +void* strnchr_esc(char* ptr,char c, int len) +{ + char* p = (char*)ptr; + char* start = p; + bool quoted = false, escaped = false; + char qc; + + while(p < start + len) + { + if(escaped) + { + escaped = false; + } + else if(*p == '\\') + { + escaped = true; + } + else if((*p == '\'' || *p == '"') && !quoted) + { + quoted = true; + qc = *p; + } + else if(quoted && *p == qc) + { + quoted = false; + } + else if(*p == c && !escaped && !quoted) + { + return p; + } + p++; + } + + return NULL; +} + +/** + * Create a COM_QUERY packet from a string. + * @param query Query to create. + * @return Pointer to GWBUF with the query or NULL if an error occurred. + */ +GWBUF* modutil_create_query(char* query) +{ + if(query == NULL) + return NULL; + + GWBUF* rval = gwbuf_alloc(strlen(query) + 5); + int pktlen = strlen(query) + 1; + unsigned char* ptr; + + if(rval) + { + ptr = (unsigned char*)rval->start; + *ptr++ = (pktlen); + *ptr++ = (pktlen)>>8; + *ptr++ = (pktlen)>>16; + *ptr++ = 0x0; + *ptr++ = 0x03; + memcpy(ptr,query,strlen(query)); + gwbuf_set_type(rval,GWBUF_TYPE_MYSQL); + } + + return rval; +} + +/** + * Count the number of statements in a query. + * @param buffer Buffer to analyze. + * @return Number of statements. + */ +int modutil_count_statements(GWBUF* buffer) +{ + char* ptr = ((char*)(buffer)->start + 5); + char* end = ((char*)(buffer)->end); + int num = 1; + + while((ptr = strnchr_esc(ptr,';', end - ptr))) + { + num++; + ptr++; + } + + return num; +} \ No newline at end of file diff --git a/server/include/modutil.h b/server/include/modutil.h index d40779c4c..59fb8d4f7 100644 --- a/server/include/modutil.h +++ b/server/include/modutil.h @@ -33,6 +33,7 @@ */ #include #include +#include #define PTR_IS_RESULTSET(b) (b[0] == 0x01 && b[1] == 0x0 && b[2] == 0x0 && b[3] == 0x01) #define PTR_IS_EOF(b) (b[0] == 0x05 && b[1] == 0x0 && b[2] == 0x0 && b[4] == 0xfe) @@ -52,6 +53,8 @@ GWBUF* modutil_get_complete_packets(GWBUF** p_readbuf); int modutil_MySQL_query_len(GWBUF* buf, int* nbytes_missing); void modutil_reply_parse_error(DCB* backend_dcb, char* errstr, uint32_t flags); void modutil_reply_auth_error(DCB* backend_dcb, char* errstr, uint32_t flags); +int modutil_count_statements(GWBUF* buffer); +GWBUF* modutil_create_query(char* query); GWBUF *modutil_create_mysql_err_msg( int packet_number, diff --git a/server/modules/filter/tee.c b/server/modules/filter/tee.c index 22d210c04..e0a6e51fe 100644 --- a/server/modules/filter/tee.c +++ b/server/modules/filter/tee.c @@ -63,7 +63,6 @@ #include #include - #define MYSQL_COM_QUIT 0x01 #define MYSQL_COM_INITDB 0x02 #define MYSQL_COM_FIELD_LIST 0x04 @@ -73,6 +72,7 @@ #define MYSQL_COM_STMT_SEND_LONG_DATA 0x18 #define MYSQL_COM_STMT_CLOSE 0x19 #define MYSQL_COM_STMT_RESET 0x1a +#define MYSQL_COM_CONNECT 0x1b #define REPLY_TIMEOUT_SECOND 5 #define REPLY_TIMEOUT_MILLISECOND 1 @@ -93,6 +93,7 @@ static unsigned char required_packets[] = { MYSQL_COM_STMT_SEND_LONG_DATA, MYSQL_COM_STMT_CLOSE, MYSQL_COM_STMT_RESET, + MYSQL_COM_CONNECT, 0 }; /** Defined in log_manager.cc */ @@ -168,15 +169,22 @@ typedef struct { bool waiting[2]; /* if the client is waiting for a reply */ int eof[2]; int replies[2]; /* Number of queries received */ + int reply_packets[2]; /* Number of OK, ERR, LOCAL_INFILE_REQUEST or RESULT_SET packets received */ DCB *branch_dcb; /* Client DCB for "branch" service */ SESSION *branch_session;/* The branch service session */ + TEE_INSTANCE *instance; int n_duped; /* Number of duplicated queries */ int n_rejected; /* Number of rejected queries */ int residual; /* Any outstanding SQL text */ GWBUF* tee_replybuf; /* Buffer for reply */ GWBUF* tee_partials[2]; + GWBUF* querybuf; SPINLOCK tee_lock; DCB* client_dcb; + int statements; /*< Number of statements in the query, + * used to identify and track multi-statement + * queries and that both the parent and the child + * branch are in sync. */ #ifdef SS_DEBUG long d_id; #endif @@ -184,7 +192,8 @@ typedef struct { typedef struct orphan_session_tt { - SESSION* session; + SESSION* session; /*< The child branch session whose parent was freed before + * the child session was in a suitable state. */ struct orphan_session_tt* next; }orphan_session_t; @@ -198,6 +207,7 @@ static orphan_session_t* allOrphans = NULL; static SPINLOCK orphanLock; static int packet_is_required(GWBUF *queue); static int detect_loops(TEE_INSTANCE *instance, HASHTABLE* ht, SERVICE* session); +int internal_route(DCB* dcb); static int hkfn( void* key) @@ -498,7 +508,11 @@ char *remote, *userName; { my_session->active = 1; my_session->residual = 0; + my_session->statements = 0; + my_session->tee_replybuf = NULL; my_session->client_dcb = session->client; + my_session->instance = my_instance; + spinlock_init(&my_session->tee_lock); if (my_instance->source && (remote = session_get_remote(session)) != NULL) @@ -544,7 +558,7 @@ char *remote, *userName; goto retblock; } - + if((dummy = filter_alloc("tee_dummy","tee_dummy")) == NULL) { dcb_close(dcb); @@ -641,6 +655,7 @@ skygw_log_write(LOGFILE_TRACE,"Tee close: %d", atomic_add(&debug_seq,1)); #endif if (my_session->active) { + if ((bsession = my_session->branch_session) != NULL) { CHK_SESSION(bsession); @@ -675,7 +690,8 @@ skygw_log_write(LOGFILE_TRACE,"Tee close: %d", atomic_add(&debug_seq,1)); } } - my_session->active = 0; + + my_session->active = 0; } } @@ -800,11 +816,14 @@ char *ptr; int length, rval, residual = 0; GWBUF *clone = NULL; unsigned char command = *((unsigned char*)queue->start + 4); + #ifdef SS_DEBUG skygw_log_write(LOGFILE_TRACE,"Tee routeQuery: %d : %s", atomic_add(&debug_seq,1), ((char*)queue->start + 5)); #endif + + spinlock_acquire(&my_session->tee_lock); if(!my_session->active) @@ -871,8 +890,10 @@ if(!my_session->active) } memset(my_session->replies,0,2*sizeof(int)); + memset(my_session->reply_packets,0,2*sizeof(int)); memset(my_session->eof,0,2*sizeof(int)); memset(my_session->waiting,1,2*sizeof(bool)); + my_session->statements = modutil_count_statements(queue); my_session->command = command; #ifdef SS_DEBUG spinlock_acquire(&debug_lock); @@ -938,6 +959,36 @@ if(!my_session->active) return rval; } +int count_replies(GWBUF* buffer) +{ + unsigned char* ptr = (unsigned char*)buffer->start; + unsigned char* end = (unsigned char*) buffer->end; + int pktlen, eof = 0; + int replies = 0; + while(ptr < end) + { + pktlen = MYSQL_GET_PACKET_LEN(ptr) + 4; + if(PTR_IS_OK(ptr) || PTR_IS_ERR(ptr) || PTR_IS_LOCAL_INFILE(ptr)) + { + replies++; + ptr += pktlen; + } + else + { + while(ptr < end && eof < 2) + { + pktlen = MYSQL_GET_PACKET_LEN(ptr) + 4; + if(PTR_IS_EOF(ptr) || PTR_IS_ERR(ptr)) eof++; + ptr += pktlen; + } + if(eof == 2) replies++; + eof = 0; + } + } + + return replies; +} + /** * The clientReply entry point. This is passed the response buffer * to which the filter should be applied. Once processed the @@ -1012,14 +1063,16 @@ clientReply (FILTER* instance, void *session, GWBUF *reply) #ifdef SS_DEBUG else { - ss_dassert(PTR_IS_RESULTSET(ptr)); + //ss_dassert(PTR_IS_RESULTSET(ptr)); skygw_log_write_flush(LOGFILE_DEBUG,"tee.c: [%d] Waiting for a result set from %s session.", my_session->d_id, branch == PARENT?"parent":"child"); } +/* ss_dassert(PTR_IS_ERR(ptr) || PTR_IS_LOCAL_INFILE(ptr)|| PTR_IS_OK(ptr) || my_session->waiting[branch] || !my_session->multipacket); +*/ #endif } @@ -1027,6 +1080,7 @@ clientReply (FILTER* instance, void *session, GWBUF *reply) { eof = modutil_count_signal_packets(complete,my_session->use_ok,my_session->eof[branch] > 0); + my_session->eof[branch] += eof; if(my_session->eof[branch] >= min_eof) { @@ -1035,11 +1089,24 @@ clientReply (FILTER* instance, void *session, GWBUF *reply) my_session->d_id, branch == PARENT?"parent":"child"); #endif - ss_dassert(my_session->eof[branch] < 3) + //ss_dassert(my_session->eof[branch] < 3) my_session->waiting[branch] = false; } } - + + + int reply_packets = count_replies(complete); + + /** COM_SET_OPTION returns a single EOF or ERR packet*/ + if(my_session->command == 0x1b && + reply_packets == 0 && + PTR_IS_EOF(ptr)) + { + reply_packets = 1; + } + + my_session->reply_packets[branch] += reply_packets; + if(branch == PARENT) { //ss_dassert(my_session->tee_replybuf == NULL); @@ -1055,7 +1122,10 @@ clientReply (FILTER* instance, void *session, GWBUF *reply) rc = 1; mpkt = my_session->multipacket[PARENT] || my_session->multipacket[CHILD]; - + if(my_session->reply_packets[branch] < my_session->statements) + { + my_session->waiting[branch] = true; + } if(my_session->tee_replybuf != NULL) { @@ -1082,8 +1152,8 @@ clientReply (FILTER* instance, void *session, GWBUF *reply) skygw_log_write_flush(LOGFILE_DEBUG,"tee.c:[%d] Routing partial response set.",my_session->d_id); #endif } - else if(my_session->eof[PARENT] == min_eof && - my_session->eof[CHILD] == min_eof) + else if(my_session->eof[PARENT] >= min_eof && + my_session->eof[CHILD] >= min_eof) { route = true; #ifdef SS_DEBUG @@ -1098,7 +1168,7 @@ clientReply (FILTER* instance, void *session, GWBUF *reply) skygw_log_write_flush(LOGFILE_DEBUG,"tee.c:[%d] Routing single packet response.",my_session->d_id); #endif route = true; - } + } } if(route) @@ -1236,3 +1306,13 @@ int detect_loops(TEE_INSTANCE *instance,HASHTABLE* ht, SERVICE* service) return false; } + +int internal_route(DCB* dcb) +{ + GWBUF* buffer = dcb->dcb_readqueue; + + /** This was set in the newSession function*/ + TEE_SESSION* session = dcb->data; + + return routeQuery((FILTER*)session->instance,session,buffer); +} \ No newline at end of file From 52fbe26e3c4addfcd626f049e5cd026919410736 Mon Sep 17 00:00:00 2001 From: Timofey Turenko Date: Thu, 5 Mar 2015 18:14:59 +0200 Subject: [PATCH 02/10] redirect all maxscale output to /dev/null/ also during start for testing --- testall.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testall.cmake b/testall.cmake index 2c6868faa..fedbdf273 100644 --- a/testall.cmake +++ b/testall.cmake @@ -1,4 +1,4 @@ -execute_process(COMMAND /bin/sh -c "${CMAKE_BINARY_DIR}/bin/maxscale -c ${CMAKE_BINARY_DIR} &>/dev/null") +execute_process(COMMAND /bin/sh -c "${CMAKE_BINARY_DIR}/bin/maxscale -c ${CMAKE_BINARY_DIR} &>/dev/null 2> /dev/null > /dev/null") execute_process(COMMAND make test RESULT_VARIABLE RVAL) execute_process(COMMAND killall maxscale) if(NOT RVAL EQUAL 0) From c937568624298fb31ce99f29a5d0676c612f909f Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Thu, 5 Mar 2015 20:46:55 +0200 Subject: [PATCH 03/10] Fixed a possible memory leak when session command history limits are used. --- server/modules/include/readwritesplit.h | 1 + server/modules/routing/readwritesplit/readwritesplit.c | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/server/modules/include/readwritesplit.h b/server/modules/include/readwritesplit.h index c79373533..7bb689bdb 100644 --- a/server/modules/include/readwritesplit.h +++ b/server/modules/include/readwritesplit.h @@ -290,6 +290,7 @@ struct router_client_session { int rses_capabilities; /*< input type, for example */ bool rses_autocommit_enabled; bool rses_transaction_active; + DCB* client_dcb; #if defined(PREP_STMT_CACHING) HASHTABLE* rses_prep_stmt[2]; #endif diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index d3c959c5b..c31b6a795 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -790,6 +790,7 @@ static void* newSession( #endif client_rses->router = router; + client_rses->client_dcb = session->client; /** * If service config has been changed, reload config from service to * router instance first. @@ -4355,8 +4356,10 @@ static bool route_session_write( LOGFILE_TRACE, "Router session exceeded session command history limit. " "Closing router session. <"))); - router_cli_ses->rses_closed = true; + gwbuf_free(querybuf); rses_end_locked_router_action(router_cli_ses); + router_cli_ses->client_dcb->func.hangup(router_cli_ses->client_dcb); + goto return_succp; } From 8c3409cf32875f34994b70287af325e00e979201 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 6 Mar 2015 12:39:42 +0200 Subject: [PATCH 04/10] Fix to MXS-3: https://mariadb.atlassian.net/browse/MXS-3 Added the GNU specific code to skygw_utils.cc. This is a temporary fix and should be revisited at some point. --- utils/skygw_utils.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/utils/skygw_utils.cc b/utils/skygw_utils.cc index 1d38d9194..a8fa45ac5 100644 --- a/utils/skygw_utils.cc +++ b/utils/skygw_utils.cc @@ -145,12 +145,16 @@ int atomic_add( int *variable, int value) { +#ifdef __GNUC__ + return (int) __sync_fetch_and_add (variable, value); +#else asm volatile( "lock; xaddl %%eax, %2;" :"=a" (value) : "a" (value), "m" (*variable) : "memory" ); return value; +#endif } @@ -2143,4 +2147,4 @@ bool is_valid_posix_path(char* path) } } return true; -} \ No newline at end of file +} From 6f034fa8821c4ffe4103227a27eb1c18775626de Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Fri, 6 Mar 2015 16:30:20 +0100 Subject: [PATCH 05/10] Nagios plugins Nagios plugins --- plugins/nagios/README | 22 ++ plugins/nagios/check_maxscale_monitors.pl | 209 ++++++++++++++++++ plugins/nagios/check_maxscale_resources.pl | 176 +++++++++++++++ plugins/nagios/check_maxscale_threads.pl | 240 +++++++++++++++++++++ plugins/nagios/maxscale_commands.cfg | 31 +++ plugins/nagios/server1.cfg | 111 ++++++++++ 6 files changed, 789 insertions(+) create mode 100644 plugins/nagios/README create mode 100755 plugins/nagios/check_maxscale_monitors.pl create mode 100755 plugins/nagios/check_maxscale_resources.pl create mode 100755 plugins/nagios/check_maxscale_threads.pl create mode 100644 plugins/nagios/maxscale_commands.cfg create mode 100644 plugins/nagios/server1.cfg diff --git a/plugins/nagios/README b/plugins/nagios/README new file mode 100644 index 000000000..bd06aa3ef --- /dev/null +++ b/plugins/nagios/README @@ -0,0 +1,22 @@ +MaxScale Nagios plugins, for Nagios 3.5.1 + + +1) copy check_maxscale_*.pl under /usr/lib64/nagios/plugins + + +2) Edit /etc/nagios/nagios.cfg + +add + +cfg_file=/etc/nagios/objects/maxscale_commands.cfg +cfg_file=/etc/nagios/objects/server1.cfg + +Please note: +- modify server IP address in server1.cfg +- maxadmin executable must be in the nagios server + + + +Restart Nagios + + diff --git a/plugins/nagios/check_maxscale_monitors.pl b/plugins/nagios/check_maxscale_monitors.pl new file mode 100755 index 000000000..2cee92625 --- /dev/null +++ b/plugins/nagios/check_maxscale_monitors.pl @@ -0,0 +1,209 @@ +#!/usr/bin/perl +# +# +# +# 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 +# +# + +# +# @file check_maxscale_monitors.pl - Nagios plugin for MaxScale monitors +# +# Revision History +# +# Date Who Description +# 06-03-2015 Massimiliano Pinto Initial implementation +# + +#use strict; +#use warnings; +use Getopt::Std; + +my %opts; +my $TIMEOUT = 15; # we don't want to wait long for a response +my %ERRORS = ('UNKNOWN' , '3', + 'OK', '0', + 'WARNING', '1', + 'CRITICAL', '2'); + +my $curr_script = "$0"; +$curr_script =~ s{.*/}{}; + +sub usage { + my $rc = shift; + + print <<"EOF"; +MaxScale monitor checker plugin for Nagios + +Usage: $curr_script [-r ] [-H ] [-P ] [-u ] [-p ] [-m ] [-h] + +Options: + -r = monitors + -h = provide this usage message + -H = which host to connect to + -P = port to use + -u = username to connect as + -p = password to use for at + -m = /path/to/maxadmin +EOF + exit $rc; +} + +%opts =( + 'r' => 'monitors', # default maxscale resource to show + 'h' => '', # give help + 'H' => 'localhost', # host + 'u' => 'root', # username + 'p' => '', # password + 'P' => 6603, # port + 'm' => '/usr/local/skysql/maxscale/bin/maxadmin', # maxadmin + ); +getopts('r:hH:u:p:P:m:', \%opts) + or usage( $ERRORS{"UNKNOWN"} ); +usage( $ERRORS{'OK'} ) if $opts{'h'}; + +my $MAXADMIN_RESOURCE = $opts{'r'}; +my $MAXADMIN = $opts{'m'}; +-x $MAXADMIN or + die "$curr_script: Failed to find required tool: $MAXADMIN. Please install it or use the -m option to point to another location."; + +my ( $state, $status ) = ( "OK", 'maxadmin ' . $MAXADMIN_RESOURCE .' succeeds.' ); + +# Just in case of problems, let's not hang Nagios +$SIG{'ALRM'} = sub { + print ("UNKNOWN: No response from MaxScale server (alarm)\n"); + exit $ERRORS{"UNKNOWN"}; +}; +alarm($TIMEOUT); + +my $command = $MAXADMIN . ' -h ' . $opts{'H'} . ' -u ' . $opts{'u'} . ' -p "' . $opts{'p'} . '" -P ' . $opts{'P'} . ' ' . "show " . $MAXADMIN_RESOURCE; + +#print "maxadmin command: $command\n"; + +open (MAXSCALE, "$command 2>&1 |") + or die "can't get data out of Maxscale: $!"; + +my $hostname = qx{hostname}; chomp $hostname; +my $waiting_backend = 0; +my $service; +my $start_output = 0; +my $n_threads = 0; +my $p_threads = 0; +my $performance_data=""; + + +my $resource_type = $MAXADMIN_RESOURCE; +chop($resource_type); + +my $resource_match = ucfirst("$resource_type Name"); + +my $this_key; +my %monitor_data; + +while ( ) { + chomp; + + if ( /Unable to connect to MaxScale/ ) { + printf "CRITICAL: $_\n"; + close(MAXSCALE); + exit(2); + } + + if ( /^Monitor\:/ ) { + $n_threads++; + $this_key = 'monitor' . $n_threads; + $monitor_data{$this_key} = { + '1name'=> '', + '2state' => '', + '3servers' => '', + '4interval' => '', + '5repl_lag' => '' + }; + + next; + } + + next if (/--/ || $_ eq ''); + + if ( /\s+Name/) { + + my $str; + my $perf_line; + my @data_row = split(':', $_); + my $name = $data_row[1]; + $name =~ s/^\s+|\s+$//g; + $monitor_data{$this_key}{'1name'}=$name; + + } + + if (/(\s+Monitor )(.*)/) { + $monitor_data{$this_key}{'2state'}=$2; + } + + if ( /Monitored servers\:/ ) { + my $server_list; + my @data_row = split(':', $_); + shift(@data_row); + foreach my $name (@data_row) { + $name =~ s/^\s+|\s+$//g; + $name =~ s/ //g; + $server_list .= $name . ":"; + } + chop($server_list); + $monitor_data{$this_key}{'3servers'}=$server_list; + } + + if ( /(Sampling interval\:)\s+(\d+) milliseconds/ ) { + #my @data_row = split(':', $_); + #my $name = $data_row[1]; + #$name =~ s/^\s+|\s+$//g; + #$monitor_data{$this_key}{'4interval'}=$name; + $monitor_data{$this_key}{'4interval'}=$2; + } + + if ( /Replication lag\:/ ) { + my @data_row = split(':', $_); + my $name = $data_row[1]; + $name =~ s/^\s+|\s+$//g; + $monitor_data{$this_key}{'5repl_lag'}=$name; + } +} + + + for my $key ( sort(keys %monitor_data) ) { + #print "-- key: [$key]\n"; + my $local_hash = {}; + $performance_data .= " $key="; + $local_hash = $monitor_data{$key}; + my %new_hash = %$local_hash; + foreach my $key (sort (keys (%new_hash))) { + $performance_data .= $new_hash{$key} . ";"; + } + } + +chop($performance_data); + +if ($n_threads) { + printf "OK: %d monitors found |%s\n", $n_threads, $performance_data; + close(MAXSCALE); + exit 0; +} else { + printf "CRITICAL: 0 monitors found\n"; + close(MAXSCALE); + exit 2; +} + diff --git a/plugins/nagios/check_maxscale_resources.pl b/plugins/nagios/check_maxscale_resources.pl new file mode 100755 index 000000000..dcb43d772 --- /dev/null +++ b/plugins/nagios/check_maxscale_resources.pl @@ -0,0 +1,176 @@ +#!/usr/bin/perl +# +# +# +# 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 +# +# + +# +# @file check_maxscale_resources.pl - Nagios plugin for MaxScale resources +# +# Revision History +# +# Date Who Description +# 06-03-2015 Massimiliano Pinto Initial implementation +# + +#use strict; +#use warnings; +use Getopt::Std; + +my %opts; +my $TIMEOUT = 15; # we don't want to wait long for a response +my %ERRORS = ('UNKNOWN' , '3', + 'OK', '0', + 'WARNING', '1', + 'CRITICAL', '2'); + +my $curr_script = "$0"; +$curr_script =~ s{.*/}{}; + +sub usage { + my $rc = shift; + + print <<"EOF"; +MaxScale monitor checker plugin for Nagios + +Usage: $curr_script [-r ] [-H ] [-P ] [-u ] [-p ] [-m ] [-h] + +Options: + -r = modules|services|filters|listeners|servers|sessions + -h = provide this usage message + -H = which host to connect to + -P = port to use + -u = username to connect as + -p = password to use for at + -m = /path/to/maxadmin +EOF + exit $rc; +} + +%opts =( + 'r' => 'services', # default maxscale resource to show + 'h' => '', # give help + 'H' => 'localhost', # host + 'u' => 'root', # username + 'p' => '', # password + 'P' => 6603, # port + 'm' => '/usr/local/skysql/maxscale/bin/maxadmin', # maxadmin + ); +getopts('r:hH:u:p:P:m:', \%opts) + or usage( $ERRORS{"UNKNOWN"} ); +usage( $ERRORS{'OK'} ) if $opts{'h'}; + +my $MAXADMIN_RESOURCE = $opts{'r'}; +my $MAXADMIN = $opts{'m'}; +-x $MAXADMIN or + die "$curr_script: Failed to find required tool: $MAXADMIN. Please install it or use the -m option to point to another location."; + +my ( $state, $status ) = ( "OK", 'maxadmin ' . $MAXADMIN_RESOURCE .' succeeds.' ); + +# Just in case of problems, let's not hang Nagios +$SIG{'ALRM'} = sub { + print ("UNKNOWN: No response from MaxScale server (alarm)\n"); + exit $ERRORS{"UNKNOWN"}; +}; +alarm($TIMEOUT); + +my $command = $MAXADMIN . ' -h ' . $opts{'H'} . ' -u ' . $opts{'u'} . ' -p "' . $opts{'p'} . '" -P ' . $opts{'P'} . ' ' . "list " . $MAXADMIN_RESOURCE; + +#print "maxadmin command: $command\n"; +open (MAXSCALE, "$command 2>&1 |") + or die "can't get data out of Maxscale: $!"; + +my $hostname = qx{hostname}; chomp $hostname; +my $waiting_backend = 0; +my $service; +my $start_output = 0; +my $n_threads = 0; +my $p_threads = 0; +my $performance_data=""; + + +my $resource_type = $MAXADMIN_RESOURCE; +chop($resource_type); + +my $resource_match = ucfirst("$resource_type Name"); + +if ($resource_type eq "listener") { + $resource_match = "Service Name"; +} +if ($resource_type eq "filter") { + $resource_match = "Filter"; +} +if ($resource_type eq "server") { + $resource_match = "Server"; +} +if ($resource_type eq "session") { + $resource_match = "Session"; +} + +#print "Matching [$resource_match]\n"; + +while ( ) { + chomp; + + if ( /Unable to connect to MaxScale/ ) { + printf "CRITICAL: $_\n"; + close(MAXSCALE); + exit(2); + } + + if ( ! /^$resource_match/ ) { + } else { + $start_output = 1; + next; + } + if ($start_output) { + next if (/--/ || $_ eq ''); + $n_threads++; + if ($resource_type ne "session") { + my $str; + my $perf_line; + my @data_row = split('\|', $_); + $performance_data .= "module$n_threads="; + foreach my $val (@data_row) { + $str = $val; + $str =~ s/^\s+|\s+$//g; + $perf_line .= $str . ';'; + } + chop($perf_line); + $performance_data .= $perf_line . ' '; + } + } +} + +chop($performance_data); + +if ($n_threads) { + if ($performance_data eq '') { + printf "OK: %d $MAXADMIN_RESOURCE found\n", $n_threads; + } else { + printf "OK: %d $MAXADMIN_RESOURCE found | %s\n", $n_threads, $performance_data; + } + close(MAXSCALE); + exit 0; +} else { + printf "CRITICAL: 0 $MAXADMIN_RESOURCE found\n"; + close(MAXSCALE); + exit 2; +} + diff --git a/plugins/nagios/check_maxscale_threads.pl b/plugins/nagios/check_maxscale_threads.pl new file mode 100755 index 000000000..b2813eb10 --- /dev/null +++ b/plugins/nagios/check_maxscale_threads.pl @@ -0,0 +1,240 @@ +#!/usr/bin/perl +# +# +# +# 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 +# +# + +# +# @file check_maxscale_threads.pl - Nagios plugin for MaxScale threads and events +# +# Revision History +# +# Date Who Description +# 06-03-2015 Massimiliano Pinto Initial implementation +# + +#use strict; +#use warnings; +use Getopt::Std; + +my %opts; +my $TIMEOUT = 15; # we don't want to wait long for a response +my %ERRORS = ('UNKNOWN' , '3', + 'OK', '0', + 'WARNING', '1', + 'CRITICAL', '2'); + +my $curr_script = "$0"; +$curr_script =~ s{.*/}{}; + +sub usage { + my $rc = shift; + + print <<"EOF"; +MaxScale monitor checker plugin for Nagios + +Usage: $curr_script [-r ] [-H ] [-P ] [-u ] [-p ] [-m ] [-h] + +Options: + -r = threads + -h = provide this usage message + -H = which host to connect to + -P = port to use + -u = username to connect as + -p = password to use for at + -m = /path/to/maxadmin +EOF + exit $rc; +} + +%opts =( + 'r' => 'threads', # default maxscale resource to show + 'h' => '', # give help + 'H' => 'localhost', # host + 'u' => 'root', # username + 'p' => '', # password + 'P' => 6603, # port + 'm' => '/usr/local/skysql/maxscale/bin/maxadmin', # maxadmin + ); +getopts('r:hH:u:p:P:m:', \%opts) + or usage( $ERRORS{"UNKNOWN"} ); +usage( $ERRORS{'OK'} ) if $opts{'h'}; + +my $MAXADMIN_RESOURCE = $opts{'r'}; +my $MAXADMIN = $opts{'m'}; +-x $MAXADMIN or + die "$curr_script: Failed to find required tool: $MAXADMIN. Please install it or use the -m option to point to another location."; + +my ( $state, $status ) = ( "OK", 'maxadmin ' . $MAXADMIN_RESOURCE .' succeeds.' ); + +# Just in case of problems, let's not hang Nagios +$SIG{'ALRM'} = sub { + print ("UNKNOWN: No response from MaxScale server (alarm)\n"); + exit $ERRORS{"UNKNOWN"}; +}; +alarm($TIMEOUT); + +my $command = $MAXADMIN . ' -h ' . $opts{'H'} . ' -u ' . $opts{'u'} . ' -p "' . $opts{'p'} . '" -P ' . $opts{'P'} . ' ' . "show " . $MAXADMIN_RESOURCE; + +#print "maxadmin command: $command\n"; + +open (MAXSCALE, "$command 2>&1 |") + or die "can't get data out of Maxscale: $!"; + +my $hostname = qx{hostname}; chomp $hostname; +my $waiting_backend = 0; +my $service; +my $start_output = 0; +my $n_threads = 0; +my $p_threads = 0; +my $performance_data=""; + + +my $resource_type = $MAXADMIN_RESOURCE; +chop($resource_type); + +my $resource_match = ucfirst("$resource_type Name"); + +my $historic_thread_load_average = 0; +my $current_thread_load_average = 0; + +my %thread_data; +my %event_data; + +my $start_queue_len = 0; + +while ( ) { + chomp; + + if ( /Unable to connect to MaxScale/ ) { + printf "CRITICAL: $_\n"; + close(MAXSCALE); + exit(2); + } + + if ( /Historic Thread Load Average/) { + my $str; + my @data_row = split(':', $_); + foreach my $val (@data_row) { + $str = $val; + $str =~ s/^\s+|\s+$//g; + } + chop($str); + $historic_thread_load_average = $str; + } + + if (/Current Thread Load Average/) { + my $str; + my @data_row = split(':', $_); + foreach my $val (@data_row) { + $str = $val; + $str =~ s/^\s+|\s+$//g; + } + chop($str); + $current_thread_load_average = $str; + } + + if (/Minute Average/) { + my $str; + my $in_str; + my @data_row = split(',', $_); + foreach my $val (@data_row) { + my ($i,$j)= split(':', $val); + $i =~ s/^\s+|\s+$//g; + $j =~ s/^\s+|\s+$//g; + if ($start_queue_len) { + $event_data{$i} = $j; + } else { + $thread_data{$i} = $j; + } + } + } + + if ( /Pending event queue length averages/) { + $start_queue_len = 1; + next; + } + + if ( ! /^\s+ID/ ) { + #printf $_ ."\n"; + } else { + #printf "[$_]" ."\n"; + $start_output = 1; + next; + } + if ($start_output && /^\s+\d/) { + #printf "Thread [$_]" . "\n"; + $n_threads++; + if (/Processing/) { + $p_threads++; + } + } +} + +close(MAXSCALE); + + +open( MAXSCALE, "/servers/maxinfo/bin/maxadmin -h 127.0.0.1 -P 8444 -uadmin -pskysql show epoll 2>&1 |" ) + or die "can't get data out of Maxscale: $!"; + +my $queue_len = 0; + +while ( ) { + chomp; + if ( ! /Current event queue length/ ) { + next; + } else { + my $str; + my @data_row = split(':', $_); + foreach my $val (@data_row) { + $str = $val; + $str =~ s/^\s+|\s+$//g; + } + $queue_len = $str; + + last; + } +} + +my $performance_data = ""; +my $performance_data_thread = ""; +my $performance_data_event = ""; + +my $in_str; +my $in_key; +my $in_val; + +my @new_thread_array = @thread_data{'15 Minute Average', '5 Minute Average', '1 Minute Average'}; +my @new_event_array = @event_data{'15 Minute Average', '5 Minute Average', '1 Minute Average'}; + +$performance_data_thread = join(';', @new_thread_array); +$performance_data_event = join(';', @new_event_array); + +$performance_data .= "threads=$historic_thread_load_average;$current_thread_load_average avg_threads=$performance_data_thread avg_events=$performance_data_event"; + +if ($p_threads < $n_threads) { + printf "OK: Processing threads: %d/%d Events: %d | $performance_data\n", $p_threads, $n_threads, $queue_len; + close(MAXSCALE); + exit 0; +} else { + printf "WARNING: Processing threads: %d/%d Events: %d | $performance_data\n", $p_threads, $n_threads, $queue_len; + close(MAXSCALE); + exit 1; +} + diff --git a/plugins/nagios/maxscale_commands.cfg b/plugins/nagios/maxscale_commands.cfg new file mode 100644 index 000000000..d6dc6f47b --- /dev/null +++ b/plugins/nagios/maxscale_commands.cfg @@ -0,0 +1,31 @@ +############################################################################### +# MAXSCALE_COMMANDS.CFG - SAMPLE COMMAND DEFINITIONS FOR NAGIOS 3.5.1 +# +# Last Modified: 06-03-2015 +# +# NOTES: This config file provides you with some example command definitions +# that you can reference in host, service, and contact definitions. +# +# You don't need to keep commands in a separate file from your other +# object definitions. This has been done just to make things easier to +# understand. +# +############################################################################### + +# check maxscale monitors +define command{ + command_name check_maxscale_monitors + command_line $USER1$/check_maxscale_monitors.pl -H $HOSTADDRESS$ -P $ARG1$ -u $ARG2$ -p $ARG3$ -r $ARG4$ +} + +# check maxscale threads +define command{ + command_name check_maxscale_threads + command_line $USER1$/check_maxscale_threads.pl -H $HOSTADDRESS$ -P $ARG1$ -u $ARG2$ -p $ARG3$ -r $ARG4$ +} + +# check maxscale resource (listeners, services, etc) +define command{ + command_name check_maxscale_resource + command_line $USER1$/check_maxscale_resources.pl -H $HOSTADDRESS$ -P $ARG1$ -u $ARG2$ -p $ARG3$ -r $ARG4$ +} diff --git a/plugins/nagios/server1.cfg b/plugins/nagios/server1.cfg new file mode 100644 index 000000000..ee7b2f8fd --- /dev/null +++ b/plugins/nagios/server1.cfg @@ -0,0 +1,111 @@ + +############################################################################### +############################################################################### +# +# HOST DEFINITION +# +############################################################################### +############################################################################### + +# Define a host for the remote machine + +define host{ + use linux-server ; Name of host template to use + ; This host definition will inherit all variables that are defined + ; in (or inherited by) the linux-server host template definition. + host_name server1 + alias server1 + address xxx.xxx.xxx.xxx + } + + + +############################################################################### +############################################################################### +# +# HOST GROUP DEFINITION +# +############################################################################### +############################################################################### + +# Define an optional hostgroup for Linux machines + +define hostgroup{ + hostgroup_name linux-real-servers ; The name of the hostgroup + alias Linux Real Servers ; Long name of the group + members server1 ; Comma separated list of hosts that belong to this group + } + + + +# Check MaxScale modules, on the remote machine. +define service{ + use local-service ; Name of service template to use + host_name server1 + service_description MaxScale_modules + check_command check_maxscale_resource!6603!admin!skysql!modules + notifications_enabled 0 + } + +# Check MaxScale services, on the remote machine. +define service{ + use local-service ; Name of service template to use + host_name server1 + service_description MaxScale_services + check_command check_maxscale_resource!6603!admin!skysql!services + notifications_enabled 0 + } + +# Check MaxScale listeners, on the remote machine. +define service{ + use local-service ; Name of service template to use + host_name server1 + service_description MaxScale_listeners + check_command check_maxscale_resource!6603!admin!skysql!listeners + notifications_enabled 0 + } + +# Check MaxScale servers, on the remote machine. +define service{ + use local-service ; Name of service template to use + host_name server1 + service_description MaxScale_servers + check_command check_maxscale_resource!6603!admin!skysql!servers + notifications_enabled 0 + } + +# Check MaxScale sessions, on the remote machine. +define service{ + use local-service ; Name of service template to use + host_name server1 + service_description MaxScale_sessions + check_command check_maxscale_resource!6603!admin!skysql!sessions + notifications_enabled 0 + } + +# Check MaxScale filters, on the remote machine. +define service{ + use local-service ; Name of service template to use + host_name server1 + service_description MaxScale_filters + check_command check_maxscale_resource!6603!admin!skysql!filters + notifications_enabled 0 + } + +# Check MaxScale monitors, on the remote machine. +define service{ + use local-service ; Name of service template to use + host_name server1 + service_description MaxScale_monitors + check_command check_maxscale_monitors!6603!admin!skysql!monitors + notifications_enabled 0 + } + +# Define a service to check Script on the local machine. +define service{ + use local-service ; Name of service template to use + host_name server1 + service_description MaxScale_threads + check_command check_maxscale_threads!6603!admin!skysql!threads + notifications_enabled 0 + } From 84f00cfe36ca355ebf1c25d5c81a1ff029f3b04a Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Fri, 6 Mar 2015 17:00:54 +0100 Subject: [PATCH 06/10] README update README update --- plugins/nagios/README | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/plugins/nagios/README b/plugins/nagios/README index bd06aa3ef..d02bc1fa6 100644 --- a/plugins/nagios/README +++ b/plugins/nagios/README @@ -2,9 +2,8 @@ MaxScale Nagios plugins, for Nagios 3.5.1 1) copy check_maxscale_*.pl under /usr/lib64/nagios/plugins - - -2) Edit /etc/nagios/nagios.cfg +2) copy maxscale_commands.cfg, server1.cfg to /etc/nagios/objects/ +3) Edit /etc/nagios/nagios.cfg add @@ -17,6 +16,6 @@ Please note: -Restart Nagios +4) Restart Nagios From b2cee0ae188918e2a2d747e2d03915bfc397d20d Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Fri, 6 Mar 2015 18:15:14 +0100 Subject: [PATCH 07/10] Added support for specific maxadmin path Added support for specific maxadmin path --- plugins/nagios/README | 25 +++++++++++++++++++++- plugins/nagios/check_maxscale_monitors.pl | 10 ++++++--- plugins/nagios/check_maxscale_resources.pl | 11 +++++++--- plugins/nagios/check_maxscale_threads.pl | 10 ++++++--- plugins/nagios/maxscale_commands.cfg | 6 +++--- plugins/nagios/server1.cfg | 2 +- 6 files changed, 50 insertions(+), 14 deletions(-) diff --git a/plugins/nagios/README b/plugins/nagios/README index d02bc1fa6..9065796c4 100644 --- a/plugins/nagios/README +++ b/plugins/nagios/README @@ -1,5 +1,16 @@ MaxScale Nagios plugins, for Nagios 3.5.1 +MaxScale must be configured with 'maxscaled' protocol for the administration interface + +[AdminInterface] +type=service +router=cli + +[AdminListener] +type=listener +service=AdminInterface +protocol=maxscaled +port=6603 1) copy check_maxscale_*.pl under /usr/lib64/nagios/plugins 2) copy maxscale_commands.cfg, server1.cfg to /etc/nagios/objects/ @@ -11,10 +22,22 @@ cfg_file=/etc/nagios/objects/maxscale_commands.cfg cfg_file=/etc/nagios/objects/server1.cfg Please note: -- modify server IP address in server1.cfg +- modify server IP address in server1.cfg, pointing to MaxScale server - maxadmin executable must be in the nagios server +- default AdminInterface port is 6603 +- default maxadmin executable path is /usr/local/skysql/maxscale/bin/maxadmin + It can be changed by -m option +Example related to server1.cfg +# Check MaxScale sessions, on the remote machine. +define service{ + use local-service ; Name of service template to use + host_name server1 + service_description MaxScale_sessions + check_command check_maxscale_resource!6603!admin!skysql!sessions!/path_to/maxadmin + notifications_enabled 0 + } 4) Restart Nagios diff --git a/plugins/nagios/check_maxscale_monitors.pl b/plugins/nagios/check_maxscale_monitors.pl index 2cee92625..2850bd754 100755 --- a/plugins/nagios/check_maxscale_monitors.pl +++ b/plugins/nagios/check_maxscale_monitors.pl @@ -72,17 +72,21 @@ EOF 'P' => 6603, # port 'm' => '/usr/local/skysql/maxscale/bin/maxadmin', # maxadmin ); + +my $MAXADMIN_DEFAULT = $opts{'m'}; + getopts('r:hH:u:p:P:m:', \%opts) or usage( $ERRORS{"UNKNOWN"} ); usage( $ERRORS{'OK'} ) if $opts{'h'}; my $MAXADMIN_RESOURCE = $opts{'r'}; my $MAXADMIN = $opts{'m'}; +if (!defined $MAXADMIN || length($MAXADMIN) == 0) { + $MAXADMIN = $MAXADMIN_DEFAULT; +} -x $MAXADMIN or die "$curr_script: Failed to find required tool: $MAXADMIN. Please install it or use the -m option to point to another location."; -my ( $state, $status ) = ( "OK", 'maxadmin ' . $MAXADMIN_RESOURCE .' succeeds.' ); - # Just in case of problems, let's not hang Nagios $SIG{'ALRM'} = sub { print ("UNKNOWN: No response from MaxScale server (alarm)\n"); @@ -117,7 +121,7 @@ my %monitor_data; while ( ) { chomp; - if ( /Unable to connect to MaxScale/ ) { + if ( /(Failed|Unable) to connect to MaxScale/ ) { printf "CRITICAL: $_\n"; close(MAXSCALE); exit(2); diff --git a/plugins/nagios/check_maxscale_resources.pl b/plugins/nagios/check_maxscale_resources.pl index dcb43d772..2cb3944e0 100755 --- a/plugins/nagios/check_maxscale_resources.pl +++ b/plugins/nagios/check_maxscale_resources.pl @@ -72,17 +72,22 @@ EOF 'P' => 6603, # port 'm' => '/usr/local/skysql/maxscale/bin/maxadmin', # maxadmin ); + +my $MAXADMIN_DEFAULT = $opts{'m'}; + getopts('r:hH:u:p:P:m:', \%opts) or usage( $ERRORS{"UNKNOWN"} ); usage( $ERRORS{'OK'} ) if $opts{'h'}; my $MAXADMIN_RESOURCE = $opts{'r'}; my $MAXADMIN = $opts{'m'}; +if (!defined $MAXADMIN || length($MAXADMIN) == 0) { + $MAXADMIN = $MAXADMIN_DEFAULT; +} + -x $MAXADMIN or die "$curr_script: Failed to find required tool: $MAXADMIN. Please install it or use the -m option to point to another location."; -my ( $state, $status ) = ( "OK", 'maxadmin ' . $MAXADMIN_RESOURCE .' succeeds.' ); - # Just in case of problems, let's not hang Nagios $SIG{'ALRM'} = sub { print ("UNKNOWN: No response from MaxScale server (alarm)\n"); @@ -128,7 +133,7 @@ if ($resource_type eq "session") { while ( ) { chomp; - if ( /Unable to connect to MaxScale/ ) { + if ( /(Failed|Unable) to connect to MaxScale/ ) { printf "CRITICAL: $_\n"; close(MAXSCALE); exit(2); diff --git a/plugins/nagios/check_maxscale_threads.pl b/plugins/nagios/check_maxscale_threads.pl index b2813eb10..7486a6b24 100755 --- a/plugins/nagios/check_maxscale_threads.pl +++ b/plugins/nagios/check_maxscale_threads.pl @@ -72,17 +72,21 @@ EOF 'P' => 6603, # port 'm' => '/usr/local/skysql/maxscale/bin/maxadmin', # maxadmin ); + +my $MAXADMIN_DEFAULT = $opts{'m'}; + getopts('r:hH:u:p:P:m:', \%opts) or usage( $ERRORS{"UNKNOWN"} ); usage( $ERRORS{'OK'} ) if $opts{'h'}; my $MAXADMIN_RESOURCE = $opts{'r'}; my $MAXADMIN = $opts{'m'}; +if (!defined $MAXADMIN || length($MAXADMIN) == 0) { + $MAXADMIN = $MAXADMIN_DEFAULT; +} -x $MAXADMIN or die "$curr_script: Failed to find required tool: $MAXADMIN. Please install it or use the -m option to point to another location."; -my ( $state, $status ) = ( "OK", 'maxadmin ' . $MAXADMIN_RESOURCE .' succeeds.' ); - # Just in case of problems, let's not hang Nagios $SIG{'ALRM'} = sub { print ("UNKNOWN: No response from MaxScale server (alarm)\n"); @@ -122,7 +126,7 @@ my $start_queue_len = 0; while ( ) { chomp; - if ( /Unable to connect to MaxScale/ ) { + if ( /(Failed|Unable) to connect to MaxScale/ ) { printf "CRITICAL: $_\n"; close(MAXSCALE); exit(2); diff --git a/plugins/nagios/maxscale_commands.cfg b/plugins/nagios/maxscale_commands.cfg index d6dc6f47b..ca3446d4e 100644 --- a/plugins/nagios/maxscale_commands.cfg +++ b/plugins/nagios/maxscale_commands.cfg @@ -15,17 +15,17 @@ # check maxscale monitors define command{ command_name check_maxscale_monitors - command_line $USER1$/check_maxscale_monitors.pl -H $HOSTADDRESS$ -P $ARG1$ -u $ARG2$ -p $ARG3$ -r $ARG4$ + command_line $USER1$/check_maxscale_monitors.pl -H $HOSTADDRESS$ -P $ARG1$ -u $ARG2$ -p $ARG3$ -r $ARG4$ -m $ARG5$ } # check maxscale threads define command{ command_name check_maxscale_threads - command_line $USER1$/check_maxscale_threads.pl -H $HOSTADDRESS$ -P $ARG1$ -u $ARG2$ -p $ARG3$ -r $ARG4$ + command_line $USER1$/check_maxscale_threads.pl -H $HOSTADDRESS$ -P $ARG1$ -u $ARG2$ -p $ARG3$ -r $ARG4$ -m $ARG5$ } # check maxscale resource (listeners, services, etc) define command{ command_name check_maxscale_resource - command_line $USER1$/check_maxscale_resources.pl -H $HOSTADDRESS$ -P $ARG1$ -u $ARG2$ -p $ARG3$ -r $ARG4$ + command_line $USER1$/check_maxscale_resources.pl -H $HOSTADDRESS$ -P $ARG1$ -u $ARG2$ -p $ARG3$ -r $ARG4$ -m $ARG5$ } diff --git a/plugins/nagios/server1.cfg b/plugins/nagios/server1.cfg index ee7b2f8fd..d571fb1aa 100644 --- a/plugins/nagios/server1.cfg +++ b/plugins/nagios/server1.cfg @@ -101,7 +101,7 @@ define service{ notifications_enabled 0 } -# Define a service to check Script on the local machine. +# Define a service to check Script on the remote machine. define service{ use local-service ; Name of service template to use host_name server1 From 22849f7c902ffbded39e3ff9c6f08dc0641bd776 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Fri, 6 Mar 2015 22:40:10 +0200 Subject: [PATCH 08/10] Added support for multiple result sets. --- server/core/modutil.c | 6 ++- server/include/modutil.h | 3 +- server/modules/filter/tee.c | 88 ++++++++++++++++++++++++------------- 3 files changed, 64 insertions(+), 33 deletions(-) diff --git a/server/core/modutil.c b/server/core/modutil.c index cf62b542c..6362d70a0 100644 --- a/server/core/modutil.c +++ b/server/core/modutil.c @@ -558,7 +558,7 @@ GWBUF* modutil_get_complete_packets(GWBUF** p_readbuf) * @return Number of EOF packets */ int -modutil_count_signal_packets(GWBUF *reply, int use_ok, int n_found) +modutil_count_signal_packets(GWBUF *reply, int use_ok, int n_found, int* more) { unsigned char* ptr = (unsigned char*) reply->start; unsigned char* end = (unsigned char*) reply->end; @@ -566,6 +566,7 @@ modutil_count_signal_packets(GWBUF *reply, int use_ok, int n_found) int pktlen, eof = 0, err = 0; int errlen = 0, eoflen = 0; int iserr = 0, iseof = 0; + bool moreresults = false; while(ptr < end) { @@ -587,6 +588,7 @@ modutil_count_signal_packets(GWBUF *reply, int use_ok, int n_found) if((ptr + pktlen) > end || (eof + n_found) >= 2) { + moreresults = PTR_EOF_MORE_RESULTS(ptr); ptr = prev; break; } @@ -616,6 +618,8 @@ modutil_count_signal_packets(GWBUF *reply, int use_ok, int n_found) } } + *more = moreresults; + return(eof + err); } diff --git a/server/include/modutil.h b/server/include/modutil.h index a465d00ab..dc7ee2937 100644 --- a/server/include/modutil.h +++ b/server/include/modutil.h @@ -40,6 +40,7 @@ #define PTR_IS_OK(b) (b[4] == 0x00) #define PTR_IS_ERR(b) (b[4] == 0xff) #define PTR_IS_LOCAL_INFILE(b) (b[4] == 0xfb) +#define PTR_EOF_MORE_RESULTS(b) ((PTR_IS_EOF(b) && ptr[7] & 0x08)) extern int modutil_is_SQL(GWBUF *); extern int modutil_is_SQL_prepare(GWBUF *); @@ -64,5 +65,5 @@ GWBUF *modutil_create_mysql_err_msg( const char *statemsg, const char *msg); -int modutil_count_signal_packets(GWBUF*,int,int); +int modutil_count_signal_packets(GWBUF*,int,int,int*); #endif diff --git a/server/modules/filter/tee.c b/server/modules/filter/tee.c index e0a6e51fe..236549c43 100644 --- a/server/modules/filter/tee.c +++ b/server/modules/filter/tee.c @@ -164,6 +164,7 @@ typedef struct { FILTER_DEF* dummy_filterdef; int active; /* filter is active? */ bool use_ok; + int client_multistatement; bool multipacket[2]; unsigned char command; bool waiting[2]; /* if the client is waiting for a reply */ @@ -512,6 +513,7 @@ char *remote, *userName; my_session->tee_replybuf = NULL; my_session->client_dcb = session->client; my_session->instance = my_instance; + my_session->client_multistatement = false; spinlock_init(&my_session->tee_lock); if (my_instance->source && @@ -877,6 +879,8 @@ if(!my_session->active) switch(command) { + case 0x1b: + my_session->client_multistatement = *((unsigned char*) queue->start + 5); case 0x03: case 0x16: case 0x17: @@ -989,6 +993,43 @@ int count_replies(GWBUF* buffer) return replies; } +int lenenc_length(uint8_t* ptr) +{ + char val = *ptr; + if(val < 251) + return 1; + else if(val == 0xfc) + return 3; + else if(val == 0xfd) + return 4; + else + return 9; +} + +uint16_t get_response_flags(uint8_t* datastart, bool ok_packet) +{ + uint8_t* ptr = datastart; + uint16_t rval = 0; + int pktlen = gw_mysql_get_byte3(ptr); + + ptr += 4; + + if(ok_packet) + { + ptr += lenenc_length(ptr); + ptr += lenenc_length(ptr); + memcpy(&rval,ptr,sizeof(uint8_t)*2); + } + else + { + /** This is an EOF packet*/ + ptr += 2; + memcpy(&rval,ptr,sizeof(uint8_t)*2); + } + + return rval; +} + /** * The clientReply entry point. This is passed the response buffer * to which the filter should be applied. Once processed the @@ -1007,7 +1048,9 @@ clientReply (FILTER* instance, void *session, GWBUF *reply) bool route = false,mpkt; GWBUF *complete = NULL; unsigned char *ptr; + uint16_t flags = 0; int min_eof = my_session->command != 0x04 ? 2 : 1; + int more_results = 0; #ifdef SS_DEBUG ptr = (unsigned char*) reply->start; skygw_log_write(LOGFILE_TRACE,"Tee clientReply [%s] [%s] [%s]: %d", @@ -1059,29 +1102,28 @@ clientReply (FILTER* instance, void *session, GWBUF *reply) { my_session->waiting[branch] = false; my_session->multipacket[branch] = false; + if(PTR_IS_OK(ptr)) + { + flags = get_response_flags(ptr,true); + more_results = (flags & 0x08) && my_session->client_multistatement; + } } #ifdef SS_DEBUG else { - //ss_dassert(PTR_IS_RESULTSET(ptr)); skygw_log_write_flush(LOGFILE_DEBUG,"tee.c: [%d] Waiting for a result set from %s session.", my_session->d_id, branch == PARENT?"parent":"child"); } -/* - ss_dassert(PTR_IS_ERR(ptr) || PTR_IS_LOCAL_INFILE(ptr)|| - PTR_IS_OK(ptr) || my_session->waiting[branch] || - !my_session->multipacket); -*/ #endif } if(my_session->waiting[branch]) { - - eof = modutil_count_signal_packets(complete,my_session->use_ok,my_session->eof[branch] > 0); - + eof = modutil_count_signal_packets(complete,my_session->use_ok,my_session->eof[branch] > 0,&more_results); + more_results &= my_session->client_multistatement; my_session->eof[branch] += eof; + if(my_session->eof[branch] >= min_eof) { #ifdef SS_DEBUG @@ -1089,43 +1131,27 @@ clientReply (FILTER* instance, void *session, GWBUF *reply) my_session->d_id, branch == PARENT?"parent":"child"); #endif - //ss_dassert(my_session->eof[branch] < 3) - my_session->waiting[branch] = false; + my_session->waiting[branch] = more_results; + if(more_results) + { + my_session->eof[branch] = 0; + } } } - - int reply_packets = count_replies(complete); - - /** COM_SET_OPTION returns a single EOF or ERR packet*/ - if(my_session->command == 0x1b && - reply_packets == 0 && - PTR_IS_EOF(ptr)) - { - reply_packets = 1; - } - - my_session->reply_packets[branch] += reply_packets; - if(branch == PARENT) { - //ss_dassert(my_session->tee_replybuf == NULL); my_session->tee_replybuf = gwbuf_append(my_session->tee_replybuf,complete); } else { if(complete) - gwbuf_free(complete); + gwbuf_free(complete); } my_session->replies[branch]++; rc = 1; mpkt = my_session->multipacket[PARENT] || my_session->multipacket[CHILD]; - - if(my_session->reply_packets[branch] < my_session->statements) - { - my_session->waiting[branch] = true; - } if(my_session->tee_replybuf != NULL) { From ea039dd01aa137f929b5d512d26f880f9867aae4 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Sat, 7 Mar 2015 06:00:01 +0200 Subject: [PATCH 09/10] Fixed to Coverity defects and a fix to tee filter not compiling with SS_DEBUG flag. --- server/core/resultset.c | 13 ++++----- server/core/test/testservice.c | 4 ++- server/modules/filter/CMakeLists.txt | 6 ++--- server/modules/filter/fwfilter.c | 2 ++ .../filter/{lagfilter.c => slavelag.c} | 27 ++++++++++--------- server/modules/filter/tee.c | 8 +----- server/modules/routing/binlog/blr_file.c | 8 +++--- server/modules/routing/binlog/blr_master.c | 4 +-- server/modules/routing/maxinfo/maxinfo.c | 4 ++- .../modules/routing/maxinfo/maxinfo_error.c | 5 ++-- server/modules/routing/maxinfo/maxinfo_exec.c | 2 +- .../modules/routing/maxinfo/maxinfo_parse.c | 6 +++++ 12 files changed, 50 insertions(+), 39 deletions(-) rename server/modules/filter/{lagfilter.c => slavelag.c} (92%) diff --git a/server/core/resultset.c b/server/core/resultset.c index cca4e3c5a..7f9d88bbf 100644 --- a/server/core/resultset.c +++ b/server/core/resultset.c @@ -73,18 +73,19 @@ resultset_free(RESULTSET *resultset) { RESULT_COLUMN *col; - if (resultset) - return; - col = resultset->column; - while (col) + if (resultset != NULL) { - RESULT_COLUMN *next; + col = resultset->column; + while (col) + { + RESULT_COLUMN *next; next = col->next; resultset_column_free(col); col = next; + } + free(resultset); } - free(resultset); } /** diff --git a/server/core/test/testservice.c b/server/core/test/testservice.c index 7474925d4..ef2560481 100644 --- a/server/core/test/testservice.c +++ b/server/core/test/testservice.c @@ -42,6 +42,7 @@ static bool success = false; int hup(DCB* dcb) { success = true; + return 1; } /** @@ -108,7 +109,8 @@ hkinit(); skygw_log_sync_all(); ss_info_dassert(0 != result, "Stop should succeed"); - dcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER); + if((dcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER)) == NULL) + return 1; ss_info_dassert(dcb != NULL, "DCB allocation failed"); session = session_alloc(service,dcb); diff --git a/server/modules/filter/CMakeLists.txt b/server/modules/filter/CMakeLists.txt index 98d855d54..84629559b 100644 --- a/server/modules/filter/CMakeLists.txt +++ b/server/modules/filter/CMakeLists.txt @@ -34,9 +34,9 @@ add_library(namedserverfilter SHARED namedserverfilter.c) target_link_libraries(namedserverfilter log_manager utils) install(TARGETS namedserverfilter DESTINATION modules) -add_library(lagfilter SHARED lagfilter.c) -target_link_libraries(lagfilter log_manager utils query_classifier) -install(TARGETS lagfilter DESTINATION modules) +add_library(slavelag SHARED slavelag.c) +target_link_libraries(slavelag log_manager utils query_classifier) +install(TARGETS slavelag DESTINATION modules) add_subdirectory(hint) diff --git a/server/modules/filter/fwfilter.c b/server/modules/filter/fwfilter.c index d33cacb2e..63d07b5a9 100644 --- a/server/modules/filter/fwfilter.c +++ b/server/modules/filter/fwfilter.c @@ -1558,6 +1558,7 @@ bool check_match_any(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *qu } qlen = gw_mysql_get_byte3(memptr); + qlen = qlen < 0xffffff ? qlen : 0xffffff; fullquery = malloc((qlen) * sizeof(char)); memcpy(fullquery,memptr + 5,qlen - 1); memset(fullquery + qlen - 1,0,1); @@ -1612,6 +1613,7 @@ bool check_match_all(FW_INSTANCE* my_instance, FW_SESSION* my_session, GWBUF *qu } qlen = gw_mysql_get_byte3(memptr); + qlen = qlen < 0xffffff ? qlen : 0xffffff; fullquery = malloc((qlen) * sizeof(char)); memcpy(fullquery,memptr + 5,qlen - 1); memset(fullquery + qlen - 1,0,1); diff --git a/server/modules/filter/lagfilter.c b/server/modules/filter/slavelag.c similarity index 92% rename from server/modules/filter/lagfilter.c rename to server/modules/filter/slavelag.c index aff236850..c12d7a2a7 100644 --- a/server/modules/filter/lagfilter.c +++ b/server/modules/filter/slavelag.c @@ -32,7 +32,7 @@ extern size_t log_ses_count[]; extern __thread log_info_t tls_log_info; /** - * @file lagfilter.c - a very simple filter designed to send queries to the + * @file slavelag.c - a very simple filter designed to send queries to the * master server after data modification has occurred. This is done to prevent * replication lag affecting the outcome of a select query. * @@ -42,8 +42,11 @@ extern __thread log_info_t tls_log_info; * is executed: * * count= Queries to route to master after data modification. - * time=