Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

gnu@perl's scratchpad

by gnu@perl (Pilgrim)
on Jun 08, 2004 at 19:46 UTC ( [id://362518]=scratchpad: print w/replies, xml ) Need Help??

==================================================== mping.pl v1.0 The program uses a tab delimeted text file 'mping.lookup' for input. +It is made up of the following fields: "#Ping IP Address" "WAN IP Address" "Loopback IP" "Customer N +ame" "PVC Name". The only important thing is that they are TAB se +perated, not just white space. ==================================================== #!/usr/bin/perl -w use strict; use IO::Handle; use Net::Ping; use Net::SNMP; use Data::Dumper; my $source_file = (shift || "./mping.lookup"); my %pid_to_host; my %host_result; my @hosts; my $OID = '.1.3.6.1.4.1.15102.2.1.1.1'; my %address2specific; open(IPS,"$source_file") or die "Cannot open source file $source_file: $!\n"; autoflush STDOUT 1; for (<IPS>) { next if $_ =~ m/^\#/; print "Empty Ping IP Field, skipping: $_\n" unless ($_ =~ m/^\d+\. +/); next unless ($_ =~ m/^\d+\./); s/\cM//; m/^(.*)\t(.*)\t(.*)\t(.*)\t(.*)$/; my $ping_ip = $1; my $wan_ip = $2; my $loopback_ip = $3; my $customer_name = $4; my $lport_string = $5; if ($ping_ip =~ m/^\d+\.\d+\.\d+\.\d+$/) { wait_for_a_kid() if keys %pid_to_host > 30; if ( my $pid = fork ) { # parent $pid_to_host{$pid} = $ping_ip.":".$wan_ip.":". $loopback_ip.":".$customer_name. ":".$lport_string; } else { # child exit ping_a_host($ping_ip); } } } 1 while wait_for_a_kid(); sub ping_a_host { my $host = shift; my $p = Net::Ping->new('icmp',7); $p->ping($host) ? 1 : 0; } sub wait_for_a_kid { my $pid = wait; my $kid_return = $?; return 0 if $pid < 0; my $host = delete $pid_to_host{$pid} or warn("Why did I see $pid ($kid_return)\n"), next; my ($ping_ip, $wan_ip, $loopback_ip, $customer_name, $lport_string) = split(":",$host); #send_trap('10.51.9.64', send_trap('ncool1', 'public', '162', $ping_ip, $wan_ip, $loopback_ip, $customer_name, $lport_string, $OID) if (!($kid_return)); #print "send_trap('10.51.9.64', 'public', '161', $ping_ip, $wan_ip, + $loopback_ip, $customer_name, $lport_string, $OID) ",scalar localtim +e(),"\n" if (!($kid_return)); print "send_trap('ncool1', 'public', '162', $ping_ip, $wan_ip, $loo +pback_ip, $customer_name, $lport_string, $OID) ", scalar localtime(), +"\n" if (!($kid_return)); 1; } sub send_trap { my ($host, $community, $port, $ping_ip, $wan_ip, $loopback_ip, $customer_name, $lport_string, $OID) = (@_); my ($session, $error) = Net::SNMP->session( -hostname => shift || $host, -community => shift || $community, -port => shift || $port, ); print "\nError in creating SNMP session\n" if ($error); my $result = $session->trap( -enterprise => $OID, -generictrap => 6, #-generictrap => 2, -specifictrap => 9999, -varbindlist => [$OID,OCTET_STRING,$ping_ip, $OID,OCTET_STRING,$wan_ip, $OID,OCTET_STRING,$loopback_ip, $OID,OCTET_STRING,$customer_name, $OID,OCTET_STRING,$lport_string, ] ); print "\nError in sending trap\n" if (!$result); $session->close; return 0; } =================================================== The revision of this program uses a database to track info and has an XML input, deletion mechanisim built in. It is still 'in t +he works' but runs just fine. =================================================== #!/usr/bin/perl -w ####################################################### # RCS INFO # ####################################################### # $Author: cmjohnso $ # $Date: 2003/10/03 19:19:20 $ # $Revision: 1.9 $ # $Source: /u01/home/cmjohnso/scripts/snmp/new/RCS/mping3.pl,v $ ####################################################### # $Log: mping3.pl,v $ # Revision 1.9 2003/10/03 19:19:20 cmjohnso # Trap sending now implemented. # Currently testing with "specifictrap" of 9998. # The production one is 9999. # # Revision 1.8 2003/10/03 15:36:29 cmjohnso # Adding debugging procedures. # Figured it was wise to back up first. # # Revision 1.7 2003/10/02 21:18:20 cmjohnso # Added a child process to clean the database. # It removes all the records over two hours old. # Might add that to a command line opt. # Also, worked onthe shutdown method. Seems to work ok, # but not that great, sometimes does not kill all the # processes and just hangs then they have to be killed # from another session. This will need more work, but # for now will have to do, shouldn't be shut down that # much anyway. # # Revision 1.6 2003/09/30 21:09:26 cmjohnso # Finally working with the child termination. # Each child receives the 'USR1' signal # and ends gracefully when it's work cycle has # completed, then the parent exits. # # Revision 1.5 2003/09/30 15:26:58 cmjohnso # Load of data, reporting of errors and # all associated functions working fine # at this time. # Will now have to work on the actual DB # monitoring / reporting /maintaining # process. # # Revision 1.4 2003/09/30 13:18:25 cmjohnso # *** empty log message *** # ####################################################### use strict; use IO::Select; use IO::Socket; use Fcntl; use POSIX qw(:signal_h WNOHANG); #use Net::SNMP; use DBI; use Sys::Hostname; use Getopt::Long qw(:config pass_through ); use XML::Simple; use Data::Dumper; $|++; umask(0177); (my $VERSION = '$Revision: 1.9 $') =~ s/\$(.+) \$/$1/; my $MAXCHILDREN = 20; my $inputFile = "./add.xml"; my $deleteFile = "./del.xml"; my $socket_path = "./unix_socket"; my $OID = '.1.3.6.1.4.1.15102.2.1.1.1'; my $DEBUG = 0; my $dump = 0; my $run = 0; my $version = 0; my %options = ( 'children=i' => \$MAXCHILDREN, 'version' => \$version, 'socket' => \$socket_path, 'debug+' => \$DEBUG, 'input' => \$inputFile, 'del' => \$deleteFile, 'dump' => \$dump, 'run' => \$run, '<>' => sub {print "Invalid Option: +$_[0]!\n";exit}, ); my %ip_epochs; my %failures; my %child_pids; GetOptions(%options); if (${$options{dump}}) { print $VERSION,"\n"; print "There are $MAXCHILDREN child processes configured\n"; print "Input file is ",${$options{input}},":\n"; print "Delete file is ",${$options{del}},":\n"; print "Comm socket is ",${$options{socket}},":\n"; print "DEBUG Level is ",${$options{'debug+'}},":\n"; exit if ! ${$options{run}}; } if (${$options{version}}) { print $VERSION,"\n"; exit if ! ${$options{run}}; } # Database specific variables my $add_file = "./add.xml"; my $del_file = "./del.xml"; my $user = 'pingapp'; my $pass = 'manager'; my $dbhost = 'pingapp'; my $dsn = "DBI:mysql:pingapp:pingapp"; my $dbh; my $sth; my $query; my $xml_add; my $xml_del; $SIG{TERM} = $SIG{INT} = sub { $SIG{TERM} = $SIG{INT} = sub { print " +\nPlease be patient, there are ".scalar keys(%child_pids)." child pro +cesses still running\n"};pprint ("IN $$\n");kill_child();unlink $sock +et_path; exit 0 }; $dbh = DBI->connect($dsn,$user,$pass,{RaiseError => 1, AutoCommit => 1 +}) or die "Could not connect to database. $DBI::errstr\n"; my $add_time = (stat($add_file))[9]; my $e_add_time = $dbh->selectrow_array("SELECT EPOCH_TIME FROM INPUT_FILES WHERE FIL +E_NAME='$add_file'"); my $del_time = (stat($del_file))[9]; my $e_del_time = $dbh->selectrow_array("SELECT EPOCH_TIME FROM INPUT_FILES WHERE FIL +E_NAME='$del_file'"); # The section below checks the mtime of the input files # obtained above stored in $add_time and $deltime # against the last mtime of each stored in the database. # if the time has changed it knows to update the # CLIENT_INFO table accrodingly. if ( $add_time > $e_add_time ) { print "Updating add Database.\n"; $dbh->prepare("REPLACE INPUT_FILES VALUES('$add_file','$add_time')" +)->execute(); $xml_add = XMLin($add_file,NormalizeSpace => 2); } if ( $del_time > $e_del_time ) { print "Updating del Database.\n"; $dbh->prepare("REPLACE INPUT_FILES VALUES('$del_file','$del_time')" +)->execute(); $xml_del = XMLin($del_file,NormalizeSpace => 2); } if ($xml_add) { my $values; for (@{$xml_add->{CLIENT}}) { $values .= "('$_->{PING_IP}',". "'$_->{WAN_IP}',". "'$_->{LOOP_IP}',". "'$_->{CUST_NM}',". "'$_->{PVC_NM}'),"; } chop $values; # removes the last comma (bad SQL syntax) $dbh->prepare("REPLACE CLIENT_INFO VALUES$values")->execute(); } if ($xml_del) { my $values; for (@{$xml_del->{CLIENT}}) { $values .= "'$_->{PING_IP}', "; } chop $values; # removes the last space (bad SQL syntax) chop $values; # removes the last comma (bad SQL syntax) $dbh->prepare("DELETE FROM CLIENT_INFO WHERE PING_IP IN ($values)")-> +execute(); } main(); END { unlink $socket_path; } sub main { my $buffer; my $setsleep = 0; my $selector = IO::Select->new(); my $listenSocket; my @sourcearray; # fork child process to maintain the Failures table; if (my $pid = fork ) { $child_pids{$pid} = 1; #in parent here; } elsif (! defined $pid) { print "Fork failed for DB cleaner -->$!\n"; exit; } else { db_cleaner($dsn,$user,$pass); exit; } # fork alerter process to send SNMP alerts; if (my $pid = fork ) { $child_pids{$pid} = 1; #in parent here; } elsif (! defined $pid) { print "Fork failed for alerter -->$!\n"; exit; } else { alerter($dsn,$user,$pass); exit; } $listenSocket = mklisten($socket_path); $selector->add($listenSocket); load_input_file($dbh, \%ip_epochs); load_array(\@sourcearray, \%ip_epochs); # fork child processes to do the ping. for (my $counter = 1;$counter <= $MAXCHILDREN;$counter++) { if (my $pid = fork) { $child_pids{$pid} = 1; # in parent, do nothing } elsif (! defined $pid) { print "fork failed! -->$!\n"; exit; } else { my $sigset = POSIX::SigSet->new(SIGINT,SIGTERM,SIGCHLD); sigprocmask(SIG_BLOCK,$sigset); close($listenSocket); ping_child($socket_path,$dsn,$dbhost,$user,$pass); sigprocmask(SIG_UNBLOCK,$sigset); exit; } } pprint (join(",",keys(%child_pids)),"\n"); while(1) { $SIG{CHLD} = \&reap_child; #waitpid(-1,&WNOHANG); my @canread = $selector->can_read(); for (@canread) { if ($_ == $listenSocket) { my $client = clientAccept($_); $client->autoflush(1); $selector->add($client); next; } else { sysread($_,$buffer,8192); chomp $buffer; if ($buffer eq "req") { my $nextline = nextInput(\@sourcearray); if (defined $nextline) { chomp $nextline; my $time = time() - $ip_epochs{$nextline}; if ( $time < 60 ) { pprint ("SLEEPING $time seconds....\n"); sleep $time; } syswrite($_,$nextline); $ip_epochs{$nextline} = time(); } else { load_array(\@sourcearray, \%ip_epochs); syswrite($_,"TRY_AGAIN"); } } else { syswrite(STDOUT,"INVALID! EXITING!\n"); $selector->remove($_); close $_; } } } } }## END MAIN ## ######## # Child processes. sub ping_child { use Net::Ping; my $SHUTDOWN = 0; my $sigset = POSIX::SigSet->new(SIGINT,SIGTERM,SIGCHLD,SIGUSR1); sigprocmask(SIG_BLOCK,$sigset); for (keys(%SIG)) { $SIG{$_} = 'DEFAULT'; } $SIG{USR1} = sub{ pprint ("Child $$ in SIGNAL\n"); $SHUTDOWN++ ;pp +rint ("\$SHUTDOWN now $SHUTDOWN\n") }; #my $old_sigset = POSIX::SigSet->new; my ($sockpath, $dsn, $host,$user, $pass) = (@_); my $clientSocket = IO::Socket::UNIX->new($sockpath); my $ping = Net::Ping->new('icmp',5); my $buffer; my $db_handle = DBI->connect($dsn,$user,$pass,{RaiseError => 1, Au +toCommit => 1}) or die "Could not connect to database. $DBI::errstr\n"; until (defined($clientSocket)) { ssleep(.250); $clientSocket = IO::Socket::UNIX->new($sockpath); } $clientSocket->autoflush(1); while (1) { pprint ("1\n"); if ($SHUTDOWN) { pprint ("Child $$ received shutdown\n");exit +0}; pprint ("2\n"); syswrite($clientSocket,"req"); pprint ("3\n"); my $bytes_read = 0; pprint ("4\n"); my $tmpval; pprint ("5\n"); if ($SHUTDOWN) { pprint ("Child $$ received shutdown\n");ex +it 0}; pprint ("6\n"); $bytes_read = sysread($clientSocket,$buffer,8192); pprint ("7\n"); chomp($buffer); pprint ("8\n"); $tmpval = $buffer; pprint ("9\n"); if ($buffer eq "TRY_AGAIN") { pprint ("10\n"); pprint ("CHILD $$ TRYING AGAIN\n"); next; } pprint ("11\n"); pprint ("IN CHILD $$ PINGING $tmpval\n"); pprint ("12\n"); my $block = POSIX::SigSet->new(SIGINT,SIGTERM,SIGCHLD); #my $pre_block = POSIX::SigSet->new; sigprocmask(SIG_BLOCK,$block); my $pingresult = $ping->ping($tmpval); sigprocmask(SIG_UNBLOCK,$block); pprint ("13\n"); if ( (! defined($pingresult)) or (!$pingresult) ) { pprint ("14\n"); my $EPOCH_TIME = time(); pprint ("15\n"); my $sth = $db_handle->prepare("INSERT INTO FAILURES". " VALUES(\'$tmpval\',\'$EPOCH_TIME\ +')"); pprint ("16\n"); die "\$sth error: $!\n" if (! $sth); pprint ("17\n"); pprint (" INSERTING \'$tmpval\' at \'$EPOCH_TIME\'\n" +); pprint ("18\n"); my $return = $sth->execute; pprint ("19\n"); die "\$return error: $!\n" if (! $return); pprint ("20\n"); } pprint ("21\n"); } $db_handle->disconnect(); exit; } ### # SUBROUTINES sub load_array { # loads the array passed in with the # data returned from the database # handle passed in. my $arrref = shift; my $hashref = shift; my $count = 0; @$arrref = sort { $$hashref{$b} <=> $$hashref{$a} } ( keys(% +$hashref)) ; } sub load_input_file { my $db_handle = shift; my $hashref = shift; my $count = 0; my $sth = $db_handle->selectcol_arrayref("SELECT PING_IP FRO +M CLIENT_INFO"); map { $$hashref{$_}=$count++ } @$sth; die "CRITICAL ERROR, cannot continue!\n$count IP's loaded from tab +le.\n" if (! $count); } sub ssleep { # sleeps for the number of seconds passed. # accepts decimal (ex 1/4 sec = .250); select(undef,undef,undef,shift); } ### # Create a listen socket sub mklisten { my $sockpath = shift; my $listenSocket = IO::Socket::UNIX->new(Local => $sockpath, Listen => $MAXCHILDREN + 1, Type => SOCK_STREAM, ) or die "Socket: $!\n"; return $listenSocket; } ### # accept on a passed listen socket sub clientAccept { my $listenSocket = shift; my $newclient = $listenSocket->accept(); $newclient->autoflush(1); return $newclient; } # Get next line from the input file sub nextInput { my $array = shift; return shift(@$array); } sub shut_down { } sub kill_child { pprint ("Please wait, stopping child processes from $$\n"); $SIG{CHLD} = \&reap_child; my $procs = kill USR1 => keys(%child_pids); #print "in kill_child\n"; pprint ("Killing $procs child processes\n"); #sleep while (reap_child()); ##sleep while (%child_pids); sleep while (scalar(keys(%child_pids)) ); #print "leaving kill_child\n"; #$SIG{CHLD} = \&reap_child; #for (keys(%child_pids)) #{ #kill 'USR1', $_; ##reap_child(); #my $pid = waitpid(-1,&WNOHANG); #delete $child_pids{$pid}; ##print "in kill_child\n"; ##sleep while (%child_pids); ##print "leaving kill_child\n"; #} } sub reap_child { pprint ("entered reap_child\n"); while ( (my $child = waitpid(-1,&WNOHANG)) > 0 ) { pprint ("reap_child: deleting $child\n"); delete $child_pids{$child}; } } sub db_cleaner { ### # blocks INT AND TERM signals from child; my $sigset = POSIX::SigSet->new(SIGINT,SIGTERM); my $old_sigset = POSIX::SigSet->new; sigprocmask(SIG_BLOCK,$sigset); # ### my $SHUTDOWN = 0; $SIG{USR1} = sub{ $SHUTDOWN++ }; tie (my $time, 'time_now'); my ($dsn,$user,$pass) = (@_); my $sleep_time = 3600; my $two_hours = 7200; # two hours in seconds my $clean_table = "DELETE FROM FAILURES WHERE ". "EPOCH_TIME < ($time - $two_hours)"; my $dbh = DBI->connect($dsn,$user,$pass,{RaiseError => 1, AutoComm +it => 1}) or die "Could not connect to database. $DBI::errstr\n"; #while (sleep $sleep_time) while (1) { if ($SHUTDOWN) { pprint ("Shutting down DB cleaner\n"); exit 0; } $dbh->do($clean_table) or warn "Could not execute table clean! -->$!\n"; sleep $sleep_time; } } sub alerter { use Net::SNMP; my $SHUTDOWN = 0; my ($dsn,$user,$pass) = (@_); my $sleep_time = 60; tie my $now, 'time_now'; ### # blocks INT AND TERM signals from child; my $sigset = POSIX::SigSet->new(SIGINT,SIGTERM); my $old_sigset = POSIX::SigSet->new; sigprocmask(SIG_BLOCK,$sigset); $SIG{USR1} = sub{ $SHUTDOWN++ }; # ### ### # Customer info table & fields used. my $T_cust = 'CUSTOMER_INFO'; # Customer info table name my $F_name = 'CUST_NAME'; my $F_P_IP = 'PING_IP'; my $F_W_IP = 'WAN_IP'; my $F_DESC = 'TXT_DESC'; my $T_fail = 'FAILURES'; # Failures table name my $F_F_IP = 'FAIL_IP'; my $F_etim = 'EPOCH_TIME'; # ### ### # Alerting thresholds my $L1_fail = 600; # 10 minutes, in seconds my $L1_thresh = 7; my $L2_fail = 1200; # 20 minutes, in seconds my $L2_thresh = 15; # ### ### # Level 1 query; my $L1_query = "SELECT C.CUST_NAME,C.PING_IP,C.WAN_IP,C.LOOPBACK_ +IP,C.TXT_DESC ". "FROM CLIENT_INFO C, FAILURES F ". #"WHERE F.EPOCH_TIME > ($now - 3600) ". "WHERE F.EPOCH_TIME > ($now - $L1_fail) ". "AND C.PING_IP=F.FAIL_IP ". "GROUP BY C.CUST_NAME ". "HAVING COUNT(C.CUST_NAME) > $L1_thresh ". "AND COUNT(C.CUST_NAME) < $L2_thresh"; # ### ### # Level 2 query; my $L2_query = "SELECT C.CUST_NAME,C.PING_IP,C.WAN_IP,C.LOOPBACK_ +IP,C.TXT_DESC ". "FROM CLIENT_INFO C, FAILURES F ". "WHERE F.EPOCH_TIME > ($now - $L2_fail) ". "AND C.PING_IP=F.FAIL_IP ". "GROUP BY C.CUST_NAME ". "HAVING COUNT(C.CUST_NAME) > $L2_thresh "; # ### ### # Data base connection my $db_handle = DBI->connect($dsn,$user,$pass,{RaiseError => 1, A +utoCommit => 1}) or die "Could not connect to database. $DBI::errs +tr\n"; # ### ### # Actual reporting/alerting section # my ($session,$error) = Net::SNMP->session( # -hostname => '10.51.9.61' , # -community => 'public', # -port => '162', # #-hostname => shift || $host, # #-community => shift || $community, # #-port => shift || $port, # ); # die "Error creating the SNMP session\n" if ($error); my $OID = '.1.3.6.1.4.1.15102.2.1.1.1'; while (1) { if ($SHUTDOWN) { pprint ("Shutting down alerter from signal\n"); exit; } my $L1_ref = $db_handle->selectall_arrayref($L1_query); my $L2_ref = $db_handle->selectall_arrayref($L2_query); #print ("L1 failures:\n",Dumper $L1_ref,"-"x20,"\n") if (@$L1_ref +); #print ("L2 failures:\n",Dumper $L2_ref,"-"x20,"\n") if (@$L2_ref +); if (@$L1_ref) { for (@$L1_ref) { # send level 1 traps $$_[4] = 'NULL' if (!$$_[4]); print "Sending trap on $$_[0]\n"; print "send_trap($$_[1],$$_[2],$$_[3],$$_[0],$$_[4],5,$OID) +\n"; send_trap($$_[1],$$_[2],$$_[3],$$_[0],$$_[4],5,$OID); } } if (@$L2_ref) { for (@$L2_ref) { # send level 2 traps $$_[4] = 'NULL' if (!$$_[4]); print "Sending trap on $$_[0]\n"; print "send_trap($$_[1],$$_[2],$$_[3],$$_[0],$$_[4],6,$OID) +\n"; send_trap($$_[1],$$_[2],$$_[3],$$_[0],$$_[4],6,$OID); } } #if (@$L2_ref) #{ # # send level 2 traps # for ( $$L2_ref[0] ) # { # $$_[4] = 'NULL' if (!$$_[4]); # print "Sending trap on $$_[0]\n"; # print "send_trap($$_[1],$$_[2],$$_[3],$$_[0],$$_[4],6,$OID +)\n"; # send_trap($$_[1],$$_[2],$$_[3],$$_[0],$$_[4],6,$OID); # } #} sleep 10; #sleep 60; } $db_handle->disconnect; exit; sub send_trap { my ($session,$error) = Net::SNMP->session( -hostname => '66.43.143.104' , -community => 'public', -port => '162', ); die "Error creating the SNMP session\n" if ($error); my ($ping_ip,$wan_ip,$loopback_ip,$customer_name,$lport_string,$s +everity,$OID) = (@_); my $result = $session->trap( -enterprise => $OID, -generictrap => 6, -specifictrap => 9999, -varbindlist => [$OID,OCTET_STRING,$ping_ip, $OID,OCTET_STRING,$wan_ip, $OID,OCTET_STRING,$loopback_ip, $OID,OCTET_STRING,$customer_name, $OID,OCTET_STRING,$lport_string, $OID,OCTET_STRING,$severity, ]); print "Error in sending trap\n" if (!$result); $session->close; } } sub pprint { my $statement; if ($DEBUG) { $statement = join("",(@_)); print $statement; } } eval { package time_now; sub TIESCALAR { my ($pkg) = @_; my $obj = time(); return (bless \$obj, $pkg); } sub FETCH { my ($r_obj) = @_; return time(); } 1; }
Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others sharing their wisdom with the Monastery: (6)
As of 2024-04-25 11:12 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found