#!/usr/bin/perl # upssched-dispatch # # Jason Healy (jhealy) # # Catch events from the UPS monitoring software, and dispatch as appropriate. # # This script takes a single argument, which is an alarm name as set in # upssched.conf. The alarm should follow this format: # # __ # # Where is the action to take (PAGE, EMAIL, SYSLOG, EXEC), # is the local UPS name (no host part needed), and # is the type of event taken from upsmon (ONBATT, COMMBAD, etc) # # may also have a special value of 'ANY', which signifies that # this event may be fired by any UPS in the system. This saves a lot # of cut and paste work in upssched.conf for similar alarms. # # The of type "EXEC" takes a parameter after a colon, which is the # action to perform. For example, "EXEC:shutdownFoo" would call the # "shutdownFoo()" method of this script. # # IMPORTANT NOTE: the 'ANY' UPS name ONLY WORKS FOR EXECUTE TARGETS. # You cannot use it with timer events (e.g., those defined with # START-TIMER) because of the way upssched queues events. The ANY # UPS causes this script to fall back to the environment variables for # the UPS name. If multiple timer events fire at the same time, only # one instance of upssched will handle them, so the environment will not # change, and the information will be wrong. At this time, the only # way around this is to either use EXECUTE (no timers), or use a different # timer name for each UPS (use the name instead of ANY). # use warnings; use strict; # Allow direct sending of e-mail use Net::SMTP; # Allow direct access to Syslog use Unix::Syslog qw(:macros :subs); # Addresses to send e-mails and pages to my @emails = ('netadmin@suffieldacademy.org', 'sysop@suffieldacademy.org'); my @pagers = ('jhealy_pager@logn.net', '8602798685@alphapage.myairmail.com'); # Set to 1 to turn on more verbose debugging my $DEBUG = 0; # command is the argument passed directly to the script my $command = $ARGV[0]; # Action to take (page, email, etc) my $action = 'EMAIL'; # Exec target (if any) my $exec = ''; # Name of the affected UPS my $ups = 'UNKNOWN'; # Type of event (ONBATT, COMMBAD, etc) my $type = 'UNKNOWN'; # Message to send in the notification my $message = ''; # parse the command out into its components if ($command =~ /^([^_]+)_([^-]+)_([^_]+)$/) { $action = $1; $ups = $2; $type = $3; # deal with the special case where the UPS type must come from the env if ('ANY' eq $ups) { $ups = $ENV{'UPSNAME'}; # strip the hostname, if any if ($ups =~ /^([^@]+)@.*$/) { $ups = $1; } } # deal with EXEC tags; split into method call if ($action =~ /EXEC:(.+)/) { $exec = $1; $action = "EXEC"; } } else { # invalid argument sent to script; send a diagnostic report instead $message = "Could not parse '$command'\n\n"; $DEBUG = 1; } # define a simple message that encapsulates the whole event $message .= "$ups: $type"; if ('ONLINE' eq $type) { $message .= "\n\n$ups is back on utility power."; } elsif ('ONBATT' eq $type) { $message .= "\n\n$ups is on battery."; } elsif ('LOWBATT' eq $type) { $message .= "\n\n$ups has a low battery (critical)!"; } elsif ('FSD' eq $type) { $message .= "\n\n$ups is going down!"; } elsif ('COMMOK' eq $type) { $message .= "\n\n$ups restablished serial connection."; } elsif ('COMMBAD' eq $type) { $message .= "\n\n$ups lost serial connection."; } elsif ('SHUTDOWN' eq $type) { $message .= "\n\n$ups has triggered a system shutdown."; } elsif ('REPLBATT' eq $type) { $message .= "\n\n$ups needs a replacement battery."; } elsif ('NOCOMM' eq $type) { $message .= "\n\n$ups could not be found during connection!"; } else { $DEBUG = 1; $message = "Unknown event type '$type'\n\n" . $message; } $message .= "\n\n[Received $command ($type) from UPS $ups.]\n"; if ($DEBUG == 1) { # add on some diagnostic output $message .= "\n"; # arguments $message .= "\nScript called with the following arguments:\n"; foreach my $i (0 .. $#ARGV) { $message .= "\tARGV[$i]: $ARGV[$i]\n"; } # environment $message .= "\nScript had the following environment:\n"; foreach my $env (sort(keys(%ENV))) { $message .= "\t$env:\t\t$ENV{$env}\n"; } } sub sendMessage($$) { my @recipients = @{shift(@_)}; my $msg = shift(@_); my $from = 'UPS Master '; # use local SMTP server so pager script gets run on this machine my $smtp = Net::SMTP->new('smtp.suffieldacademy.org'); $smtp->mail($from); $smtp->recipient(@recipients); $smtp->data(); $smtp->datasend("To: $recipients[0]\n"); $smtp->datasend("From: $from\n"); $smtp->datasend("Subject: UPS $ups Warning\n"); $smtp->datasend("\n"); $smtp->datasend($msg); $smtp->dataend(); $smtp->quit(); } # Logs a message to syslog only sub slog(;$) { my $msg = shift(@_) || $message; openlog "upssched-dispatch", LOG_PID, LOG_DAEMON; syslog LOG_ALERT, "%s", $msg; closelog; } # Sends message to "email" list above sub email(;$) { my $msg = shift(@_) || $message; sendMessage(\@emails, $msg); } # Sends message to "pagers" list above sub page(;$) { my $msg = shift(@_) || $message; sendMessage(\@pagers, $msg); } # Shuts down a host using a passwordless ssh key sub sshShutdown($;$$$) { my $host = shift(@_); my $user = shift(@_) || 'root'; my $key = shift(@_) || '/etc/nut/sshShutdown.key'; my $command = shift(@_) || 'shutdown -h +0'; # ConnectTimeout prevents us from hanging too long on unreachable hosts # No strict key checks means we won't hang on terminal input for unknown keys my $output = qx{ssh -i $key -o ConnectTimeout=5 -o StrictHostKeyChecking=no $user\@$host $command}; slog("Executed sshShutdown for $host: $output"); } # Determine how to dispatch the message. Currently, there are 4 possible # values: page, email, syslog, and exec. Each are independent, and only # one will be run by this script. If you'd like to take multiple actions, # specify multiple targets in the 'upssched.conf' file. if ('SYSLOG' eq $action) { slog(); } elsif ('EMAIL' eq $action) { email(); } elsif ('PAGE' eq $action) { page(); } elsif ('EXEC' eq $action && '' ne $exec) { if ('shutdownNs1' eq $exec) { sshShutdown('ns1.suffieldacademy.org', 'root'); } elsif ('shutdownWiz' eq $exec) { sshShutdown('wiz.suffieldacademy.org', 'root'); } elsif ('shutdownWoz' eq $exec) { sshShutdown('woz.suffieldacademy.org', 'root'); } elsif ('shutdownRon' eq $exec) { sshShutdown('ron.suffieldacademy.org', 'root'); } elsif ('shutdownCampusManager' eq $exec) { sshShutdown('campus-manager.net.suffieldacademy.org', 'root'); } elsif ('shutdownBigdog' eq $exec) { sshShutdown('bigdog.suffieldacademy.org', 'admin'); } elsif ('shutdownFm' eq $exec) { sshShutdown('fm.suffieldacademy.org', 'admin'); } elsif ('fsd' eq $exec) { sshShutdown('ns1.suffieldacademy.org', 'root'); sshShutdown('wiz.suffieldacademy.org', 'root'); sshShutdown('woz.suffieldacademy.org', 'root'); sshShutdown('ron.suffieldacademy.org', 'root'); sshShutdown('campus-manager.net.suffieldacademy.org', 'root'); sshShutdown('bigdog.suffieldacademy.org', 'admin'); sshShutdown('fm.suffieldacademy.org', 'admin'); } else { $message = "Unknown EXEC target '$exec'\n\n" . $message; email(); } } else { $message = "Unknown action '$action'\n\n" . $message; email(); } exit 0;