#!/usr/bin/perl -w

package PerfDataConsumers;
use strict;
use RRDPerf qw/update_rrd graph_rrd/;
my $debug = 0;

BEGIN{
	use Exporter ();
	@PerfDataConsumers::ISA       = qw(Exporter);
	@PerfDataConsumers::EXPORT    = qw();
	@PerfDataConsumers::EXPORT_OK = qw(%consumers %graphers);
}

=head1 NAME

PerfDataConsumers.pm - PerfData handlers, user expandable.

=head1 AUTHOR

(c) 2003 Hannes Schulz <mail@hannes-schulz.de>

=head1 VERSION

Version 1.0

=head1 SYNOPSIS

This module provides functions for consuming performance data created by
Nagios. It is supposed to be easily expandable for later use(r)s.

For every service you want to create a graph for, put a consumer and a graph
function in here and note the service description and the function ref in the
hash (see VARIABLES).

=head2 DEBUGGING

Set C<$debug> to true in the script. If your crontab has a line

  MAILTO=you@sap.com

you should get the results by mail every hour.

=head2 EXAMPLE

  sub consume_ping{
  	my ($timet,$sdesc,$hn,$outp,$perf) = @_;
  	$_ = $outp;
  	my ($loss,$rta) = m/loss = ([\d\.]+)%, RTA = ([\d\.]+) ms/;
  
  	unless (defined $loss && defined $rta){
  		warn "$hn: Error while parsing $sdesc: $perf\n" if($debug);
  		return;
  	}
  
  	update_rrd($sdesc,$hn, 900, $timet, 
  		"loss", $loss,
  		"RTA", $rta
  	);
  }
  
  sub graph_ping{
  	my ($sdesc, $hostname) = @_;
  	graph_rrd($sdesc, $hostname,"Ping-Statistics","loss","RTA");
  }
  $graphers{"PING"}  = \&graph_ping;
  $consumers{"PING"} = \&consume_ping;


=head1 DESCRIPTION

This is middleware between C<PerfDataConsumer.pl> and RRPerf.pm. 
It is designed to be easily expandable, the only thing you WILL have to do is
parse the performance output into variables and hand them over to the
L<RRDPerf.pm>-functions. See there for details/parameters.

You MAY want to do as complicated things as we did for "Logon Totals", but it
I<is not nessessary>.

=head1 CAVEATS

Watch your spelling.

 $graphers{"PING"} = \&graph_ping;

is entirely different than

 $graphers{"PINg"} = \&graph_ping;

and won't work at all.

=head2 VARIABLES

=over 4

=item %consumers

contains a list of all consumers in this Module and references to the subs
handling them:

 %consumers = ("PING" => \&consume_ping);

=cut

our %consumers = ();

=item %graphers

contains a list of all graphers in this Module and references to the subs
handling them:

 %graphers = ("PING" => \&graph_ping);

=cut

our %graphers = ();

=back

=head2 METHODS

=over 4

=item PING {{{1

graphs for loss and RTA 

=cut

sub consume_ping{
	my ($timet,$sdesc,$hn,$outp,$perf) = @_;
	$_ = $outp;
	my ($loss,$rta) = m/loss = ([\d\.]+)%, RTA = ([\d\.]+) ms/;

	unless (defined $loss && defined $rta){
		warn "$hn: Error while parsing $sdesc: $perf\n" if($debug);
		return;
	}
	warn "$sdesc($hn): loss = $loss, rta = $rta \n" if($debug);

	update_rrd($sdesc,$hn, 900, $timet, 
		"loss", $loss,
		"RTA", $rta
	);
}

sub graph_ping{
	my ($sdesc, $hostname) = @_;
	graph_rrd($sdesc, $hostname,"Ping-Statistics","loss","RTA");
}
$graphers{"PING"}  = \&graph_ping;
$consumers{"PING"} = \&consume_ping;

=item Outbound Bytes Total Since Boot {{{1

graphs for  Outbound Bytes Total Since Boot

=cut

sub consume_draoutbbytes{
	my ($timet,$sdesc,$hn,$outp,$perf) = @_;
	$_ = $outp;
	my ($hour,$day) = m/hour=(\d+) day=(\d+)\s*$/;

	unless (defined $day and defined $hour){
		warn "$hn: Error while parsing $sdesc: $outp\n" if($debug);
		return;
	}
	warn "$sdesc($hn): $day, $hour \n" if($debug);

	update_rrd("draoutbbytes",$hn, 900, $timet, 
		"hour_MB_times_10", 10*$hour/1048576,
		"day_MB", $day/1048576
	);
}

sub graph_draoutbbytes{
	my ($sdesc, $hostname) = @_;
	graph_rrd("draoutbbytes", $hostname,"DRA Outbound Bytes Total Since Boot",
      "day_MB","hour_MB_times_10");
}
$graphers{"DRA Outbound Bytes"}  = \&graph_draoutbbytes;
$consumers{"DRA Outbound Bytes"} = \&consume_draoutbbytes;

=item Inbound Bytes Total Since Boot {{{1

graphs for Inbound Bytes Total Since Boot

=cut

sub consume_drainbbytes{
	my ($timet,$sdesc,$hn,$outp,$perf) = @_;
	$_ = $outp;
	my ($hour,$day) = m/hour=(\d+) day=(\d+)\s*$/;

	unless (defined $day and defined $hour){
		warn "$hn: Error while parsing $sdesc: $outp\n" if($debug);
		return;
	}
	warn "$sdesc($hn): $day, $hour \n" if($debug);

	update_rrd("drainbbytes",$hn, 900, $timet, 
		"hour_MB_times_10", 10*$hour/1048576,
		"day_MB", $day/1048576
	);
}

sub graph_drainbbytes{
	my ($sdesc, $hostname) = @_;
	graph_rrd("drainbbytes", $hostname,"DRA Inbound Bytes Total Since Boot",
		"day_MB","hour_MB_times_10");
}
$graphers{"DRA Inbound Bytes"}  = \&graph_drainbbytes;
$consumers{"DRA Inbound Bytes"} = \&consume_drainbbytes;

=item LogonTotalALL {{{1

logon total for ALL hosts.

=cut

sub consume_logontotalall{
	my ($timet,$sdesc,$hn,$outp,$perf) = @_;
	$_ = $outp;
	my ($dval,$l24) = m/all=(\d+)\sday=(\d+)\s*$/o;

	unless (defined $dval and defined $l24){
		warn "$hn: Error while parsing $sdesc: $outp\n" if($debug);
		return;
	}
	warn "$sdesc($hn): dval = $dval day=$l24\n" if($debug);

	update_rrd($sdesc,$hn, 3600, $timet, 
		"logons", $dval
	);
	update_rrd("$sdesc-day",$hn, 3600, $timet, 
		"logons_last_24h", $l24
	);
}

sub graph_logontotalall{
	my ($sdesc, $hostname) = @_;
	graph_rrd($sdesc, $hostname,"All Hosts Logon Statistics","logons");
	graph_rrd("$sdesc-day", $hostname,"All Hosts Logons in 24h window","logons_last_24h");
}
$graphers{"logonALL"}  = \&graph_logontotalall;
$consumers{"logonALL"} = \&consume_logontotalall;

=item W2K-Memuse {{{1

graphs for Memory use on W2K

=cut

sub consume_w2kmem{
	my ($timet,$sdesc,$hn,$outp,$perf) = @_;
	$_ = $outp;
	my ($used) = m/used:.*? \((\d+)%\)$/;

	unless (defined $used){
		warn "$hn: Error while parsing $sdesc: $outp\n" if($debug);
		return;
	};
	warn "$sdesc($hn): used = $used\n" if($debug);

	update_rrd($sdesc,$hn, 1800, $timet, 
		"used_perc", $used
	);
}
sub graph_w2kmem{
	my ($sdesc, $hostname) = @_;
	graph_rrd($sdesc, $hostname,"W2K-Memory-Statistics","used_perc");
}
$graphers{"W2K-MemUse"}  = \&graph_w2kmem;
$consumers{"W2K-MemUse"} = \&consume_w2kmem;

=item DNS  {{{1

graphs for DNS-Query-time

=cut

sub consume_dns{
	my ($timet,$sdesc,$hn,$outp,$perf) = @_;
	$_ = $perf;
	my ($tm) = m/DurationMS=(\d+)$/;

	unless (defined $tm){
		warn "$hn: Error while parsing $sdesc: $perf\n" if($debug);
		return;
	};
	warn "$sdesc($hn): duration = $tm ms\n" if($debug);

	update_rrd($sdesc,$hn, 1800, $timet, 
		"time_ms", $tm
	);
}

sub graph_dns{
	my ($sdesc, $hostname) = @_;
	graph_rrd($sdesc, $hostname,"DNS-Statistics","time_ms");
}

$graphers{"DNS-Query"}  = \&graph_dns;
$consumers{"DNS-Query"} = \&consume_dns;

=item W2K-CPU {{{1

graphs for CPU on W2K

=cut

sub consume_w2kcpu{
	my ($timet,$sdesc,$hn,$outp,$perf) = @_;
	$_ = $perf;
	my ($cpu1,$cpu2,$cpu3) = m/:(\d+) .*:(\d+) .*:(\d+)\s*$/;

	unless (defined $cpu1 and defined $cpu2 and defined $cpu3){
		warn "$hn: Error while parsing $sdesc: $perf\n" if($debug);
		return;
	};
	warn "$sdesc($hn): cpu = $cpu1, $cpu2, $cpu3\n" if($debug);

	update_rrd($sdesc,$hn, 1800, $timet, 
		"last10min", $cpu1,
		"last60min", $cpu2,
		"last24hours", $cpu3
	);
}
sub graph_w2kcpu{
	my ($sdesc, $hostname) = @_;
	graph_rrd($sdesc, $hostname,"W2K-CPU-Statistics",
		"last10min",
		"last60min",
		"last24hours");
}
$graphers{"W2K-CPU"}  = \&graph_w2kcpu;
$consumers{"W2K-CPU"} = \&consume_w2kcpu;

# }}}1
=item W2K-Drive-C {{{1

graphs for Disk C use on W2K

=cut

sub consume_w2kdrivec{
	my ($timet,$sdesc,$hn,$outp,$perf) = @_;
	$_ = $outp;
	my ($used) = m/used: [\d\.]+ \w+ \((\d+)%\) -/;

	if (!defined $used){
		warn "$hn: Error while parsing $sdesc: $outp\n" if($debug);
		return;
	};
	warn "$sdesc: used = $used \n" if($debug);

	update_rrd($sdesc,$hn, 1800, $timet, 
		"used_perc", $used
	);
}

sub graph_w2kdrivec{
	my ($sdesc, $hostname) = @_;
	graph_rrd($sdesc, $hostname,"W2K-Drive-C-Statistics","used_perc");
}
$graphers{"Disk Usage Drive C"}  = \&graph_w2kdrivec;
$consumers{"Disk Usage Drive C"} = \&consume_w2kdrivec;

# }}}1
=item W2K-Drive-D {{{1

graphs for Disk D use on W2K

=cut

sub consume_w2kdrived{
	my ($timet,$sdesc,$hn,$outp,$perf) = @_;
	$_ = $outp;
	my ($used) = m/used: [\d\.]+ \w+ \((\d+)%\) -/;

	if (!defined $used){
		warn "$hn: Error while parsing $sdesc: $outp\n" if($debug);
		return;
	};
	warn "$sdesc($hn): used = $used \n" if($debug);

	update_rrd($sdesc,$hn, 1800, $timet, 
		"used_perc", $used
	);
}

sub graph_w2kdrived{
	my ($sdesc, $hostname) = @_;
	graph_rrd($sdesc, $hostname,"W2K-Drive-D-Statistics","used_perc");
}
$graphers{"Disk Usage Drive D"}  = \&graph_w2kdrived;
$consumers{"Disk Usage Drive D"} = \&consume_w2kdrived;

# }}}1
=item Logon Total {{{1

graphs for the total logons on some computer

=cut

sub consume_totallogon{
	my ($timet,$sdesc,$hn,$outp,$perf) = @_;

	my($hour,$day) = ($outp =~ m/hour=(\d+) day=(\d+)\s*$/);

	unless(defined $hour and defined $day){
		warn "$hn: Error while parsing $sdesc: $outp\n" if($debug);
		return;
	}
	warn "$sdesc($hn): $hour, $day \n" if($debug);

	update_rrd($sdesc,$hn, 3600, $timet, 
		"per_hour_times_10", $hour*10,
		"per_day", $day
	);
}

sub graph_totallogon{
	my ($sdesc, $hostname) = @_;
	graph_rrd($sdesc, $hostname,"Total-Logon-Statistics",
       "per_day","per_hour_times_10");
}
$graphers{"logon-total"}  = \&graph_totallogon;
$consumers{"logon-total"} = \&consume_totallogon;


=item Performance-NRPEP  {{{1

graphs for NRPEP-Performance (Load, CPU, Memory/Swap)

=cut

sub consume_perfnrpep{
  my ($timet,$sdesc,$hn,$outp,$perf) = @_;
  $_ = $perf;

  my @data = split /@/;

  my $d;
  foreach $d (@data){

	if(my ($t,$min1,$min5,$min15) = ($d =~ m/^\s*uptime!(\d+):([\d\.]+):([\d\.]+):([\d\.]+)\s*$/o)){
	  warn "uptime($hn): $min1, $min5, $min15 \n" if($debug);
	  update_rrd("load-$sdesc",$hn, 900, $t,
		  "load_perc_1min",  $min1,
		  "load_perc_5min",  $min5,
		  "load_perc_15min", $min15
	  );
	}
	elsif(my ($t2,$usr,$sys,$idle) = ( $d =~ m/^\s*cpu!(\d+):(\d+):(\d+):(\d+)\s*$/o)){
	  warn "cpu($hn): $usr, $sys, $idle \n" if($debug);
	  update_rrd("cpu-$sdesc",$hn, 900, $t2,
		  "cpu_perc_usr",  $usr,
		  "cpu_perc_sys",  $sys,
		  "cpu_perc_idls", $idle
	  );
	}
	elsif(my ($t3,$totm,$usem,$tots,$uses) = ( $d =~ m/^\s*mem!(\d+):(\d+):(\d+):(\d+):(\d+)\s*$/o)){
	  warn "cpu($hn): $totm, $usem, $tots, $uses \n" if($debug);
	  update_rrd("mem-$sdesc",$hn, 900, $t3,
		  "mem_perc_mem",  100*$usem/$totm,
		  "mem_perc_swap", 100*$uses/$tots
	  );
	}
  }
}

sub graph_perfnrpep{
	my ($sdesc, $hostname) = @_;
	graph_rrd("load-$sdesc", $hostname, "Load Average",
		"load_perc_1min",
		"load_perc_5min",
		"load_perc_15min"
	);
	graph_rrd("cpu-$sdesc", $hostname, "CPU load",
		"cpu_perc_usr",
		"cpu_perc_sys",
		"cpu_perc_idls"
	);
	graph_rrd("mem-$sdesc", $hostname, "Memory Usage",
		"mem_perc_mem",
		"mem_perc_swap"
	);
}

$graphers{"perf-nrpep"}  = \&graph_perfnrpep;
$consumers{"perf-nrpep"} = \&consume_perfnrpep;


# }}}1
=item Disk-NRPEP  {{{1

graphs for NRPEP-Diskspace (all drives)

=cut

sub consume_disknrpep{
  my ($timet,$sdesc,$hn,$outp,$perf) = @_;
  $_ = $perf;

  my @data = split /@/;
  my @tosav = ();

  my $d;
  my ($drive,$t,$used,$avail);
  open(DRIVES, ">/usr/local/nagios_indep/var/rrds/$hn/drives.txt");
  foreach $d (@data){
	if(($drive,$t,$used,$avail) = ($d =~ m/^\s*fs:(.+)!(\d+):(\d+):(\d+)\s*$/o)){
	  warn "fs($hn): $used, $avail\n" if($debug);
	  $drive =~ y!/!_!;
	  $drive =~ s!^/(.*)$!$1!;
	  push @tosav, $drive, (100*$used/($used+$avail));
	  print DRIVES "$drive\n"
	}
  }
  close DRIVES;
  update_rrd("fs-$sdesc",$hn, 1800, $t,
	@tosav
  );
}

sub graph_disknrpep{
	my ($sdesc, $hn) = @_;
	my @drives = ();

    open(DRIVES, "</usr/local/nagios_indep/var/rrds/$hn/drives.txt") or return;
	while(<DRIVES>){
		chomp;
		push @drives, $_
	}
	close DRIVES;
	
	graph_rrd("fs-$sdesc", $hn, "File System Usage (%)",
		@drives
	);
}

$graphers{"disk-nrpep"}  = \&graph_disknrpep;
$consumers{"disk-nrpep"} = \&consume_disknrpep;


# }}}1
=item BIND-stats {{{1

graphs for the BIND-statistics

=cut

sub consume_bindstats{
	my ($timet,$sdesc,$hn,$outp,$perf) = @_;
	my ($ls) = ($outp =~ /aqueries=(\d+)\b/);

	return if($ls !~ m/^\d+$/o);

	my $lphfile = "/usr/local/nagios_indep/var/rrds/$hn/qph.txt";
	my $lpdfile = "/usr/local/nagios_indep/var/rrds/$hn/qpd.txt";
	my $tofile  = "/usr/local/nagios_indep/var/rrds/$hn/toffset.txt";

	unless (defined $ls){
		warn "$hn: Error while parsing $sdesc: $perf\n" if($debug);
		return;
	}
	warn "$sdesc($hn): $ls \n" if($debug);

	my ($lph,$lpht) = split /:/,`cat $lphfile 2>/dev/null`;
	my ($lpd,$lpdt) = split /:/,`cat $lpdfile 2>/dev/null`;
	my $to = `cat $tofile`;

	# Maybe create new files... {{{
	if(!defined $lph){
		$lph  = $ls;
		$lpht = $timet;
		`echo "$ls:$timet" > $lphfile`;
	}
	if(!defined $lpd){
		$lpd  = $ls;
		$lpdt = $timet;
		`echo "$ls:$timet" > $lpdfile`;
	} # }}}

	my ($hval, $dval);

	# save value per-hour if defined only! {{{
	if(($timet - $lpht)){
		$hval = ($ls - $lph) / (($timet - $lpht)/3600);
		$hval = ($hval > 0)? $hval : 0
	}else{
		$hval = 0;
	} # }}}

	# update file if we're over the period  {{{
	if(($timet -$lpht) > 3600){
		`echo "$ls:$timet" > $lphfile`;
	} # }}}

	# save value per-day if defined only! {{{
	if(($timet - $lpdt)){
		$dval = ($ls - $lpd);
		$dval = ($dval > 0)? $dval : 0
	}else{
		$dval = 0;
	} # }}}
	
	# update file if we're at a different DAY. {{{
	if((localtime($lpdt  + ($to * 3600)))[3] != 
       (localtime($timet + ($to * 3600)))[3]){
		`echo "$ls:$timet" > $lpdfile`;
	} # }}}

	update_rrd($sdesc,$hn, 3600, $timet, 
		"per_hour_times_10", $hval*10,
		"per_day", $dval
	);
}

sub graph_bindstats{
	my ($sdesc, $hostname) = @_;
	graph_rrd($sdesc, $hostname,"BIND-Statistics",
       "per_day","per_hour_times_10");
}
$graphers{"bind-stats"}  = \&graph_bindstats;
$consumers{"bind-stats"} = \&consume_bindstats;



=back

=cut


1;

# vim:fdm=marker
