#!/opt/vdops/bin/perl # This script queries APC 79xx management cards for problems and creates a # report identifying salient issues. # V Who When What # --------------------------------------------------------------------------- # 1.2.1 skendric 2011-02-23 Reduce default logging # 1.2.0 skendric 2011-02-21 Upgrade to Netops 1.4.0 # 1.1.3 skendric 2010-12-30 Add @insane to report # 1.1.2 skendric 2010-12-17 Futz with owner/owner_backup # 1.1.1 skendric 2010-04-16 Handle models with a single power supply # 1.1.0 skendric 2009-12-23 Remove Net::SNMP support # 1.0.3 skendric 2009-05-17 Support SNMP.pm # 1.0.2 skendric 2009-04-19 Distinguish between silent and unresponsive # 1.0.1 skendric 2009-03-20 Add @down_for_maintenance # 1.0.0 skendric 2008-09-08 First Version # # Author: Stuart Kendrick, sbk {put at sign here} skendric {put dot here} com # # Source: http://www.skendric.com/device # # This software is available under the GNU GENERAL PUBLIC LICENSE, see # http://www.fsf.org/licenses/gpl.html # # # This script takes the following approach: # -Parses the hosts table for a list of targets (or accepts a command- # line list) # -Queries a bunch of APC-specific variables # -Produces a report # # # Requirements: # -The target(s) must be pingable # # -The following MIB modules stashed in /opt/vdops/share/snmp/mibs, # or wherever it is that you store MIB modules: # PowerNet-MIB # # -PERL modules: the WI::Netops collection # # # Assumptions: # # # Tested on: # -APC 7202/7911/7930/7941 cards # -perl-5.12.2 # -net-snmp-5.6 # # # Instructions: # -Customize the script for your site: find the 'user-configurable # variables' section and modify as appropriate # -Type "apc-pdu-alarm" to see the command-line options # -Try it out # # # # Caveats: # # # Known Bugs: # # # To do: # # Begin script # Load modules use strict; use warnings; use feature 'say'; use feature 'switch'; use Carp qw(carp cluck croak confess); use Data::Dumper; use English qw( -no_match_vars ); use Getopt::Std; use List::MoreUtils qw(any uniq); use WI::Netops::APCTools 1.2.2; use WI::Netops::HostTools 1.0.4; use WI::Netops::NetopsTools 2.2.3; use WI::Netops::NetopsData 1.4.0; use WI::Netops::PingTools 1.1.7; use WI::Netops::SNMPTools 1.5.3; use WI::Netops::Utilities 1.4.4; # Declare global variables my %load_status; # rPDULoadStatusLoadState my %load_alarm; # rPDULoadPhaseConfigAlarm.phase1 my %load_bank_alarm; # rPDULoadBankConfigAlarm my %phase_status; # rPDUStatusPhaseState my %ps_alarm; # rPDUPowerSupplyAlarm my %ps1_status; # rPDUPowerSupply1Status my %ps2_status; # rPDUPowerSupply2Status my %volts; # rPDUIdentDeviceLinetoLineVoltage my %watts; # rPDUIdentDevicePowerWatts # Define global variables $program_name = 'apc-pdu-alarm'; $usage = 'Usage: apc-pdu-alarm -s {yes|no} [-d {integer}] [-m big|little] [-r] [-a | -e {expr} | -f {filename} | target1 target2 target3 ...]'; $version = '1.2.1'; # Grab arguments getopts('ad:e:f:m:rs:', \%option); @target = @ARGV; # Set mode if ($option{r}) { $mode = 'report' } elsif (-t STDIN) { $mode = 'interactive' } else { $mode = 'batch' } ### Begin Main Program ############################################### { check_args(); # Check arguments read_config(); # Read Netops config file compile_mibs(); # Compile MIB files build_target(); # Populate @target target_check(); # Look for errors in @target basic_info(); # Gather information sanity_check(); # Check for major errors do_the_work(); # Do the work identify_alarms(); # Count devices with alarms write_alarm_log(); # Record issues print_report(); # Print report notify_staff(); # Mail report } ##### End Main Program ############################################### ######################################################################## # Do the work: acquire image name ######################################################################## sub do_the_work { # Debug trace trace_location('begin') if $debug; # Notify operator print_it('Querying targets...'); unless ($dome) { sleep $short; return 1; } # Loop through targets for my $target (@target) { my ($alarm, $val); # Acquire rPDUIdentDeviceLinetoLineVoltage $val = snmpGet({host=>$target, oid=>'rPDUIdentDeviceLinetoLineVoltage'}); $val //= $DASH; #log_it("rPDUIdentDeviceLinetoLineVoltage on $target = $val"); $volts{$target} = $val; # Acquire rPDUIdentDevicePowerWatts $val = snmpGet({host => $target, oid => 'rPDUIdentDevicePowerWatts'} ); $val //= $DASH; #log_it("rPDUIdentDevicePowerWatts on $target = $val"); $watts{$target} = $val; # Acquire rPDUPowerSupply1Status $val = snmpGet({host => $target, oid => 'rPDUPowerSupply1Status'} ); $val //= $DASH; #log_it("rPDUPowerSupply1Status on $target = $val"); given ($val) { when (/ok/i) { $val = 'ok' } when (/fail/i) { $val = 'fail' } when (/not/i) { $val = 'np' } } if ($val eq 'fail') { log_it("rPDUPowerSupply1Statuson $target = $val"); $alarm_count{$target}++; push @{$alarms{$target}}, 'ps status'; } $ps1_status{$target} = $val; # Acquire rPDUPowerSupply2Status $val = snmpGet({host => $target, oid => 'rPDUPowerSupply2Status'} ); $val //= $DASH; #log_it("rPDUPowerSupply2Status on $target = $val"); given ($val) { when (/ok/i) { $val = 'ok' } when (/fail/i) { $val = 'fail' } when (/not/i) { $val = 'np' } } if ($val eq 'fail') { log_it("rPDUPowerSupply2Statuson $target = $val"); $alarm_count{$target}++; push @{$alarms{$target}}, 'ps status'; } $ps2_status{$target} = $val; # Acquire rPDUPowerSupplyAlarm $val = snmpGet({host => $target, oid => 'rPDUPowerSupplyAlarm'} ); $val //= $DASH; given ($val) { when (/ok/i) { $val = 'ok' } when (/fail/i) { $val = 'fail' } } if ($val eq 'fail') { log_it("rPDUPowerSupplyAlarm on $target = $val"); $alarm_count{$target}++; push @{$alarms{$target}}, 'ps alarm'; } $ps_alarm{$target} = $val; # Acquire rPDULoadPhaseConfigAlarm.phase1 say 'Getting rPDULoadPhaseConfigAlarm.phase1' if $debug > 2; $val = snmpGet({host => $target, oid => 'rPDULoadPhaseConfigAlarm.phase1'}); $val //= $DASH; #log_it("rPDULoadPhaseConfigAlarm.phase1 on $target = $val"); $val = 'ok' if $val eq 'noLoadAlarm'; unless ($val eq 'ok' or $val eq $DASH) { log_it("rPDULoadPhaseConfigAlarm.phase1 on $target = $val"); $alarm_count{$target}++; push @{$alarms{$target}}, 'phase alarm'; } $load_alarm{$target} = $val; # Acquire rPDULoadBankConfigAlarm $val = acquire_apc_pdu_load_bank_alarm($target); $val = 'ok' if $val eq 'noLoadAlarm'; unless ($val eq 'ok' or $val eq $DASH) { log_it("rPDULoadBankConfigAlarm on $target = $val"); $alarm_count{$target}++; push @{$alarms{$target}}, 'bank alarm'; } $load_bank_alarm{$target} = $val; # Acquire rPDULoadStatusLoadState $alarm = acquire_apc_pdu_load_state($target); $val = 'ok' if $val eq 'phaseLoadNormal'; unless ($val eq 'ok' or $val eq $DASH) { log_it("rPDULoadStatusLoadState on $target = $val"); $alarm_count{$target}++; push @{$alarms{$target}}, 'load state'; } $load_status{$target} = $val; # Acquire rPDUStatusPhaseState $val = acquire_apc_pdu_phase_state($target); $val = 'ok' if $val eq 'phaseLoadNormal'; unless ($val eq 'ok' or $val eq $DASH) { log_it("rPDUStatusPhaseState on $target = $val"); $alarm_count{$target}++; push @{$alarms{$target}}, 'phase state'; } $phase_status{$target} = $val; # Entertain operator print $BANG if $mode eq 'interactive'; } # Debug info if ($debug > 2) { for my $target (@target) { if (defined $alarm_count{$target} and $alarm_count{$target} > 0) { say "alarm_count{$target} = $alarm_count{$target}"; } } } # Make things look pretty say "\n" if $mode eq 'interactive'; # Debug trace trace_location('end') if $debug; return 1; } ######################################################################## # Tell the operator what I discovered ######################################################################## sub print_report { my $handle; my $now = get_now(); my $operating_range; my $t; my $total = @target; # If we are running in test mode, skip this routine unless ($dome) { print_it("Running in test mode, cannot print a meaningful report\n"); return 1; } # Debug trace trace_location('begin') if $debug; # Direct output to screen or to file if ($mode eq 'interactive') { $handle = *STDOUT; } else { open $handle, '>', $report_file or die "Cannot open $report_file: $!\n"; } print {$handle} <