#!/usr/bin/perl -w
# Plugin for monitor postgres connections.
#
# Licenced under GPL v2.
#
#       You must also activate Postgresql statistics. See
#	http://www.postgresql.org/docs/8.1/interactive/monitoring-locks.html
#       for how to enable this. Specifically, the following lines must
#       exist in your postgresql.conf:
#
#           stats_start_collector = true
#           stats_block_level = true

use strict;

my $dbuser = 'hobbit';

my $out = "";
my $date = scalar localtime;
my @db = ();

eval {
	use DBI;
	use DBD::Pg;

	my $dbh = DBI->connect ("DBI:Pg:dbname=postgres", "", "", {RaiseError => 1}) || die;

	$out .= "Connections:\n"; #GAUGE

	my $sql = "SELECT datname, count (pg_stat_activity.datname)
		FROM pg_database LEFT JOIN pg_stat_activity USING (datname)
		WHERE datallowconn AND datname <> 'template1'
		GROUP BY datname ORDER BY datname";
	my $sth = $dbh->prepare($sql);
	$sth->execute();
	while ( my ($dbname, $curr_conn) = $sth->fetchrow_array ) {
		$out .= "$dbname : $curr_conn\n";
		push @db, $dbname;
	}

	$dbh->disconnect;
};

my $color = "green";
if ($@) {
	$color = "red";
	$out = "ERROR: $@\n\n$out";
}

open BB, "| $ENV{BB} $ENV{BBDISP} @";
print BB "status $ENV{MACHINE}.postgres $color $date\n$out";
close BB;

exit unless @db;

##############

eval {

my %dbh;
foreach my $dbname (@db) {
	$dbh{$dbname} = DBI->connect ("DBI:Pg:dbname=$dbname", "", "", {RaiseError => 1}) || die "";
}

##############

$out = "Table activity:\n"; # DERIVE
foreach my $dbname (@db) {
	$out .= "\n";

	my $sql = "SELECT SUM(n_tup_ins), SUM(n_tup_upd), SUM(n_tup_del)
		   FROM pg_stat_user_tables";
	#my $sql = "select sum(n_tup_ins),sum(n_tup_upd),sum(n_tup_del) from pg_stat_".$statscope."_tables;";
	my $sth = $dbh{$dbname}->prepare($sql);
	$sth->execute();
	my ($n_tup_ins,$n_tup_upd,$n_tup_del) = $sth->fetchrow();
	$out .= "${dbname}_delete : $n_tup_del\n";
	$out .= "${dbname}_insert : $n_tup_ins\n";
	$out .= "${dbname}_update : $n_tup_upd\n";
}
open BB, "| $ENV{BB} $ENV{BBDISP} @";
print BB "status $ENV{MACHINE}.pgtbl $color $date\n$out";
close BB;

##############

$out = "Tuple reads:\n"; # DERIVE
foreach my $dbname (@db) {
	$out .= "\n";

	my $sql = "SELECT SUM(seq_tup_read), SUM(idx_tup_fetch) FROM pg_stat_user_tables";
	my $sth = $dbh{$dbname}->prepare($sql);
	$sth->execute();
	my ($seq_tup_read,$idx_tup_fetch) = $sth->fetchrow();
	$out .= "${dbname}_idx_fetch : $idx_tup_fetch\n";
	$out .= "${dbname}_seq_read : $seq_tup_read\n";
}
open BB, "| $ENV{BB} $ENV{BBDISP} @";
print BB "status $ENV{MACHINE}.pgtpl $color $date\n$out";
close BB;

##############

$out = "Scans initiated:\n"; # DERIVE
foreach my $dbname (@db) {
	$out .= "\n";

	my $sql = "SELECT SUM(seq_scan), SUM(idx_scan) FROM pg_stat_user_tables";
	my $sth = $dbh{$dbname}->prepare($sql);
	$sth->execute();
	my ($seq_scan,$idx_scan) = $sth->fetchrow();
	$out .= "${dbname}_idx_scan : $idx_scan\n";
	$out .= "${dbname}_seq_scan : $seq_scan\n";
}
open BB, "| $ENV{BB} $ENV{BBDISP} @";
print BB "status $ENV{MACHINE}.pgscn $color $date\n$out";
close BB;

##############

$out = "Cache blocks:\n"; # DERIVE
foreach my $dbname (@db) {
	$out .= "\n";

	my $sql = "SELECT blks_read, blks_hit FROM pg_stat_database WHERE datname='$dbname'";
	my $sth = $dbh{$dbname}->prepare($sql);
	$sth->execute();
	my ($blks_read,$blks_hit) = $sth->fetchrow();
	$out .= "${dbname}_blks_hit : $blks_hit\n";
	$out .= "${dbname}_blks_read : $blks_read\n";
	#my $read_hitratio = $blks_read+$blks_hit != 0 ? sprintf "%.2f", ($blks_hit/($blks_read+$blks_hit))*100 : 100;
	#$out .= "${dbname}_read_hitratio : $read_hitratio\n"; # GAUGE
}
open BB, "| $ENV{BB} $ENV{BBDISP} @";
print BB "status $ENV{MACHINE}.pgblk $color $date\n$out";
close BB;

##############

$out = "Xlog activity:\n"; #DERIVE
foreach my $dbname (@db) {
	$out .= "\n";

	my $sql = "SELECT xact_commit, xact_rollback FROM pg_stat_database WHERE datname='$dbname'";
	my $sth = $dbh{$dbname}->prepare($sql);
	$sth->execute();
	my ($curr_xact_commit,$curr_xact_rollback) = $sth->fetchrow();
	$out .= "${dbname}_commit : $curr_xact_commit\n";
	$out .= "${dbname}_rollback : $curr_xact_rollback\n";
}
open BB, "| $ENV{BB} $ENV{BBDISP} @";
print BB "status $ENV{MACHINE}.pgxlg $color $date\n$out";
close BB;

##############

$out = "Locks:\n"; #GAUGE
foreach my $dbname (@db) {
	$out .= "\n";

	my $sql="SELECT mode, COUNT(mode) FROM pg_locks GROUP BY mode ORDER BY mode";
	my $sth = $dbh{$dbname}->prepare ($sql);
	$sth->execute ();
	my $locks = 0;
	my $exlocks = 0;
	while (my ($mode, $count) = $sth->fetchrow ()) {
	    if ($mode =~ /exclusive/i) {
		$exlocks = $exlocks + $count;
	    }
	    $locks = $locks+$count;
	}
	$out .= "${dbname}_exlocks : $exlocks\n";
	$out .= "${dbname}_locks : $locks\n";
}
open BB, "| $ENV{BB} $ENV{BBDISP} @";
print BB "status $ENV{MACHINE}.pglck $color $date\n$out";
close BB;

##############

foreach my $dbname (@db) {
	$dbh{$dbname}->disconnect;
}

};

if ($@) {
	print "$0 $date ERROR: $@";
}
