#!/usr/bin/perl -w
#
# Plugin to monitor the percent of table space used on isam and myisam
# tables on a mysql server.
#
# Parameters:
#
# 	config
# 	autoconf
# 	suggest
#
# Configuration variables
#
#   mysqlopts    - Options to pass to mysql
#	statefile    - where to put the statefile
#	ignore       - Tables to ignore. Regex.
#	absolute     - use tables sizes, not percent of maximum
#
# $Log: mysql_isam_space_.in,v $
# Revision 1.1  2005/11/06 13:18:28  lupe
# Initial revision
#
# Revision 1.2  2004/05/20 13:57:12  jimmyo
# Set categories to some of the plugins.
#
# Revision 1.1  2004/01/02 18:50:00  jimmyo
# Renamed occurrances of lrrd -> munin
#
# Revision 1.1.1.1  2004/01/02 15:18:07  jimmyo
# Import of LRRD CVS tree after renaming to Munin
#
# Revision 1.15  2003/12/08 10:52:31  jimmyo
# Bugfix when printing values
#
# Revision 1.14  2003/11/07 17:43:16  jimmyo
# Cleanups and log entries
#
#
#
#%# family=auto
#%# capabilities=suggest autoconf

my $DB = `basename $0 | sed 's/^mysql_isam_space_//g' | tr '_' '-'` ;
chomp $DB;
my $STATEFILE = $ENV{'statefile'} || "/var/db/munin-pluginstate/plugin-mysql_isam_space.state";
my $MYSQLSHOW = $ENV{'mysqlshow'} || 'mysqlshow';
my $ABSOLUTE = exists $ENV{'absolute'};
my @mysql_opts = ();

if (exists $ENV{'mysqlopts'})
{
	@mysql_opts = split /\s+/, $ENV{'mysqlopts'};
}

my $tables = undef;
my $databses = 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 (<IN>)
	{
		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 = <IN>;
	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 (<IN>)
		{
			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
			{
				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 1;
	}

	# 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 = <IN>;

	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 1;
}

# vim:syntax=perl
