#!@@PERL@@ -w # -*- perl -*- =head1 NAME mysql_isam_space_ - Wildcard plugin to monitor the percent of table space used on isam and myisam tables on a mysql server. =head1 CONFIGURATION Configuration parameters for @@CONFDIR@@/PLUGIN, if you need to override the defaults below: [mysql_isam_space_*] env.mysqlopts - Options to pass to mysql env.statefile - where to put the statefile env.ignore - Tables to ignore. Regex. env.absolute - use tables sizes, not percent of maximum =head2 DEFAULT CONFIGURATION [mysql_isam_space_*] env.mysqlopts env.statefile $ENV{MUNIN_PLUGSTATE}/plugin-mysql_isam_space.state env.ignore env.absolute 0 =head1 AUTHOR Unknown author =head1 LICENSE GPLv2 =head1 MAGIC MARKERS =begin comment These magic markers are used by munin-node-configure when installing munin-node. =end comment #%# family=contrib #%# capabilities=suggest autoconf =cut my $DB = `basename $0 | sed 's/^mysql_isam_space_//g' | tr '_' '-'` ; chomp $DB; my $STATEFILE = $ENV{'statefile'} || "$ENV{MUNIN_PLUGSTATE}/plugin-mysql_isam_space.state"; my $MYSQLSHOW = $ENV{'mysqlshow'} || `command -v mysqlshow`; my $ABSOLUTE = $ENV{'absolute'} || 0; my @mysql_opts = (); chomp $MYSQLSHOW; if (exists $ENV{'mysqlopts'}) { @mysql_opts = split /\s+/, $ENV{'mysqlopts'}; } my $tables = undef; my $databases = undef; &print_autoconf () if ($ARGV[0] and $ARGV[0] eq "autoconf"); if (&use_statefile) { ($databases, $tables) = &read_statefile; } else { ($databases, $tables) = &fetch_state_from_mysql; } &print_config ($tables) if ($ARGV[0] and $ARGV[0] eq "config"); &print_suggest ($databases, $tables) if ($ARGV[0] and $ARGV[0] eq "suggest"); foreach my $t (keys %{$tables}) { printf ("%s.%s %f\n", $t, "value ", $tables->{$t}); } sub print_config { my $tables = shift; print "graph_title MySQL \"$DB\" isam/myisam table-space usage\n"; print "graph_args --base 1000\n"; print "graph_vlabel percent\n"; print "graph_category mysql\n"; foreach my $t (keys %{$tables}) { my $label = $t; if (length ($t) >= 20) { $label = sprintf ("...%17s", substr ($t, -17)); } print $t, ".label $label\n"; print $t, ".warning 0:90\n"; print $t, ".critical 0:95\n"; } exit 0; } sub read_statefile { my $tables = shift; my $databases = undef; # Read the statefile open (IN, $STATEFILE) or die "Could not open \"$STATEFILE\" for reading: $!"; while () { tr/זרו/eoa/; if (/^$DB\.([^\s]+)\s+([\d\.]+)\s*$/) { $tables->{$1} = $2; } if (/^([^\.]+)\./) { if (! grep (/^$1$/, @{$databases})) { push (@{$databases}, $1); } } } close (IN); return ($databases, $tables); } sub use_statefile { if (-f $STATEFILE) { if ((stat ($STATEFILE))[9] > (time - 3600)) { return 1; } } return 0; } sub fetch_state_from_mysql { my $tables = shift; my $databases = (); # Fork off mysqlshow my $pid = open (IN, '-|'); if (! defined ($pid)) { # Fork fail die "Couldn't fork."; } elsif ($pid == 0) { # Child exec ($MYSQLSHOW, @mysql_opts, "--status", "-v"); } my @mysql_status = ; close (IN); if ($? != 0) { exit 1; } foreach $line (@mysql_status) { $line =~ tr/זרו/eoa/; if ($line =~ /^\|\s+([^\|\s]+)\s+\|\s+([^\|\s]+)\s+\|/) { next if ($1 eq "Databases"); next if ($2 eq "N/A"); if ($2 > 0) { push (@{$databases}, $1) } } } if ((! -e $STATEFILE) or (-f $STATEFILE)) { &makedirfor ($STATEFILE); open (OUT, ">$STATEFILE") or die "Could not open \"$STATEFILE\" for writing: $!"; } else { die "Outfile exists and isn't a \"file\"."; } foreach my $db (@{$databases}) { # Fork off mysqlshow my $pid = open (IN, '-|'); if (! defined ($pid)) { # Fork fail die "Couldn't fork."; } elsif ($pid == 0) { # Child exec ($MYSQLSHOW, @mysql_opts, "--status", "-v", $db); # Notreached. } my %index; my $headerseen; while () { my @fields = split (/\s*\|\s*/); next if @fields < 2; # Separator line if (! $headerseen and $fields[1] eq "Name") { # Header line, grab field names %index = map {($fields[$_], $_)} 0..$#fields; } else { # mysqlshow says many fun things sometimes, sanity-check next if $fields[$index{Data_length}] eq ""; next if ($fields[$index{Max_data_length}] eq "") && ( ! $ABSOLUTE ); next if ($fields[$index{Max_data_length}] == 0) && ( ! $ABSOLUTE ); my $value = $ABSOLUTE ? $fields[$index{Data_length}] : (100*$fields[$index{Data_length}]/$fields[$index{Max_data_length}]); printf OUT ("%s.%s %f\n", $db, $fields[1], $value); $tables->{$fields[1]} = $value if $DB eq $db; } } close (IN); } close (OUT); return ($databases, $tables); } sub makedirfor { my $filename = shift; (my $dir = $filename) =~ s/\/[^\/]+$//; if ($dir ne $filename and ! -d $dir) { &makedirfor ($dir); mkdir ($dir, 0700); } return $dir; } sub print_suggest { my ($databases, $tables) = @_; foreach my $db (@{$databases}) { print "$db\n"; } exit 0; } sub print_autoconf { # First check that mysqlshow exists... if (! -x $MYSQLSHOW) { print "no (mysqlshow not found)\n"; exit 0; } # Fork off mysqlshow my $pid = open (IN, '-|'); if (! defined ($pid)) { # Fork fail die "Couldn't fork."; } elsif ($pid == 0) { # Child close STDERR; open (STDERR, ">&STDOUT"); exec ($MYSQLSHOW, @mysql_opts, "--status", "-v"); } my @slurp = ; close (IN); if ($? == 0) { print "yes\n"; exit 0; } elsif (grep (/Can't connect to local MySQL server/, @slurp)) { print "no (could not connect to mysql)\n"; } else { print "no\n"; } exit 0; } # vim:syntax=perl