diff options
Diffstat (limited to 'perl/manager/snmptosql')
-rwxr-xr-x | perl/manager/snmptosql | 523 |
1 files changed, 523 insertions, 0 deletions
diff --git a/perl/manager/snmptosql b/perl/manager/snmptosql new file mode 100755 index 0000000..ae51733 --- /dev/null +++ b/perl/manager/snmptosql @@ -0,0 +1,523 @@ +#!/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; +} |