/[nagios-plugins-perl]/trunk/plugins/check_mysql_health.pl
ViewVC logotype

Contents of /trunk/plugins/check_mysql_health.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 196 - (show annotations) (download)
Tue May 29 10:17:21 2018 UTC (2 years, 6 months ago) by racvision
File MIME type: text/plain
File size: 126484 byte(s)
add commands & perl scripts for check_phpfpm_status check_mysql_health
1 #! /usr/bin/perl -w
2 # nagios: -epn
3
4 my %ERRORS=( OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3 );
5 my %ERRORCODES=( 0 => 'OK', 1 => 'WARNING', 2 => 'CRITICAL', 3 => 'UNKNOWN' );
6 package DBD::MySQL::Server::Instance::Innodb;
7
8 use strict;
9
10 our @ISA = qw(DBD::MySQL::Server::Instance);
11
12
13 sub new {
14 my $class = shift;
15 my %params = @_;
16 my $self = {
17 handle => $params{handle},
18 internals => undef,
19 warningrange => $params{warningrange},
20 criticalrange => $params{criticalrange},
21 };
22 bless $self, $class;
23 $self->init(%params);
24 return $self;
25 }
26
27 sub init {
28 my $self = shift;
29 my %params = @_;
30 $self->init_nagios();
31 if ($params{mode} =~ /server::instance::innodb/) {
32 $self->{internals} =
33 DBD::MySQL::Server::Instance::Innodb::Internals->new(%params);
34 }
35 }
36
37 sub nagios {
38 my $self = shift;
39 my %params = @_;
40 if ($params{mode} =~ /server::instance::innodb/) {
41 $self->{internals}->nagios(%params);
42 $self->merge_nagios($self->{internals});
43 }
44 }
45
46
47 package DBD::MySQL::Server::Instance::Innodb::Internals;
48
49 use strict;
50
51 our @ISA = qw(DBD::MySQL::Server::Instance::Innodb);
52
53 our $internals; # singleton, nur ein einziges mal instantiierbar
54
55 sub new {
56 my $class = shift;
57 my %params = @_;
58 unless ($internals) {
59 $internals = {
60 handle => $params{handle},
61 bufferpool_hitrate => undef,
62 wait_free => undef,
63 log_waits => undef,
64 have_innodb => undef,
65 warningrange => $params{warningrange},
66 criticalrange => $params{criticalrange},
67 };
68 bless($internals, $class);
69 $internals->init(%params);
70 }
71 return($internals);
72 }
73
74 sub init {
75 my $self = shift;
76 my %params = @_;
77 my $dummy;
78 $self->debug("enter init");
79 $self->init_nagios();
80 if (DBD::MySQL::Server::return_first_server()->version_is_minimum("5.1")) {
81 ($dummy, $self->{have_innodb}) = $self->{handle}->fetchrow_array(q{
82 SELECT ENGINE, SUPPORT FROM INFORMATION_SCHEMA.ENGINES WHERE ENGINE='InnoDB'
83 });
84 } else {
85 ($dummy, $self->{have_innodb}) = $self->{handle}->fetchrow_array(q{
86 SHOW VARIABLES LIKE 'have_innodb'
87 });
88 }
89 if ($self->{have_innodb} eq "NO") {
90 $self->add_nagios_critical("the innodb engine has a problem (have_innodb=no)");
91 } elsif ($self->{have_innodb} eq "DISABLED") {
92 # add_nagios_ok later
93 } elsif ($params{mode} =~ /server::instance::innodb::bufferpool::hitrate/) {
94 ($dummy, $self->{bufferpool_reads})
95 = $self->{handle}->fetchrow_array(q{
96 SHOW /*!50000 global */ STATUS LIKE 'Innodb_buffer_pool_reads'
97 });
98 ($dummy, $self->{bufferpool_read_requests})
99 = $self->{handle}->fetchrow_array(q{
100 SHOW /*!50000 global */ STATUS LIKE 'Innodb_buffer_pool_read_requests'
101 });
102 if (! defined $self->{bufferpool_reads}) {
103 $self->add_nagios_critical("no innodb buffer pool info available");
104 } else {
105 $self->valdiff(\%params, qw(bufferpool_reads
106 bufferpool_read_requests));
107 $self->{bufferpool_hitrate_now} =
108 $self->{delta_bufferpool_read_requests} > 0 ?
109 100 - (100 * $self->{delta_bufferpool_reads} /
110 $self->{delta_bufferpool_read_requests}) : 100;
111 $self->{bufferpool_hitrate} =
112 $self->{bufferpool_read_requests} > 0 ?
113 100 - (100 * $self->{bufferpool_reads} /
114 $self->{bufferpool_read_requests}) : 100;
115 }
116 } elsif ($params{mode} =~ /server::instance::innodb::bufferpool::waitfree/) {
117 ($dummy, $self->{bufferpool_wait_free})
118 = $self->{handle}->fetchrow_array(q{
119 SHOW /*!50000 global */ STATUS LIKE 'Innodb_buffer_pool_wait_free'
120 });
121 if (! defined $self->{bufferpool_wait_free}) {
122 $self->add_nagios_critical("no innodb buffer pool info available");
123 } else {
124 $self->valdiff(\%params, qw(bufferpool_wait_free));
125 $self->{bufferpool_wait_free_rate} =
126 $self->{delta_bufferpool_wait_free} / $self->{delta_timestamp};
127 }
128 } elsif ($params{mode} =~ /server::instance::innodb::logwaits/) {
129 ($dummy, $self->{log_waits})
130 = $self->{handle}->fetchrow_array(q{
131 SHOW /*!50000 global */ STATUS LIKE 'Innodb_log_waits'
132 });
133 if (! defined $self->{log_waits}) {
134 $self->add_nagios_critical("no innodb log info available");
135 } else {
136 $self->valdiff(\%params, qw(log_waits));
137 $self->{log_waits_rate} =
138 $self->{delta_log_waits} / $self->{delta_timestamp};
139 }
140 } elsif ($params{mode} =~ /server::instance::innodb::needoptimize/) {
141 #fragmentation=$(($datafree * 100 / $datalength))
142
143 #http://www.electrictoolbox.com/optimize-tables-mysql-php/
144 my @result = $self->{handle}->fetchall_array(q{
145 SHOW TABLE STATUS WHERE Data_free / Data_length > 0.1 AND Data_free > 102400
146 });
147 printf "%s\n", Data::Dumper::Dumper(\@result);
148
149 }
150 }
151
152 sub nagios {
153 my $self = shift;
154 my %params = @_;
155 my $now = $params{lookback} ? '_now' : '';
156 if ($self->{have_innodb} eq "DISABLED") {
157 $self->add_nagios_ok("the innodb engine has been disabled");
158 } elsif (! $self->{nagios_level}) {
159 if ($params{mode} =~ /server::instance::innodb::bufferpool::hitrate/) {
160 my $refkey = 'bufferpool_hitrate'.($params{lookback} ? '_now' : '');
161 $self->add_nagios(
162 $self->check_thresholds($self->{$refkey}, "99:", "95:"),
163 sprintf "innodb buffer pool hitrate at %.2f%%", $self->{$refkey});
164 $self->add_perfdata(sprintf "bufferpool_hitrate=%.2f%%;%s;%s;0;100",
165 $self->{bufferpool_hitrate},
166 $self->{warningrange}, $self->{criticalrange});
167 $self->add_perfdata(sprintf "bufferpool_hitrate_now=%.2f%%",
168 $self->{bufferpool_hitrate_now});
169 } elsif ($params{mode} =~ /server::instance::innodb::bufferpool::waitfree/) {
170 $self->add_nagios(
171 $self->check_thresholds($self->{bufferpool_wait_free_rate}, "1", "10"),
172 sprintf "%ld innodb buffer pool waits in %ld seconds (%.4f/sec)",
173 $self->{delta_bufferpool_wait_free}, $self->{delta_timestamp},
174 $self->{bufferpool_wait_free_rate});
175 $self->add_perfdata(sprintf "bufferpool_free_waits_rate=%.4f;%s;%s;0;100",
176 $self->{bufferpool_wait_free_rate},
177 $self->{warningrange}, $self->{criticalrange});
178 } elsif ($params{mode} =~ /server::instance::innodb::logwaits/) {
179 $self->add_nagios(
180 $self->check_thresholds($self->{log_waits_rate}, "1", "10"),
181 sprintf "%ld innodb log waits in %ld seconds (%.4f/sec)",
182 $self->{delta_log_waits}, $self->{delta_timestamp},
183 $self->{log_waits_rate});
184 $self->add_perfdata(sprintf "innodb_log_waits_rate=%.4f;%s;%s;0;100",
185 $self->{log_waits_rate},
186 $self->{warningrange}, $self->{criticalrange});
187 }
188 }
189 }
190
191
192
193
194 package DBD::MySQL::Server::Instance::MyISAM;
195
196 use strict;
197
198 our @ISA = qw(DBD::MySQL::Server::Instance);
199
200
201 sub new {
202 my $class = shift;
203 my %params = @_;
204 my $self = {
205 handle => $params{handle},
206 internals => undef,
207 warningrange => $params{warningrange},
208 criticalrange => $params{criticalrange},
209 };
210 bless $self, $class;
211 $self->init(%params);
212 return $self;
213 }
214
215 sub init {
216 my $self = shift;
217 my %params = @_;
218 $self->init_nagios();
219 if ($params{mode} =~ /server::instance::myisam/) {
220 $self->{internals} =
221 DBD::MySQL::Server::Instance::MyISAM::Internals->new(%params);
222 }
223 }
224
225 sub nagios {
226 my $self = shift;
227 my %params = @_;
228 if ($params{mode} =~ /server::instance::myisam/) {
229 $self->{internals}->nagios(%params);
230 $self->merge_nagios($self->{internals});
231 }
232 }
233
234
235 package DBD::MySQL::Server::Instance::MyISAM::Internals;
236
237 use strict;
238
239 our @ISA = qw(DBD::MySQL::Server::Instance::MyISAM);
240
241 our $internals; # singleton, nur ein einziges mal instantiierbar
242
243 sub new {
244 my $class = shift;
245 my %params = @_;
246 unless ($internals) {
247 $internals = {
248 handle => $params{handle},
249 keycache_hitrate => undef,
250 warningrange => $params{warningrange},
251 criticalrange => $params{criticalrange},
252 };
253 bless($internals, $class);
254 $internals->init(%params);
255 }
256 return($internals);
257 }
258
259 sub init {
260 my $self = shift;
261 my %params = @_;
262 my $dummy;
263 $self->debug("enter init");
264 $self->init_nagios();
265 if ($params{mode} =~ /server::instance::myisam::keycache::hitrate/) {
266 ($dummy, $self->{key_reads})
267 = $self->{handle}->fetchrow_array(q{
268 SHOW /*!50000 global */ STATUS LIKE 'Key_reads'
269 });
270 ($dummy, $self->{key_read_requests})
271 = $self->{handle}->fetchrow_array(q{
272 SHOW /*!50000 global */ STATUS LIKE 'Key_read_requests'
273 });
274 if (! defined $self->{key_read_requests}) {
275 $self->add_nagios_critical("no myisam keycache info available");
276 } else {
277 $self->valdiff(\%params, qw(key_reads key_read_requests));
278 $self->{keycache_hitrate} =
279 $self->{key_read_requests} > 0 ?
280 100 - (100 * $self->{key_reads} /
281 $self->{key_read_requests}) : 100;
282 $self->{keycache_hitrate_now} =
283 $self->{delta_key_read_requests} > 0 ?
284 100 - (100 * $self->{delta_key_reads} /
285 $self->{delta_key_read_requests}) : 100;
286 }
287 } elsif ($params{mode} =~ /server::instance::myisam::sonstnochwas/) {
288 }
289 }
290
291 sub nagios {
292 my $self = shift;
293 my %params = @_;
294 if (! $self->{nagios_level}) {
295 if ($params{mode} =~ /server::instance::myisam::keycache::hitrate/) {
296 my $refkey = 'keycache_hitrate'.($params{lookback} ? '_now' : '');
297 $self->add_nagios(
298 $self->check_thresholds($self->{$refkey}, "99:", "95:"),
299 sprintf "myisam keycache hitrate at %.2f%%", $self->{$refkey});
300 $self->add_perfdata(sprintf "keycache_hitrate=%.2f%%;%s;%s",
301 $self->{keycache_hitrate},
302 $self->{warningrange}, $self->{criticalrange});
303 $self->add_perfdata(sprintf "keycache_hitrate_now=%.2f%%;%s;%s",
304 $self->{keycache_hitrate_now},
305 $self->{warningrange}, $self->{criticalrange});
306 }
307 }
308 }
309
310
311 package DBD::MySQL::Server::Instance::Replication;
312
313 use strict;
314
315 our @ISA = qw(DBD::MySQL::Server::Instance);
316
317
318 sub new {
319 my $class = shift;
320 my %params = @_;
321 my $self = {
322 handle => $params{handle},
323 internals => undef,
324 warningrange => $params{warningrange},
325 criticalrange => $params{criticalrange},
326 };
327 bless $self, $class;
328 $self->init(%params);
329 return $self;
330 }
331
332 sub init {
333 my $self = shift;
334 my %params = @_;
335 $self->init_nagios();
336 if ($params{mode} =~ /server::instance::replication/) {
337 $self->{internals} =
338 DBD::MySQL::Server::Instance::Replication::Internals->new(%params);
339 }
340 }
341
342 sub nagios {
343 my $self = shift;
344 my %params = @_;
345 if ($params{mode} =~ /server::instance::replication/) {
346 $self->{internals}->nagios(%params);
347 $self->merge_nagios($self->{internals});
348 }
349 }
350
351
352 package DBD::MySQL::Server::Instance::Replication::Internals;
353
354 use strict;
355
356 our @ISA = qw(DBD::MySQL::Server::Instance::Replication);
357
358 our $internals; # singleton, nur ein einziges mal instantiierbar
359
360 sub new {
361 my $class = shift;
362 my %params = @_;
363 unless ($internals) {
364 $internals = {
365 handle => $params{handle},
366 seconds_behind_master => undef,
367 slave_io_running => undef,
368 slave_sql_running => undef,
369 warningrange => $params{warningrange},
370 criticalrange => $params{criticalrange},
371 };
372 bless($internals, $class);
373 $internals->init(%params);
374 }
375 return($internals);
376 }
377
378 sub init {
379 my $self = shift;
380 my %params = @_;
381 $self->debug("enter init");
382 $self->init_nagios();
383 if ($params{mode} =~ /server::instance::replication::slavelag/) {
384 # "show slave status", "Seconds_Behind_Master"
385 my $slavehash = $self->{handle}->selectrow_hashref(q{
386 SHOW SLAVE STATUS
387 });
388 if ((! defined $slavehash->{Seconds_Behind_Master}) &&
389 (lc $slavehash->{Slave_IO_Running} eq 'no')) {
390 $self->add_nagios_critical(
391 "unable to get slave lag, because io thread is not running");
392 } elsif (! defined $slavehash->{Seconds_Behind_Master}) {
393 $self->add_nagios_critical(sprintf "unable to get replication info%s",
394 $self->{handle}->{errstr} ? $self->{handle}->{errstr} : "");
395 } else {
396 $self->{seconds_behind_master} = $slavehash->{Seconds_Behind_Master};
397 }
398 } elsif ($params{mode} =~ /server::instance::replication::slaveiorunning/) {
399 # "show slave status", "Slave_IO_Running"
400 my $slavehash = $self->{handle}->selectrow_hashref(q{
401 SHOW SLAVE STATUS
402 });
403 if (! defined $slavehash->{Slave_IO_Running}) {
404 $self->add_nagios_critical(sprintf "unable to get replication info%s",
405 $self->{handle}->{errstr} ? $self->{handle}->{errstr} : "");
406 } else {
407 $self->{slave_io_running} = $slavehash->{Slave_IO_Running};
408 }
409 } elsif ($params{mode} =~ /server::instance::replication::slavesqlrunning/) {
410 # "show slave status", "Slave_SQL_Running"
411 my $slavehash = $self->{handle}->selectrow_hashref(q{
412 SHOW SLAVE STATUS
413 });
414 if (! defined $slavehash->{Slave_SQL_Running}) {
415 $self->add_nagios_critical(sprintf "unable to get replication info%s",
416 $self->{handle}->{errstr} ? $self->{handle}->{errstr} : "");
417 } else {
418 $self->{slave_sql_running} = $slavehash->{Slave_SQL_Running};
419 }
420 }
421 }
422
423 sub nagios {
424 my $self = shift;
425 my %params = @_;
426 if (! $self->{nagios_level}) {
427 if ($params{mode} =~ /server::instance::replication::slavelag/) {
428 $self->add_nagios(
429 $self->check_thresholds($self->{seconds_behind_master}, "10", "20"),
430 sprintf "Slave is %d seconds behind master",
431 $self->{seconds_behind_master});
432 $self->add_perfdata(sprintf "slave_lag=%d;%s;%s",
433 $self->{seconds_behind_master},
434 $self->{warningrange}, $self->{criticalrange});
435 } elsif ($params{mode} =~ /server::instance::replication::slaveiorunning/) {
436 if (lc $self->{slave_io_running} eq "yes") {
437 $self->add_nagios_ok("Slave io is running");
438 } else {
439 $self->add_nagios_critical("Slave io is not running");
440 }
441 } elsif ($params{mode} =~ /server::instance::replication::slavesqlrunning/) {
442 if (lc $self->{slave_sql_running} eq "yes") {
443 $self->add_nagios_ok("Slave sql is running");
444 } else {
445 $self->add_nagios_critical("Slave sql is not running");
446 }
447 }
448 }
449 }
450
451
452
453 package DBD::MySQL::Server::Instance;
454
455 use strict;
456
457 our @ISA = qw(DBD::MySQL::Server);
458
459
460 sub new {
461 my $class = shift;
462 my %params = @_;
463 my $self = {
464 handle => $params{handle},
465 uptime => $params{uptime},
466 replication_user => $params{replication_user},
467 warningrange => $params{warningrange},
468 criticalrange => $params{criticalrange},
469 threads_connected => undef,
470 threads_created => undef,
471 connections => undef,
472 threadcache_hitrate => undef,
473 querycache_hitrate => undef,
474 lowmem_prunes_per_sec => undef,
475 slow_queries_per_sec => undef,
476 longrunners => undef,
477 tablecache_hitrate => undef,
478 index_usage => undef,
479 engine_innodb => undef,
480 engine_myisam => undef,
481 replication => undef,
482 };
483 bless $self, $class;
484 $self->init(%params);
485 return $self;
486 }
487
488 sub init {
489 my $self = shift;
490 my %params = @_;
491 my $dummy;
492 $self->init_nagios();
493 if ($params{mode} =~ /server::instance::connectedthreads/) {
494 ($dummy, $self->{threads_connected}) = $self->{handle}->fetchrow_array(q{
495 SHOW /*!50000 global */ STATUS LIKE 'Threads_connected'
496 });
497 } elsif ($params{mode} =~ /server::instance::createdthreads/) {
498 ($dummy, $self->{threads_created}) = $self->{handle}->fetchrow_array(q{
499 SHOW /*!50000 global */ STATUS LIKE 'Threads_created'
500 });
501 $self->valdiff(\%params, qw(threads_created));
502 $self->{threads_created_per_sec} = $self->{delta_threads_created} /
503 $self->{delta_timestamp};
504 } elsif ($params{mode} =~ /server::instance::runningthreads/) {
505 ($dummy, $self->{threads_running}) = $self->{handle}->fetchrow_array(q{
506 SHOW /*!50000 global */ STATUS LIKE 'Threads_running'
507 });
508 } elsif ($params{mode} =~ /server::instance::cachedthreads/) {
509 ($dummy, $self->{threads_cached}) = $self->{handle}->fetchrow_array(q{
510 SHOW /*!50000 global */ STATUS LIKE 'Threads_cached'
511 });
512 } elsif ($params{mode} =~ /server::instance::abortedconnects/) {
513 ($dummy, $self->{connects_aborted}) = $self->{handle}->fetchrow_array(q{
514 SHOW /*!50000 global */ STATUS LIKE 'Aborted_connects'
515 });
516 $self->valdiff(\%params, qw(connects_aborted));
517 $self->{connects_aborted_per_sec} = $self->{delta_connects_aborted} /
518 $self->{delta_timestamp};
519 } elsif ($params{mode} =~ /server::instance::abortedclients/) {
520 ($dummy, $self->{clients_aborted}) = $self->{handle}->fetchrow_array(q{
521 SHOW /*!50000 global */ STATUS LIKE 'Aborted_clients'
522 });
523 $self->valdiff(\%params, qw(clients_aborted));
524 $self->{clients_aborted_per_sec} = $self->{delta_clients_aborted} /
525 $self->{delta_timestamp};
526 } elsif ($params{mode} =~ /server::instance::threadcachehitrate/) {
527 ($dummy, $self->{threads_created}) = $self->{handle}->fetchrow_array(q{
528 SHOW /*!50000 global */ STATUS LIKE 'Threads_created'
529 });
530 ($dummy, $self->{connections}) = $self->{handle}->fetchrow_array(q{
531 SHOW /*!50000 global */ STATUS LIKE 'Connections'
532 });
533 $self->valdiff(\%params, qw(threads_created connections));
534 if ($self->{delta_connections} > 0) {
535 $self->{threadcache_hitrate_now} =
536 100 - ($self->{delta_threads_created} * 100.0 /
537 $self->{delta_connections});
538 } else {
539 $self->{threadcache_hitrate_now} = 100;
540 }
541 $self->{threadcache_hitrate} = 100 -
542 ($self->{threads_created} * 100.0 / $self->{connections});
543 $self->{connections_per_sec} = $self->{delta_connections} /
544 $self->{delta_timestamp};
545 } elsif ($params{mode} =~ /server::instance::querycachehitrate/) {
546 ($dummy, $self->{qcache_inserts}) = $self->{handle}->fetchrow_array(q{
547 SHOW /*!50000 global */ STATUS LIKE 'Qcache_inserts'
548 });
549 ($dummy, $self->{qcache_not_cached}) = $self->{handle}->fetchrow_array(q{
550 SHOW /*!50000 global */ STATUS LIKE 'Qcache_not_cached'
551 });
552 ($dummy, $self->{com_select}) = $self->{handle}->fetchrow_array(q{
553 SHOW /*!50000 global */ STATUS LIKE 'Com_select'
554 });
555 ($dummy, $self->{qcache_hits}) = $self->{handle}->fetchrow_array(q{
556 SHOW /*!50000 global */ STATUS LIKE 'Qcache_hits'
557 });
558 # SHOW VARIABLES WHERE Variable_name = 'have_query_cache' for 5.x, but LIKE is compatible
559 ($dummy, $self->{have_query_cache}) = $self->{handle}->fetchrow_array(q{
560 SHOW VARIABLES LIKE 'have_query_cache'
561 });
562 # SHOW VARIABLES WHERE Variable_name = 'query_cache_size'
563 ($dummy, $self->{query_cache_size}) = $self->{handle}->fetchrow_array(q{
564 SHOW VARIABLES LIKE 'query_cache_size'
565 });
566 $self->valdiff(\%params, qw(com_select qcache_hits));
567 $self->{querycache_hitrate_now} =
568 ($self->{delta_com_select} + $self->{delta_qcache_hits}) > 0 ?
569 100 * $self->{delta_qcache_hits} /
570 ($self->{delta_com_select} + $self->{delta_qcache_hits}) :
571 0;
572 $self->{querycache_hitrate} =
573 ($self->{qcache_not_cached} + $self->{qcache_inserts} + $self->{qcache_hits}) > 0 ?
574 100 * $self->{qcache_hits} /
575 ($self->{qcache_not_cached} + $self->{qcache_inserts} + $self->{qcache_hits}) :
576 0;
577 $self->{selects_per_sec} =
578 $self->{delta_com_select} / $self->{delta_timestamp};
579 } elsif ($params{mode} =~ /server::instance::querycachelowmemprunes/) {
580 ($dummy, $self->{lowmem_prunes}) = $self->{handle}->fetchrow_array(q{
581 SHOW /*!50000 global */ STATUS LIKE 'Qcache_lowmem_prunes'
582 });
583 $self->valdiff(\%params, qw(lowmem_prunes));
584 $self->{lowmem_prunes_per_sec} = $self->{delta_lowmem_prunes} /
585 $self->{delta_timestamp};
586 } elsif ($params{mode} =~ /server::instance::slowqueries/) {
587 ($dummy, $self->{slow_queries}) = $self->{handle}->fetchrow_array(q{
588 SHOW /*!50000 global */ STATUS LIKE 'Slow_queries'
589 });
590 $self->valdiff(\%params, qw(slow_queries));
591 $self->{slow_queries_per_sec} = $self->{delta_slow_queries} /
592 $self->{delta_timestamp};
593 } elsif ($params{mode} =~ /server::instance::longprocs/) {
594 if (DBD::MySQL::Server::return_first_server()->version_is_minimum("5.1")) {
595 ($self->{longrunners}) = $self->{handle}->fetchrow_array(qq(
596 SELECT
597 COUNT(*)
598 FROM
599 information_schema.processlist
600 WHERE user <> ?
601 AND id <> CONNECTION_ID()
602 AND time > 60
603 AND command <> 'Sleep'
604 ), $self->{replication_user});
605 } else {
606 $self->{longrunners} = 0 if ! defined $self->{longrunners};
607 foreach ($self->{handle}->fetchall_array(q{
608 SHOW PROCESSLIST
609 })) {
610 my($id, $user, $host, $db, $command, $tme, $state, $info) = @{$_};
611 if (($user ne $self->{replication_user}) &&
612 ($tme > 60) &&
613 ($command ne 'Sleep')) {
614 $self->{longrunners}++;
615 }
616 }
617 }
618 } elsif ($params{mode} =~ /server::instance::tablecachehitrate/) {
619 ($dummy, $self->{open_tables}) = $self->{handle}->fetchrow_array(q{
620 SHOW /*!50000 global */ STATUS LIKE 'Open_tables'
621 });
622 ($dummy, $self->{opened_tables}) = $self->{handle}->fetchrow_array(q{
623 SHOW /*!50000 global */ STATUS LIKE 'Opened_tables'
624 });
625 if (DBD::MySQL::Server::return_first_server()->version_is_minimum("5.1.3")) {
626 # SHOW VARIABLES WHERE Variable_name = 'table_open_cache'
627 ($dummy, $self->{table_cache}) = $self->{handle}->fetchrow_array(q{
628 SHOW VARIABLES LIKE 'table_open_cache'
629 });
630 } else {
631 # SHOW VARIABLES WHERE Variable_name = 'table_cache'
632 ($dummy, $self->{table_cache}) = $self->{handle}->fetchrow_array(q{
633 SHOW VARIABLES LIKE 'table_cache'
634 });
635 }
636 $self->{table_cache} ||= 0;
637 #$self->valdiff(\%params, qw(open_tables opened_tables table_cache));
638 # _now ist hier sinnlos, da opened_tables waechst, aber open_tables wieder
639 # schrumpfen kann weil tabellen geschlossen werden.
640 if ($self->{opened_tables} != 0 && $self->{table_cache} != 0) {
641 $self->{tablecache_hitrate} =
642 100 * $self->{open_tables} / $self->{opened_tables};
643 $self->{tablecache_fillrate} =
644 100 * $self->{open_tables} / $self->{table_cache};
645 } elsif ($self->{opened_tables} == 0 && $self->{table_cache} != 0) {
646 $self->{tablecache_hitrate} = 100;
647 $self->{tablecache_fillrate} =
648 100 * $self->{open_tables} / $self->{table_cache};
649 } else {
650 $self->{tablecache_hitrate} = 0;
651 $self->{tablecache_fillrate} = 0;
652 $self->add_nagios_critical("no table cache");
653 }
654 } elsif ($params{mode} =~ /server::instance::tablelockcontention/) {
655 ($dummy, $self->{table_locks_waited}) = $self->{handle}->fetchrow_array(q{
656 SHOW /*!50000 global */ STATUS LIKE 'Table_locks_waited'
657 });
658 ($dummy, $self->{table_locks_immediate}) = $self->{handle}->fetchrow_array(q{
659 SHOW /*!50000 global */ STATUS LIKE 'Table_locks_immediate'
660 });
661 $self->valdiff(\%params, qw(table_locks_waited table_locks_immediate));
662 $self->{table_lock_contention} =
663 ($self->{table_locks_waited} + $self->{table_locks_immediate}) > 0 ?
664 100 * $self->{table_locks_waited} /
665 ($self->{table_locks_waited} + $self->{table_locks_immediate}) :
666 100;
667 $self->{table_lock_contention_now} =
668 ($self->{delta_table_locks_waited} + $self->{delta_table_locks_immediate}) > 0 ?
669 100 * $self->{delta_table_locks_waited} /
670 ($self->{delta_table_locks_waited} + $self->{delta_table_locks_immediate}) :
671 100;
672 } elsif ($params{mode} =~ /server::instance::tableindexusage/) {
673 # http://johnjacobm.wordpress.com/2007/06/
674 # formula for calculating the percentage of full table scans
675 ($dummy, $self->{handler_read_first}) = $self->{handle}->fetchrow_array(q{
676 SHOW /*!50000 global */ STATUS LIKE 'Handler_read_first'
677 });
678 ($dummy, $self->{handler_read_key}) = $self->{handle}->fetchrow_array(q{
679 SHOW /*!50000 global */ STATUS LIKE 'Handler_read_key'
680 });
681 ($dummy, $self->{handler_read_next}) = $self->{handle}->fetchrow_array(q{
682 SHOW /*!50000 global */ STATUS LIKE 'Handler_read_next'
683 });
684 ($dummy, $self->{handler_read_prev}) = $self->{handle}->fetchrow_array(q{
685 SHOW /*!50000 global */ STATUS LIKE 'Handler_read_prev'
686 });
687 ($dummy, $self->{handler_read_rnd}) = $self->{handle}->fetchrow_array(q{
688 SHOW /*!50000 global */ STATUS LIKE 'Handler_read_rnd'
689 });
690 ($dummy, $self->{handler_read_rnd_next}) = $self->{handle}->fetchrow_array(q{
691 SHOW /*!50000 global */ STATUS LIKE 'Handler_read_rnd_next'
692 });
693 $self->valdiff(\%params, qw(handler_read_first handler_read_key
694 handler_read_next handler_read_prev handler_read_rnd
695 handler_read_rnd_next));
696 my $delta_reads = $self->{delta_handler_read_first} +
697 $self->{delta_handler_read_key} +
698 $self->{delta_handler_read_next} +
699 $self->{delta_handler_read_prev} +
700 $self->{delta_handler_read_rnd} +
701 $self->{delta_handler_read_rnd_next};
702 my $reads = $self->{handler_read_first} +
703 $self->{handler_read_key} +
704 $self->{handler_read_next} +
705 $self->{handler_read_prev} +
706 $self->{handler_read_rnd} +
707 $self->{handler_read_rnd_next};
708 $self->{index_usage_now} = ($delta_reads == 0) ? 0 :
709 100 - (100.0 * ($self->{delta_handler_read_rnd} +
710 $self->{delta_handler_read_rnd_next}) /
711 $delta_reads);
712 $self->{index_usage} = ($reads == 0) ? 0 :
713 100 - (100.0 * ($self->{handler_read_rnd} +
714 $self->{handler_read_rnd_next}) /
715 $reads);
716 } elsif ($params{mode} =~ /server::instance::tabletmpondisk/) {
717 ($dummy, $self->{created_tmp_tables}) = $self->{handle}->fetchrow_array(q{
718 SHOW /*!50000 global */ STATUS LIKE 'Created_tmp_tables'
719 });
720 ($dummy, $self->{created_tmp_disk_tables}) = $self->{handle}->fetchrow_array(q{
721 SHOW /*!50000 global */ STATUS LIKE 'Created_tmp_disk_tables'
722 });
723 $self->valdiff(\%params, qw(created_tmp_tables created_tmp_disk_tables));
724 $self->{pct_tmp_on_disk} = $self->{created_tmp_tables} > 0 ?
725 100 * $self->{created_tmp_disk_tables} / $self->{created_tmp_tables} :
726 100;
727 $self->{pct_tmp_on_disk_now} = $self->{delta_created_tmp_tables} > 0 ?
728 100 * $self->{delta_created_tmp_disk_tables} / $self->{delta_created_tmp_tables} :
729 100;
730 } elsif ($params{mode} =~ /server::instance::openfiles/) {
731 ($dummy, $self->{open_files_limit}) = $self->{handle}->fetchrow_array(q{
732 SHOW VARIABLES LIKE 'open_files_limit'
733 });
734 ($dummy, $self->{open_files}) = $self->{handle}->fetchrow_array(q{
735 SHOW /*!50000 global */ STATUS LIKE 'Open_files'
736 });
737 $self->{pct_open_files} = 100 * $self->{open_files} / $self->{open_files_limit};
738 } elsif ($params{mode} =~ /server::instance::needoptimize/) {
739 $self->{fragmented} = [];
740 #http://www.electrictoolbox.com/optimize-tables-mysql-php/
741 my @result = $self->{handle}->fetchall_array(q{
742 SHOW TABLE STATUS
743 });
744 foreach (@result) {
745 my ($name, $engine, $data_length, $data_free) =
746 ($_->[0], $_->[1], $_->[6 ], $_->[9]);
747 next if ($params{name} && $params{name} ne $name);
748 my $fragmentation = $data_length ? $data_free * 100 / $data_length : 0;
749 push(@{$self->{fragmented}},
750 [$name, $fragmentation, $data_length, $data_free]);
751 }
752 } elsif ($params{mode} =~ /server::instance::myisam/) {
753 $self->{engine_myisam} = DBD::MySQL::Server::Instance::MyISAM->new(
754 %params
755 );
756 } elsif ($params{mode} =~ /server::instance::innodb/) {
757 $self->{engine_innodb} = DBD::MySQL::Server::Instance::Innodb->new(
758 %params
759 );
760 } elsif ($params{mode} =~ /server::instance::replication/) {
761 $self->{replication} = DBD::MySQL::Server::Instance::Replication->new(
762 %params
763 );
764 }
765 }
766
767 sub nagios {
768 my $self = shift;
769 my %params = @_;
770 if (! $self->{nagios_level}) {
771 if ($params{mode} =~ /server::instance::connectedthreads/) {
772 $self->add_nagios(
773 $self->check_thresholds($self->{threads_connected}, 10, 20),
774 sprintf "%d client connection threads", $self->{threads_connected});
775 $self->add_perfdata(sprintf "threads_connected=%d;%d;%d",
776 $self->{threads_connected},
777 $self->{warningrange}, $self->{criticalrange});
778 } elsif ($params{mode} =~ /server::instance::createdthreads/) {
779 $self->add_nagios(
780 $self->check_thresholds($self->{threads_created_per_sec}, 10, 20),
781 sprintf "%.2f threads created/sec", $self->{threads_created_per_sec});
782 $self->add_perfdata(sprintf "threads_created_per_sec=%.2f;%.2f;%.2f",
783 $self->{threads_created_per_sec},
784 $self->{warningrange}, $self->{criticalrange});
785 } elsif ($params{mode} =~ /server::instance::runningthreads/) {
786 $self->add_nagios(
787 $self->check_thresholds($self->{threads_running}, 10, 20),
788 sprintf "%d running threads", $self->{threads_running});
789 $self->add_perfdata(sprintf "threads_running=%d;%d;%d",
790 $self->{threads_running},
791 $self->{warningrange}, $self->{criticalrange});
792 } elsif ($params{mode} =~ /server::instance::cachedthreads/) {
793 $self->add_nagios(
794 $self->check_thresholds($self->{threads_cached}, 10, 20),
795 sprintf "%d cached threads", $self->{threads_cached});
796 $self->add_perfdata(sprintf "threads_cached=%d;%d;%d",
797 $self->{threads_cached},
798 $self->{warningrange}, $self->{criticalrange});
799 } elsif ($params{mode} =~ /server::instance::abortedconnects/) {
800 $self->add_nagios(
801 $self->check_thresholds($self->{connects_aborted_per_sec}, 1, 5),
802 sprintf "%.2f aborted connections/sec", $self->{connects_aborted_per_sec});
803 $self->add_perfdata(sprintf "connects_aborted_per_sec=%.2f;%.2f;%.2f",
804 $self->{connects_aborted_per_sec},
805 $self->{warningrange}, $self->{criticalrange});
806 } elsif ($params{mode} =~ /server::instance::abortedclients/) {
807 $self->add_nagios(
808 $self->check_thresholds($self->{clients_aborted_per_sec}, 1, 5),
809 sprintf "%.2f aborted (client died) connections/sec", $self->{clients_aborted_per_sec});
810 $self->add_perfdata(sprintf "clients_aborted_per_sec=%.2f;%.2f;%.2f",
811 $self->{clients_aborted_per_sec},
812 $self->{warningrange}, $self->{criticalrange});
813 } elsif ($params{mode} =~ /server::instance::threadcachehitrate/) {
814 my $refkey = 'threadcache_hitrate'.($params{lookback} ? '_now' : '');
815 $self->add_nagios(
816 $self->check_thresholds($self->{$refkey}, "90:", "80:"),
817 sprintf "thread cache hitrate %.2f%%", $self->{$refkey});
818 $self->add_perfdata(sprintf "thread_cache_hitrate=%.2f%%;%s;%s",
819 $self->{threadcache_hitrate},
820 $self->{warningrange}, $self->{criticalrange});
821 $self->add_perfdata(sprintf "thread_cache_hitrate_now=%.2f%%",
822 $self->{threadcache_hitrate_now});
823 $self->add_perfdata(sprintf "connections_per_sec=%.2f",
824 $self->{connections_per_sec});
825 } elsif ($params{mode} =~ /server::instance::querycachehitrate/) {
826 my $refkey = 'querycache_hitrate'.($params{lookback} ? '_now' : '');
827 if ((lc $self->{have_query_cache} eq 'yes') && ($self->{query_cache_size})) {
828 $self->add_nagios(
829 $self->check_thresholds($self->{$refkey}, "90:", "80:"),
830 sprintf "query cache hitrate %.2f%%", $self->{$refkey});
831 } else {
832 $self->check_thresholds($self->{$refkey}, "90:", "80:");
833 $self->add_nagios_ok(
834 sprintf "query cache hitrate %.2f%% (because it's turned off)",
835 $self->{querycache_hitrate});
836 }
837 $self->add_perfdata(sprintf "qcache_hitrate=%.2f%%;%s;%s",
838 $self->{querycache_hitrate},
839 $self->{warningrange}, $self->{criticalrange});
840 $self->add_perfdata(sprintf "qcache_hitrate_now=%.2f%%",
841 $self->{querycache_hitrate_now});
842 $self->add_perfdata(sprintf "selects_per_sec=%.2f",
843 $self->{selects_per_sec});
844 } elsif ($params{mode} =~ /server::instance::querycachelowmemprunes/) {
845 $self->add_nagios(
846 $self->check_thresholds($self->{lowmem_prunes_per_sec}, "1", "10"),
847 sprintf "%d query cache lowmem prunes in %d seconds (%.2f/sec)",
848 $self->{delta_lowmem_prunes}, $self->{delta_timestamp},
849 $self->{lowmem_prunes_per_sec});
850 $self->add_perfdata(sprintf "qcache_lowmem_prunes_rate=%.2f;%s;%s",
851 $self->{lowmem_prunes_per_sec},
852 $self->{warningrange}, $self->{criticalrange});
853 } elsif ($params{mode} =~ /server::instance::slowqueries/) {
854 $self->add_nagios(
855 $self->check_thresholds($self->{slow_queries_per_sec}, "0.1", "1"),
856 sprintf "%d slow queries in %d seconds (%.2f/sec)",
857 $self->{delta_slow_queries}, $self->{delta_timestamp},
858 $self->{slow_queries_per_sec});
859 $self->add_perfdata(sprintf "slow_queries_rate=%.2f%%;%s;%s",
860 $self->{slow_queries_per_sec},
861 $self->{warningrange}, $self->{criticalrange});
862 } elsif ($params{mode} =~ /server::instance::longprocs/) {
863 $self->add_nagios(
864 $self->check_thresholds($self->{longrunners}, 10, 20),
865 sprintf "%d long running processes", $self->{longrunners});
866 $self->add_perfdata(sprintf "long_running_procs=%d;%d;%d",
867 $self->{longrunners},
868 $self->{warningrange}, $self->{criticalrange});
869 } elsif ($params{mode} =~ /server::instance::tablecachehitrate/) {
870 if ($self->{tablecache_fillrate} < 95) {
871 $self->add_nagios_ok(
872 sprintf "table cache hitrate %.2f%%, %.2f%% filled",
873 $self->{tablecache_hitrate},
874 $self->{tablecache_fillrate});
875 $self->check_thresholds($self->{tablecache_hitrate}, "99:", "95:");
876 } else {
877 $self->add_nagios(
878 $self->check_thresholds($self->{tablecache_hitrate}, "99:", "95:"),
879 sprintf "table cache hitrate %.2f%%", $self->{tablecache_hitrate});
880 }
881 $self->add_perfdata(sprintf "tablecache_hitrate=%.2f%%;%s;%s",
882 $self->{tablecache_hitrate},
883 $self->{warningrange}, $self->{criticalrange});
884 $self->add_perfdata(sprintf "tablecache_fillrate=%.2f%%",
885 $self->{tablecache_fillrate});
886 } elsif ($params{mode} =~ /server::instance::tablelockcontention/) {
887 my $refkey = 'table_lock_contention'.($params{lookback} ? '_now' : '');
888 if ($self->{uptime} > 10800) { # MySQL Bug #30599
889 $self->add_nagios(
890 $self->check_thresholds($self->{$refkey}, "1", "2"),
891 sprintf "table lock contention %.2f%%", $self->{$refkey});
892 } else {
893 $self->check_thresholds($self->{$refkey}, "1", "2");
894 $self->add_nagios_ok(
895 sprintf "table lock contention %.2f%% (uptime < 10800)",
896 $self->{$refkey});
897 }
898 $self->add_perfdata(sprintf "tablelock_contention=%.2f%%;%s;%s",
899 $self->{table_lock_contention},
900 $self->{warningrange}, $self->{criticalrange});
901 $self->add_perfdata(sprintf "tablelock_contention_now=%.2f%%",
902 $self->{table_lock_contention_now});
903 } elsif ($params{mode} =~ /server::instance::tableindexusage/) {
904 my $refkey = 'index_usage'.($params{lookback} ? '_now' : '');
905 $self->add_nagios(
906 $self->check_thresholds($self->{$refkey}, "90:", "80:"),
907 sprintf "index usage %.2f%%", $self->{$refkey});
908 $self->add_perfdata(sprintf "index_usage=%.2f%%;%s;%s",
909 $self->{index_usage},
910 $self->{warningrange}, $self->{criticalrange});
911 $self->add_perfdata(sprintf "index_usage_now=%.2f%%",
912 $self->{index_usage_now});
913 } elsif ($params{mode} =~ /server::instance::tabletmpondisk/) {
914 my $refkey = 'pct_tmp_on_disk'.($params{lookback} ? '_now' : '');
915 $self->add_nagios(
916 $self->check_thresholds($self->{$refkey}, "25", "50"),
917 sprintf "%.2f%% of %d tables were created on disk",
918 $self->{$refkey}, $self->{delta_created_tmp_tables});
919 $self->add_perfdata(sprintf "pct_tmp_table_on_disk=%.2f%%;%s;%s",
920 $self->{pct_tmp_on_disk},
921 $self->{warningrange}, $self->{criticalrange});
922 $self->add_perfdata(sprintf "pct_tmp_table_on_disk_now=%.2f%%",
923 $self->{pct_tmp_on_disk_now});
924 } elsif ($params{mode} =~ /server::instance::openfiles/) {
925 $self->add_nagios(
926 $self->check_thresholds($self->{pct_open_files}, 80, 95),
927 sprintf "%.2f%% of the open files limit reached (%d of max. %d)",
928 $self->{pct_open_files},
929 $self->{open_files}, $self->{open_files_limit});
930 $self->add_perfdata(sprintf "pct_open_files=%.3f%%;%.3f;%.3f",
931 $self->{pct_open_files},
932 $self->{warningrange},
933 $self->{criticalrange});
934 $self->add_perfdata(sprintf "open_files=%d;%d;%d",
935 $self->{open_files},
936 $self->{open_files_limit} * $self->{warningrange} / 100,
937 $self->{open_files_limit} * $self->{criticalrange} / 100);
938 } elsif ($params{mode} =~ /server::instance::needoptimize/) {
939 foreach (@{$self->{fragmented}}) {
940 $self->add_nagios(
941 $self->check_thresholds($_->[1], 10, 25),
942 sprintf "table %s is %.2f%% fragmented", $_->[0], $_->[1]);
943 if ($params{name}) {
944 $self->add_perfdata(sprintf "'%s_frag'=%.2f%%;%d;%d",
945 $_->[0], $_->[1], $self->{warningrange}, $self->{criticalrange});
946 }
947 }
948 } elsif ($params{mode} =~ /server::instance::myisam/) {
949 $self->{engine_myisam}->nagios(%params);
950 $self->merge_nagios($self->{engine_myisam});
951 } elsif ($params{mode} =~ /server::instance::innodb/) {
952 $self->{engine_innodb}->nagios(%params);
953 $self->merge_nagios($self->{engine_innodb});
954 } elsif ($params{mode} =~ /server::instance::replication/) {
955 $self->{replication}->nagios(%params);
956 $self->merge_nagios($self->{replication});
957 }
958 }
959 }
960
961
962
963 package DBD::MySQL::Server;
964
965 use strict;
966 use Time::HiRes;
967 use IO::File;
968 use File::Copy 'cp';
969 use Data::Dumper;
970
971
972 {
973 our $verbose = 0;
974 our $scream = 0; # scream if something is not implemented
975 our $access = "dbi"; # how do we access the database.
976 our $my_modules_dyn_dir = ""; # where we look for self-written extensions
977
978 my @servers = ();
979 my $initerrors = undef;
980
981 sub add_server {
982 push(@servers, shift);
983 }
984
985 sub return_servers {
986 return @servers;
987 }
988
989 sub return_first_server() {
990 return $servers[0];
991 }
992
993 }
994
995 sub new {
996 my $class = shift;
997 my %params = @_;
998 my $self = {
999 mode => $params{mode},
1000 access => $params{method} || 'dbi',
1001 hostname => $params{hostname},
1002 database => $params{database} || 'information_schema',
1003 port => $params{port},
1004 socket => $params{socket},
1005 username => $params{username},
1006 password => $params{password},
1007 replication_user => $params{replication_user},
1008 mycnf => $params{mycnf},
1009 mycnfgroup => $params{mycnfgroup},
1010 timeout => $params{timeout},
1011 warningrange => $params{warningrange},
1012 criticalrange => $params{criticalrange},
1013 verbose => $params{verbose},
1014 report => $params{report},
1015 negate => $params{negate},
1016 labelformat => $params{labelformat},
1017 version => 'unknown',
1018 instance => undef,
1019 handle => undef,
1020 };
1021 bless $self, $class;
1022 $self->init_nagios();
1023 if ($self->dbconnect(%params)) {
1024 ($self->{dummy}, $self->{version}) = $self->{handle}->fetchrow_array(
1025 #q{ SHOW VARIABLES WHERE Variable_name = 'version' }
1026 q{ SHOW VARIABLES LIKE 'version' }
1027 );
1028 $self->{version} = (split "-", $self->{version})[0];
1029 ($self->{dummy}, $self->{uptime}) = $self->{handle}->fetchrow_array(
1030 q{ SHOW STATUS LIKE 'Uptime' }
1031 );
1032 DBD::MySQL::Server::add_server($self);
1033 $self->init(%params);
1034 }
1035 return $self;
1036 }
1037
1038 sub init {
1039 my $self = shift;
1040 my %params = @_;
1041 $params{handle} = $self->{handle};
1042 $params{uptime} = $self->{uptime};
1043 $self->set_global_db_thresholds(\%params);
1044 if ($params{mode} =~ /^server::instance/) {
1045 $self->{instance} = DBD::MySQL::Server::Instance->new(%params);
1046 } elsif ($params{mode} =~ /^server::sql/) {
1047 $self->set_local_db_thresholds(%params);
1048 if ($params{regexp}) {
1049 # sql output is treated as text
1050 if ($params{name2} eq $params{name}) {
1051 $self->add_nagios_unknown(sprintf "where's the regexp????");
1052 } else {
1053 $self->{genericsql} =
1054 $self->{handle}->fetchrow_array($params{selectname});
1055 if (! defined $self->{genericsql}) {
1056 $self->add_nagios_unknown(sprintf "got no valid response for %s",
1057 $params{selectname});
1058 }
1059 }
1060 } else {
1061 # sql output must be a number (or array of numbers)
1062 @{$self->{genericsql}} =
1063 $self->{handle}->fetchrow_array($params{selectname});
1064 if ($self->{handle}->{errstr}) {
1065 $self->add_nagios_unknown(sprintf "got no valid response for %s: %s",
1066 $params{selectname}, $self->{handle}->{errstr});
1067 } elsif (! (defined $self->{genericsql} &&
1068 (scalar(grep {
1069 /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/
1070 } @{$self->{genericsql}})) ==
1071 scalar(@{$self->{genericsql}}))) {
1072 $self->add_nagios_unknown(sprintf "got no valid response for %s",
1073 $params{selectname});
1074 } elsif (! defined $self->{genericsql}) {
1075 $self->add_nagios_unknown(sprintf "got no valid response for %s",
1076 $params{selectname});
1077 } else {
1078 # name2 in array
1079 # units in array
1080 }
1081 }
1082 } elsif ($params{mode} =~ /^server::uptime/) {
1083 # already set with the connection. but use minutes here
1084 } elsif ($params{mode} =~ /^server::connectiontime/) {
1085 $self->{connection_time} = $self->{tac} - $self->{tic};
1086 } elsif ($params{mode} =~ /^my::([^:.]+)/) {
1087 my $class = $1;
1088 my $loaderror = undef;
1089 substr($class, 0, 1) = uc substr($class, 0, 1);
1090 foreach my $libpath (split(":", $DBD::MySQL::Server::my_modules_dyn_dir)) {
1091 foreach my $extmod (glob $libpath."/CheckMySQLHealth*.pm") {
1092 eval {
1093 $self->trace(sprintf "loading module %s", $extmod);
1094 require $extmod;
1095 };
1096 if ($@) {
1097 $loaderror = $extmod;
1098 $self->trace(sprintf "failed loading module %s: %s", $extmod, $@);
1099 }
1100 }
1101 }
1102 my $obj = {
1103 handle => $params{handle},
1104 warningrange => $params{warningrange},
1105 criticalrange => $params{criticalrange},
1106 };
1107 bless $obj, "My$class";
1108 $self->{my} = $obj;
1109 if ($self->{my}->isa("DBD::MySQL::Server")) {
1110 my $dos_init = $self->can("init");
1111 my $dos_nagios = $self->can("nagios");
1112 my $my_init = $self->{my}->can("init");
1113 my $my_nagios = $self->{my}->can("nagios");
1114 if ($my_init == $dos_init) {
1115 $self->add_nagios_unknown(
1116 sprintf "Class %s needs an init() method", ref($self->{my}));
1117 } elsif ($my_nagios == $dos_nagios) {
1118 $self->add_nagios_unknown(
1119 sprintf "Class %s needs a nagios() method", ref($self->{my}));
1120 } else {
1121 $self->{my}->init_nagios(%params);
1122 $self->{my}->init(%params);
1123 }
1124 } else {
1125 $self->add_nagios_unknown(
1126 sprintf "Class %s is not a subclass of DBD::MySQL::Server%s",
1127 ref($self->{my}),
1128 $loaderror ? sprintf " (syntax error in %s?)", $loaderror : "" );
1129 }
1130 } else {
1131 printf "broken mode %s\n", $params{mode};
1132 }
1133 }
1134
1135 sub dump {
1136 my $self = shift;
1137 my $message = shift || "";
1138 printf "%s %s\n", $message, Data::Dumper::Dumper($self);
1139 }
1140
1141 sub nagios {
1142 my $self = shift;
1143 my %params = @_;
1144 if (! $self->{nagios_level}) {
1145 if ($params{mode} =~ /^server::instance/) {
1146 $self->{instance}->nagios(%params);
1147 $self->merge_nagios($self->{instance});
1148 } elsif ($params{mode} =~ /^server::database/) {
1149 $self->{database}->nagios(%params);
1150 $self->merge_nagios($self->{database});
1151 } elsif ($params{mode} =~ /^server::uptime/) {
1152 $self->add_nagios(
1153 $self->check_thresholds($self->{uptime} / 60, "10:", "5:"),
1154 sprintf "database is up since %d minutes", $self->{uptime} / 60);
1155 $self->add_perfdata(sprintf "uptime=%ds",
1156 $self->{uptime});
1157 } elsif ($params{mode} =~ /^server::connectiontime/) {
1158 $self->add_nagios(
1159 $self->check_thresholds($self->{connection_time}, 1, 5),
1160 sprintf "%.2f seconds to connect as %s",
1161 $self->{connection_time}, ($self->{username} || getpwuid($<)));
1162 $self->add_perfdata(sprintf "connection_time=%.4fs;%d;%d",
1163 $self->{connection_time},
1164 $self->{warningrange}, $self->{criticalrange});
1165 } elsif ($params{mode} =~ /^server::sql/) {
1166 if ($params{regexp}) {
1167 if (substr($params{name2}, 0, 1) eq '!') {
1168 $params{name2} =~ s/^!//;
1169 if ($self->{genericsql} !~ /$params{name2}/) {
1170 $self->add_nagios_ok(
1171 sprintf "output %s does not match pattern %s",
1172 $self->{genericsql}, $params{name2});
1173 } else {
1174 $self->add_nagios_critical(
1175 sprintf "output %s matches pattern %s",
1176 $self->{genericsql}, $params{name2});
1177 }
1178 } else {
1179 if ($self->{genericsql} =~ /$params{name2}/) {
1180 $self->add_nagios_ok(
1181 sprintf "output %s matches pattern %s",
1182 $self->{genericsql}, $params{name2});
1183 } else {
1184 $self->add_nagios_critical(
1185 sprintf "output %s does not match pattern %s",
1186 $self->{genericsql}, $params{name2});
1187 }
1188 }
1189 } else {
1190 $self->add_nagios(
1191 # the first item in the list will trigger the threshold values
1192 $self->check_thresholds($self->{genericsql}[0], 1, 5),
1193 sprintf "%s: %s%s",
1194 $params{name2} ? lc $params{name2} : lc $params{selectname},
1195 # float as float, integers as integers
1196 join(" ", map {
1197 (sprintf("%d", $_) eq $_) ? $_ : sprintf("%f", $_)
1198 } @{$self->{genericsql}}),
1199 $params{units} ? $params{units} : "");
1200 my $i = 0;
1201 # workaround... getting the column names from the database would be nicer
1202 my @names2_arr = split(/\s+/, $params{name2});
1203 foreach my $t (@{$self->{genericsql}}) {
1204 $self->add_perfdata(sprintf "\'%s\'=%s%s;%s;%s",
1205 $names2_arr[$i] ? lc $names2_arr[$i] : lc $params{selectname},
1206 # float as float, integers as integers
1207 (sprintf("%d", $t) eq $t) ? $t : sprintf("%f", $t),
1208 $params{units} ? $params{units} : "",
1209 ($i == 0) ? $self->{warningrange} : "",
1210 ($i == 0) ? $self->{criticalrange} : ""
1211 );
1212 $i++;
1213 }
1214 }
1215 } elsif ($params{mode} =~ /^my::([^:.]+)/) {
1216 $self->{my}->nagios(%params);
1217 $self->merge_nagios($self->{my});
1218 }
1219 }
1220 }
1221
1222
1223 sub init_nagios {
1224 my $self = shift;
1225 no strict 'refs';
1226 if (! ref($self)) {
1227 my $nagiosvar = $self."::nagios";
1228 my $nagioslevelvar = $self."::nagios_level";
1229 $$nagiosvar = {
1230 messages => {
1231 0 => [],
1232 1 => [],
1233 2 => [],
1234 3 => [],
1235 },
1236 perfdata => [],
1237 };
1238 $$nagioslevelvar = $ERRORS{OK},
1239 } else {
1240 $self->{nagios} = {
1241 messages => {
1242 0 => [],
1243 1 => [],
1244 2 => [],
1245 3 => [],
1246 },
1247 perfdata => [],
1248 };
1249 $self->{nagios_level} = $ERRORS{OK},
1250 }
1251 }
1252
1253 sub check_thresholds {
1254 my $self = shift;
1255 my $value = shift;
1256 my $defaultwarningrange = shift;
1257 my $defaultcriticalrange = shift;
1258 my $level = $ERRORS{OK};
1259 $self->{warningrange} = defined $self->{warningrange} ?
1260 $self->{warningrange} : $defaultwarningrange;
1261 $self->{criticalrange} = defined $self->{criticalrange} ?
1262 $self->{criticalrange} : $defaultcriticalrange;
1263
1264 if ($self->{warningrange} =~ /^([-+]?[0-9]*\.?[0-9]+)$/) {
1265 # warning = 10, warn if > 10 or < 0
1266 $level = $ERRORS{WARNING}
1267 if ($value > $1 || $value < 0);
1268 } elsif ($self->{warningrange} =~ /^([-+]?[0-9]*\.?[0-9]+):$/) {
1269 # warning = 10:, warn if < 10
1270 $level = $ERRORS{WARNING}
1271 if ($value < $1);
1272 } elsif ($self->{warningrange} =~ /^~:([-+]?[0-9]*\.?[0-9]+)$/) {
1273 # warning = ~:10, warn if > 10
1274 $level = $ERRORS{WARNING}
1275 if ($value > $1);
1276 } elsif ($self->{warningrange} =~ /^([-+]?[0-9]*\.?[0-9]+):([-+]?[0-9]*\.?[0-9]+)$/) {
1277 # warning = 10:20, warn if < 10 or > 20
1278 $level = $ERRORS{WARNING}
1279 if ($value < $1 || $value > $2);
1280 } elsif ($self->{warningrange} =~ /^@([-+]?[0-9]*\.?[0-9]+):([-+]?[0-9]*\.?[0-9]+)$/) {
1281 # warning = @10:20, warn if >= 10 and <= 20
1282 $level = $ERRORS{WARNING}
1283 if ($value >= $1 && $value <= $2);
1284 }
1285 if ($self->{criticalrange} =~ /^([-+]?[0-9]*\.?[0-9]+)$/) {
1286 # critical = 10, crit if > 10 or < 0
1287 $level = $ERRORS{CRITICAL}
1288 if ($value > $1 || $value < 0);
1289 } elsif ($self->{criticalrange} =~ /^([-+]?[0-9]*\.?[0-9]+):$/) {
1290 # critical = 10:, crit if < 10
1291 $level = $ERRORS{CRITICAL}
1292 if ($value < $1);
1293 } elsif ($self->{criticalrange} =~ /^~:([-+]?[0-9]*\.?[0-9]+)$/) {
1294 # critical = ~:10, crit if > 10
1295 $level = $ERRORS{CRITICAL}
1296 if ($value > $1);
1297 } elsif ($self->{criticalrange} =~ /^([-+]?[0-9]*\.?[0-9]+):([-+]?[0-9]*\.?[0-9]+)$/) {
1298 # critical = 10:20, crit if < 10 or > 20
1299 $level = $ERRORS{CRITICAL}
1300 if ($value < $1 || $value > $2);
1301 } elsif ($self->{criticalrange} =~ /^@([-+]?[0-9]*\.?[0-9]+):([-+]?[0-9]*\.?[0-9]+)$/) {
1302 # critical = @10:20, crit if >= 10 and <= 20
1303 $level = $ERRORS{CRITICAL}
1304 if ($value >= $1 && $value <= $2);
1305 }
1306 return $level;
1307 #
1308 # syntax error must be reported with returncode -1
1309 #
1310 }
1311
1312 sub add_nagios {
1313 my $self = shift;
1314 my $level = shift;
1315 my $message = shift;
1316 push(@{$self->{nagios}->{messages}->{$level}}, $message);
1317 # recalc current level
1318 foreach my $llevel (qw(CRITICAL WARNING UNKNOWN OK)) {
1319 if (scalar(@{$self->{nagios}->{messages}->{$ERRORS{$llevel}}})) {
1320 $self->{nagios_level} = $ERRORS{$llevel};
1321 }
1322 }
1323 }
1324
1325 sub add_nagios_ok {
1326 my $self = shift;
1327 my $message = shift;
1328 $self->add_nagios($ERRORS{OK}, $message);
1329 }
1330
1331 sub add_nagios_warning {
1332 my $self = shift;
1333 my $message = shift;
1334 $self->add_nagios($ERRORS{WARNING}, $message);
1335 }
1336
1337 sub add_nagios_critical {
1338 my $self = shift;
1339 my $message = shift;
1340 $self->add_nagios($ERRORS{CRITICAL}, $message);
1341 }
1342
1343 sub add_nagios_unknown {
1344 my $self = shift;
1345 my $message = shift;
1346 $self->add_nagios($ERRORS{UNKNOWN}, $message);
1347 }
1348
1349 sub add_perfdata {
1350 my $self = shift;
1351 my $data = shift;
1352 push(@{$self->{nagios}->{perfdata}}, $data);
1353 }
1354
1355 sub merge_nagios {
1356 my $self = shift;
1357 my $child = shift;
1358 foreach my $level (0..3) {
1359 foreach (@{$child->{nagios}->{messages}->{$level}}) {
1360 $self->add_nagios($level, $_);
1361 }
1362 #push(@{$self->{nagios}->{messages}->{$level}},
1363 # @{$child->{nagios}->{messages}->{$level}});
1364 }
1365 push(@{$self->{nagios}->{perfdata}}, @{$child->{nagios}->{perfdata}});
1366 }
1367
1368 sub calculate_result {
1369 my $self = shift;
1370 my $labels = shift || {};
1371 my $multiline = 0;
1372 map {
1373 $self->{nagios_level} = $ERRORS{$_} if
1374 (scalar(@{$self->{nagios}->{messages}->{$ERRORS{$_}}}));
1375 } ("OK", "UNKNOWN", "WARNING", "CRITICAL");
1376 if ($ENV{NRPE_MULTILINESUPPORT} &&
1377 length join(" ", @{$self->{nagios}->{perfdata}}) > 200) {
1378 $multiline = 1;
1379 }
1380 my $all_messages = join(($multiline ? "\n" : ", "), map {
1381 join(($multiline ? "\n" : ", "), @{$self->{nagios}->{messages}->{$ERRORS{$_}}})
1382 } grep {
1383 scalar(@{$self->{nagios}->{messages}->{$ERRORS{$_}}})
1384 } ("CRITICAL", "WARNING", "UNKNOWN", "OK"));
1385 my $bad_messages = join(($multiline ? "\n" : ", "), map {
1386 join(($multiline ? "\n" : ", "), @{$self->{nagios}->{messages}->{$ERRORS{$_}}})
1387 } grep {
1388 scalar(@{$self->{nagios}->{messages}->{$ERRORS{$_}}})
1389 } ("CRITICAL", "WARNING", "UNKNOWN"));
1390 my $good_messages = join(($multiline ? "\n" : ", "), map {
1391 join(($multiline ? "\n" : ", "), @{$self->{nagios}->{messages}->{$ERRORS{$_}}})
1392 } grep {
1393 scalar(@{$self->{nagios}->{messages}->{$ERRORS{$_}}})
1394 } ("OK"));
1395 my $all_messages_short = $bad_messages ? $bad_messages : 'no problems';
1396 # if mode = my-....
1397 # and there are some ok-messages
1398 # output them instead of "no problems"
1399 if ($self->{mode} =~ /^my\:\:/ && $good_messages) {
1400 $all_messages_short = $bad_messages ? $bad_messages : $good_messages;
1401 }
1402 my $all_messages_html = "<table style=\"border-collapse: collapse;\">".
1403 join("", map {
1404 my $level = $_;
1405 join("", map {
1406 sprintf "<tr valign=\"top\"><td class=\"service%s\">%s</td></tr>",
1407 $level, $_;
1408 } @{$self->{nagios}->{messages}->{$ERRORS{$_}}});
1409 } grep {
1410 scalar(@{$self->{nagios}->{messages}->{$ERRORS{$_}}})
1411 } ("CRITICAL", "WARNING", "UNKNOWN", "OK")).
1412 "</table>";
1413 if (exists $self->{identstring}) {
1414 $self->{nagios_message} .= $self->{identstring};
1415 }
1416 if ($self->{report} eq "long") {
1417 $self->{nagios_message} .= $all_messages;
1418 } elsif ($self->{report} eq "short") {
1419 $self->{nagios_message} .= $all_messages_short;
1420 } elsif ($self->{report} eq "html") {
1421 $self->{nagios_message} .= $all_messages_short."\n".$all_messages_html;
1422 }
1423 foreach my $from (keys %{$self->{negate}}) {
1424 if ((uc $from) =~ /^(OK|WARNING|CRITICAL|UNKNOWN)$/ &&
1425 (uc $self->{negate}->{$from}) =~ /^(OK|WARNING|CRITICAL|UNKNOWN)$/) {
1426 if ($self->{nagios_level} == $ERRORS{uc $from}) {
1427 $self->{nagios_level} = $ERRORS{uc $self->{negate}->{$from}};
1428 }
1429 }
1430 }
1431 if ($self->{labelformat} eq "pnp4nagios") {
1432 $self->{perfdata} = join(" ", @{$self->{nagios}->{perfdata}});
1433 } else {
1434 $self->{perfdata} = join(" ", map {
1435 my $perfdata = $_;
1436 if ($perfdata =~ /^(.*?)=(.*)/) {
1437 my $label = $1;
1438 my $data = $2;
1439 if (exists $labels->{$label} &&
1440 exists $labels->{$label}->{$self->{labelformat}}) {
1441 $labels->{$label}->{$self->{labelformat}}."=".$data;
1442 } else {
1443 $perfdata;
1444 }
1445 } else {
1446 $perfdata;
1447 }
1448 } @{$self->{nagios}->{perfdata}});
1449 }
1450 }
1451
1452 sub set_global_db_thresholds {
1453 my $self = shift;
1454 my $params = shift;
1455 my $warning = undef;
1456 my $critical = undef;
1457 return unless defined $params->{dbthresholds};
1458 $params->{name0} = $params->{dbthresholds};
1459 # :pluginmode :name :warning :critical
1460 # mode empty
1461 #
1462 eval {
1463 if ($self->{handle}->fetchrow_array(q{
1464 SELECT table_name FROM information_schema.tables
1465 WHERE table_schema = ?
1466 AND table_name = 'CHECK_MYSQL_HEALTH_THRESHOLDS';
1467 }, $self->{database})) { # either --database... or information_schema
1468 my @dbthresholds = $self->{handle}->fetchall_array(q{
1469 SELECT * FROM check_mysql_health_thresholds
1470 });
1471 $params->{dbthresholds} = \@dbthresholds;
1472 foreach (@dbthresholds) {
1473 if (($_->[0] eq $params->{cmdlinemode}) &&
1474 (! defined $_->[1] || ! $_->[1])) {
1475 ($warning, $critical) = ($_->[2], $_->[3]);
1476 }
1477 }
1478 }
1479 };
1480 if (! $@) {
1481 if ($warning) {
1482 $params->{warningrange} = $warning;
1483 $self->trace("read warningthreshold %s from database", $warning);
1484 }
1485 if ($critical) {
1486 $params->{criticalrange} = $critical;
1487 $self->trace("read criticalthreshold %s from database", $critical);
1488 }
1489 }
1490 }
1491
1492 sub set_local_db_thresholds {
1493 my $self = shift;
1494 my %params = @_;
1495 my $warning = undef;
1496 my $critical = undef;
1497 # :pluginmode :name :warning :critical
1498 # mode name0
1499 # mode name2
1500 # mode name
1501 #
1502 # first: argument of --dbthresholds, it it exists
1503 # second: --name2
1504 # third: --name
1505 if (ref($params{dbthresholds}) eq 'ARRAY') {
1506 my $marker;
1507 foreach (@{$params{dbthresholds}}) {
1508 if ($_->[0] eq $params{cmdlinemode}) {
1509 if (defined $_->[1] && $params{name0} && $_->[1] eq $params{name0}) {
1510 ($warning, $critical) = ($_->[2], $_->[3]);
1511 $marker = $params{name0};
1512 last;
1513 } elsif (defined $_->[1] && $params{name2} && $_->[1] eq $params{name2}) {
1514 ($warning, $critical) = ($_->[2], $_->[3]);
1515 $marker = $params{name2};
1516 last;
1517 } elsif (defined $_->[1] && $params{name} && $_->[1] eq $params{name}) {
1518 ($warning, $critical) = ($_->[2], $_->[3]);
1519 $marker = $params{name};
1520 last;
1521 }
1522 }
1523 }
1524 if ($warning) {
1525 $self->{warningrange} = $warning;
1526 $self->trace("read warningthreshold %s for %s from database",
1527 $marker, $warning);
1528 }
1529 if ($critical) {
1530 $self->{criticalrange} = $critical;
1531 $self->trace("read criticalthreshold %s for %s from database",
1532 $marker, $critical);
1533 }
1534 }
1535 }
1536
1537 sub debug {
1538 my $self = shift;
1539 my $msg = shift;
1540 if ($DBD::MySQL::Server::verbose) {
1541 printf "%s %s\n", $msg, ref($self);
1542 }
1543 }
1544
1545 sub dbconnect {
1546 my $self = shift;
1547 my %params = @_;
1548 my $retval = undef;
1549 $self->{tic} = Time::HiRes::time();
1550 $self->{handle} = DBD::MySQL::Server::Connection->new(%params);
1551 if ($self->{handle}->{errstr}) {
1552 if ($params{mode} =~ /^server::tnsping/ &&
1553 $self->{handle}->{errstr} =~ /ORA-01017/) {
1554 $self->add_nagios($ERRORS{OK},
1555 sprintf "connection established to %s.", $self->{connect});
1556 $retval = undef;
1557 } elsif ($self->{handle}->{errstr} eq "alarm\n") {
1558 $self->add_nagios($ERRORS{CRITICAL},
1559 sprintf "connection could not be established within %d seconds",
1560 $self->{timeout});
1561 } else {
1562 $self->add_nagios($ERRORS{CRITICAL},
1563 sprintf "cannot connect to %s. %s",
1564 $self->{database}, $self->{handle}->{errstr});
1565 $retval = undef;
1566 }
1567 } else {
1568 $retval = $self->{handle};
1569 }
1570 $self->{tac} = Time::HiRes::time();
1571 return $retval;
1572 }
1573
1574 sub trace {
1575 my $self = shift;
1576 my $format = shift;
1577 $self->{trace} = -f "/tmp/check_mysql_health.trace" ? 1 : 0;
1578 if ($self->{verbose}) {
1579 printf("%s: ", scalar localtime);
1580 printf($format, @_);
1581 }
1582 if ($self->{trace}) {
1583 my $logfh = new IO::File;
1584 $logfh->autoflush(1);
1585 if ($logfh->open("/tmp/check_mysql_health.trace", "a")) {
1586 $logfh->printf("%s: ", scalar localtime);
1587 $logfh->printf($format, @_);
1588 $logfh->printf("\n");
1589 $logfh->close();
1590 }
1591 }
1592 }
1593
1594 sub DESTROY {
1595 my $self = shift;
1596 my $handle1 = "null";
1597 my $handle2 = "null";
1598 if (defined $self->{handle}) {
1599 $handle1 = ref($self->{handle});
1600 if (defined $self->{handle}->{handle}) {
1601 $handle2 = ref($self->{handle}->{handle});
1602 }
1603 }
1604 $self->trace(sprintf "DESTROY %s with handle %s %s", ref($self), $handle1, $handle2);
1605 if (ref($self) eq "DBD::MySQL::Server") {
1606 }
1607 $self->trace(sprintf "DESTROY %s exit with handle %s %s", ref($self), $handle1, $handle2);
1608 if (ref($self) eq "DBD::MySQL::Server") {
1609 #printf "humpftata\n";
1610 }
1611 }
1612
1613 sub save_state {
1614 my $self = shift;
1615 my %params = @_;
1616 my $extension = "";
1617 my $mode = $params{mode};
1618 if ($params{connect} && $params{connect} =~ /(\w+)\/(\w+)@(\w+)/) {
1619 $params{connect} = $3;
1620 } elsif ($params{connect}) {
1621 # just to be sure
1622 $params{connect} =~ s/\//_/g;
1623 }
1624 if ($^O =~ /MSWin/) {
1625 $mode =~ s/::/_/g;
1626 $params{statefilesdir} = $self->system_vartmpdir();
1627 }
1628 if (! -d $params{statefilesdir}) {
1629 eval {
1630 use File::Path;
1631 mkpath $params{statefilesdir};
1632 };
1633 }
1634 if ($@ || ! -w $params{statefilesdir}) {
1635 $self->add_nagios($ERRORS{CRITICAL},
1636 sprintf "statefilesdir %s does not exist or is not writable\n",
1637 $params{statefilesdir});
1638 return;
1639 }
1640 my $statefile = sprintf "%s_%s", $params{hostname}, $mode;
1641 $extension .= $params{differenciator} ? "_".$params{differenciator} : "";
1642 $extension .= $params{socket} ? "_".$params{socket} : "";
1643 $extension .= $params{port} ? "_".$params{port} : "";
1644 $extension .= $params{database} ? "_".$params{database} : "";
1645 $extension .= $params{tablespace} ? "_".$params{tablespace} : "";
1646 $extension .= $params{datafile} ? "_".$params{datafile} : "";
1647 $extension .= $params{name} ? "_".$params{name} : "";
1648 $extension =~ s/\//_/g;
1649 $extension =~ s/\(/_/g;
1650 $extension =~ s/\)/_/g;
1651 $extension =~ s/\*/_/g;
1652 $extension =~ s/\s/_/g;
1653 $statefile .= $extension;
1654 $statefile = lc $statefile;
1655 $statefile = sprintf "%s/%s", $params{statefilesdir}, $statefile;
1656 if (open(STATE, ">$statefile")) {
1657 if ((ref($params{save}) eq "HASH") && exists $params{save}->{timestamp}) {
1658 $params{save}->{localtime} = scalar localtime $params{save}->{timestamp};
1659 }
1660 printf STATE Data::Dumper::Dumper($params{save});
1661 close STATE;
1662 } else {
1663 $self->add_nagios($ERRORS{CRITICAL},
1664 sprintf "statefile %s is not writable", $statefile);
1665 }
1666 $self->debug(sprintf "saved %s to %s",
1667 Data::Dumper::Dumper($params{save}), $statefile);
1668 }
1669
1670 sub load_state {
1671 my $self = shift;
1672 my %params = @_;
1673 my $extension = "";
1674 my $mode = $params{mode};
1675 if ($params{connect} && $params{connect} =~ /(\w+)\/(\w+)@(\w+)/) {
1676 $params{connect} = $3;
1677 } elsif ($params{connect}) {
1678 # just to be sure
1679 $params{connect} =~ s/\//_/g;
1680 }
1681 if ($^O =~ /MSWin/) {
1682 $mode =~ s/::/_/g;
1683 $params{statefilesdir} = $self->system_vartmpdir();
1684 }
1685 my $statefile = sprintf "%s_%s", $params{hostname}, $mode;
1686 $extension .= $params{differenciator} ? "_".$params{differenciator} : "";
1687 $extension .= $params{socket} ? "_".$params{socket} : "";
1688 $extension .= $params{port} ? "_".$params{port} : "";
1689 $extension .= $params{database} ? "_".$params{database} : "";
1690 $extension .= $params{tablespace} ? "_".$params{tablespace} : "";
1691 $extension .= $params{datafile} ? "_".$params{datafile} : "";
1692 $extension .= $params{name} ? "_".$params{name} : "";
1693 $extension =~ s/\//_/g;
1694 $extension =~ s/\(/_/g;
1695 $extension =~ s/\)/_/g;
1696 $extension =~ s/\*/_/g;
1697 $extension =~ s/\s/_/g;
1698 $statefile .= $extension;
1699 $statefile = lc $statefile;
1700 $statefile = sprintf "%s/%s", $params{statefilesdir}, $statefile;
1701 if ( -f $statefile) {
1702 our $VAR1;
1703 eval {
1704 require $statefile;
1705 };
1706 if($@) {
1707 $self->add_nagios($ERRORS{CRITICAL},
1708 sprintf "statefile %s is corrupt", $statefile);
1709 }
1710 $self->debug(sprintf "load %s", Data::Dumper::Dumper($VAR1));
1711 return $VAR1;
1712 } else {
1713 return undef;
1714 }
1715 }
1716
1717 sub valdiff {
1718 my $self = shift;
1719 my $pparams = shift;
1720 my %params = %{$pparams};
1721 my @keys = @_;
1722 my $now = time;
1723 my $last_values = $self->load_state(%params) || eval {
1724 my $empty_events = {};
1725 foreach (@keys) {
1726 $empty_events->{$_} = 0;
1727 }
1728 $empty_events->{timestamp} = 0;
1729 if ($params{lookback}) {
1730 $empty_events->{lookback_history} = {};
1731 }
1732 $empty_events;
1733 };
1734 foreach (@keys) {
1735 if ($params{lookback}) {
1736 # find a last_value in the history which fits lookback best
1737 # and overwrite $last_values->{$_} with historic data
1738 if (exists $last_values->{lookback_history}->{$_}) {
1739 foreach my $date (sort {$a <=> $b} keys %{$last_values->{lookback_history}->{$_}}) {
1740 if ($date >= ($now - $params{lookback})) {
1741 $last_values->{$_} = $last_values->{lookback_history}->{$_}->{$date};
1742 $last_values->{timestamp} = $date;
1743 last;
1744 } else {
1745 delete $last_values->{lookback_history}->{$_}->{$date};
1746 }
1747 }
1748 }
1749 }
1750 $last_values->{$_} = 0 if ! exists $last_values->{$_};
1751 if ($self->{$_} >= $last_values->{$_}) {
1752 $self->{'delta_'.$_} = $self->{$_} - $last_values->{$_};
1753 } else {
1754 # vermutlich db restart und zaehler alle auf null
1755 $self->{'delta_'.$_} = $self->{$_};
1756 }
1757 $self->debug(sprintf "delta_%s %f", $_, $self->{'delta_'.$_});
1758 }
1759 $self->{'delta_timestamp'} = $now - $last_values->{timestamp};
1760 $params{save} = eval {
1761 my $empty_events = {};
1762 foreach (@keys) {
1763 $empty_events->{$_} = $self->{$_};
1764 }
1765 $empty_events->{timestamp} = $now;
1766 if ($params{lookback}) {
1767 $empty_events->{lookback_history} = $last_values->{lookback_history};
1768 foreach (@keys) {
1769 $empty_events->{lookback_history}->{$_}->{$now} = $self->{$_};
1770 }
1771 }
1772 $empty_events;
1773 };
1774 $self->save_state(%params);
1775 }
1776
1777 sub requires_version {
1778 my $self = shift;
1779 my $version = shift;
1780 my @instances = DBD::MySQL::Server::return_servers();
1781 my $instversion = $instances[0]->{version};
1782 if (! $self->version_is_minimum($version)) {
1783 $self->add_nagios($ERRORS{UNKNOWN},
1784 sprintf "not implemented/possible for MySQL release %s", $instversion);
1785 }
1786 }
1787
1788 sub version_is_minimum {
1789 # the current version is newer or equal
1790 my $self = shift;
1791 my $version = shift;
1792 my $newer = 1;
1793 my @instances = DBD::MySQL::Server::return_servers();
1794 my @v1 = map { $_ eq "x" ? 0 : $_ } split(/\./, $version);
1795 my @v2 = split(/\./, $instances[0]->{version});
1796 if (scalar(@v1) > scalar(@v2)) {
1797 push(@v2, (0) x (scalar(@v1) - scalar(@v2)));
1798 } elsif (scalar(@v2) > scalar(@v1)) {
1799 push(@v1, (0) x (scalar(@v2) - scalar(@v1)));
1800 }
1801 foreach my $pos (0..$#v1) {
1802 if ($v2[$pos] > $v1[$pos]) {
1803 $newer = 1;
1804 last;
1805 } elsif ($v2[$pos] < $v1[$pos]) {
1806 $newer = 0;
1807 last;
1808 }
1809 }
1810 #printf STDERR "check if %s os minimum %s\n", join(".", @v2), join(".", @v1);
1811 return $newer;
1812 }
1813
1814 sub instance_thread {
1815 my $self = shift;
1816 my @instances = DBD::MySQL::Server::return_servers();
1817 return $instances[0]->{thread};
1818 }
1819
1820 sub windows_server {
1821 my $self = shift;
1822 my @instances = DBD::MySQL::Server::return_servers();
1823 if ($instances[0]->{os} =~ /Win/i) {
1824 return 1;
1825 } else {
1826 return 0;
1827 }
1828 }
1829
1830 sub system_vartmpdir {
1831 my $self = shift;
1832 if ($^O =~ /MSWin/) {
1833 return $self->system_tmpdir();
1834 } else {
1835 return "/var/tmp/check_mysql_health";
1836 }
1837 }
1838
1839 sub system_oldvartmpdir {
1840 my $self = shift;
1841 return "/tmp";
1842 }
1843
1844 sub system_tmpdir {
1845 my $self = shift;
1846 if ($^O =~ /MSWin/) {
1847 return $ENV{TEMP} if defined $ENV{TEMP};
1848 return $ENV{TMP} if defined $ENV{TMP};
1849 return File::Spec->catfile($ENV{windir}, 'Temp')
1850 if defined $ENV{windir};
1851 return 'C:\Temp';
1852 } else {
1853 return "/tmp";
1854 }
1855 }
1856
1857 sub decode_password {
1858 my $self = shift;
1859 my $password = shift;
1860 if ($password && $password =~ /^rfc3986:\/\/(.*)/) {
1861 $password = $1;
1862 $password =~ s/\%([A-Fa-f0-9]{2})/pack('C', hex($1))/seg;
1863 }
1864 return $password;
1865 }
1866
1867
1868 package DBD::MySQL::Server::Connection;
1869
1870 use strict;
1871
1872 our @ISA = qw(DBD::MySQL::Server);
1873
1874
1875 sub new {
1876 my $class = shift;
1877 my %params = @_;
1878 my $self = {
1879 mode => $params{mode},
1880 timeout => $params{timeout},
1881 access => $params{method} || "dbi",
1882 hostname => $params{hostname},
1883 database => $params{database} || "information_schema",
1884 port => $params{port},
1885 socket => $params{socket},
1886 username => $params{username},
1887 password => $params{password},
1888 mycnf => $params{mycnf},
1889 mycnfgroup => $params{mycnfgroup},
1890 handle => undef,
1891 };
1892 bless $self, $class;
1893 if ($params{method} eq "dbi") {
1894 bless $self, "DBD::MySQL::Server::Connection::Dbi";
1895 } elsif ($params{method} eq "mysql") {
1896 bless $self, "DBD::MySQL::Server::Connection::Mysql";
1897 } elsif ($params{method} eq "sqlrelay") {
1898 bless $self, "DBD::MySQL::Server::Connection::Sqlrelay";
1899 }
1900 $self->init(%params);
1901 return $self;
1902 }
1903
1904
1905 package DBD::MySQL::Server::Connection::Dbi;
1906
1907 use strict;
1908 use Net::Ping;
1909
1910 our @ISA = qw(DBD::MySQL::Server::Connection);
1911
1912
1913 sub init {
1914 my $self = shift;
1915 my %params = @_;
1916 my $retval = undef;
1917 if ($self->{mode} =~ /^server::tnsping/) {
1918 if (! $self->{connect}) {
1919 $self->{errstr} = "Please specify a database";
1920 } else {
1921 $self->{sid} = $self->{connect};
1922 $self->{username} ||= time; # prefer an existing user
1923 $self->{password} = time;
1924 }
1925 } else {
1926 if (
1927 ($self->{hostname} ne 'localhost' && (! $self->{username} || ! $self->{password})) &&
1928 (! $self->{mycnf}) ) {
1929 $self->{errstr} = "Please specify hostname, username and password or a .cnf file";
1930 return undef;
1931 }
1932 $self->{dsn} = "DBI:mysql:";
1933 $self->{dsn} .= sprintf "database=%s", $self->{database};
1934 if ($self->{mycnf}) {
1935 $self->{dsn} .= sprintf ";mysql_read_default_file=%s", $self->{mycnf};
1936 if ($self->{mycnfgroup}) {
1937 $self->{dsn} .= sprintf ";mysql_read_default_group=%s", $self->{mycnfgroup};
1938 }
1939 } else {
1940 $self->{dsn} .= sprintf ";host=%s", $self->{hostname};
1941 $self->{dsn} .= sprintf ";port=%s", $self->{port}
1942 unless $self->{socket} || $self->{hostname} eq 'localhost';
1943 $self->{dsn} .= sprintf ";mysql_socket=%s", $self->{socket}
1944 if $self->{socket};
1945 }
1946 }
1947 if (! exists $self->{errstr}) {
1948 eval {
1949 require DBI;
1950 use POSIX ':signal_h';
1951 if ($^O =~ /MSWin/) {
1952 local $SIG{'ALRM'} = sub {
1953 die "alarm\n";
1954 };
1955 } else {
1956 my $mask = POSIX::SigSet->new( SIGALRM );
1957 my $action = POSIX::SigAction->new(
1958 sub { die "alarm\n" ; }, $mask);
1959 my $oldaction = POSIX::SigAction->new();
1960 sigaction(SIGALRM ,$action ,$oldaction );
1961 }
1962 alarm($self->{timeout} - 1); # 1 second before the global unknown timeout
1963 if ($self->{handle} = DBI->connect(
1964 $self->{dsn},
1965 $self->{username},
1966 $self->decode_password($self->{password}),
1967 { RaiseError => 0, AutoCommit => 0, PrintError => 1 })) {
1968 # $self->{handle}->do(q{
1969 # ALTER SESSION SET NLS_NUMERIC_CHARACTERS=".," });
1970 $retval = $self;
1971 } else {
1972 $self->{errstr} = DBI::errstr();
1973 }
1974 };
1975 if ($@) {
1976 $self->{errstr} = $@;
1977 $retval = undef;
1978 }
1979 }
1980 $self->{tac} = Time::HiRes::time();
1981 return $retval;
1982 }
1983
1984 sub selectrow_hashref {
1985 my $self = shift;
1986 my $sql = shift;
1987 my @arguments = @_;
1988 my $sth = undef;
1989 my $hashref = undef;
1990 eval {
1991 $self->trace(sprintf "SQL:\n%s\nARGS:\n%s\n",
1992 $sql, Data::Dumper::Dumper(\@arguments));
1993 # helm auf! jetzt wirds dreckig.
1994 if ($sql =~ /^\s*SHOW/) {
1995 $hashref = $self->{handle}->selectrow_hashref($sql);
1996 } else {
1997 $sth = $self->{handle}->prepare($sql);
1998 if (scalar(@arguments)) {
1999 $sth->execute(@arguments);
2000 } else {
2001 $sth->execute();
2002 }
2003 $hashref = $sth->selectrow_hashref();
2004 }
2005 $self->trace(sprintf "RESULT:\n%s\n",
2006 Data::Dumper::Dumper($hashref));
2007 };
2008 if ($@) {
2009 $self->debug(sprintf "bumm %s", $@);
2010 }
2011 if (-f "/tmp/check_mysql_health_simulation/".$self->{mode}) {
2012 my $simulation = do { local (@ARGV, $/) =
2013 "/tmp/check_mysql_health_simulation/".$self->{mode}; <> };
2014 # keine lust auf den scheiss
2015 }
2016 return $hashref;
2017 }
2018
2019 sub fetchrow_array {
2020 my $self = shift;
2021 my $sql = shift;
2022 my @arguments = @_;
2023 my $sth = undef;
2024 my @row = ();
2025 my $stderrvar;
2026 *SAVEERR = *STDERR;
2027 open ERR ,'>',\$stderrvar;
2028 *STDERR = *ERR;
2029 eval {
2030 $self->trace(sprintf "SQL:\n%s\nARGS:\n%s\n",
2031 $sql, Data::Dumper::Dumper(\@arguments));
2032 $sth = $self->{handle}->prepare($sql);
2033 if (scalar(@arguments)) {
2034 $sth->execute(@arguments);
2035 } else {
2036 $sth->execute();
2037 }
2038 @row = $sth->fetchrow_array();
2039 $self->trace(sprintf "RESULT:\n%s\n",
2040 Data::Dumper::Dumper(\@row));
2041 };
2042 *STDERR = *SAVEERR;
2043 if ($@) {
2044 $self->debug(sprintf "bumm %s", $@);
2045 $self->{errstr} = $@;
2046 return (undef);
2047 } elsif ($stderrvar) {
2048 $self->{errstr} = $stderrvar;
2049 return (undef);
2050 } elsif ($sth->errstr()) {
2051 $self->{errstr} = $sth->errstr();
2052 return (undef);
2053 }
2054 if (-f "/tmp/check_mysql_health_simulation/".$self->{mode}) {
2055 my $simulation = do { local (@ARGV, $/) =
2056 "/tmp/check_mysql_health_simulation/".$self->{mode}; <> };
2057 @row = split(/\s+/, (split(/\n/, $simulation))[0]);
2058 }
2059 return $row[0] unless wantarray;
2060 return @row;
2061 }
2062
2063 sub fetchall_array {
2064 my $self = shift;
2065 my $sql = shift;
2066 my @arguments = @_;
2067 my $sth = undef;
2068 my $rows = undef;
2069 eval {
2070 $self->trace(sprintf "SQL:\n%s\nARGS:\n%s\n",
2071 $sql, Data::Dumper::Dumper(\@arguments));
2072 $sth = $self->{handle}->prepare($sql);
2073 if (scalar(@arguments)) {
2074 $sth->execute(@arguments);
2075 } else {
2076 $sth->execute();
2077 }
2078 $rows = $sth->fetchall_arrayref();
2079 $self->trace(sprintf "RESULT:\n%s\n",
2080 Data::Dumper::Dumper($rows));
2081 };
2082 if ($@) {
2083 printf STDERR "bumm %s\n", $@;
2084 }
2085 if (-f "/tmp/check_mysql_health_simulation/".$self->{mode}) {
2086 my $simulation = do { local (@ARGV, $/) =
2087 "/tmp/check_mysql_health_simulation/".$self->{mode}; <> };
2088 @{$rows} = map { [ split(/\s+/, $_) ] } split(/\n/, $simulation);
2089 }
2090 return @{$rows};
2091 }
2092
2093 sub func {
2094 my $self = shift;
2095 $self->{handle}->func(@_);
2096 }
2097
2098
2099 sub execute {
2100 my $self = shift;
2101 my $sql = shift;
2102 eval {
2103 my $sth = $self->{handle}->prepare($sql);
2104 $sth->execute();
2105 };
2106 if ($@) {
2107 printf STDERR "bumm %s\n", $@;
2108 }
2109 }
2110
2111 sub errstr {
2112 my $self = shift;
2113 return $self->{errstr};
2114 }
2115
2116 sub DESTROY {
2117 my $self = shift;
2118 $self->trace(sprintf "disconnecting DBD %s",
2119 $self->{handle} ? "with handle" : "without handle");
2120 $self->{handle}->disconnect() if $self->{handle};
2121 }
2122
2123 package DBD::MySQL::Server::Connection::Mysql;
2124
2125 use strict;
2126 use File::Temp qw/tempfile/;
2127
2128 our @ISA = qw(DBD::MySQL::Server::Connection);
2129
2130
2131 sub init {
2132 my $self = shift;
2133 my %params = @_;
2134 my $retval = undef;
2135 $self->{loginstring} = "traditional";
2136 ($self->{sql_commandfile_handle}, $self->{sql_commandfile}) =
2137 tempfile($self->{mode}."XXXXX", SUFFIX => ".sql",
2138 DIR => $self->system_tmpdir() );
2139 close $self->{sql_commandfile_handle};
2140 ($self->{sql_resultfile_handle}, $self->{sql_resultfile}) =
2141 tempfile($self->{mode}."XXXXX", SUFFIX => ".out",
2142 DIR => $self->system_tmpdir() );
2143 close $self->{sql_resultfile_handle};
2144 if ($self->{mode} =~ /^server::tnsping/) {
2145 if (! $self->{connect}) {
2146 $self->{errstr} = "Please specify a database";
2147 } else {
2148 $self->{sid} = $self->{connect};
2149 $self->{username} ||= time; # prefer an existing user
2150 $self->{password} = time;
2151 }
2152 } else {
2153 if (! $self->{username} || ! $self->{password}) {
2154 $self->{errstr} = "Please specify database, username and password";
2155 return undef;
2156 } elsif (! (($self->{hostname} && $self->{port}) || $self->{socket})) {
2157 $self->{errstr} = "Please specify hostname and port or socket";
2158 return undef;
2159 }
2160 }
2161 if (! exists $self->{errstr}) {
2162 $self->{password} = $self->decode_password($self->{password});
2163 eval {
2164 my $mysql = '/'.'usr'.'/'.'bin'.'/'.'mysql';
2165 if (! -x $mysql) {
2166 die "nomysql\n";
2167 }
2168 if ($self->{loginstring} eq "traditional") {
2169 $self->{sqlplus} = sprintf "%s ", $mysql;
2170 $self->{sqlplus} .= sprintf "--batch --raw --skip-column-names ";
2171 $self->{sqlplus} .= sprintf "--database=%s ", $self->{database};
2172 $self->{sqlplus} .= sprintf "--host=%s ", $self->{hostname};
2173 $self->{sqlplus} .= sprintf "--port=%s ", $self->{port}
2174 unless $self->{socket} || $self->{hostname} eq "localhost";
2175 $self->{sqlplus} .= sprintf "--socket=%s ", $self->{socket}
2176 if $self->{socket};
2177 $self->{sqlplus} .= sprintf "--user=%s --password='%s' < %s > %s",
2178 $self->{username}, $self->{password},
2179 $self->{sql_commandfile}, $self->{sql_resultfile};
2180 }
2181
2182 use POSIX ':signal_h';
2183 if ($^O =~ /MSWin/) {
2184 local $SIG{'ALRM'} = sub {
2185 die "alarm\n";
2186 };
2187 } else {
2188 my $mask = POSIX::SigSet->new( SIGALRM );
2189 my $action = POSIX::SigAction->new(
2190 sub { die "alarm\n" ; }, $mask);
2191 my $oldaction = POSIX::SigAction->new();
2192 sigaction(SIGALRM ,$action ,$oldaction );
2193 }
2194 alarm($self->{timeout} - 1); # 1 second before the global unknown timeout
2195
2196 my $answer = $self->fetchrow_array(
2197 q{ SELECT 42 FROM dual});
2198 die unless defined $answer and $answer == 42;
2199 $retval = $self;
2200 };
2201 if ($@) {
2202 $self->{errstr} = $@;
2203 $self->{errstr} =~ s/at $0 .*//g;
2204 chomp $self->{errstr};
2205 $retval = undef;
2206 }
2207 }
2208 $self->{tac} = Time::HiRes::time();
2209 return $retval;
2210 }
2211
2212 sub selectrow_hashref {
2213 my $self = shift;
2214 my $sql = shift;
2215 my @arguments = @_;
2216 my $sth = undef;
2217 my $hashref = undef;
2218 foreach (@arguments) {
2219 # replace the ? by the parameters
2220 if (/^\d+$/) {
2221 $sql =~ s/\?/$_/;
2222 } else {
2223 $sql =~ s/\?/'$_'/;
2224 }
2225 }
2226 if ($sql =~ /^\s*SHOW/) {
2227 $sql .= '\G'; # http://dev.mysql.com/doc/refman/5.1/de/show-slave-status.html
2228 }
2229 $self->trace(sprintf "SQL (? resolved):\n%s\nARGS:\n%s\n",
2230 $sql, Data::Dumper::Dumper(\@arguments));
2231 $self->create_commandfile($sql);
2232 my $exit_output = `$self->{sqlplus}`;
2233 if ($?) {
2234 printf STDERR "fetchrow_array exit bumm \n";
2235 my $output = do { local (@ARGV, $/) = $self->{sql_resultfile}; <> };
2236 my @oerrs = map {
2237 /((ERROR \d+).*)/ ? $1 : ();
2238 } split(/\n/, $output);
2239 $self->{errstr} = join(" ", @oerrs);
2240 } else {
2241 my $output = do { local (@ARGV, $/) = $self->{sql_resultfile}; <> };
2242 if ($sql =~ /^\s*SHOW/) {
2243 map {
2244 if (/^\s*([\w_]+):\s*(.*)/) {
2245 $hashref->{$1} = $2;
2246 }
2247 } split(/\n/, $output);
2248 } else {
2249 # i dont mess around here and you shouldn't either
2250 }
2251 $self->trace(sprintf "RESULT:\n%s\n",
2252 Data::Dumper::Dumper($hashref));
2253 }
2254 unlink $self->{sql_commandfile};
2255 unlink $self->{sql_resultfile};
2256 return $hashref;
2257 }
2258
2259 sub fetchrow_array {
2260 my $self = shift;
2261 my $sql = shift;
2262 my @arguments = @_;
2263 my $sth = undef;
2264 my @row = ();
2265 foreach (@arguments) {
2266 # replace the ? by the parameters
2267 if (/^\d+$/) {
2268 $sql =~ s/\?/$_/;
2269 } else {
2270 $sql =~ s/\?/'$_'/;
2271 }
2272 }
2273 $self->trace(sprintf "SQL (? resolved):\n%s\nARGS:\n%s\n",
2274 $sql, Data::Dumper::Dumper(\@arguments));
2275 $self->create_commandfile($sql);
2276 my $exit_output = `$self->{sqlplus}`;
2277 if ($?) {
2278 printf STDERR "fetchrow_array exit bumm \n";
2279 my $output = do { local (@ARGV, $/) = $self->{sql_resultfile}; <> };
2280 my @oerrs = map {
2281 /((ERROR \d+).*)/ ? $1 : ();
2282 } split(/\n/, $output);
2283 $self->{errstr} = join(" ", @oerrs);
2284 } else {
2285 my $output = do { local (@ARGV, $/) = $self->{sql_resultfile}; <> };
2286 @row = map { convert($_) }
2287 map { s/^\s+([\.\d]+)$/$1/g; $_ } # strip leading space from numbers
2288 map { s/\s+$//g; $_ } # strip trailing space
2289 split(/\t/, (split(/\n/, $output))[0]);
2290 $self->trace(sprintf "RESULT:\n%s\n",
2291 Data::Dumper::Dumper(\@row));
2292 }
2293 if ($@) {
2294 $self->debug(sprintf "bumm %s", $@);
2295 }
2296 unlink $self->{sql_commandfile};
2297 unlink $self->{sql_resultfile};
2298 return $row[0] unless wantarray;
2299 return @row;
2300 }
2301
2302 sub fetchall_array {
2303 my $self = shift;
2304 my $sql = shift;
2305 my @arguments = @_;
2306 my $sth = undef;
2307 my $rows = undef;
2308 foreach (@arguments) {
2309 # replace the ? by the parameters
2310 if (/^\d+$/) {
2311 $sql =~ s/\?/$_/;
2312 } else {
2313 $sql =~ s/\?/'$_'/;
2314 }
2315 }
2316 $self->trace(sprintf "SQL (? resolved):\n%s\nARGS:\n%s\n",
2317 $sql, Data::Dumper::Dumper(\@arguments));
2318 $self->create_commandfile($sql);
2319 my $exit_output = `$self->{sqlplus}`;
2320 if ($?) {
2321 printf STDERR "fetchrow_array exit bumm %s\n", $exit_output;
2322 my $output = do { local (@ARGV, $/) = $self->{sql_resultfile}; <> };
2323 my @oerrs = map {
2324 /((ERROR \d+).*)/ ? $1 : ();
2325 } split(/\n/, $output);
2326 $self->{errstr} = join(" ", @oerrs);
2327 } else {
2328 my $output = do { local (@ARGV, $/) = $self->{sql_resultfile}; <> };
2329 my @rows = map { [
2330 map { convert($_) }
2331 map { s/^\s+([\.\d]+)$/$1/g; $_ }
2332 map { s/\s+$//g; $_ }
2333 split /\t/
2334 ] } grep { ! /^\d+ rows selected/ }
2335 grep { ! /^Elapsed: / }
2336 grep { ! /^\s*$/ } split(/\n/, $output);
2337 $rows = \@rows;
2338 $self->trace(sprintf "RESULT:\n%s\n",
2339 Data::Dumper::Dumper($rows));
2340 }
2341 if ($@) {
2342 $self->debug(sprintf "bumm %s", $@);
2343 }
2344 unlink $self->{sql_commandfile};
2345 unlink $self->{sql_resultfile};
2346 return @{$rows};
2347 }
2348
2349 sub func {
2350 my $self = shift;
2351 my $function = shift;
2352 $self->{handle}->func(@_);
2353 }
2354
2355 sub convert {
2356 my $n = shift;
2357 # mostly used to convert numbers in scientific notation
2358 if ($n =~ /^\s*\d+\s*$/) {
2359 return $n;
2360 } elsif ($n =~ /^\s*([-+]?)(\d*[\.,]*\d*)[eE]{1}([-+]?)(\d+)\s*$/) {
2361 my ($vor, $num, $sign, $exp) = ($1, $2, $3, $4);
2362 $n =~ s/E/e/g;
2363 $n =~ s/,/\./g;
2364 $num =~ s/,/\./g;
2365 my $sig = $sign eq '-' ? "." . ($exp - 1 + length $num) : '';
2366 my $dec = sprintf "%${sig}f", $n;
2367 $dec =~ s/\.[0]+$//g;
2368 return $dec;
2369 } elsif ($n =~ /^\s*([-+]?)(\d+)[\.,]*(\d*)\s*$/) {
2370 return $1.$2.".".$3;
2371 } elsif ($n =~ /^\s*(.*?)\s*$/) {
2372 return $1;
2373 } else {
2374 return $n;
2375 }
2376 }
2377
2378
2379 sub execute {
2380 my $self = shift;
2381 my $sql = shift;
2382 eval {
2383 my $sth = $self->{handle}->prepare($sql);
2384 $sth->execute();
2385 };
2386 if ($@) {
2387 printf STDERR "bumm %s\n", $@;
2388 }
2389 }
2390
2391 sub errstr {
2392 my $self = shift;
2393 return $self->{errstr};
2394 }
2395
2396 sub DESTROY {
2397 my $self = shift;
2398 $self->trace("try to clean up command and result files");
2399 unlink $self->{sql_commandfile} if -f $self->{sql_commandfile};
2400 unlink $self->{sql_resultfile} if -f $self->{sql_resultfile};
2401 }
2402
2403 sub create_commandfile {
2404 my $self = shift;
2405 my $sql = shift;
2406 open CMDCMD, "> $self->{sql_commandfile}";
2407 printf CMDCMD "%s\n", $sql;
2408 close CMDCMD;
2409 }
2410
2411 sub decode_password {
2412 my $self = shift;
2413 my $password = shift;
2414 $password = $self->SUPER::decode_password($password);
2415 # we call '...%s/%s@...' inside backticks where the second %s is the password
2416 # abc'xcv -> ''abc'\''xcv''
2417 # abc'`xcv -> ''abc'\''\`xcv''
2418 if ($password && $password =~ /'/) {
2419 $password = "'".join("\\'", map { "'".$_."'"; } split("'", $password))."'";
2420 }
2421 return $password;
2422 }
2423
2424
2425 package DBD::MySQL::Server::Connection::Sqlrelay;
2426
2427 use strict;
2428 use Net::Ping;
2429
2430 our @ISA = qw(DBD::MySQL::Server::Connection);
2431
2432
2433 sub init {
2434 my $self = shift;
2435 my %params = @_;
2436 my $retval = undef;
2437 if ($self->{mode} =~ /^server::tnsping/) {
2438 if (! $self->{connect}) {
2439 $self->{errstr} = "Please specify a database";
2440 } else {
2441 if ($self->{connect} =~ /([\.\w]+):(\d+)/) {
2442 $self->{host} = $1;
2443 $self->{port} = $2;
2444 $self->{socket} = "";
2445 } elsif ($self->{connect} =~ /([\.\w]+):([\w\/]+)/) {
2446 $self->{host} = $1;
2447 $self->{socket} = $2;
2448 $self->{port} = "";
2449 }
2450 }
2451 } else {
2452 if (! $self->{hostname} || ! $self->{username} || ! $self->{password}) {
2453 if ($self->{hostname} && $self->{hostname} =~ /(\w+?)\/(.+)@([\.\w]+):(\d+)/) {
2454 $self->{username} = $1;
2455 $self->{password} = $2;
2456 $self->{hostname} = $3;
2457 $self->{port} = $4;
2458 $self->{socket} = "";
2459 } elsif ($self->{hostname} && $self->{hostname} =~ /(\w+?)\/(.+)@([\.\w]+):([\w\/]+)/) {
2460 $self->{username} = $1;
2461 $self->{password} = $2;
2462 $self->{hostname} = $3;
2463 $self->{socket} = $4;
2464 $self->{port} = "";
2465 } else {
2466 $self->{errstr} = "Please specify database, username and password";
2467 return undef;
2468 }
2469 } else {
2470 if ($self->{hostname} =~ /([\.\w]+):(\d+)/) {
2471 $self->{hostname} = $1;
2472 $self->{port} = $2;
2473 $self->{socket} = "";
2474 } elsif ($self->{hostname} =~ /([\.\w]+):([\w\/]+)/) {
2475 $self->{hostname} = $1;
2476 $self->{socket} = $2;
2477 $self->{port} = "";
2478 } else {
2479 $self->{errstr} = "Please specify hostname, username, password and port/socket";
2480 return undef;
2481 }
2482 }
2483 }
2484 if (! exists $self->{errstr}) {
2485 eval {
2486 require DBI;
2487 use POSIX ':signal_h';
2488 if ($^O =~ /MSWin/) {
2489 local $SIG{'ALRM'} = sub {
2490 die "alarm\n";
2491 };
2492 } else {
2493 my $mask = POSIX::SigSet->new( SIGALRM );
2494 my $action = POSIX::SigAction->new(
2495 sub { die "alarm\n" ; }, $mask);
2496 my $oldaction = POSIX::SigAction->new();
2497 sigaction(SIGALRM ,$action ,$oldaction );
2498 }
2499 alarm($self->{timeout} - 1); # 1 second before the global unknown timeout
2500 if ($self->{handle} = DBI->connect(
2501 sprintf("DBI:SQLRelay:host=%s;port=%d;socket=%s",
2502 $self->{hostname}, $self->{port}, $self->{socket}),
2503 $self->{username},
2504 $self->decode_password($self->{password}),
2505 { RaiseError => 1, AutoCommit => 0, PrintError => 1 })) {
2506 $retval = $self;
2507 if ($self->{mode} =~ /^server::tnsping/ && $self->{handle}->ping()) {
2508 # database connected. fake a "unknown user"
2509 $self->{errstr} = "ORA-01017";
2510 }
2511 } else {
2512 $self->{errstr} = DBI::errstr();
2513 }
2514 };
2515 if ($@) {
2516 $self->{errstr} = $@;
2517 $self->{errstr} =~ s/at [\w\/\.]+ line \d+.*//g;
2518 $retval = undef;
2519 }
2520 }
2521 $self->{tac} = Time::HiRes::time();
2522 return $retval;
2523 }
2524
2525 sub fetchrow_array {
2526 my $self = shift;
2527 my $sql = shift;
2528 my @arguments = @_;
2529 my $sth = undef;
2530 my @row = ();
2531 $self->trace(sprintf "fetchrow_array: %s", $sql);
2532 eval {
2533 $sth = $self->{handle}->prepare($sql);
2534 if (scalar(@arguments)) {
2535 $sth->execute(@arguments);
2536 } else {
2537 $sth->execute();
2538 }
2539 @row = $sth->fetchrow_array();
2540 };
2541 if ($@) {
2542 $self->debug(sprintf "bumm %s", $@);
2543 }
2544 if (-f "/tmp/check_mysql_health_simulation/".$self->{mode}) {
2545 my $simulation = do { local (@ARGV, $/) =
2546 "/tmp/check_mysql_health_simulation/".$self->{mode}; <> };
2547 @row = split(/\s+/, (split(/\n/, $simulation))[0]);
2548 }
2549 return $row[0] unless wantarray;
2550 return @row;
2551 }
2552
2553 sub fetchall_array {
2554 my $self = shift;
2555 my $sql = shift;
2556 my @arguments = @_;
2557 my $sth = undef;
2558 my $rows = undef;
2559 $self->trace(sprintf "fetchall_array: %s", $sql);
2560 eval {
2561 $sth = $self->{handle}->prepare($sql);
2562 if (scalar(@arguments)) {
2563 $sth->execute(@arguments);
2564 } else {
2565 $sth->execute();
2566 }
2567 $rows = $sth->fetchall_arrayref();
2568 };
2569 if ($@) {
2570 printf STDERR "bumm %s\n", $@;
2571 }
2572 if (-f "/tmp/check_mysql_health_simulation/".$self->{mode}) {
2573 my $simulation = do { local (@ARGV, $/) =
2574 "/tmp/check_mysql_health_simulation/".$self->{mode}; <> };
2575 @{$rows} = map { [ split(/\s+/, $_) ] } split(/\n/, $simulation);
2576 }
2577 return @{$rows};
2578 }
2579
2580 sub func {
2581 my $self = shift;
2582 $self->{handle}->func(@_);
2583 }
2584
2585 sub execute {
2586 my $self = shift;
2587 my $sql = shift;
2588 eval {
2589 my $sth = $self->{handle}->prepare($sql);
2590 $sth->execute();
2591 };
2592 if ($@) {
2593 printf STDERR "bumm %s\n", $@;
2594 }
2595 }
2596
2597 sub DESTROY {
2598 my $self = shift;
2599 #$self->trace(sprintf "disconnecting DBD %s",
2600 # $self->{handle} ? "with handle" : "without handle");
2601 #$self->{handle}->disconnect() if $self->{handle};
2602 }
2603
2604
2605
2606
2607 package DBD::MySQL::Cluster;
2608
2609 use strict;
2610 use Time::HiRes;
2611 use IO::File;
2612 use Data::Dumper;
2613
2614
2615 {
2616 our $verbose = 0;
2617 our $scream = 0; # scream if something is not implemented
2618 our $access = "dbi"; # how do we access the database.
2619 our $my_modules_dyn_dir = ""; # where we look for self-written extensions
2620
2621 my @clusters = ();
2622 my $initerrors = undef;
2623
2624 sub add_cluster {
2625 push(@clusters, shift);
2626 }
2627
2628 sub return_clusters {
2629 return @clusters;
2630 }
2631
2632 sub return_first_cluster() {
2633 return $clusters[0];
2634 }
2635
2636 }
2637
2638 sub new {
2639 my $class = shift;
2640 my %params = @_;
2641 my $self = {
2642 hostname => $params{hostname},
2643 port => $params{port},
2644 username => $params{username},
2645 password => $params{password},
2646 timeout => $params{timeout},
2647 warningrange => $params{warningrange},
2648 criticalrange => $params{criticalrange},
2649 version => 'unknown',
2650 nodes => [],
2651 ndbd_nodes => 0,
2652 ndb_mgmd_nodes => 0,
2653 mysqld_nodes => 0,
2654 };
2655 bless $self, $class;
2656 $self->init_nagios();
2657 if ($self->connect(%params)) {
2658 DBD::MySQL::Cluster::add_cluster($self);
2659 $self->init(%params);
2660 }
2661 return $self;
2662 }
2663
2664 sub init {
2665 my $self = shift;
2666 my %params = @_;
2667 if ($self->{show}) {
2668 my $type = undef;
2669 foreach (split /\n/, $self->{show}) {
2670 if (/\[(\w+)\((\w+)\)\]\s+(\d+) node/) {
2671 $type = uc $2;
2672 } elsif (/id=(\d+)(.*)/) {
2673 push(@{$self->{nodes}}, DBD::MySQL::Cluster::Node->new(
2674 type => $type,
2675 id => $1,
2676 status => $2,
2677 ));
2678 }
2679 }
2680 } else {
2681 }
2682 if ($params{mode} =~ /^cluster::ndbdrunning/) {
2683 foreach my $node (@{$self->{nodes}}) {
2684 $node->{type} eq "NDB" && $node->{status} eq "running" && $self->{ndbd_nodes}++;
2685 $node->{type} eq "MGM" && $node->{status} eq "running" && $self->{ndb_mgmd_nodes}++;
2686 $node->{type} eq "API" && $node->{status} eq "running" && $self->{mysqld_nodes}++;
2687 }
2688 } else {
2689 printf "broken mode %s\n", $params{mode};
2690 }
2691 }
2692
2693 sub dump {
2694 my $self = shift;
2695 my $message = shift || "";
2696 printf "%s %s\n", $message, Data::Dumper::Dumper($self);
2697 }
2698
2699 sub nagios {
2700 my $self = shift;
2701 my %params = @_;
2702 my $dead_ndb = 0;
2703 my $dead_api = 0;
2704 if (! $self->{nagios_level}) {
2705 if ($params{mode} =~ /^cluster::ndbdrunning/) {
2706 foreach my $node (grep { $_->{type} eq "NDB"} @{$self->{nodes}}) {
2707 next if $params{selectname} && $params{selectname} ne $_->{id};
2708 if (! $node->{connected}) {
2709 $self->add_nagios_critical(
2710 sprintf "ndb node %d is not connected", $node->{id});
2711 $dead_ndb++;
2712 }
2713 }
2714 foreach my $node (grep { $_->{type} eq "API"} @{$self->{nodes}}) {
2715 next if $params{selectname} && $params{selectname} ne $_->{id};
2716 if (! $node->{connected}) {
2717 $self->add_nagios_critical(
2718 sprintf "api node %d is not connected", $node->{id});
2719 $dead_api++;
2720 }
2721 }
2722 if (! $dead_ndb) {
2723 $self->add_nagios_ok("all ndb nodes are connected");
2724 }
2725 if (! $dead_api) {
2726 $self->add_nagios_ok("all api nodes are connected");
2727 }
2728 }
2729 }
2730 $self->add_perfdata(sprintf "ndbd_nodes=%d ndb_mgmd_nodes=%d mysqld_nodes=%d",
2731 $self->{ndbd_nodes}, $self->{ndb_mgmd_nodes}, $self->{mysqld_nodes});
2732 }
2733
2734
2735 sub init_nagios {
2736 my $self = shift;
2737 no strict 'refs';
2738 if (! ref($self)) {
2739 my $nagiosvar = $self."::nagios";
2740 my $nagioslevelvar = $self."::nagios_level";
2741 $$nagiosvar = {
2742 messages => {
2743 0 => [],
2744 1 => [],
2745 2 => [],
2746 3 => [],
2747 },
2748 perfdata => [],
2749 };
2750 $$nagioslevelvar = $ERRORS{OK},
2751 } else {
2752 $self->{nagios} = {
2753 messages => {
2754 0 => [],
2755 1 => [],
2756 2 => [],
2757 3 => [],
2758 },
2759 perfdata => [],
2760 };
2761 $self->{nagios_level} = $ERRORS{OK},
2762 }
2763 }
2764
2765 sub check_thresholds {
2766 my $self = shift;
2767 my $value = shift;
2768 my $defaultwarningrange = shift;
2769 my $defaultcriticalrange = shift;
2770 my $level = $ERRORS{OK};
2771 $self->{warningrange} = $self->{warningrange} ?
2772 $self->{warningrange} : $defaultwarningrange;
2773 $self->{criticalrange} = $self->{criticalrange} ?
2774 $self->{criticalrange} : $defaultcriticalrange;
2775 if ($self->{warningrange} !~ /:/ && $self->{criticalrange} !~ /:/) {
2776 # warning = 10, critical = 20, warn if > 10, crit if > 20
2777 $level = $ERRORS{WARNING} if $value > $self->{warningrange};
2778 $level = $ERRORS{CRITICAL} if $value > $self->{criticalrange};
2779 } elsif ($self->{warningrange} =~ /([\d\.]+):/ &&
2780 $self->{criticalrange} =~ /([\d\.]+):/) {
2781 # warning = 98:, critical = 95:, warn if < 98, crit if < 95
2782 $self->{warningrange} =~ /([\d\.]+):/;
2783 $level = $ERRORS{WARNING} if $value < $1;
2784 $self->{criticalrange} =~ /([\d\.]+):/;
2785 $level = $ERRORS{CRITICAL} if $value < $1;
2786 }
2787 return $level;
2788 #
2789 # syntax error must be reported with returncode -1
2790 #
2791 }
2792
2793 sub add_nagios {
2794 my $self = shift;
2795 my $level = shift;
2796 my $message = shift;
2797 push(@{$self->{nagios}->{messages}->{$level}}, $message);
2798 # recalc current level
2799 foreach my $llevel (qw(CRITICAL WARNING UNKNOWN OK)) {
2800 if (scalar(@{$self->{nagios}->{messages}->{$ERRORS{$llevel}}})) {
2801 $self->{nagios_level} = $ERRORS{$llevel};
2802 }
2803 }
2804 }
2805
2806 sub add_nagios_ok {
2807 my $self = shift;
2808 my $message = shift;
2809 $self->add_nagios($ERRORS{OK}, $message);
2810 }
2811
2812 sub add_nagios_warning {
2813 my $self = shift;
2814 my $message = shift;
2815 $self->add_nagios($ERRORS{WARNING}, $message);
2816 }
2817
2818 sub add_nagios_critical {
2819 my $self = shift;
2820 my $message = shift;
2821 $self->add_nagios($ERRORS{CRITICAL}, $message);
2822 }
2823
2824 sub add_nagios_unknown {
2825 my $self = shift;
2826 my $message = shift;
2827 $self->add_nagios($ERRORS{UNKNOWN}, $message);
2828 }
2829
2830 sub add_perfdata {
2831 my $self = shift;
2832 my $data = shift;
2833 push(@{$self->{nagios}->{perfdata}}, $data);
2834 }
2835
2836 sub merge_nagios {
2837 my $self = shift;
2838 my $child = shift;
2839 foreach my $level (0..3) {
2840 foreach (@{$child->{nagios}->{messages}->{$level}}) {
2841 $self->add_nagios($level, $_);
2842 }
2843 #push(@{$self->{nagios}->{messages}->{$level}},
2844 # @{$child->{nagios}->{messages}->{$level}});
2845 }
2846 push(@{$self->{nagios}->{perfdata}}, @{$child->{nagios}->{perfdata}});
2847 }
2848
2849
2850 sub calculate_result {
2851 my $self = shift;
2852 if ($ENV{NRPE_MULTILINESUPPORT} &&
2853 length join(" ", @{$self->{nagios}->{perfdata}}) > 200) {
2854 foreach my $level ("CRITICAL", "WARNING", "UNKNOWN", "OK") {
2855 # first the bad news
2856 if (scalar(@{$self->{nagios}->{messages}->{$ERRORS{$level}}})) {
2857 $self->{nagios_message} .=
2858 "\n".join("\n", @{$self->{nagios}->{messages}->{$ERRORS{$level}}});
2859 }
2860 }
2861 $self->{nagios_message} =~ s/^\n//g;
2862 $self->{perfdata} = join("\n", @{$self->{nagios}->{perfdata}});
2863 } else {
2864 foreach my $level ("CRITICAL", "WARNING", "UNKNOWN", "OK") {
2865 # first the bad news
2866 if (scalar(@{$self->{nagios}->{messages}->{$ERRORS{$level}}})) {
2867 $self->{nagios_message} .=
2868 join(", ", @{$self->{nagios}->{messages}->{$ERRORS{$level}}}).", ";
2869 }
2870 }
2871 $self->{nagios_message} =~ s/, $//g;
2872 $self->{perfdata} = join(" ", @{$self->{nagios}->{perfdata}});
2873 }
2874 foreach my $level ("OK", "UNKNOWN", "WARNING", "CRITICAL") {
2875 if (scalar(@{$self->{nagios}->{messages}->{$ERRORS{$level}}})) {
2876 $self->{nagios_level} = $ERRORS{$level};
2877 }
2878 }
2879 }
2880
2881 sub debug {
2882 my $self = shift;
2883 my $msg = shift;
2884 if ($DBD::MySQL::Cluster::verbose) {
2885 printf "%s %s\n", $msg, ref($self);
2886 }
2887 }
2888
2889 sub connect {
2890 my $self = shift;
2891 my %params = @_;
2892 my $retval = undef;
2893 $self->{tic} = Time::HiRes::time();
2894 eval {
2895 use POSIX ':signal_h';
2896 local $SIG{'ALRM'} = sub {
2897 die "alarm\n";
2898 };
2899 my $mask = POSIX::SigSet->new( SIGALRM );
2900 my $action = POSIX::SigAction->new(
2901 sub { die "connection timeout\n" ; }, $mask);
2902 my $oldaction = POSIX::SigAction->new();
2903 sigaction(SIGALRM ,$action ,$oldaction );
2904 alarm($self->{timeout} - 1); # 1 second before the global unknown timeout
2905 my $ndb_mgm = "ndb_mgm";
2906 $params{hostname} = "127.0.0.1" if ! $params{hostname};
2907 $ndb_mgm .= sprintf " --ndb-connectstring=%s", $params{hostname}
2908 if $params{hostname};
2909 $ndb_mgm .= sprintf ":%d", $params{port}
2910 if $params{port};
2911 $self->{show} = `$ndb_mgm -e show 2>&1`;
2912 if ($? == -1) {
2913 $self->add_nagios_critical("ndb_mgm failed to execute $!");
2914 } elsif ($? & 127) {
2915 $self->add_nagios_critical("ndb_mgm failed to execute $!");
2916 } elsif ($? >> 8 != 0) {
2917 $self->add_nagios_critical("ndb_mgm unable to connect");
2918 } else {
2919 if ($self->{show} !~ /Cluster Configuration/) {
2920 $self->add_nagios_critical("got no cluster configuration");
2921 } else {
2922 $retval = 1;
2923 }
2924 }
2925 };
2926 if ($@) {
2927 $self->{errstr} = $@;
2928 $self->{errstr} =~ s/at $0 .*//g;
2929 chomp $self->{errstr};
2930 $self->add_nagios_critical($self->{errstr});
2931 $retval = undef;
2932 }
2933 $self->{tac} = Time::HiRes::time();
2934 return $retval;
2935 }
2936
2937 sub trace {
2938 my $self = shift;
2939 my $format = shift;
2940 $self->{trace} = -f "/tmp/check_mysql_health.trace" ? 1 : 0;
2941 if ($self->{verbose}) {
2942 printf("%s: ", scalar localtime);
2943 printf($format, @_);
2944 }
2945 if ($self->{trace}) {
2946 my $logfh = new IO::File;
2947 $logfh->autoflush(1);
2948 if ($logfh->open("/tmp/check_mysql_health.trace", "a")) {
2949 $logfh->printf("%s: ", scalar localtime);
2950 $logfh->printf($format, @_);
2951 $logfh->printf("\n");
2952 $logfh->close();
2953 }
2954 }
2955 }
2956
2957 sub DESTROY {
2958 my $self = shift;
2959 my $handle1 = "null";
2960 my $handle2 = "null";
2961 if (defined $self->{handle}) {
2962 $handle1 = ref($self->{handle});
2963 if (defined $self->{handle}->{handle}) {
2964 $handle2 = ref($self->{handle}->{handle});
2965 }
2966 }
2967 $self->trace(sprintf "DESTROY %s with handle %s %s", ref($self), $handle1, $handle2);
2968 if (ref($self) eq "DBD::MySQL::Cluster") {
2969 }
2970 $self->trace(sprintf "DESTROY %s exit with handle %s %s", ref($self), $handle1, $handle2);
2971 if (ref($self) eq "DBD::MySQL::Cluster") {
2972 #printf "humpftata\n";
2973 }
2974 }
2975
2976 sub save_state {
2977 my $self = shift;
2978 my %params = @_;
2979 my $extension = "";
2980 mkdir $params{statefilesdir} unless -d $params{statefilesdir};
2981 my $statefile = sprintf "%s/%s_%s",
2982 $params{statefilesdir}, $params{hostname}, $params{mode};
2983 $extension .= $params{differenciator} ? "_".$params{differenciator} : "";
2984 $extension .= $params{socket} ? "_".$params{socket} : "";
2985 $extension .= $params{port} ? "_".$params{port} : "";
2986 $extension .= $params{database} ? "_".$params{database} : "";
2987 $extension .= $params{tablespace} ? "_".$params{tablespace} : "";
2988 $extension .= $params{datafile} ? "_".$params{datafile} : "";
2989 $extension .= $params{name} ? "_".$params{name} : "";
2990 $extension =~ s/\//_/g;
2991 $extension =~ s/\(/_/g;
2992 $extension =~ s/\)/_/g;
2993 $extension =~ s/\*/_/g;
2994 $extension =~ s/\s/_/g;
2995 $statefile .= $extension;
2996 $statefile = lc $statefile;
2997 open(STATE, ">$statefile");
2998 if ((ref($params{save}) eq "HASH") && exists $params{save}->{timestamp}) {
2999 $params{save}->{localtime} = scalar localtime $params{save}->{timestamp};
3000 }
3001 printf STATE Data::Dumper::Dumper($params{save});
3002 close STATE;
3003 $self->debug(sprintf "saved %s to %s",
3004 Data::Dumper::Dumper($params{save}), $statefile);
3005 }
3006
3007 sub load_state {
3008 my $self = shift;
3009 my %params = @_;
3010 my $extension = "";
3011 my $statefile = sprintf "%s/%s_%s",
3012 $params{statefilesdir}, $params{hostname}, $params{mode};
3013 $extension .= $params{differenciator} ? "_".$params{differenciator} : "";
3014 $extension .= $params{socket} ? "_".$params{socket} : "";
3015 $extension .= $params{port} ? "_".$params{port} : "";
3016 $extension .= $params{database} ? "_".$params{database} : "";
3017 $extension .= $params{tablespace} ? "_".$params{tablespace} : "";
3018 $extension .= $params{datafile} ? "_".$params{datafile} : "";
3019 $extension .= $params{name} ? "_".$params{name} : "";
3020 $extension =~ s/\//_/g;
3021 $extension =~ s/\(/_/g;
3022 $extension =~ s/\)/_/g;
3023 $extension =~ s/\*/_/g;
3024 $extension =~ s/\s/_/g;
3025 $statefile .= $extension;
3026 $statefile = lc $statefile;
3027 if ( -f $statefile) {
3028 our $VAR1;
3029 eval {
3030 require $statefile;
3031 };
3032 if($@) {
3033 printf "rumms\n";
3034 }
3035 $self->debug(sprintf "load %s", Data::Dumper::Dumper($VAR1));
3036 return $VAR1;
3037 } else {
3038 return undef;
3039 }
3040 }
3041
3042 sub valdiff {
3043 my $self = shift;
3044 my $pparams = shift;
3045 my %params = %{$pparams};
3046 my @keys = @_;
3047 my $last_values = $self->load_state(%params) || eval {
3048 my $empty_events = {};
3049 foreach (@keys) {
3050 $empty_events->{$_} = 0;
3051 }
3052 $empty_events->{timestamp} = 0;
3053 $empty_events;
3054 };
3055 foreach (@keys) {
3056 $self->{'delta_'.$_} = $self->{$_} - $last_values->{$_};
3057 $self->debug(sprintf "delta_%s %f", $_, $self->{'delta_'.$_});
3058 }
3059 $self->{'delta_timestamp'} = time - $last_values->{timestamp};
3060 $params{save} = eval {
3061 my $empty_events = {};
3062 foreach (@keys) {
3063 $empty_events->{$_} = $self->{$_};
3064 }
3065 $empty_events->{timestamp} = time;
3066 $empty_events;
3067 };
3068 $self->save_state(%params);
3069 }
3070
3071 sub requires_version {
3072 my $self = shift;
3073 my $version = shift;
3074 my @instances = DBD::MySQL::Cluster::return_clusters();
3075 my $instversion = $instances[0]->{version};
3076 if (! $self->version_is_minimum($version)) {
3077 $self->add_nagios($ERRORS{UNKNOWN},
3078 sprintf "not implemented/possible for MySQL release %s", $instversion);
3079 }
3080 }
3081
3082 sub version_is_minimum {
3083 # the current version is newer or equal
3084 my $self = shift;
3085 my $version = shift;
3086 my $newer = 1;
3087 my @instances = DBD::MySQL::Cluster::return_clusters();
3088 my @v1 = map { $_ eq "x" ? 0 : $_ } split(/\./, $version);
3089 my @v2 = split(/\./, $instances[0]->{version});
3090 if (scalar(@v1) > scalar(@v2)) {
3091 push(@v2, (0) x (scalar(@v1) - scalar(@v2)));
3092 } elsif (scalar(@v2) > scalar(@v1)) {
3093 push(@v1, (0) x (scalar(@v2) - scalar(@v1)));
3094 }
3095 foreach my $pos (0..$#v1) {
3096 if ($v2[$pos] > $v1[$pos]) {
3097 $newer = 1;
3098 last;
3099 } elsif ($v2[$pos] < $v1[$pos]) {
3100 $newer = 0;
3101 last;
3102 }
3103 }
3104 #printf STDERR "check if %s os minimum %s\n", join(".", @v2), join(".", @v1);
3105 return $newer;
3106 }
3107
3108 sub instance_rac {
3109 my $self = shift;
3110 my @instances = DBD::MySQL::Cluster::return_clusters();
3111 return (lc $instances[0]->{parallel} eq "yes") ? 1 : 0;
3112 }
3113
3114 sub instance_thread {
3115 my $self = shift;
3116 my @instances = DBD::MySQL::Cluster::return_clusters();
3117 return $instances[0]->{thread};
3118 }
3119
3120 sub windows_cluster {
3121 my $self = shift;
3122 my @instances = DBD::MySQL::Cluster::return_clusters();
3123 if ($instances[0]->{os} =~ /Win/i) {
3124 return 1;
3125 } else {
3126 return 0;
3127 }
3128 }
3129
3130 sub system_vartmpdir {
3131 my $self = shift;
3132 if ($^O =~ /MSWin/) {
3133 return $self->system_tmpdir();
3134 } else {
3135 return "/var/tmp/check_mysql_health";
3136 }
3137 }
3138
3139 sub system_oldvartmpdir {
3140 my $self = shift;
3141 return "/tmp";
3142 }
3143
3144 sub system_tmpdir {
3145 my $self = shift;
3146 if ($^O =~ /MSWin/) {
3147 return $ENV{TEMP} if defined $ENV{TEMP};
3148 return $ENV{TMP} if defined $ENV{TMP};
3149 return File::Spec->catfile($ENV{windir}, 'Temp')
3150 if defined $ENV{windir};
3151 return 'C:\Temp';
3152 } else {
3153 return "/tmp";
3154 }
3155 }
3156
3157
3158 package DBD::MySQL::Cluster::Node;
3159
3160 use strict;
3161
3162 our @ISA = qw(DBD::MySQL::Cluster);
3163
3164
3165 sub new {
3166 my $class = shift;
3167 my %params = @_;
3168 my $self = {
3169 mode => $params{mode},
3170 timeout => $params{timeout},
3171 type => $params{type},
3172 id => $params{id},
3173 status => $params{status},
3174 };
3175 bless $self, $class;
3176 $self->init(%params);
3177 if ($params{type} eq "NDB") {
3178 bless $self, "DBD::MySQL::Cluster::Node::NDB";
3179 $self->init(%params);
3180 }
3181 return $self;
3182 }
3183
3184 sub init {
3185 my $self = shift;
3186 my %params = @_;
3187 if ($self->{status} =~ /@(\d+\.\d+\.\d+\.\d+)\s/) {
3188 $self->{addr} = $1;
3189 $self->{connected} = 1;
3190 } elsif ($self->{status} =~ /accepting connect from (\d+\.\d+\.\d+\.\d+)/) {
3191 $self->{addr} = $1;
3192 $self->{connected} = 0;
3193 }
3194 if ($self->{status} =~ /starting,/) {
3195 $self->{status} = "starting";
3196 } elsif ($self->{status} =~ /shutting,/) {
3197 $self->{status} = "shutting";
3198 } else {
3199 $self->{status} = $self->{connected} ? "running" : "dead";
3200 }
3201 }
3202
3203
3204 package DBD::MySQL::Cluster::Node::NDB;
3205
3206 use strict;
3207
3208 our @ISA = qw(DBD::MySQL::Cluster::Node);
3209
3210
3211 sub init {
3212 my $self = shift;
3213 my %params = @_;
3214 if ($self->{status} =~ /Nodegroup:\s*(\d+)/) {
3215 $self->{nodegroup} = $1;
3216 }
3217 $self->{master} = ($self->{status} =~ /Master\)/) ? 1 : 0;
3218 }
3219
3220
3221 package Extraopts;
3222
3223 use strict;
3224 use File::Basename;
3225 use Data::Dumper;
3226
3227 sub new {
3228 my $class = shift;
3229 my %params = @_;
3230 my $self = {
3231 file => $params{file},
3232 commandline => $params{commandline},
3233 config => {},
3234 section => 'default_no_section',
3235 };
3236 bless $self, $class;
3237 $self->prepare_file_and_section();
3238 $self->init();
3239 return $self;
3240 }
3241
3242 sub prepare_file_and_section {
3243 my $self = shift;
3244 if (! defined $self->{file}) {
3245 # ./check_stuff --extra-opts
3246 $self->{section} = basename($0);
3247 $self->{file} = $self->get_default_file();
3248 } elsif ($self->{file} =~ /^[^@]+$/) {
3249 # ./check_stuff --extra-opts=special_opts
3250 $self->{section} = $self->{file};
3251 $self->{file} = $self->get_default_file();
3252 } elsif ($self->{file} =~ /^@(.*)/) {
3253 # ./check_stuff --extra-opts=@/etc/myconfig.ini
3254 $self->{section} = basename($0);
3255 $self->{file} = $1;
3256 } elsif ($self->{file} =~ /^(.*?)@(.*)/) {
3257 # ./check_stuff --extra-opts=special_opts@/etc/myconfig.ini
3258 $self->{section} = $1;
3259 $self->{file} = $2;
3260 }
3261 }
3262
3263 sub get_default_file {
3264 my $self = shift;
3265 foreach my $default (qw(/etc/nagios/plugins.ini
3266 /usr/local/nagios/etc/plugins.ini
3267 /usr/local/etc/nagios/plugins.ini
3268 /etc/opt/nagios/plugins.ini
3269 /etc/nagios-plugins.ini
3270 /usr/local/etc/nagios-plugins.ini
3271 /etc/opt/nagios-plugins.ini)) {
3272 if (-f $default) {
3273 return $default;
3274 }
3275 }
3276 return undef;
3277 }
3278
3279 sub init {
3280 my $self = shift;
3281 if (! defined $self->{file}) {
3282 $self->{errors} = sprintf 'no extra-opts file specified and no default file found';
3283 } elsif (! -f $self->{file}) {
3284 $self->{errors} = sprintf 'could not open %s', $self->{file};
3285 } else {
3286 my $data = do { local (@ARGV, $/) = $self->{file}; <> };
3287 my $in_section = 'default_no_section';
3288 foreach my $line (split(/\n/, $data)) {
3289 if ($line =~ /\[(.*)\]/) {
3290 $in_section = $1;
3291 } elsif ($line =~ /(.*?)\s*=\s*(.*)/) {
3292 $self->{config}->{$in_section}->{$1} = $2;
3293 }
3294 }
3295 }
3296 }
3297
3298 sub is_valid {
3299 my $self = shift;
3300 return ! exists $self->{errors};
3301 }
3302
3303 sub overwrite {
3304 my $self = shift;
3305 my %commandline = ();
3306 if (scalar(keys %{$self->{config}->{default_no_section}}) > 0) {
3307 foreach (keys %{$self->{config}->{default_no_section}}) {
3308 $commandline{$_} = $self->{config}->{default_no_section}->{$_};
3309 }
3310 }
3311 if (exists $self->{config}->{$self->{section}}) {
3312 foreach (keys %{$self->{config}->{$self->{section}}}) {
3313 $commandline{$_} = $self->{config}->{$self->{section}}->{$_};
3314 }
3315 }
3316 foreach (keys %commandline) {
3317 if (! exists $self->{commandline}->{$_}) {
3318 $self->{commandline}->{$_} = $commandline{$_};
3319 }
3320 }
3321 }
3322
3323
3324
3325 package main;
3326
3327 use strict;
3328 use Getopt::Long qw(:config no_ignore_case);
3329 use File::Basename;
3330 use lib dirname($0);
3331
3332
3333
3334 use vars qw ($PROGNAME $REVISION $CONTACT $TIMEOUT $STATEFILESDIR $needs_restart %commandline);
3335
3336 $PROGNAME = "check_mysql_health";
3337 $REVISION = '$Revision: 2.2.2 $';
3338 $CONTACT = 'gerhard.lausser@consol.de';
3339 $TIMEOUT = 60;
3340 $STATEFILESDIR = '/var/tmp/check_mysql_health';
3341 $needs_restart = 0;
3342
3343 my @modes = (
3344 ['server::connectiontime',
3345 'connection-time', undef,
3346 'Time to connect to the server' ],
3347 ['server::uptime',
3348 'uptime', undef,
3349 'Time the server is running' ],
3350 ['server::instance::connectedthreads',
3351 'threads-connected', undef,
3352 'Number of currently open connections' ],
3353 ['server::instance::threadcachehitrate',
3354 'threadcache-hitrate', undef,
3355 'Hit rate of the thread-cache' ],
3356 ['server::instance::createdthreads',
3357 'threads-created', undef,
3358 'Number of threads created per sec' ],
3359 ['server::instance::runningthreads',
3360 'threads-running', undef,
3361 'Number of currently running threads' ],
3362 ['server::instance::cachedthreads',
3363 'threads-cached', undef,
3364 'Number of currently cached threads' ],
3365 ['server::instance::abortedconnects',
3366 'connects-aborted', undef,
3367 'Number of aborted connections per sec' ],
3368 ['server::instance::abortedclients',
3369 'clients-aborted', undef,
3370 'Number of aborted connections (because the client died) per sec' ],
3371 ['server::instance::replication::slavelag',
3372 'slave-lag', ['replication-slave-lag'],
3373 'Seconds behind master' ],
3374 ['server::instance::replication::slaveiorunning',
3375 'slave-io-running', ['replication-slave-io-running'],
3376 'Slave io running: Yes' ],
3377 ['server::instance::replication::slavesqlrunning',
3378 'slave-sql-running', ['replication-slave-sql-running'],
3379 'Slave sql running: Yes' ],
3380 ['server::instance::querycachehitrate',
3381 'qcache-hitrate', ['querycache-hitrate'],
3382 'Query cache hitrate' ],
3383 ['server::instance::querycachelowmemprunes',
3384 'qcache-lowmem-prunes', ['querycache-lowmem-prunes'],
3385 'Query cache entries pruned because of low memory' ],
3386 ['server::instance::myisam::keycache::hitrate',
3387 'keycache-hitrate', ['myisam-keycache-hitrate'],
3388 'MyISAM key cache hitrate' ],
3389 ['server::instance::innodb::bufferpool::hitrate',
3390 'bufferpool-hitrate', ['innodb-bufferpool-hitrate'],
3391 'InnoDB buffer pool hitrate' ],
3392 ['server::instance::innodb::bufferpool::waitfree',
3393 'bufferpool-wait-free', ['innodb-bufferpool-wait-free'],
3394 'InnoDB buffer pool waits for clean page available' ],
3395 ['server::instance::innodb::logwaits',
3396 'log-waits', ['innodb-log-waits'],
3397 'InnoDB log waits because of a too small log buffer' ],
3398 ['server::instance::tablecachehitrate',
3399 'tablecache-hitrate', undef,
3400 'Table cache hitrate' ],
3401 ['server::instance::tablelockcontention',
3402 'table-lock-contention', undef,
3403 'Table lock contention' ],
3404 ['server::instance::tableindexusage',
3405 'index-usage', undef,
3406 'Usage of indices' ],
3407 ['server::instance::tabletmpondisk',
3408 'tmp-disk-tables', undef,
3409 'Percent of temp tables created on disk' ],
3410 ['server::instance::needoptimize',
3411 'table-fragmentation', undef,
3412 'Show tables which should be optimized' ],
3413 ['server::instance::openfiles',
3414 'open-files', undef,
3415 'Percent of opened files' ],
3416 ['server::instance::slowqueries',
3417 'slow-queries', undef,
3418 'Slow queries' ],
3419 ['server::instance::longprocs',
3420 'long-running-procs', undef,
3421 'long running processes' ],
3422 ['cluster::ndbdrunning',
3423 'cluster-ndbd-running', undef,
3424 'ndnd nodes are up and running' ],
3425 ['server::sql',
3426 'sql', undef,
3427 'any sql command returning a single number' ],
3428 );
3429
3430 # rrd data store names are limited to 19 characters
3431 my %labels = (
3432 bufferpool_hitrate => {
3433 groundwork => 'bp_hitrate',
3434 },
3435 bufferpool_hitrate_now => {
3436 groundwork => 'bp_hitrate_now',
3437 },
3438 bufferpool_free_waits_rate => {
3439 groundwork => 'bp_freewaits',
3440 },
3441 innodb_log_waits_rate => {
3442 groundwork => 'inno_log_waits',
3443 },
3444 keycache_hitrate => {
3445 groundwork => 'kc_hitrate',
3446 },
3447 keycache_hitrate_now => {
3448 groundwork => 'kc_hitrate_now',
3449 },
3450 threads_created_per_sec => {
3451 groundwork => 'thrds_creat_per_s',
3452 },
3453 connects_aborted_per_sec => {
3454 groundwork => 'conn_abrt_per_s',
3455 },
3456 clients_aborted_per_sec => {
3457 groundwork => 'clnt_abrt_per_s',
3458 },
3459 thread_cache_hitrate => {
3460 groundwork => 'tc_hitrate',
3461 },
3462 thread_cache_hitrate_now => {
3463 groundwork => 'tc_hitrate_now',
3464 },
3465 qcache_lowmem_prunes_rate => {
3466 groundwork => 'qc_lowm_prnsrate',
3467 },
3468 slow_queries_rate => {
3469 groundwork => 'slow_q_rate',
3470 },
3471 tablecache_hitrate => {
3472 groundwork => 'tac_hitrate',
3473 },
3474 tablecache_fillrate => {
3475 groundwork => 'tac_fillrate',
3476 },
3477 tablelock_contention => {
3478 groundwork => 'tl_contention',
3479 },
3480 tablelock_contention_now => {
3481 groundwork => 'tl_contention_now',
3482 },
3483 pct_tmp_table_on_disk => {
3484 groundwork => 'tmptab_on_disk',
3485 },
3486 pct_tmp_table_on_disk_now => {
3487 groundwork => 'tmptab_on_disk_now',
3488 },
3489 );
3490
3491 sub print_usage () {
3492 print <<EOUS;
3493 Usage:
3494 $PROGNAME [-v] [-t <timeout>] [[--hostname <hostname>]
3495 [--port <port> | --socket <socket>]
3496 --username <username> --password <password>] --mode <mode>
3497 [--method mysql]
3498 $PROGNAME [-h | --help]
3499 $PROGNAME [-V | --version]
3500
3501 Options:
3502 --hostname
3503 the database server's hostname
3504 --port
3505 the database's port. (default: 3306)
3506 --socket
3507 the database's unix socket.
3508 --username
3509 the mysql db user
3510 --password
3511 the mysql db user's password
3512 --database
3513 the database's name. (default: information_schema)
3514 --replication-user
3515 the database's replication user name (default: replication)
3516 --warning
3517 the warning range
3518 --critical
3519 the critical range
3520 --mode
3521 the mode of the plugin. select one of the following keywords:
3522 EOUS
3523 my $longest = length ((reverse sort {length $a <=> length $b} map { $_->[1] } @modes)[0]);
3524 my $format = " %-".
3525 (length ((reverse sort {length $a <=> length $b} map { $_->[1] } @modes)[0])).
3526 "s\t(%s)\n";
3527 foreach (@modes) {
3528 printf $format, $_->[1], $_->[3];
3529 }
3530 printf "\n";
3531 print <<EOUS;
3532 --name
3533 the name of something that needs to be further specified,
3534 currently only used for sql statements
3535 --name2
3536 if name is a sql statement, this statement would appear in
3537 the output and the performance data. This can be ugly, so
3538 name2 can be used to appear instead.
3539 --regexp
3540 if this parameter is used, name will be interpreted as a
3541 regular expression.
3542 --units
3543 one of %, KB, MB, GB. This is used for a better output of mode=sql
3544 and for specifying thresholds for mode=tablespace-free
3545 --labelformat
3546 one of pnp4nagios (which is the default) or groundwork.
3547 It is used to shorten performance data labels to 19 characters.
3548
3549 In mode sql you can url-encode the statement so you will not have to mess
3550 around with special characters in your Nagios service definitions.
3551 Instead of
3552 --name="select count(*) from v\$session where status = 'ACTIVE'"
3553 you can say
3554 --name=select%20count%28%2A%29%20from%20v%24session%20where%20status%20%3D%20%27ACTIVE%27
3555 For your convenience you can call check_mysql_health with the --mode encode
3556 option and it will encode the standard input.
3557
3558 You can find the full documentation at
3559 https://labs.consol.de/nagios/check_mysql_health/
3560
3561 EOUS
3562
3563 }
3564
3565 sub print_help () {
3566 print "Copyright (c) 2009 Gerhard Lausser\n\n";
3567 print "\n";
3568 print " Check various parameters of MySQL databases \n";
3569 print "\n";
3570 print_usage();
3571 support();
3572 }
3573
3574
3575 sub print_revision ($$) {
3576 my $commandName = shift;
3577 my $pluginRevision = shift;
3578 $pluginRevision =~ s/^\$Revision: //;
3579 $pluginRevision =~ s/ \$\s*$//;
3580 print "$commandName ($pluginRevision)\n";
3581 print "This nagios plugin comes with ABSOLUTELY NO WARRANTY. You may redistribute\ncopies of this plugin under the terms of the GNU General Public License.\n";
3582 }
3583
3584 sub support () {
3585 my $support='Send email to gerhard.lausser@consol.de if you have questions\nregarding use of this software. \nPlease include version information with all correspondence (when possible,\nuse output from the --version option of the plugin itself).\n';
3586 $support =~ s/@/\@/g;
3587 $support =~ s/\\n/\n/g;
3588 print $support;
3589 }
3590
3591 sub contact_author ($$) {
3592 my $item = shift;
3593 my $strangepattern = shift;
3594 if ($commandline{verbose}) {
3595 printf STDERR
3596 "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n".
3597 "You found a line which is not recognized by %s\n".
3598 "This means, certain components of your system cannot be checked.\n".
3599 "Please contact the author %s and\nsend him the following output:\n\n".
3600 "%s /%s/\n\nThank you!\n".
3601 "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n",
3602 $PROGNAME, $CONTACT, $item, $strangepattern;
3603 }
3604 }
3605
3606 %commandline = ();
3607 my @params = (
3608 "timeout|t=i",
3609 "version|V",
3610 "help|h",
3611 "verbose|v",
3612 "debug|d",
3613 "hostname|H=s",
3614 "database=s",
3615 "port|P=s",
3616 "socket|S=s",
3617 "username|u=s",
3618 "password|p=s",
3619 "replication-user=s",
3620 "mycnf=s",
3621 "mycnfgroup=s",
3622 "mode|m=s",
3623 "name=s",
3624 "name2=s",
3625 "regexp",
3626 "perfdata",
3627 "warning=s",
3628 "critical=s",
3629 "dbthresholds:s",
3630 "absolute|a",
3631 "environment|e=s%",
3632 "negate=s%",
3633 "method=s",
3634 "runas|r=s",
3635 "scream",
3636 "shell",
3637 "eyecandy",
3638 "encode",
3639 "units=s",
3640 "lookback=i",
3641 "3",
3642 "statefilesdir=s",
3643 "with-mymodules-dyn-dir=s",
3644 "report=s",
3645 "labelformat=s",
3646 "extra-opts:s");
3647
3648 if (! GetOptions(\%commandline, @params)) {
3649 print_help();
3650 exit $ERRORS{UNKNOWN};
3651 }
3652
3653 if (exists $commandline{'extra-opts'}) {
3654 # read the extra file and overwrite other parameters
3655 my $extras = Extraopts->new(file => $commandline{'extra-opts'}, commandline =>
3656 \%commandline);
3657 if (! $extras->is_valid()) {
3658 printf "extra-opts are not valid: %s\n", $extras->{errors};
3659 exit $ERRORS{UNKNOWN};
3660 } else {
3661 $extras->overwrite();
3662 }
3663 }
3664
3665 if (exists $commandline{version}) {
3666 print_revision($PROGNAME, $REVISION);
3667 exit $ERRORS{OK};
3668 }
3669
3670 if (exists $commandline{help}) {
3671 print_help();
3672 exit $ERRORS{OK};
3673 } elsif (! exists $commandline{mode}) {
3674 printf "Please select a mode\n";
3675 print_help();
3676 exit $ERRORS{OK};
3677 }
3678
3679 if ($commandline{mode} eq "encode") {
3680 my $input = <>;
3681 chomp $input;
3682 $input =~ s/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg;
3683 printf "%s\n", $input;
3684 exit $ERRORS{OK};
3685 }
3686
3687 if (exists $commandline{3}) {
3688 $ENV{NRPE_MULTILINESUPPORT} = 1;
3689 }
3690
3691 if (exists $commandline{timeout}) {
3692 $TIMEOUT = $commandline{timeout};
3693 }
3694
3695 if (exists $commandline{verbose}) {
3696 $DBD::MySQL::Server::verbose = exists $commandline{verbose};
3697 }
3698
3699 if (exists $commandline{scream}) {
3700 # $DBD::MySQL::Server::hysterical = exists $commandline{scream};
3701 }
3702
3703 if (exists $commandline{method}) {
3704 # snmp or mysql cmdline
3705 } else {
3706 $commandline{method} = "dbi";
3707 }
3708
3709 if (exists $commandline{report}) {
3710 # short, long, html
3711 } else {
3712 $commandline{report} = "long";
3713 }
3714
3715 if (exists $commandline{labelformat}) {
3716 # groundwork
3717 } else {
3718 $commandline{labelformat} = "pnp4nagios";
3719 }
3720
3721 if (exists $commandline{'with-mymodules-dyn-dir'}) {
3722 $DBD::MySQL::Server::my_modules_dyn_dir = $commandline{'with-mymodules-dyn-dir'};
3723 } else {
3724 $DBD::MySQL::Server::my_modules_dyn_dir = '/usr/local/nagios/libexec';
3725 }
3726
3727 if (exists $commandline{environment}) {
3728 # if the desired environment variable values are different from
3729 # the environment of this running script, then a restart is necessary.
3730 # because setting $ENV does _not_ change the environment of the running script.
3731 foreach (keys %{$commandline{environment}}) {
3732 if ((! $ENV{$_}) || ($ENV{$_} ne $commandline{environment}->{$_})) {
3733 $needs_restart = 1;
3734 $ENV{$_} = $commandline{environment}->{$_};
3735 printf STDERR "new %s=%s forces restart\n", $_, $ENV{$_}
3736 if $DBD::MySQL::Server::verbose;
3737 }
3738 }
3739 # e.g. called with --runas dbnagio. shlib_path environment variable is stripped
3740 # during the sudo.
3741 # so the perl interpreter starts without a shlib_path. but --runas cares for
3742 # a --environment shlib_path=...
3743 # so setting the environment variable in the code above and restarting the
3744 # perl interpreter will help it find shared libs
3745 }
3746
3747 if (exists $commandline{runas}) {
3748 # remove the runas parameter
3749 # exec sudo $0 ... the remaining parameters
3750 $needs_restart = 1;
3751 # if the calling script has a path for shared libs and there is no --environment
3752 # parameter then the called script surely needs the variable too.
3753 foreach my $important_env (qw(LD_LIBRARY_PATH SHLIB_PATH
3754 ORACLE_HOME TNS_ADMIN ORA_NLS ORA_NLS33 ORA_NLS10)) {
3755 if ($ENV{$important_env} && ! scalar(grep { /^$important_env=/ }
3756 keys %{$commandline{environment}})) {
3757 $commandline{environment}->{$important_env} = $ENV{$important_env};
3758 printf STDERR "add important --environment %s=%s\n",
3759 $important_env, $ENV{$important_env} if $DBD::MySQL::Server::verbose;
3760 }
3761 }
3762 }
3763
3764 if ($needs_restart) {
3765 my @newargv = ();
3766 my $runas = undef;
3767 if (exists $commandline{runas}) {
3768 $runas = $commandline{runas};
3769 delete $commandline{runas};
3770 }
3771 foreach my $option (keys %commandline) {
3772 if (grep { /^$option/ && /=/ } @params) {
3773 if (ref ($commandline{$option}) eq "HASH") {
3774 foreach (keys %{$commandline{$option}}) {
3775 push(@newargv, sprintf "--%s", $option);
3776 push(@newargv, sprintf "%s=%s", $_, $commandline{$option}->{$_});
3777 }
3778 } else {
3779 push(@newargv, sprintf "--%s", $option);
3780 push(@newargv, sprintf "%s", $commandline{$option});
3781 }
3782 } else {
3783 push(@newargv, sprintf "--%s", $option);
3784 }
3785 }
3786 if ($runas) {
3787 exec "sudo", "-S", "-u", $runas, $0, @newargv;
3788 } else {
3789 exec $0, @newargv;
3790 # this makes sure that even a SHLIB or LD_LIBRARY_PATH are set correctly
3791 # when the perl interpreter starts. Setting them during runtime does not
3792 # help loading e.g. libclntsh.so
3793 }
3794 exit;
3795 }
3796
3797 if (exists $commandline{shell}) {
3798 # forget what you see here.
3799 system("/bin/sh");
3800 }
3801
3802 if (! exists $commandline{statefilesdir}) {
3803 if (exists $ENV{OMD_ROOT}) {
3804 $commandline{statefilesdir} = $ENV{OMD_ROOT}."/var/tmp/check_mysql_health";
3805 } else {
3806 $commandline{statefilesdir} = $STATEFILESDIR;
3807 }
3808 }
3809
3810 if (exists $commandline{name}) {
3811 if ($^O =~ /MSWin/ && $commandline{name} =~ /^'(.*)'$/) {
3812 # putting arguments in single ticks under Windows CMD leaves the ' intact
3813 # we remove them
3814 $commandline{name} = $1;
3815 }
3816 # objects can be encoded like an url
3817 # with s/([^A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg;
3818 if (($commandline{mode} ne "sql") ||
3819 (($commandline{mode} eq "sql") &&
3820 ($commandline{name} =~ /select%20/i))) { # protect ... like '%cac%' ... from decoding
3821 $commandline{name} =~ s/\%([A-Fa-f0-9]{2})/pack('C', hex($1))/seg;
3822 }
3823 if ($commandline{name} =~ /^0$/) {
3824 # without this, $params{selectname} would be treated like undef
3825 $commandline{name} = "00";
3826 }
3827 }
3828
3829 $SIG{'ALRM'} = sub {
3830 printf "UNKNOWN - %s timed out after %d seconds\n", $PROGNAME, $TIMEOUT;
3831 exit $ERRORS{UNKNOWN};
3832 };
3833 alarm($TIMEOUT);
3834
3835 my $nagios_level = $ERRORS{UNKNOWN};
3836 my $nagios_message = "";
3837 my $perfdata = "";
3838 if ($commandline{mode} =~ /^my-([^\-.]+)/) {
3839 my $param = $commandline{mode};
3840 $param =~ s/\-/::/g;
3841 push(@modes, [$param, $commandline{mode}, undef, 'my extension']);
3842 } elsif ((! grep { $commandline{mode} eq $_ } map { $_->[1] } @modes) &&
3843 (! grep { $commandline{mode} eq $_ } map { defined $_->[2] ? @{$_->[2]} : () } @modes)) {
3844 printf "UNKNOWN - mode %s\n", $commandline{mode};
3845 print_usage();
3846 exit 3;
3847 }
3848 my %params = (
3849 timeout => $TIMEOUT,
3850 mode => (
3851 map { $_->[0] }
3852 grep {
3853 ($commandline{mode} eq $_->[1]) ||
3854 ( defined $_->[2] && grep { $commandline{mode} eq $_ } @{$_->[2]})
3855 } @modes
3856 )[0],
3857 cmdlinemode => $commandline{mode},
3858 method => $commandline{method} ||
3859 $ENV{NAGIOS__SERVICEMYSQL_METH} ||
3860 $ENV{NAGIOS__HOSTMYSQL_METH} || 'dbi',
3861 hostname => $commandline{hostname} ||
3862 $ENV{NAGIOS__SERVICEMYSQL_HOST} ||
3863 $ENV{NAGIOS__HOSTMYSQL_HOST} || 'localhost',
3864 database => $commandline{database} ||
3865 $ENV{NAGIOS__SERVICEMYSQL_DATABASE} ||
3866 $ENV{NAGIOS__HOSTMYSQL_DATABASE} || 'information_schema',
3867 port => $commandline{port} || (($commandline{mode} =~ /^cluster/) ?
3868 ($ENV{NAGIOS__SERVICENDBMGM_PORT} || $ENV{NAGIOS__HOSTNDBMGM_PORT} || 1186) :
3869 ($ENV{NAGIOS__SERVICEMYSQL_PORT} || $ENV{NAGIOS__HOSTMYSQL_PORT} || 3306)),
3870 socket => $commandline{socket} ||
3871 $ENV{NAGIOS__SERVICEMYSQL_SOCKET} ||
3872 $ENV{NAGIOS__HOSTMYSQL_SOCKET},
3873 username => $commandline{username} ||
3874 $ENV{NAGIOS__SERVICEMYSQL_USER} ||
3875 $ENV{NAGIOS__HOSTMYSQL_USER},
3876 password => $commandline{password} ||
3877 $ENV{NAGIOS__SERVICEMYSQL_PASS} ||
3878 $ENV{NAGIOS__HOSTMYSQL_PASS},
3879 replication_user => $commandline{'replication-user'} || 'replication',
3880 mycnf => $commandline{mycnf} ||
3881 $ENV{NAGIOS__SERVICEMYSQL_MYCNF} ||
3882 $ENV{NAGIOS__HOSTMYSQL_MYCNF},
3883 mycnfgroup => $commandline{mycnfgroup} ||
3884 $ENV{NAGIOS__SERVICEMYSQL_MYCNFGROUP} ||
3885 $ENV{NAGIOS__HOSTMYSQL_MYCNFGROUP},
3886 warningrange => $commandline{warning},
3887 criticalrange => $commandline{critical},
3888 dbthresholds => $commandline{dbthresholds},
3889 absolute => $commandline{absolute},
3890 lookback => $commandline{lookback},
3891 selectname => $commandline{name} || $commandline{tablespace} || $commandline{datafile},
3892 regexp => $commandline{regexp},
3893 name => $commandline{name},
3894 name2 => $commandline{name2} || $commandline{name},
3895 units => $commandline{units},
3896 lookback => $commandline{lookback} || 0,
3897 eyecandy => $commandline{eyecandy},
3898 statefilesdir => $commandline{statefilesdir},
3899 verbose => $commandline{verbose},
3900 report => $commandline{report},
3901 labelformat => $commandline{labelformat},
3902 negate => $commandline{negate},
3903 );
3904
3905 my $server = undef;
3906 my $cluster = undef;
3907
3908 if ($params{mode} =~ /^(server|my)/) {
3909 $server = DBD::MySQL::Server->new(%params);
3910 $server->nagios(%params);
3911 $server->calculate_result(\%labels);
3912 $nagios_message = $server->{nagios_message};
3913 $nagios_level = $server->{nagios_level};
3914 $perfdata = $server->{perfdata};
3915 } elsif ($params{mode} =~ /^cluster/) {
3916 $cluster = DBD::MySQL::Cluster->new(%params);
3917 $cluster->nagios(%params);
3918 $cluster->calculate_result(\%labels);
3919 $nagios_message = $cluster->{nagios_message};
3920 $nagios_level = $cluster->{nagios_level};
3921 $perfdata = $cluster->{perfdata};
3922 }
3923
3924 printf "%s - %s", $ERRORCODES{$nagios_level}, $nagios_message;
3925 printf " | %s", $perfdata if $perfdata;
3926 printf "\n";
3927 exit $nagios_level;
3928
3929
3930 __END__
3931
3932

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC 1.1.8