| #!/usr/bin/perl |
| |
| use NetSNMP::manager::getValues qw(getValues); |
| use SNMP; |
| use DBI; |
| use Net::SMTP; |
| |
| #=========================================================================== |
| # Global defines |
| #=========================================================================== |
| |
| $hostname = 'localhost'; # Host that serves the mSQL Database |
| $dbname = 'snmp'; # mySQL Database name |
| $smtpserver = 'localhost'; |
| $smtpfrom = 'Net-SNMP Manager <wjhardaker@ucdavis.edu>'; # <=== CHANGE ME ======== |
| $doit = 1; |
| $somehosts = 0; |
| |
| sub usage { |
| print "$0 [-H host] [-u user] [-p password] [-l hostlist,...] [-v] [-h] [-n] [-d] [-m mib-to-load] <-m mibnode>\n"; |
| exit 0; |
| } |
| |
| while ($#ARGV > -1) { |
| $_ = shift @ARGV; |
| usage if (/-h/); |
| $hostname = shift if (/-H/); |
| if (/-l/) { |
| my $arg = shift; |
| my @a = split(/,/,$arg); |
| my $i; |
| $somehosts = 1; |
| foreach $i (@a) { |
| $dohost{$i} = 1; |
| } |
| } |
| $user = shift if (/-u/); |
| $pass = shift if (/-p/); |
| $verbose = 1 if (/-v/); |
| $delete = 1 if (/-d/); |
| $doit = 0 if (/-n/); |
| $tableexpr = shift if (/-t/); |
| if (/-m/) { |
| # load some mibs |
| # SNMP::loadModules(shift); |
| $ENV{'MIBS'} = shift; |
| } |
| if (/-M/) { |
| # add a mib directory to look in |
| $ENV{'MIBDIRS'} = shift; |
| # SNMP::addMibDirs(shift); |
| } |
| } |
| |
| init_mib; |
| |
| #=========================================================================== |
| # Connect to the mSQL database with the appropriate driver |
| ( $dbh = DBI->connect("DBI:mysql:database=$dbname;host=$hostname", $user, $pass)) |
| or die "\tConnect not ok: $DBI::errstr\n"; |
| |
| # |
| # delete history rows every so often. |
| # |
| my %count = getValues($dbh, 'setup', 'deletecount'); |
| |
| if (!defined($count{'max'})) { |
| # default is to delete history rows once an hour. |
| $dbh->do("insert into setup values('deletecount','max','6')"); |
| $count{'max'} = 6; |
| } |
| |
| if (!defined($count{'current'})) { |
| $dbh->do("insert into setup values('deletecount','current','0')"); |
| } else { |
| $count{'current'}++; |
| if ($count{'max'} <= $count{'current'}) { |
| $count{'current'} = 0; |
| $deletehist = 1; |
| } |
| $dbh->do("update setup set valcol = $count{'current'} where lookup = 'deletecount' and varcol = 'current'"); |
| } |
| |
| #=========================================================================== |
| # Get host records from database and process |
| |
| $cursor = getcursor("SELECT distinct host FROM hosttables"); |
| nexthost: while ( $hostrow = $cursor->fetchrow_hashref ) { |
| |
| my $host = $hostrow->{'host'}; |
| |
| next if ($somehosts && !defined($dohost{$host})); |
| |
| #set up the session |
| print STDERR " starting $host\n" if ($verbose); |
| my $x = $dbh->prepare("select groupname from hostgroups where host = '$host'"); |
| my $y = $x->execute(); |
| my $group = ${$x->fetchrow_hashref}{'groupname'}; |
| my @args = ('authgroup','default'); |
| print STDERR "$host...$y\n" if ($verbose); |
| if (defined($y) && "$y" ne "0E0") { |
| push @args,'authgroup',$group; |
| } |
| push @args,'authhost',$host; |
| print STDERR "$host: $group\n" if ($verbose); |
| |
| print STDERR "authvals: ", join(", ", @args), "\n" if ($verbose); |
| my %authvals = getValues($dbh, @args); |
| if ($verbose) { |
| print STDERR "parms for $host:"; |
| foreach my $i (keys(%authvals)) { |
| print STDERR "$i => $authvals{$i}, "; |
| } |
| print STDERR "\n"; |
| } |
| |
| my $sess = new SNMP::Session ( DestHost => $host, |
| UseSprintValue => 1, |
| %authvals ); |
| print STDERR "Sess ($host): $sess, ref=" . ref($sess). "\n" if ($verbose); |
| if (ref ($sess) ne "SNMP::Session") { |
| # print STDERR "ack: \$sess not a SNMP::Session for $host ($!)\n"; |
| hosterror("$host"); |
| next nexthost; |
| } |
| |
| # get various bits of system information. |
| my $sysDescr = $sess->get('sysDescr.0'); |
| my $sysId = SNMP::translateObj($sess->get('sysObjectID.0')); |
| my $versiontag = $sess->get('versionTag.0'); |
| my $sysuptime = $sess->get('sysUpTime.0'); |
| |
| if ($sysDescr eq "" || $sysId eq "" || $versiontag eq "" || |
| $sysuptime eq "") { |
| hosterror("$host","Problem collecting basic info"); |
| next; |
| } |
| |
| $dbh->do("update hostgroups set sysObjectId = '$sysId', sysDescr = '$sysDescr', versionTag = '$versiontag', sysUpTime = '$sysuptime' where host = '$host'"); |
| |
| # translate the sysUpTime to a real number for future use: |
| { |
| my ($d,$h,$m,$s,$fs) = ($sysuptime =~ /^(\d+):(\d+):(\d+):(\d+)\.(\d+)$/); |
| $sysuptime = $fs + $s*100 + $m*100*60 + $h*100*60*60 + $d*100*60*60*24; |
| } |
| |
| # get a list of tables we want to store |
| $cmd = "SELECT * FROM hosttables where (host = '$host')"; |
| print STDERR " $cmd\n" if ($verbose); |
| ( $tblh = $dbh->prepare( $cmd ) ) |
| or warn "\nnot ok: $DBI::errstr\n"; |
| ( $tblh->execute ) |
| or print( "\tnot ok: $DBI::errstr\n" ); |
| |
| while ( $tablelist = $tblh->fetchrow_hashref ) { |
| next if (defined($tableexpr) && $tablelist->{'tablename'} !~ /$tableexpr/); |
| print STDERR "starting table $tablelist->{'tablename'}\n" if ($verbose); |
| my $mib = $SNMP::MIB{SNMP::translateObj($tablelist->{'tablename'})}; |
| if (!$mib) { |
| warn "mib node $tablelist->{'tablename'} doesn't exist"; |
| next; |
| } |
| my $children = get_children($mib); |
| |
| # create the table in our database if it doesn't exist. |
| setuptable($dbh, $tablelist->{tablename}, $delete); |
| if ($tablelist->{'keephistory'} > 0) { |
| setuptable($dbh, $tablelist->{tablename}, $delete, "hist"); |
| } |
| |
| $var = |
| new SNMP::Varbind([SNMP::translateObj($tablelist->{'tablename'})]); |
| my $void = SNMP::translateObj($tablelist->{'tablename'}); |
| my $val = $sess->getnext($var); |
| print STDERR "init err: $sess->{'ErrorStr'}\n" if ($verbose); |
| if ($sess->{'ErrorStr'} =~ /Timeout/) { |
| print STDERR "$host timed out\n" if ($verbose); |
| hosterror($host); |
| next nexthost; |
| } |
| $initlabel = ""; |
| print STDERR " starting $tablelist->{tablename}\n" if ($verbose); |
| my %tbl_ids; |
| while (1) { |
| my $varlabel = SNMP::Varbind::tag($var); |
| print STDERR "last $host " . SNMP::translateObj($varlabel) . ": $void\n" if ($verbose && SNMP::translateObj($varlabel) !~ /^$void/); |
| |
| last if (SNMP::translateObj($varlabel) !~ /^$void/); |
| $varlabel = SNMP::translateObj(SNMP::Varbind::tag($var)) if ($varlabel =~ /^[\.0-9]+$/); |
| $initlabel = $varlabel if ($initlabel eq ""); |
| |
| my $val = $sess->getnext($var); |
| if ($sess->{'ErrorStr'} =~ /Timeout/) { |
| print STDERR "$host timed out\n" if ($verbose); |
| hosterror($host); |
| next nexthost; |
| } |
| last if ($sess->{'ErrorStr'}); |
| my $id = SNMP::Varbind::iid($var); |
| print STDERR "$initlabel = $varlabel\n" if ($verbose); |
| last if ($varlabel ne $initlabel); |
| my %vals; |
| $tbl_ids{$id} = 1; |
| foreach $c (@$children) { |
| my $oid = $$c{'objectID'} . "." . $id; |
| my $newvar = new SNMP::Varbind([$oid]); |
| my $val = $sess->get($newvar); |
| my $label = SNMP::translateObj($$c{'objectID'}); |
| $vals{$label} = $val; |
| } |
| my $cmd; |
| |
| # check to see if the error previously existed and then |
| # delete the old entry. |
| my $olderr = |
| checkrowforerrors($tablelist->{'tablename'}, $host, $id); |
| $dbh->do("delete from $tablelist->{tablename} where ( host = '$host' and oidindex = '$id')"); |
| $res = $dbh->do("select * from $tablelist->{'tablename'} where ( host = '$host' and oidindex = '$id')"); |
| print STDERR " result: $res\n" if ($verbose); |
| if ($res ne "0E0") { |
| $cmd = "update $tablelist->{'tablename'} set "; |
| foreach $h (keys(%vals)) { |
| $cmd .= "$h = '$vals{$h}', "; |
| } |
| $cmd .= " updated = NULL where (host = '$host' and oidindex = '$id')"; |
| |
| } else { |
| $cmd = "insert into $tablelist->{'tablename'}(host, oidindex, " . join(", ",keys(%vals)) . |
| ") values('$host', '$id', '" . |
| join("', '",values(%vals)). "')"; |
| } |
| |
| print STDERR " $cmd\n" if ($verbose); |
| $dbh->do("$cmd") |
| or warn "\nnot ok: $cmd => $DBI::errstr\n" if ($doit); |
| |
| if ($tablelist->{'keephistory'} > 0) { |
| $cmd = "insert into $tablelist->{'tablename'}hist (host, oidindex, sysUpTime, " |
| . join(", ",keys(%vals)) |
| . ") values('$host', '$id', $sysuptime, '" |
| . join("', '",values(%vals)). "')"; |
| print STDERR " $cmd\n" if ($verbose); |
| $dbh->do("$cmd") |
| or warn "\nnot ok: $cmd -> $DBI::errstr\n" if ($doit); |
| |
| } |
| |
| my $newerr = |
| checkrowforerrors($tablelist->{'tablename'}, $host, $id); |
| if ($newerr->{retval} != $olderr->{retval}) { |
| logerror($host, $newerr->{retval}, $newerr->{errfield}, |
| $newerr->{errvalue}); |
| } |
| } # snmp loop |
| |
| # delete the data beyond the number of days requested. |
| if ($deletehist && $tablelist->{'keephistory'} > 0) { |
| $dbh->do("delete from $tablelist->{'tablename'}hist where (unix_timestamp() - unix_timestamp(updated)) > $tablelist->{'keephistory'}*24*60*60 and host = '$host'") or warn "\nnot ok: $DBI::errstr\n" if ($doit); |
| } |
| |
| my $curs = getcursor("select oidindex from $tablelist->{tablename} where host = '$host'"); |
| my $row; |
| while ($row = $curs->fetchrow_hashref) { |
| print STDERR " $row->{oidindex}\n" if ($verbose); |
| if (!defined($tbl_ids{$row->{oidindex}})) { |
| $dbh->do("delete from $tablelist->{tablename} where oidindex = '$row->{oidindex}'"); |
| print STDERR "deleting: $host $tablelist->{tablename} $row->{oidindex}\n" if ($verbose); |
| } |
| } |
| print STDERR " done with $tablelist->{tablename}\n" if ($verbose); |
| } # table loop |
| |
| if (isbadhost($host)) { |
| # let them out, they're no longer being bad. |
| print STDERR "deleting: delete from hosterrors where host = '$host'\n" if ($verbose); |
| $dbh->do("delete from hosterrors where host = '$host'"); |
| mailusers("$host responding again", "$host responding again", |
| getoncallforhost($host)); |
| } |
| print STDERR " done with $host\n" if ($verbose); |
| } # host loop |
| |
| # disconnect |
| $cursor->finish(); |
| $dbh->disconnect(); |
| |
| # |
| # Subroutines |
| # |
| |
| # setup a table in the database based on a MIB table. |
| sub setuptable { |
| |
| my %conversions = qw(INTEGER integer INTEGER32 integer OCTETSTR varchar(254) COUNTER integer UINTEGER integer IPADDR varchar(254) OBJECTID varchar(254) GAGUE integer OPAQUE varchar(254) TICKS integer GAUGE integer); |
| |
| # set up mib info |
| my ($dbh, $mibnode, $delete, $suffix) = @_; |
| |
| my $mib = $SNMP::MIB{SNMP::translateObj($mibnode)}; |
| my $children = get_children($mib); |
| my ($cmd, $j); |
| |
| if ($delete) { |
| $cmd = "drop table if exists $mib->{label}"; |
| print STDERR "cmd: $cmd\n" if ($verbose); |
| $dbh->do($cmd) |
| or warn "\nnot ok: $cmd -> $DBI::errstr\n" if ($doit); |
| } elsif (($ret = $dbh->do("show tables like '$mib->{label}$suffix'")) ne "0E0") { |
| # the table already exists |
| return; |
| } |
| |
| print STDERR "show tables like $mib->{label}$suffix: $ret\n" if($verbose); |
| print STDERR " creating table for $mibnode ($mib->{label}$suffix)\n" if ($verbose); |
| |
| $cmd = "create table $mib->{label}$suffix (id integer auto_increment primary key, host varchar(32) not null, oidindex varchar(32) not null"; |
| foreach $j (sort { $a->{'subID'} <=> $b->{'subID'} } @$children) { |
| if (!defined($conversions{$j->{type}})) { |
| print STDERR "no conversion for $j->{label} = ". $j->{type} . "!\n"; |
| return; |
| } |
| $cmd .= ", $j->{label} $conversions{$j->{type}}"; |
| } |
| $cmd .= ", updated timestamp"; |
| $cmd .= ", sysUpTime integer" if (defined($suffix)); |
| $cmd .= ",key oidindex (oidindex), key host (host))"; |
| |
| print STDERR "cmd: $cmd\n" if ($verbose); |
| $dbh->do("$cmd") |
| or warn "\nnot ok: $cmd -> $DBI::errstr\n" if ($doit); |
| |
| } |
| |
| sub getoncall { |
| my @groups = @_; |
| my $cur; |
| my $row; |
| my ($emails, @days, @hours, @two, $i); |
| my %dayscon = qw(Sun 0 Mon 1 Tue 2 Wed 3 Thu 4 Fri 5 Sat 6); |
| my @now = localtime(time()); |
| my %people; |
| my $group; |
| |
| foreach $group (@groups) { |
| $cur = getcursor("select * from oncall where groupname = '$group'"); |
| row: while ( $row = $cur->fetchrow_hashref ) { |
| @days = split(/,/,$row->{'days'}); |
| foreach $i (@days) { |
| @two = split(/-/,$i); |
| if ($row->{'days'} eq "*" || |
| (defined($dayscon{$i}) && $dayscon{$i} == $now[6]) || |
| (defined($dayscon{$two[0]}) && defined($dayscon{$two[1]}) && |
| (($dayscon{$two[0]} <= $now[6] && |
| $dayscon{$two[1]} >= $now[6]) || |
| (($dayscon{$two[0]} > $dayscon{$two[1]}) && |
| ($dayscon{$two[0]} <= $now[6] || |
| $dayscon{$two[1]} >= $now[6]))))) { |
| # we hit a valid day range |
| print STDERR " hit it $row->{'email'} $now[6]\t($i)\t$row->{'days'}\n" |
| if ($verbose); |
| $people{$row->{'email'}} = $row->{'email'}; |
| } else { |
| print STDERR "not hit it $row->{'email'} $now[6]\t($i)\t$row->{'days'}\n" |
| if ($verbose); |
| } |
| } |
| } |
| } |
| return keys(%people); |
| } |
| |
| sub getoncallforhost { |
| my $host = shift; |
| return getoncall(getgroupsforhost($host)); |
| } |
| |
| sub getcursor { |
| my $cmd = shift; |
| my $cursor; |
| print STDERR "cmd: $cmd\n" if ($verbose); |
| ( $cursor = $dbh->prepare( $cmd )) |
| or die "\nnot ok: $DBI::errstr\n"; |
| ( $cursor->execute ) |
| or print( "\tnot ok: $DBI::errstr\n" ); |
| return $cursor; |
| } |
| |
| my %expressions; |
| sub getexpr { |
| my $table = shift; |
| print "ref: ",ref($expressions{$table}),"\n" if ($verbose); |
| if (!defined($expressions{$table})) { |
| my $exprs = getcursor("SELECT * FROM errorexpressions where (tablename = '$table')"); |
| while ( $expr = $exprs->fetchrow_hashref ) { |
| push @{$expressions{$table}{'expr'}},$expr->{expression}; |
| push @{$expressions{$table}{'returnfield'}},$expr->{returnfield}; |
| } |
| } |
| if (ref($expressions{$table}) ne "HASH") { |
| # no expressions for this table. |
| $expressions{$table}{'expr'} = []; |
| $expressions{$table}{'returnfield'} = []; |
| } |
| return $expressions{$table}; |
| } |
| |
| sub checkrowforerrors { |
| my ($table, $host, $id) = @_; |
| my $error; |
| |
| my $lastres = 0, $lastfield = ''; |
| my $expressions = getexpr($table); |
| my $i; |
| for($i=0; $i <= $#{$expressions->{'expr'}}; $i++) { |
| if (!defined($expressions->{'prepared'}[$i])) { |
| $expressions->{'prepared'}[$i] = $dbh->prepare("select * from $table where $expressions->{'expr'}[$i] and host = ? and oidindex = ?") |
| or warn "\nnot ok: $DBI::errstr\n"; |
| print STDERR "preparing select * from $table where $expressions->{'expr'}[$i] and host = ? and oidindex = ? ==> ",ref($expressions->{'prepared'}[$i]),"\n" if($verbose); |
| } |
| my $prepared = $expressions->{'prepared'}[$i]; |
| print STDERR "x: ",ref($prepared),"\n" if($verbose); |
| $prepared->execute($host, $id) or warn "\nnot ok: $DBI::errstr\n"; |
| while ( $error = $prepared->fetchrow_hashref ) { |
| print STDERR "$host: $expressions->{returnfield}[$i] = $error->{$expressions->{returnfield}[$i]}\n" if ($verbose); |
| return {'retval', 1, |
| 'errfield', $expressions->{returnfield}[$i], |
| 'errvalue', $error->{$expressions->{returnfield}[$i]}}; |
| } |
| $lastres = $error->{$expressions->{returnfield}[$i]}; |
| $lastfield = $expressions->{returnfield}[$i]; |
| } |
| return {'retval', 0, |
| 'errfield', $lastfield, |
| 'errvalue', $lastres}; |
| } |
| |
| sub logerror { |
| my ($host, $err, $field, $result) = @_; |
| my $groups = getcursor("SELECT distinct groupname FROM hosttables where host = '$host'"); |
| my ($group, $person); |
| my $msg = (($err) ? "error" : "normal"); |
| |
| my @people = getoncallforhost($host); |
| $msg = "$msg: $host"; |
| $msg .= " $field = $result" if ($field || $result); |
| mailusers("SNMP: $msg: $host $field", "$msg\n", @people); |
| } |
| |
| sub mailusers { |
| my $subject = shift; |
| my $msg = shift; |
| my @people = @_; |
| my $person; |
| my $smtpsock = Net::SMTP->new($smtpserver); |
| |
| $smtpsock->mail($smtpfrom); |
| my $error = $smtpsock->recipient(@people); |
| if (!$error) { |
| print STDERR "failed to send mail to ",join(",",@people),"\n"; |
| } |
| $smtpsock->data(); |
| $subject =~ s/\n//; |
| $smtpsock->datasend("To: " . join(", ",@people) . "\n"); |
| $smtpsock->datasend("From: $smtpfrom\n"); |
| $smtpsock->datasend("Subject: $subject\n"); |
| $smtpsock->datasend("\n"); |
| $smtpsock->datasend("$msg\n"); |
| $smtpsock->dataend(); |
| $smtpsock->quit; |
| print STDERR "mailed ",join(",",@people)," with $msg, $subject ($!)\n" if ($verbose); |
| } |
| |
| sub hosterror { |
| my $host = shift; |
| my $error = shift || "No response"; |
| my $groups = getcursor("SELECT distinct groupname FROM hosttables where host = '$host'"); |
| my ($group, $person); |
| my %mailed; |
| |
| return if (isbadhost($host)); # only send out a message once. |
| |
| $dbh->do("insert into hosterrors(host, errormsg) values('$host','$error');"); |
| my @people = getoncallforhost($host); |
| mailusers("No Response from $host", "$host: $error", @people); |
| } |
| |
| sub isbadhost { |
| my $host = shift; |
| my $hosterr = getcursor("SELECT distinct host FROM hosterrors where host = '$host'"); |
| if ($hosterr->fetchrow_hashref) { |
| return 1; |
| } |
| return 0; |
| } |
| |
| sub getgroupsforhost { |
| my $host = shift; |
| my @retgroups; |
| my $groups = getcursor("SELECT distinct groupname FROM hosttables where host = '$host'"); |
| while( $group = $groups->fetchrow_hashref ) { |
| push @retgroups,$group->{'groupname'}; |
| } |
| @retgroups; |
| } |
| |
| sub get_children { |
| my $mib = shift; |
| my $children = $$mib{'children'}; |
| if (ref($children) ne "ARRAY") { |
| warn "$mib has no chlidren"; |
| return; |
| } |
| |
| if ($#{$children} == 0 && $mib->{'label'} =~ /Table$/) { |
| # is a table, use entry? |
| $children = $children->[0]{'children'}; |
| if (ref($children) ne "ARRAY") { |
| warn "$mib has no chlidren"; |
| return; |
| } |
| } |
| return $children; |
| } |