#!/usr/bin/perl -w # Written by Tim Ellis # # use unixToSnmp.pl to get information from a host via SNMP, then # alert on it accordingly use strict; use DBI; use POSIX qw/setsid/; use POSIX qw/strftime/; # variables that need changing my $unixToSnmp = "/usr/lib/nagios/plugins/unixToSnmp.pl"; # other arguments that may be passed in my $community = "public"; my $ipAddr = ""; my $posixCommand = ""; my $verbose = ""; my $logFile = ""; my $threshold; my @extraArgs; # stuff for the Nagios alert message and alert decision my $msg = ""; my $warnThresh; my $critThresh; my $warnings = 0; my $criticals = 0; my $unknowns = 0; # list of "commands" and the SNMP command they'll execute my $commandHash; $commandHash->{'ps' } = "$unixToSnmp --community=___community --hostname=___ipAddr --command=___command"; $commandHash->{'loadavg' } = "$unixToSnmp --community=___community --hostname=___ipAddr --command=___command"; $commandHash->{'hostname-s' } = "$unixToSnmp --community=___community --hostname=___ipAddr --command=___command"; $commandHash->{'ifconfig' } = "$unixToSnmp --community=___community --hostname=___ipAddr --command=___command"; $commandHash->{'route-n' } = "$unixToSnmp --community=___community --hostname=___ipAddr --command=___command"; $commandHash->{'arp' } = "$unixToSnmp --community=___community --hostname=___ipAddr --command=___command"; $commandHash->{'df-k' } = "$unixToSnmp --community=___community --hostname=___ipAddr --command=___command"; $commandHash->{'df-m' } = "$unixToSnmp --community=___community --hostname=___ipAddr --command=___command"; $commandHash->{'miscinfo' } = "$unixToSnmp --community=___community --hostname=___ipAddr --command=___command"; # need at least one arg if (scalar @ARGV == 0) { &doArgs(); exit 255; } # process input arguments foreach my $arg (@ARGV) { if ($arg =~ /^-+h(elp)*$/) { doArgs(); exit 0; } elsif ($arg =~ /^-+hostname=(.+)$/) { $ipAddr = $1; } elsif ($arg =~ /^-+community=(.+)$/) { $community = $1; } elsif ($arg =~ /^-+command=(.+)$/) { $posixCommand = $1; } elsif ($arg =~ /^-+threshold=(.+)$/) { $threshold = $1; } elsif ($arg =~ /^-+verbose$/) { $verbose = 1; } elsif ($arg =~ /^-+logfile=(.+)$/) { $logFile = $1; } else { push (@extraArgs, $arg); } } # if they want to log to a file, open it, later doLog() will # write into this file if ($logFile) { open (LOG, ">> $logFile") || die "Can't open $logFile for writing."; close (STDERR); open (STDERR, ">> $logFile"); } # we'll overwrite this from time to time my $currTime = strftime ('%Y-%m-%d %H:%M:%S', localtime); # run the command specified my $cmdToRun = $commandHash->{$posixCommand}; if (!defined $cmdToRun) { doArgs(); exit 255; } $cmdToRun =~ s/___community/$community/g; $cmdToRun =~ s/___ipAddr/$ipAddr/g; $cmdToRun =~ s/___command/$posixCommand/g; # tack on all the extra args passed to this script to the callee foreach my $extraArg (@extraArgs) { $cmdToRun .= " $extraArg"; } my $output = `$cmdToRun`; chomp ($output); # now do special processing based on each alert type if ($posixCommand =~ /^df-[k,m]/) { if ($threshold =~ /^([\d,.]+),([\d,.]+)$/) { $warnThresh = $1; $critThresh = $2; } else { $msg = "UNKNOWN: Can't parse input threshold argument \"$threshold\""; nagiosExit ($msg); } foreach my $outLine (split (/\n/, $output)) { if ($outLine =~ /\s+(.+?)\s+.+pct: (.+?)\s+/) { my $pct = $2 + 0; if ($pct > $critThresh) { $msg .= " [crit: $1 at $pct%]"; $criticals++; } elsif ($pct > $warnThresh) { $msg .= " [warn: $1 at $pct%]"; $warnings++; } } else { $msg .= "bizarre output from subscript: $outLine ; "; $unknowns++; } } } else { print "$output\n"; $msg = "UNKNOWN: No logic for determining if thresholds passed for command=$posixCommand"; nagiosExit ($msg); } # now decide how to exit the script. Critical? Warning? OK? if ($criticals > 0) { $msg = "CRITICAL:$msg"; } elsif ($warnings > 0) { $msg = "WARNING:$msg"; } elsif ($unknowns > 0) { $msg = "UNKNOWN:$msg"; } #print "debug: ct:$critThresh/wt:$warnThresh c:$criticals w:$warnings u:$unknowns\n"; if ($msg) { nagiosExit ($msg); } else { nagiosExit ("OK: No thresholds passed."); } # quit if ($logFile) { close LOG; } exit 0; # ----------------------------------------------------------------- # ----------------------------------------------------------------- # subroutines # ----------------------------------------------------------------- # ----------------------------------------------------------------- # usage sub doArgs { print "\n"; print "usage: $0 --hostname= --command=\n"; print " [--community=] [--logfile=] [--verbose] [--help]\n"; print " --hostname host to use to report the stats\n"; print " --community SNMP community to query as\n"; print " --command POSIX command (with nonstandard output) to query\n"; print " --threshold if results exceed these thresholds, return CRITICAL or WARNING, otherwise return OK\n"; print " --logfile send log output to logFileName rather than STDERR\n"; print " --help get help\n"; print " --verbose be verbose\n"; print "Valid commands for this script are:\n"; foreach my $key (sort keys %{$commandHash}) { printf " %12s ",$key; if ($key =~ /df-[k,m]/) { print " --threshold=w%,c% Partition percent full warning/critial\n"; } elsif ($key eq 'loadavg') { print " --threshold=w1,w5,w15,c1,c5,c15** load average warning/critical\n"; } elsif ($key eq 'ifconfig') { print " --threshold=w%,c%** Interface percent errors/discards warning/critial\n"; } elsif ($key eq 'miscinfo') { print " --threshold=w,W,c,C min,MAX num processes to trigger warning/critical\n"; } else { print "\n"; } } print " ** This threshold isn't implemented yet.\n"; print "Examples (for brevity I leave out the --hostname and --community params):\n"; print " $0 --command=ifconfig eth0 --threshold=1,2 # (only eth0 interface)\n"; print " $0 --command=df / --threshold=85,90 # (only / partition)\n"; print " $0 --command=df FixedDisk --threshold=85,90 # (only fixed disks - not, eg, RAM)\n"; print " $0 --command=miscinfo --threshold=25,100,10,1000\n"; print " $0 --command=loadavg --threshold=5,4,3,10,8,6\n"; } # output something for logging -- if the input is " X yyyyyyy" then stick # a timestamp in the whitespace between X and yyyy, otherwise, just output # what we were given. sub doLog { my $toLog = shift; my $toPrint; $currTime = strftime ('%Y-%m-%d %H:%M:%S', localtime); if ($toLog =~ /^\s([^\s])\s(.+)$/) { $toPrint = " $1 $currTime :: $2\n"; } else { $toPrint = " - $currTime :: $toLog\n"; } # send output to STDERR or log file, whichever user specified if ($logFile) { print LOG $toPrint; } else { print STDERR $toPrint; } } # run a system command. exit on failure. log the command if $verbose sub doSystem { my $cmd = shift; if ($verbose) { &doLog (" + $cmd"); } system ($cmd); if ($? >> 8) { &doLog (" E Child process returned " . $? >> 8 . " exit status. Dying."); exit 255; } } # change single quotes to doubled-up single quotes sub doQuote { my $arg = shift; $arg =~ s/'/''/g; return $arg; } sub nagiosExit { my $msg = shift; print "$msg\n"; if ($msg =~ /^OK/) { exit 0; } elsif ($msg =~ /^WARNING/) { exit 1; } elsif ($msg =~ /^CRITICAL/) { exit 2; } elsif ($msg =~ /^UNKNOWN/) { exit 3; } }