From 7516047c1511d3f7a3caf6f5d46d9656726ec7d0 Mon Sep 17 00:00:00 2001 From: Digimer Date: Mon, 2 Nov 2020 11:43:35 -0500 Subject: [PATCH] * Created Convert->celsius_to_fahrenheit, ->fahrenheit_to_celsius and ->format_mmddyy_to_yymmdd. * Created (but not yet finished) scan-apc-ups. Signed-off-by: Digimer --- Anvil/Tools/Convert.pm | 172 + scancore-agents/scan-apc-pdu/scan-apc-pdu | 7 +- scancore-agents/scan-apc-pdu/scan-apc-pdu.xml | 7 +- scancore-agents/scan-apc-ups/scan-apc-ups | 4467 +++++++++++++++++ scancore-agents/scan-apc-ups/scan-apc-ups.sql | 317 ++ scancore-agents/scan-apc-ups/scan-apc-ups.xml | 158 + share/words.xml | 2 + 7 files changed, 5119 insertions(+), 11 deletions(-) create mode 100755 scancore-agents/scan-apc-ups/scan-apc-ups create mode 100644 scancore-agents/scan-apc-ups/scan-apc-ups.sql create mode 100644 scancore-agents/scan-apc-ups/scan-apc-ups.xml diff --git a/Anvil/Tools/Convert.pm b/Anvil/Tools/Convert.pm index 950e71bd..7fdacda5 100644 --- a/Anvil/Tools/Convert.pm +++ b/Anvil/Tools/Convert.pm @@ -14,7 +14,10 @@ my $THIS_FILE = "Convert.pm"; ### Methods; # add_commas # bytes_to_human_readable +# celsius_to_fahrenheit # cidr +# fahrenheit_to_celsius +# format_mmddyy_to_yymmdd # host_name_to_ip # human_readable_to_bytes # round @@ -80,6 +83,7 @@ sub parent # Public methods # ############################################################################################################# + =head2 add_commas This takes an integer and inserts commas to make it more readable by people. @@ -138,6 +142,7 @@ sub add_commas return ($number); } + =head2 bytes_to_human_readable This takes a number of bytes and converts it to a a human-readable format. Optionally, you can request the human readable size be returned using specific units. @@ -490,6 +495,61 @@ sub bytes_to_human_readable return($human_readable_size); } + +=head3 celsius_to_fahrenheit + +This takes value and converts it from celsius to fahrenheit. If there is a problem, C<< !!error!! >> is returned. + +Parameters; + +=head3 temperature (required) + +This is the temperature to convert from fahrenheit to celsius. + +=cut +sub celsius_to_fahrenheit +{ + my $self = shift; + my $parameter = shift; + my $anvil = $self->parent; + my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Convert->celsius_to_fahrenheit()" }}); + + # Now see if the user passed the values in a hash reference or directly. + my $temperature = defined $parameter->{temperature} ? $parameter->{temperature} : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { temperature => $temperature }}); + + if (not $temperature) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Convert->celsius_to_fahrenheit()", parameter => "temperature" }}); + return("!!error!!"); + } + if ($temperature !~ /^\d/) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0165", variables => { $temperature => temperature }}); + return("!!error!!"); + } + + # Split off the value from the suffix, if any. + if ($temperature =~ /^(\d+\.\d+).*/) + { + $temperature = $1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { temperature => $temperature }}); + } + elsif ($temperature =~ /^(\d+)(.*)/) + { + $temperature = $1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { temperature => $temperature }}); + } + + # Convert the temperature. + my $new_temperature = (($temperature * 1.8) + 32); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { new_temperature => $new_temperature }}); + + return($new_temperature); +} + + =head2 cidr This takes an IPv4 CIDR notation and returns the dotted-decimal subnet mask, or the reverse. @@ -611,6 +671,115 @@ sub cidr return($output); } + +=head3 fahrenheit_to_celsius + +This takes value and converts it from fahrenheit to celsius. If there is a problem, C<< !!error!! >> is returned. + +Parameters; + +=head3 temperature (required) + +This is the temperature to convert from celsius to fahrenheit. + +=cut +sub fahrenheit_to_celsius +{ + my $self = shift; + my $parameter = shift; + my $anvil = $self->parent; + my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Convert->fahrenheit_to_celsius()" }}); + + # Now see if the user passed the values in a hash reference or directly. + my $temperature = defined $parameter->{temperature} ? $parameter->{temperature} : ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { temperature => $temperature }}); + + if (not $temperature) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Convert->fahrenheit_to_celsius()", parameter => "temperature" }}); + return("!!error!!"); + } + if ($temperature !~ /^\d/) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0165", variables => { $temperature => temperature }}); + return("!!error!!"); + } + + # Split off the value from the suffix, if any. + if ($temperature =~ /^(\d+\.\d+).*/) + { + $temperature = $1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { temperature => $temperature }}); + } + elsif ($temperature =~ /^(\d+)(.*)/) + { + $temperature = $1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { temperature => $temperature }}); + } + + # Convert the temperature. + my $new_temperature = (($temperature - 32) / 1.8); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { new_temperature => $new_temperature }}); + + return($new_temperature); +} + + +=head2 format_mmddyy_to_yymmdd + +This converts a C<< mm/dd/yy >> or C<< mm/dd/yyyy >> string into the more sensible yy/mm/dd or yyyy/mm/dd string. + +Returns C<< !!error!! >> if something goes wrong. + +Parameters; + +=head3 date (required) + +This is the C<< mm/dd/yy >> or C<< mm/dd/yyyy >> format to be converted. + +=cut +sub format_mmddyy_to_yymmdd +{ + my $self = shift; + my $parameter = shift; + my $anvil = $self->parent; + my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Convert->format_mmddyy_to_yymmdd()" }}); + + my $date = defined $parameter->{date} ? $parameter->{date} : ""; + my $output = ""; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { + date => $date, + }}); + + if (not $date) + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Convert->host_name_to_ip()", parameter => "host_name" }}); + return("!!error!!"); + } + + # Split off the value from the suffix, if any. + if ($date =~ /^(\d\d)\/(\d\d)\/(\d\d\d\d)/) + { + $date = "$3/$1/$2"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { date => $date }}); + } + elsif ($date =~ /^(\d\d)\/(\d\d)\/(\d\d)/) + { + $date = "$3/$1/$2"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { date => $date }}); + } + else + { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "error_0164", variables => { date => $date }}); + return("!!error!!"); + } + + return($date); +} + + =head2 host_name_to_ip This method takes a host name and tries to convert it to an IP address. If it fails, it will return C<< 0 >>. @@ -664,6 +833,7 @@ sub host_name_to_ip return($ip); } + =head2 human_readable_to_bytes This takes a "human readable" size with an ISO suffix and converts it back to a base byte size as accurately as possible. @@ -866,6 +1036,7 @@ sub human_readable_to_bytes return ($bytes); } + =head2 round This takes a number and rounds it to a given number of places after the decimal (defaulting to an even integer). This does financial-type rounding. @@ -988,6 +1159,7 @@ sub round return ($rounded_number); } + =head2 time This takes a number of seconds and converts it into a human readable string. Returns C<< #!error!# >> is an error is encountered. diff --git a/scancore-agents/scan-apc-pdu/scan-apc-pdu b/scancore-agents/scan-apc-pdu/scan-apc-pdu index 1cc625e9..7a3a0578 100755 --- a/scancore-agents/scan-apc-pdu/scan-apc-pdu +++ b/scancore-agents/scan-apc-pdu/scan-apc-pdu @@ -477,7 +477,7 @@ FROM } - return($count); + return(0); } # This reads in the last scan data from one of the databases and compares it against the just-read data. If @@ -486,11 +486,6 @@ sub find_changes { my ($anvil) = @_; - # If we can't ping or can't connect to a PDU, these keys will be used to see how long that's been the - # case to decide when to send an alert. - #my $ping_condition_key = "scan-apc-pdu::".$fence_uuid."::no_ping"; - #my $connection_condition_key = "scan-apc-pdu::".$fence_uuid."::no_connection"; - # This stores all the queries so that they're committed in one transaction. $anvil->data->{sys}{queries} = []; diff --git a/scancore-agents/scan-apc-pdu/scan-apc-pdu.xml b/scancore-agents/scan-apc-pdu/scan-apc-pdu.xml index fe4e255d..e22d8bb2 100644 --- a/scancore-agents/scan-apc-pdu/scan-apc-pdu.xml +++ b/scancore-agents/scan-apc-pdu/scan-apc-pdu.xml @@ -9,12 +9,9 @@ NOTE: All string keys MUST be prefixed with the agent name! ie: 'scan_apc_pdu_lo --> - + - - - - + Failed to read the number of phases for the PDU: [#!variable!name!#] at IP: [#!variable!ip!#] (sn: #!variable!serial_number!#). diff --git a/scancore-agents/scan-apc-ups/scan-apc-ups b/scancore-agents/scan-apc-ups/scan-apc-ups new file mode 100755 index 00000000..5c93c0c2 --- /dev/null +++ b/scancore-agents/scan-apc-ups/scan-apc-ups @@ -0,0 +1,4467 @@ +#!/usr/bin/perl +# +# This uses data in the 'upses' database table (any that use this agent' to get a list of APC-brand UPSes to scan. +# +# Examples; +# +# Exit codes; +# 0 - Success +# 1 - Something went wrong, agent aborted run. +# +# 255 - The host's UUID isn't in the hosts table yet, ScanCore itself hasn't been run. +# +# TODO: Support UPSes with extended runtime batteries +# + +# Use my modules. +use strict; +use warnings; +use Anvil::Tools; +use Data::Dumper; +use Socket; +no warnings 'recursion'; + +# Disable buffering +$| = 1; + +# Prevent a discrepency between UID/GID and EUID/EGID from throwing an error. +$< = $>; +$( = $); + +my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0]; +my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0]; +if (($running_directory =~ /^\./) && ($ENV{PWD})) +{ + $running_directory =~ s/^\./$ENV{PWD}/; +} + +my $anvil = Anvil::Tools->new({log_level => 2, log_secure => 1}); +$anvil->Log->level({set => 2}); +$anvil->Log->secure({set => 1}); + +# Make sure we're running as 'root' +# $< == real UID, $> == effective UID +if (($< != 0) && ($> != 0)) +{ + # Not root + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, key => "error_0005"}); + $anvil->nice_exit({exit_code => 1}); +} + +# Get the handle to the AN::Tools and preset some variables I will use. +$anvil->data->{'scan-apc-ups'} = { + disable => 0, + language => "en_CA", + log_file => "/var/log/ScanCore.log", + log_level => 1, + log_language => "en_CA", + log_db_transactions => 0, + # CSV of UPSes to scan. If set, /etc/hosts will not be processed. If not set, hosts + # will be searched for any entry with 'ups' in its name and added to this. + upses => "", + # Once parsed, the UPSes will be scanned using this hash where the key is the UPS + # name and the value is the IP address. + ups => {}, + # By default, a temperature sensor will go into warning state this number of °C below + # critical temperature. + temperature_warning_delta => 3, + # It will be marked as 'clear' when the temperature drops this many °C below the + # critical temperature. + temperature_clear_delta => 5, + temperature_shutdown_delta => 5, + # If the battery charge drops below this level, a warning level alert will be + # triggered. + low_charge_percentage_warning => 20, + # If the battery charge rate climbs above this level, the user will be informed that + # the UPS is back to an acceptible charge + low_charge_percentage_ok => 25, + # This is the number of volts above the low voltage trasfer lever or below the high + # voltage transfer level needed to clear the alert. + transfer_voltage_clear_delta => 2, + # This is the number of scans in a row that an alert state needs to exist for before + # we trigger an alarm. + sensor_loss_count_to_alarm => 3, +}; +$anvil->data->{oids} = { + battery => { + # Estimated next replacement date (in 'mm/dd/yy' or 'mm/dd/yyyy' format.) + replacement_date => ".1.3.6.1.4.1.318.1.1.1.2.2.21.0", + # 1 => OK, 2 => Replacement needed + health => ".1.3.6.1.4.1.318.1.1.1.2.2.4.0", + model => ".1.3.6.1.4.1.318.1.1.1.2.2.19.0", + # High precision, (1000 = 100.0% charge) + percentage_charge_hp => ".1.3.6.1.4.1.318.1.1.1.2.3.1.0", + # Last approximate replacement date (mm/dd/yy or yyyy format) + last_replacement_date => ".1.3.6.1.4.1.318.1.1.1.2.1.3.0", + # 1 => unknown, 2 => batteryNormal, 3 => batteryLow, + # 4 => batteryInFaultCondition + 'state' => ".1.3.6.1.4.1.318.1.1.1.2.1.1.0", + # High-precision, (315 == 31.5) + temperature_hp => ".1.3.6.1.4.1.318.1.1.1.2.3.2.0", + # Temperature alarm upper limit (in even degrees, 40 = 40*C or *F + # (see ups::temperature_units)) + alarm_temperature => ".1.3.6.1.4.1.318.1.1.10.1.2.2.1.3.1", + # High precision voltage (271 = 27.1 vDC) + voltage_hp => ".1.3.6.1.4.1.318.1.1.1.2.3.4.0", + }, + input => { + # High precision, (600 == 60.0 Hz) + frequency_hp => ".1.3.6.1.4.1.318.1.1.1.3.3.4.0", + # 1 => Auto, 2 => Low, 3 => Medium, 4 => High + sensitivity => ".1.3.6.1.4.1.318.1.1.1.5.2.7.0", + # High precision, (1245 => "124.5 vAC) + voltage_hp => ".1.3.6.1.4.1.318.1.1.1.3.3.1.0", + # High precision; Maximum and minimum voltage in the last 60 seconds (1245 == 124.5 vAC) + '1m_maximum_input_voltage_hp' => ".1.3.6.1.4.1.318.1.1.1.3.3.2.0", + '1m_minimum_input_voltage_hp' => ".1.3.6.1.4.1.318.1.1.1.3.3.3.0", + }, + ups => { + # Time in tickss after AC restore before UPS powers on (ie: 1000 = 10 seconds) + ac_restore_delay => ".1.3.6.1.4.1.318.1.1.1.5.2.9.0", + # Delay time from when the shutdown command is sent until when the UPS + # actually powers off (measured in ticks) + shutdown_delay => ".1.3.6.1.4.1.318.1.1.1.5.2.10.0", + firmware_version => ".1.3.6.1.4.1.318.1.1.1.1.2.1.0", + # The modes are documented as 'scan_apc_ups_health_00{01..20}' with + # the integer value correlating to the returned health integer value. + health => ".1.3.6.1.4.1.318.1.1.1.4.1.1.0", + # Voltage at which TRIM ONLINE kicks in (127 == 127 vAC) + high_transfer_voltage => ".1.3.6.1.4.1.318.1.1.1.5.2.2.0", + # 1 => noTransfer, 2 => highLineVoltage, 3 => brownout, + # 4 => blackout, 5 => smallMomentarySag, 6 => deepMomentarySag, + # 7 => smallMomentarySpike, 8 => largeMomentarySpike, + # 9 => selfTest, 10 => rateOfVoltageChange + last_transfer_reason => ".1.3.6.1.4.1.318.1.1.1.3.2.5.0", + # Voltage at which BOOST ONLINE kicks in (106 == 106vAC) + low_transfer_voltage => ".1.3.6.1.4.1.318.1.1.1.5.2.3.0", + # Manufactured date (in 'mm/dd/yy' or 'mm/dd/yyyy' format.) + manufactured_date => ".1.3.6.1.4.1.318.1.1.1.1.2.2.0", + model => ".1.3.6.1.4.1.318.1.1.1.1.2.5.0", + # Temperature units (1 = *C, 2 = *F) + temperature_units => ".1.3.6.1.4.1.318.1.1.10.1.2.2.1.5.1", + serial_number => ".1.3.6.1.4.1.318.1.1.1.1.2.3.0", + # This is used to power off the UPS. Set: + # 2 = turnUpsOff (no delay), + # 3 = turnUpsOffGracefully (use delay) + power_off => ".1.3.6.1.4.1.318.1.1.1.6.2.1.0", + }, + nmc => { + firmware_version => ".1.3.6.1.4.1.318.1.4.2.4.1.4.1", + serial_number => ".1.3.6.1.4.1.318.1.4.2.4.1.2.1", + mac_address => ".1.3.6.1.2.1.4.30.1.4.2", + }, + output => { + # High precision, Current load percentage (58 = 5.8%) + load_percentage_hp => ".1.3.6.1.4.1.318.1.1.1.4.3.3.0", + # Time in ticks on batteries, 0 == not on batteries + time_on_batteries => ".1.3.6.1.4.1.318.1.1.1.2.1.2.0", + # In ticks (10ms) + estimated_runtime => ".1.3.6.1.4.1.318.1.1.1.2.2.3.0", + # high-precision, (600 == 60.0 Hz) + frequency_hp => ".1.3.6.1.4.1.318.1.1.1.4.3.2.0", + # High precision (1245 = 124.5 vAC) + voltage_hp => ".1.3.6.1.4.1.318.1.1.1.4.3.1.0", + # Total output power (in 10 watt-hour increments, divide by 100 + # for kW/hr + total_output => ".1.3.6.1.4.1.318.1.1.1.4.3.6.0", + }, +}; +$anvil->data->{snmp} = { + alternate => { + 'SUART' => { + }, + }, + community => { + 'read' => "public", + version => "2c", + 'write' => "private", + }, +}; + +$anvil->Storage->read_config(); +$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0115", variables => { program => $THIS_FILE }}); + +# Read switches +$anvil->Get->switches; + +# If we're disabled and '--force' wasn't used, exit. +if (($anvil->data->{scancore}{'scan-apc-ups'}{disable}) && (not $anvil->data->{switches}{force})) +{ + # Exit. + $anvil->nice_exit({exit_code => 0}); +} + +# These are the tables used by this agent. The order matters as it controls to order the tables are created +# and sync'ed. For purges, this array is walked backwards. +$anvil->data->{scancore}{'scan-apc-ups'}{tables} = ["scan_apc_upses", "scan_apc_ups_battery", "scan_apc_ups_input", "scan_apc_ups_output"]; + +# Handle start-up tasks +my $problem = $anvil->ScanCore->agent_startup({ + debug => 3, + agent => $THIS_FILE, + tables => $anvil->data->{scancore}{'scan-apc-ups'}{tables}, +}); +if ($problem) +{ + $anvil->nice_exit({exit_code => 1}); +} + +if ($anvil->data->{switches}{purge}) +{ + # This can be called when doing bulk-database purges. + $anvil->Database->purge_data({ + debug => 2, + tables => $anvil->data->{scancore}{'scan-apc-ups'}{tables}, + }); + $anvil->nice_exit({exit_code => 0}); +} + +# Find the UPSes. The number of UPSes found is returned. If 0, we exit +if (not find_upss($anvil)) +{ + # No UPSes found. + $anvil->Log->entry({log_level => 1, message_key => "scan_apc_ups_message_0001", file => $THIS_FILE, line => __LINE__}); + $anvil->nice_exit({exit_code => 1}); +} + +# Read the last state of any UPSes we already know about +read_last_scan($anvil); + +# Gather details on UPSes. +gather_ups_data($anvil); + +# Look for changes. +find_changes($anvil); + +# Process temperatures! +process_temperatures($anvil); + +# Update the database +$anvil->Database->insert_or_update_updated({updated_by => $THIS_FILE}); + +# Clean up and go away. +$anvil->nice_exit({exit_code => 0}); + + +############################################################################################################# +# Functions # +############################################################################################################# + +# This reads in the various temperature sensors we read from this run and will set the temperature table +# and/or set/clear warnings/critical states. +sub process_temperatures +{ + my ($anvil) = @_; + + ### NOTE: We use 'sensor_host' to hold the serial number of the device hosting the sensor. + # First, read in all existing entries. We'll compare and UPDATE or INSERT as needed and DELETE any + # stale entries. + my $query = " +SELECT + temperature_uuid, + temperature_sensor_name, + temperature_sensor_host, + temperature_celsius, + temperature_state, + temperature_is +FROM + temperature +WHERE + temperature_host_uuid = ".$anvil->Database->quote($anvil->data->{sys}{host_uuid})." +AND + temperature_agent_name = ".$anvil->Database->quote($THIS_FILE)." +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + + # One or more records were found. + foreach my $row (@{$results}) + { + my $temperature_uuid = $row->[0]; + my $temperature_sensor_name = $row->[1]; + my $temperature_sensor_host = $row->[2]; + my $temperature_celsius = $row->[3]; + my $temperature_state = $row->[4]; + my $temperature_is = $row->[5]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "temperature_uuid" => $temperature_uuid, + "temperature_sensor_name" => $temperature_sensor_name, + "temperature_sensor_host" => $temperature_sensor_host, + "temperature_celsius" => $temperature_celsius, + "temperature_state" => $temperature_state, + "temperature_is" => $temperature_is, + }}); + + $anvil->data->{old}{temperature}{$temperature_sensor_name}{$temperature_sensor_host} = { + temperature_uuid => $temperature_uuid, + temperature_celsius => $temperature_celsius, + temperature_state => $temperature_state, + temperature_is => $temperature_is, + }; + } + + # Loop through the temperature from this scan. + foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{new}{temperature}}) + { + foreach my $serial_number (sort {$a cmp $b} keys %{$anvil->data->{new}{temperature}{$variable}}) + { + my $new_temperature_celsius = $anvil->data->{new}{temperature}{$variable}{$serial_number}{temperature_celsius}; + my $new_temperature_state = $anvil->data->{new}{temperature}{$variable}{$serial_number}{temperature_state}; + my $new_temperature_is = $anvil->data->{new}{temperature}{$variable}{$serial_number}{temperature_is}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "variable" => $variable, + "serial_number" => $serial_number, + "new_temperature_celsius" => $new_temperature_celsius, + "new_temperature_state" => $new_temperature_state, + "new_temperature_is" => $new_temperature_is, + }}); + + # If the state is 'warning', set a health weight of 1 and set critical to 2. + if ($new_temperature_state eq "warning") + { + my $health_source_name = "temperature:".$serial_number; + $anvil->data->{health}{new}{$health_source_name} = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "health::new::$health_source_name" => $anvil->data->{health}{new}{$health_source_name}, + }}); + } + elsif ($new_temperature_state eq "critical") + { + my $health_source_name = "temperature:".$serial_number; + $anvil->data->{health}{new}{$health_source_name} = 2; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "health::new::$health_source_name" => $anvil->data->{health}{new}{$health_source_name}, + }}); + } + + # Now see if the variable was seen before and, if so, if it changed. + if (ref($anvil->data->{old}{temperature}{$variable}{$serial_number})) + { + # Update the existing entry, if needed. + my $temperature_uuid = $anvil->data->{old}{temperature}{$variable}{$serial_number}{temperature_uuid}; + my $old_temperature_celsius = $anvil->data->{old}{temperature}{$variable}{$serial_number}{temperature_celsius}; + my $old_temperature_state = $anvil->data->{old}{temperature}{$variable}{$serial_number}{temperature_state}; + my $old_temperature_is = $anvil->data->{old}{temperature}{$variable}{$serial_number}{temperature_is}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "temperature_uuid" => $temperature_uuid, + "old_temperature_celsius" => $old_temperature_celsius, + "old_temperature_state" => $old_temperature_state, + "old_temperature_is" => $old_temperature_is, + }}); + + if (($new_temperature_celsius ne $old_temperature_celsius) or + ($new_temperature_state ne $old_temperature_state) or + ($new_temperature_is ne $old_temperature_is)) + { + # Something changed, update. + my $query = " +UPDATE + temperature +SET + temperature_celsius = ".$anvil->Database->quote($new_temperature_celsius).", + temperature_state = ".$anvil->Database->quote($new_temperature_state).", + temperature_is = ".$anvil->Database->quote($new_temperature_is).", + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +WHERE + temperature_uuid = ".$anvil->Database->quote($temperature_uuid)."; +"; + push @{$anvil->data->{sys}{queries}}, $query; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + } + else + { + # No change. + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "variable" => $variable, + "serial_number" => $serial_number, + }}); + } + + # We still want this value, so delete it from the hash so it doesn't get + # deleted in the next step. + delete $anvil->data->{old}{temperature}{$variable}{$serial_number}; + } + else + { + # New entry + my $query = " +INSERT INTO + temperature +( + temperature_uuid, + temperature_host_uuid, + temperature_sensor_host, + temperature_sensor_name, + temperature_agent_name, + temperature_celsius, + temperature_state, + temperature_is, + modified_date +) VALUES ( + ".$anvil->Database->quote($anvil->Get->uuid).", + ".$anvil->Database->quote($anvil->data->{sys}{host_uuid}).", + ".$anvil->Database->quote($serial_number).", + ".$anvil->Database->quote($variable).", + ".$anvil->Database->quote($THIS_FILE).", + ".$anvil->Database->quote($new_temperature_celsius).", + ".$anvil->Database->quote($new_temperature_state).", + ".$anvil->Database->quote($new_temperature_is).", + ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +); +"; + push @{$anvil->data->{sys}{queries}}, $query; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + } + } + } + + # Now, if any undeleted old entries remain, delete them from the database. + foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{old}{temperature}}) + { + foreach my $serial_number (sort {$a cmp $b} keys %{$anvil->data->{old}{temperature}{$variable}}) + { + my $old_temperature_uuid = $anvil->data->{old}{temperature}{$variable}{$serial_number}{temperature_uuid}; + my $old_temperature = $anvil->data->{old}{temperature}{$variable}{$serial_number}{temperature_celsius}; + my $old_state = $anvil->data->{old}{temperature}{$variable}{$serial_number}{temperature_state}; + my $old_is = $anvil->data->{old}{temperature}{$variable}{$serial_number}{temperature_is}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "variable" => $variable, + "serial_number" => $serial_number, + "old_temperature_uuid" => $old_temperature_uuid, + "old_temperature" => $old_temperature, + "old_state" => $old_state, + "old_is" => $old_is, + }}); + + # Mark the sensor as DELETEd. + my $query = " +UPDATE + temperature +SET + temperature_state = 'DELETED', + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +WHERE + temperature_uuid = ".$anvil->Database->quote($old_temperature_uuid)."; +"; + push @{$anvil->data->{sys}{queries}}, $query; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + + $query = " +DELETE FROM + temperature +WHERE + temperature_uuid = ".$anvil->Database->quote($old_temperature_uuid)."; +"; + push @{$anvil->data->{sys}{queries}}, $query; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + } + } + + # Commit the queries. + $anvil->DB->commit_sql({source => $THIS_FILE, line => __LINE__}); + + return(0); +} + +# This reads in the last scan data from one of the databases and compares it against the just-read data. If +# anything changed, register an alert. +sub find_changes +{ + my ($anvil) = @_; + + # This stores all the queries so that they're committed in one transaction. + $anvil->data->{sys}{queries} = []; + $anvil->data->{'scan-apc-pdu'}{alert_sort} = 1; + + # Loop through each UPS we've seen this pass + foreach my $scan_apc_ups_uuid (sort {$a cmp $b} keys %{$anvil->data->{ups}{scan_apc_ups_uuid}}) + { + my $new_ups = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{new_ups}; + my $ups_uuid = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_upses}{scan_apc_ups_ups_uuid}; + my $new_scan_apc_ups_serial_number = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_serial_number}; + my $new_scan_apc_ups_name = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_name}; + my $new_scan_apc_ups_ip = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_ip}; + my $new_scan_apc_ups_ac_restore_delay = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_ac_restore_delay}; + my $new_scan_apc_ups_shutdown_delay = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_shutdown_delay}; + my $new_scan_apc_ups_firmware_version = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_firmware_version}; + my $new_scan_apc_ups_health = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_health}; + my $new_scan_apc_ups_high_transfer_voltage = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_high_transfer_voltage}; + my $clear_high_transfer_voltage = $new_scan_apc_ups_high_transfer_voltage - 4; + my $new_scan_apc_ups_low_transfer_voltage = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_low_transfer_voltage}; + my $new_scan_apc_ups_last_transfer_reason = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_last_transfer_reason}; + my $new_scan_apc_ups_manufactured_date = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_manufactured_date}; + my $new_scan_apc_ups_model = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_model}; + my $new_scan_apc_ups_temperature_units = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_temperature_units}; + my $new_scan_apc_ups_nmc_firmware_version = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_firmware_version}; + my $new_scan_apc_ups_nmc_serial_number = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_serial_number}; + my $new_scan_apc_ups_nmc_mac_address = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_mac_address}; + my $new_scan_apc_ups_input_1m_minimum_input_voltage = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_1m_minimum_input_voltage}; + my $new_scan_apc_ups_input_1m_maximum_input_voltage = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_1m_maximum_input_voltage}; + my $new_scan_apc_ups_input_sensitivity = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_sensitivity}; + my $new_scan_apc_ups_input_frequency = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_frequency}; + my $new_scan_apc_ups_output_voltage = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_voltage}; + my $new_scan_apc_ups_output_total_output = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_total_output}; + my $new_scan_apc_ups_output_frequency = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_frequency}; + my $new_scan_apc_ups_output_time_on_batteries = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_time_on_batteries}; + my $new_scan_apc_ups_output_load_percentage = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_load_percentage}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + new_ups => $new_ups, + ups_uuid => $ups_uuid, + new_scan_apc_ups_serial_number => $new_scan_apc_ups_serial_number, + new_scan_apc_ups_ip => $new_scan_apc_ups_ip, + new_scan_apc_ups_ac_restore_delay => $new_scan_apc_ups_ac_restore_delay, + new_scan_apc_ups_shutdown_delay => $new_scan_apc_ups_shutdown_delay, + new_scan_apc_ups_firmware_version => $new_scan_apc_ups_firmware_version, + new_scan_apc_ups_health => $new_scan_apc_ups_health, + new_scan_apc_ups_high_transfer_voltage => $new_scan_apc_ups_high_transfer_voltage, + new_scan_apc_ups_low_transfer_voltage => $new_scan_apc_ups_low_transfer_voltage, + new_scan_apc_ups_last_transfer_reason => $new_scan_apc_ups_last_transfer_reason, + new_scan_apc_ups_manufactured_date => $new_scan_apc_ups_manufactured_date, + new_scan_apc_ups_model => $new_scan_apc_ups_model, + new_scan_apc_ups_temperature_units => $new_scan_apc_ups_temperature_units, + new_scan_apc_ups_nmc_firmware_version => $new_scan_apc_ups_nmc_firmware_version, + new_scan_apc_ups_nmc_serial_number => $new_scan_apc_ups_nmc_serial_number, + new_scan_apc_ups_nmc_mac_address => $new_scan_apc_ups_nmc_mac_address, + new_scan_apc_ups_battery_health => $new_scan_apc_ups_battery_health, + new_scan_apc_ups_battery_percentage_charge => $new_scan_apc_ups_battery_percentage_charge, + new_scan_apc_ups_battery_last_replacement_date => $new_scan_apc_ups_battery_last_replacement_date, + new_scan_apc_ups_battery_state => $new_scan_apc_ups_battery_state, + new_scan_apc_ups_battery_temperature => $new_scan_apc_ups_battery_temperature, + new_scan_apc_ups_battery_alarm_temperature => $new_scan_apc_ups_battery_alarm_temperature, + new_scan_apc_ups_battery_voltage => $new_scan_apc_ups_battery_voltage, + new_scan_apc_ups_input_1m_minimum_input_voltage => $new_scan_apc_ups_input_1m_minimum_input_voltage, + new_scan_apc_ups_input_1m_maximum_input_voltage => $new_scan_apc_ups_input_1m_maximum_input_voltage, + new_scan_apc_ups_input_sensitivity => $new_scan_apc_ups_input_sensitivity, + new_scan_apc_ups_input_frequency => $new_scan_apc_ups_input_frequency, + new_scan_apc_ups_output_voltage => $new_scan_apc_ups_output_voltage, + new_scan_apc_ups_output_total_output => $new_scan_apc_ups_output_total_output, + new_scan_apc_ups_output_time_on_batteries => $new_scan_apc_ups_output_time_on_batteries, + new_scan_apc_ups_output_load_percentage => $new_scan_apc_ups_output_load_percentage, + }}); + + # Have I seen this UPS before? + if ($new_ups) + { + # New, INSERT it. + my $query = " +INSERT INTO + scan_apc_ups +( + scan_apc_ups_uuid, + scan_apc_ups_name, + scan_apc_ups_ip, + scan_apc_ups_ac_restore_delay, + scan_apc_ups_shutdown_delay, + scan_apc_ups_firmware_version, + scan_apc_ups_health, + scan_apc_ups_high_transfer_voltage, + scan_apc_ups_low_transfer_voltage, + scan_apc_ups_last_transfer_reason, + scan_apc_ups_manufactured_date, + scan_apc_ups_model, + scan_apc_ups_temperature_units, + scan_apc_ups_serial_number, + scan_apc_ups_nmc_firmware_version, + scan_apc_ups_nmc_serial_number, + scan_apc_ups_nmc_mac_address, + modified_date +) VALUES ( + ".$anvil->Database->quote($scan_apc_ups_uuid).", + ".$anvil->Database->quote($new_scan_apc_ups_name).", + ".$anvil->Database->quote($new_scan_apc_ups_ip).", + ".$anvil->Database->quote($new_scan_apc_ups_ac_restore_delay).", + ".$anvil->Database->quote($new_scan_apc_ups_shutdown_delay).", + ".$anvil->Database->quote($new_scan_apc_ups_firmware_version).", + ".$anvil->Database->quote($new_scan_apc_ups_health).", + ".$anvil->Database->quote($new_scan_apc_ups_high_transfer_voltage).", + ".$anvil->Database->quote($new_scan_apc_ups_low_transfer_voltage).", + ".$anvil->Database->quote($new_scan_apc_ups_last_transfer_reason).", + ".$anvil->Database->quote($new_scan_apc_ups_manufactured_date).", + ".$anvil->Database->quote($new_scan_apc_ups_model).", + ".$anvil->Database->quote($new_scan_apc_ups_temperature_units).", + ".$anvil->Database->quote($new_scan_apc_ups_serial_number).", + ".$anvil->Database->quote($new_scan_apc_ups_nmc_firmware_version).", + ".$anvil->Database->quote($new_scan_apc_ups_nmc_serial_number).", + ".$anvil->Database->quote($new_scan_apc_ups_nmc_mac_address).", + ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +);"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + $anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__}); + + my $variables = { + name => $new_scan_apc_ups_name, + model => $new_scan_apc_ups_model, + serial_number => $new_scan_apc_ups_serial_number, + manufactured_date => $new_scan_apc_ups_manufactured_date, + firmware_version => $new_scan_apc_ups_firmware_version, + ip_address => $new_scan_apc_ups_ip, + nmc_serial_number => $new_scan_apc_ups_nmc_serial_number, + nmc_firmware_version => $new_scan_apc_ups_nmc_firmware_version, + nmc_mac_address => $new_scan_apc_ups_nmc_mac_address, + ac_restore_delay => $new_scan_apc_ups_ac_restore_delay, + shutdown_delay => $new_scan_apc_ups_shutdown_delay, + health => "#!string!scan_apc_ups_health_".sprintf("%04d", $new_scan_apc_ups_health)."!#" + high_transfer_voltage => $new_scan_apc_ups_high_transfer_voltage, + low_transfer_voltage => $new_scan_apc_ups_low_transfer_voltage, + last_transfer_reason => "#!string!scan_apc_ups_last_xfer_".sprintf("%04d", $new_scan_apc_ups_last_transfer_reason)."!#", + }; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_apc_pdu_message_0002", variables => $variables}); + $anvil->Alert->register({ + alert_level => "warning", + message => "scan_apc_pdu_message_0002", + message_variables => $variables, + set_by => $THIS_FILE, + sort_position => $anvil->data->{'scan-apc-pdu'}{alert_sort}++, + }); + + } + else + { + # Existing UPS, look for changes. + my $old_scan_apc_ups_ups_uuid = $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_ups_uuid}; + my $old_scan_apc_ups_serial_number = $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_serial_number}; + my $old_scan_apc_ups_name = $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_name}; + my $old_scan_apc_ups_ip = $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_ip}; + my $old_scan_apc_ups_ac_restore_delay = $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_ac_restore_delay}; + my $old_scan_apc_ups_shutdown_delay = $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_shutdown_delay}; + my $old_scan_apc_ups_firmware_version = $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_firmware_version}; + my $old_scan_apc_ups_health = $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_health}; + my $old_scan_apc_ups_high_transfer_voltage = $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_high_transfer_voltage}; + my $old_scan_apc_ups_low_transfer_voltage = $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_low_transfer_voltage}; + my $old_scan_apc_ups_last_transfer_reason = $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_last_transfer_reason}; + my $old_scan_apc_ups_manufactured_date = $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_manufactured_date}; + my $old_scan_apc_ups_model = $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_model}; + my $old_scan_apc_ups_temperature_units = $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_temperature_units}; + my $old_scan_apc_ups_nmc_firmware_version = $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_firmware_version}; + my $old_scan_apc_ups_nmc_serial_number = $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_serial_number}; + my $old_scan_apc_ups_nmc_mac_address = $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_mac_address}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + old_scan_apc_ups_ups_uuid => $old_scan_apc_ups_ups_uuid, + old_scan_apc_ups_serial_number => $old_scan_apc_ups_serial_number, + old_scan_apc_ups_name => $old_scan_apc_ups_name, + old_scan_apc_ups_ip => $old_scan_apc_ups_ip, + old_scan_apc_ups_ac_restore_delay => $old_scan_apc_ups_ac_restore_delay, + old_scan_apc_ups_shutdown_delay => $old_scan_apc_ups_shutdown_delay, + old_scan_apc_ups_firmware_version => $old_scan_apc_ups_firmware_version, + old_scan_apc_ups_health => $old_scan_apc_ups_health, + old_scan_apc_ups_high_transfer_voltage => $old_scan_apc_ups_high_transfer_voltage, + old_scan_apc_ups_low_transfer_voltage => $old_scan_apc_ups_low_transfer_voltage, + old_scan_apc_ups_last_transfer_reason => $old_scan_apc_ups_last_transfer_reason, + old_scan_apc_ups_manufactured_date => $old_scan_apc_ups_manufactured_date, + old_scan_apc_ups_model => $old_scan_apc_ups_model, + old_scan_apc_ups_temperature_units => $old_scan_apc_ups_temperature_units, + old_scan_apc_ups_nmc_firmware_version => $old_scan_apc_ups_nmc_firmware_version, + old_scan_apc_ups_nmc_serial_number => $old_scan_apc_ups_nmc_serial_number, + old_scan_apc_ups_nmc_mac_address => $old_scan_apc_ups_nmc_mac_address, + }}); + + # Look for batteries + foreach my $battery_number (sort {$a cmp $b} keys %{$anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}}) + { + my $scan_apc_ups_battery_replacement_date = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_replacement_date}; + my $scan_apc_ups_battery_health = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_health}; + my $scan_apc_ups_battery_model = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_model}; + my $scan_apc_ups_battery_percentage_charge = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_percentage_charge}; + my $scan_apc_ups_battery_last_replacement_date = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_last_replacement_date}; + my $scan_apc_ups_battery_state = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_state}; + my $scan_apc_ups_battery_temperature = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_temperature}; + my $scan_apc_ups_battery_alarm_temperature = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_alarm_temperature}; + my $scan_apc_ups_battery_voltage = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_voltage}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + scan_apc_ups_battery_health => $scan_apc_ups_battery_health, + scan_apc_ups_battery_percentage_charge => $scan_apc_ups_battery_percentage_charge, + scan_apc_ups_battery_last_replacement_date => $scan_apc_ups_battery_last_replacement_date, + scan_apc_ups_battery_state => $scan_apc_ups_battery_state, + scan_apc_ups_battery_temperature => $scan_apc_ups_battery_temperature, + scan_apc_ups_battery_alarm_temperature => $scan_apc_ups_battery_alarm_temperature, + scan_apc_ups_battery_voltage => $scan_apc_ups_battery_voltage, + }}); + + if (exists $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery_number}{$i}{scan_apc_ups_battery_uuid}) + { + my $scan_apc_ups_battery_uuid = $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery_number}{$i}{scan_apc_ups_battery_uuid}; + my $old_scan_apc_ups_battery_replacement_date = $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_replacement_date}; + my $old_scan_apc_ups_battery_health = $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_health}; + my $old_scan_apc_ups_battery_model = $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_model}; + my $old_scan_apc_ups_battery_percentage_charge = $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_percentage_charge}; + my $old_scan_apc_ups_battery_last_replacement_date = $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_last_replacement_date}; + my $old_scan_apc_ups_battery_state = $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_state}; + my $old_scan_apc_ups_battery_temperature = $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_temperature}; + my $old_scan_apc_ups_battery_alarm_temperature = $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_alarm_temperature}; + my $old_scan_apc_ups_battery_voltage = $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_voltage}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + scan_apc_ups_battery_uuid => $scan_apc_ups_battery_uuid, + old_scan_apc_ups_battery_health => $old_scan_apc_ups_battery_health, + old_scan_apc_ups_battery_percentage_charge => $old_scan_apc_ups_battery_percentage_charge, + old_scan_apc_ups_battery_last_replacement_date => $old_scan_apc_ups_battery_last_replacement_date, + old_scan_apc_ups_battery_state => $old_scan_apc_ups_battery_state, + old_scan_apc_ups_battery_temperature => $old_scan_apc_ups_battery_temperature, + old_scan_apc_ups_battery_alarm_temperature => $old_scan_apc_ups_battery_alarm_temperature, + old_scan_apc_ups_battery_voltage => $old_scan_apc_ups_battery_voltage, + }}); + } + else + { + # New battery + my $scan_apc_ups_battery_uuid = $anvil->Get->uuid; + my $query = " +INSERT INTO + scan_apc_ups_battery +( + scan_apc_ups_battery_uuid + scan_apc_ups_battery_scan_apc_ups_uuid, + scan_apc_ups_battery_replacement_date, + scan_apc_ups_battery_health, + scan_apc_ups_battery_model, + scan_apc_ups_battery_percentage_charge, + scan_apc_ups_battery_last_replacement_date, + scan_apc_ups_battery_state, + scan_apc_ups_battery_temperature, + scan_apc_ups_battery_alarm_temperature, + scan_apc_ups_battery_voltage, + modified_date +) VALUES ( + ".$anvil->Database->quote($scan_apc_ups_battery_uuid).", + ".$anvil->Database->quote($scan_apc_ups_uuid).", + ".$anvil->Database->quote($new_scan_apc_ups_battery_replacement_date).", + ".$anvil->Database->quote($new_scan_apc_ups_battery_health).", + ".$anvil->Database->quote($new_scan_apc_ups_battery_model).", + ".$anvil->Database->quote($new_scan_apc_ups_battery_percentage_charge).", + ".$anvil->Database->quote($new_scan_apc_ups_battery_last_replacement_date).", + ".$anvil->Database->quote($new_scan_apc_ups_battery_state).", + ".$anvil->Database->quote($new_scan_apc_ups_battery_temperature).", + ".$anvil->Database->quote($new_scan_apc_ups_battery_alarm_temperature).", + ".$anvil->Database->quote($new_scan_apc_ups_battery_voltage).", + ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +);"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$anvil->data->{sys}{queries}}, $query; + } + + } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ### NOTE: For now, we do it this way because we look for UPSes by their name/IPs, so we don't have + ### the serial number yet. This may get reworked later. + # As I read in each UPS, I will record the serial numbers. At the end, I will check in the database + # for any entries that were not found and mark them as lost. + my $seen_upses = []; + + # Loop through each UPS + my $id = $anvil->data->{sys}{read_db_id}; + my $host_name = $anvil->hostname; + + # If a UPS crosses its shutdown threshold, it will be added to this array. If both/all UPSes need + # to be shutdown, the Anvil! will be stopped. + my $shutdown_ups = []; + foreach my $ups_name (sort {$a cmp $b} keys %{$anvil->data->{'scan-apc-ups'}{ups}}) + { + # Convert all the long hashes into shorter variables + my $new_scan_apc_ups_ip = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{ip}; + my $new_scan_apc_ups_model = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{model}; + my $new_scan_apc_ups_serial_number = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_serial_number} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_serial_number} : "--"; + my $new_scan_apc_ups_name = $ups_name; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_ip" => $new_scan_apc_ups_ip, + "new_scan_apc_ups_model" => $new_scan_apc_ups_model, + "new_scan_apc_ups_serial_number" => $new_scan_apc_ups_serial_number, + "new_scan_apc_ups_name" => $new_scan_apc_ups_name, + }}); + + # Record this UPS + push @{$seen_upses}, $new_scan_apc_ups_serial_number; + + ############################################################################################# + # Base UPS and NMC information # + ############################################################################################# + + my $new_scan_apc_ups_ac_restore_delay = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_ac_restore_delay} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_ac_restore_delay} : 0; + my $new_scan_apc_ups_shutdown_delay = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_shutdown_delay} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_shutdown_delay} : 0; + my $new_scan_apc_ups_firmware_version = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_firmware_version} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_firmware_version} : 0; + my $new_scan_apc_ups_health = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_health} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_health} : 0; + my $new_scan_apc_ups_high_transfer_voltage = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_high_transfer_voltage} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_high_transfer_voltage} : 0; + my $new_scan_apc_ups_low_transfer_voltage = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_low_transfer_voltage} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_low_transfer_voltage} : 0; + my $new_scan_apc_ups_last_transfer_reason = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_last_transfer_reason} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_last_transfer_reason} : 0; + my $new_scan_apc_ups_manufactured_date = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_manufactured_date} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_manufactured_date} : 0; + my $new_scan_apc_ups_temperature_units = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_temperature_units} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_temperature_units} : 0; + my $new_scan_apc_ups_nmc_firmware_version = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_firmware_version} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_firmware_version} : 0; + my $new_scan_apc_ups_nmc_serial_number = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_serial_number} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_serial_number} : 0; + my $new_scan_apc_ups_nmc_mac_address = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_mac_address} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_mac_address} : 0; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_ac_restore_delay" => $new_scan_apc_ups_ac_restore_delay, + "new_scan_apc_ups_shutdown_delay" => $new_scan_apc_ups_shutdown_delay, + "new_scan_apc_ups_firmware_version" => $new_scan_apc_ups_firmware_version, + "new_scan_apc_ups_health" => $new_scan_apc_ups_health, + "new_scan_apc_ups_high_transfer_voltage" => $new_scan_apc_ups_high_transfer_voltage, + "new_scan_apc_ups_low_transfer_voltage" => $new_scan_apc_ups_low_transfer_voltage, + "new_scan_apc_ups_last_transfer_reason" => $new_scan_apc_ups_last_transfer_reason, + "new_scan_apc_ups_manufactured_date" => $new_scan_apc_ups_manufactured_date, + "new_scan_apc_ups_temperature_units" => $new_scan_apc_ups_temperature_units, + "new_scan_apc_ups_nmc_firmware_version" => $new_scan_apc_ups_nmc_firmware_version, + "new_scan_apc_ups_nmc_serial_number" => $new_scan_apc_ups_nmc_serial_number, + "new_scan_apc_ups_nmc_mac_address" => $new_scan_apc_ups_nmc_mac_address, + }}); + + ############################################################################################# + # Battery information # + ############################################################################################# + + my $new_scan_apc_ups_battery_replacement_date = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{1}{scan_apc_ups_battery_replacement_date} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{1}{scan_apc_ups_battery_replacement_date} : 0; + my $new_scan_apc_ups_battery_health = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{1}{scan_apc_ups_battery_health} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{1}{scan_apc_ups_battery_health} : 0; + my $new_scan_apc_ups_battery_model = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{1}{scan_apc_ups_battery_model} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{1}{scan_apc_ups_battery_model} : 0; + my $new_scan_apc_ups_battery_percentage_charge = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{1}{scan_apc_ups_battery_percentage_charge} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{1}{scan_apc_ups_battery_percentage_charge} : 0; + my $new_scan_apc_ups_battery_last_replacement_date = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{1}{scan_apc_ups_battery_last_replacement_date} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{1}{scan_apc_ups_battery_last_replacement_date} : 0; + my $new_scan_apc_ups_battery_state = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{1}{scan_apc_ups_battery_state} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{1}{scan_apc_ups_battery_state} : 0; + my $new_scan_apc_ups_battery_temperature = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{1}{scan_apc_ups_battery_temperature} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{1}{scan_apc_ups_battery_temperature} : 0; + my $new_scan_apc_ups_battery_alarm_temperature = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{1}{scan_apc_ups_battery_alarm_temperature} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{1}{scan_apc_ups_battery_alarm_temperature} : 0; + my $new_scan_apc_ups_battery_voltage = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{1}{scan_apc_ups_battery_voltage} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{1}{scan_apc_ups_battery_voltage} : 0; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_battery_replacement_date" => $new_scan_apc_ups_battery_replacement_date, + "new_scan_apc_ups_battery_health" => $new_scan_apc_ups_battery_health, + "new_scan_apc_ups_battery_model" => $new_scan_apc_ups_battery_model, + "new_scan_apc_ups_battery_percentage_charge" => $new_scan_apc_ups_battery_percentage_charge, + "new_scan_apc_ups_battery_last_replacement_date" => $new_scan_apc_ups_battery_last_replacement_date, + "new_scan_apc_ups_battery_state" => $new_scan_apc_ups_battery_state, + "new_scan_apc_ups_battery_temperature" => $new_scan_apc_ups_battery_temperature, + "new_scan_apc_ups_battery_alarm_temperature" => $new_scan_apc_ups_battery_alarm_temperature, + "new_scan_apc_ups_battery_voltage" => $new_scan_apc_ups_battery_voltage, + }}); + + ### NOTE: We want to record the current battery temperature in all cases for our health file. + ### We'll handle alerts later. + # These are thresholds. We'll compare our current temp against these + my $alert_temperature = ($new_scan_apc_ups_battery_alarm_temperature - $anvil->data->{'scan-apc-ups'}{temperature_warning_delta}); + my $sensor_host_key = "ups:".$new_scan_apc_ups_serial_number.":battery"; + my $variable = "scan_apc_ups_battery_temperature"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "alert_temperature" => $alert_temperature, + "sensor_host_key" => $sensor_host_key, + "variable" => $variable, + }}); + if ($new_scan_apc_ups_battery_temperature > $new_scan_apc_ups_battery_alarm_temperature) + { + # Critical + $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_celsius} = $new_scan_apc_ups_battery_temperature; + $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_state} = "critical"; + $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_is} = "high"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new::temperature::${variable}::${sensor_host_key}::temperature_celsius" => $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_celsius}, + "new::temperature::${variable}::${sensor_host_key}::temperature_state" => $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_state}, + "new::temperature::${variable}::${sensor_host_key}::temperature_is" => $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_is}, + }}); + } + elsif ($new_scan_apc_ups_battery_temperature > $alert_temperature) + { + # Warning + $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_celsius} = $new_scan_apc_ups_battery_temperature; + $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_state} = "warning"; + $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_is} = "high"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new::temperature::${variable}::${sensor_host_key}::temperature_celsius" => $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_celsius}, + "new::temperature::${variable}::${sensor_host_key}::temperature_state" => $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_state}, + "new::temperature::${variable}::${sensor_host_key}::temperature_is" => $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_is}, + }}); + } + else + { + # Nominal + $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_celsius} = $new_scan_apc_ups_battery_temperature; + $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_state} = "ok"; + $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_is} = "nominal"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new::temperature::${variable}::${sensor_host_key}::temperature_celsius" => $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_celsius}, + "new::temperature::${variable}::${sensor_host_key}::temperature_state" => $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_state}, + "new::temperature::${variable}::${sensor_host_key}::temperature_is" => $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_is}, + }}); + } + + ############################################################################################# + # Input Information # + ############################################################################################# + + my $new_scan_apc_ups_input_frequency = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_frequency} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_frequency} : 0; + my $new_scan_apc_ups_input_sensitivity = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_sensitivity} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_sensitivity} : 0; + my $new_scan_apc_ups_input_voltage = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_voltage} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_voltage} : 0; + my $new_scan_apc_ups_input_1m_maximum_input_voltage = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_1m_maximum_input_voltage} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_1m_maximum_input_voltage} : 0; + my $new_scan_apc_ups_input_1m_minimum_input_voltage = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_1m_minimum_input_voltage} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_1m_minimum_input_voltage} : 0; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_input_frequency" => $new_scan_apc_ups_input_frequency, + "new_scan_apc_ups_input_sensitivity" => $new_scan_apc_ups_input_sensitivity, + "new_scan_apc_ups_input_voltage" => $new_scan_apc_ups_input_voltage, + "new_scan_apc_ups_input_1m_maximum_input_voltage" => $new_scan_apc_ups_input_1m_maximum_input_voltage, + "new_scan_apc_ups_input_1m_minimum_input_voltage" => $new_scan_apc_ups_input_1m_minimum_input_voltage, + }}); + + ############################################################################################# + # Output Information # + ############################################################################################# + + my $new_scan_apc_ups_output_load_percentage = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_load_percentage} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_load_percentage} : 0; + my $new_scan_apc_ups_output_time_on_batteries = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_time_on_batteries} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_time_on_batteries} : 0; + my $new_scan_apc_ups_output_estimated_runtime = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_estimated_runtime} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_estimated_runtime} : 0; + my $new_scan_apc_ups_output_frequency = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_frequency} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_frequency} : 0; + my $new_scan_apc_ups_output_voltage = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_voltage} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_voltage} : 0; + my $new_scan_apc_ups_output_total_output = defined $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_total_output} ? $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_total_output} : 0; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_output_load_percentage" => $new_scan_apc_ups_output_load_percentage, + "new_scan_apc_ups_output_time_on_batteries" => $new_scan_apc_ups_output_time_on_batteries, + "new_scan_apc_ups_output_estimated_runtime" => $new_scan_apc_ups_output_estimated_runtime, + "new_scan_apc_ups_output_frequency" => $new_scan_apc_ups_output_frequency, + "new_scan_apc_ups_output_voltage" => $new_scan_apc_ups_output_voltage, + "new_scan_apc_ups_output_total_output" => $new_scan_apc_ups_output_total_output, + }}); + + # This returns the 'scan_apc_ups_uuid' if the UPS is found. + if (read_last_scan($an, $new_scan_apc_ups_serial_number)) + { + # The UPS exists. Look for updates. We're using the serial number for referencing the + # SQL data to catch hostname changes. + my $ups_serial_number = $new_scan_apc_ups_serial_number; + + ##################################################################################### + # Base UPS and NMC information # + ##################################################################################### + + my $scan_apc_ups_uuid = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_uuid}; + my $old_scan_apc_ups_name = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_name}; + my $old_scan_apc_ups_ip = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_ip}; + my $old_scan_apc_ups_ac_restore_delay = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_ac_restore_delay}; + my $old_scan_apc_ups_shutdown_delay = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_shutdown_delay}; + my $old_scan_apc_ups_firmware_version = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_firmware_version}; + my $old_scan_apc_ups_health = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_health}; + my $old_scan_apc_ups_high_transfer_voltage = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_high_transfer_voltage}; + my $old_scan_apc_ups_low_transfer_voltage = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_low_transfer_voltage}; + my $old_scan_apc_ups_last_transfer_reason = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_last_transfer_reason}; + my $old_scan_apc_ups_manufactured_date = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_manufactured_date}; + my $old_scan_apc_ups_model = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_model}; + my $old_scan_apc_ups_temperature_units = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_temperature_units}; + my $old_scan_apc_ups_nmc_firmware_version = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_nmc_firmware_version}; + my $old_scan_apc_ups_nmc_serial_number = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_nmc_serial_number}; + my $old_scan_apc_ups_nmc_mac_address = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_nmc_mac_address}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "scan_apc_ups_uuid" => $scan_apc_ups_uuid, + "old_scan_apc_ups_name" => $old_scan_apc_ups_name, + "old_scan_apc_ups_ip" => $old_scan_apc_ups_ip, + "old_scan_apc_ups_ac_restore_delay" => $old_scan_apc_ups_ac_restore_delay, + "old_scan_apc_ups_shutdown_delay" => $old_scan_apc_ups_shutdown_delay, + "old_scan_apc_ups_firmware_version" => $old_scan_apc_ups_firmware_version, + "old_scan_apc_ups_health" => $old_scan_apc_ups_health, + "old_scan_apc_ups_high_transfer_voltage" => $old_scan_apc_ups_high_transfer_voltage, + "old_scan_apc_ups_low_transfer_voltage" => $old_scan_apc_ups_low_transfer_voltage, + "old_scan_apc_ups_last_transfer_reason" => $old_scan_apc_ups_last_transfer_reason, + "old_scan_apc_ups_manufactured_date" => $old_scan_apc_ups_manufactured_date, + "old_scan_apc_ups_model" => $old_scan_apc_ups_model, + "old_scan_apc_ups_temperature_units" => $old_scan_apc_ups_temperature_units, + "old_scan_apc_ups_nmc_firmware_version" => $old_scan_apc_ups_nmc_firmware_version, + "old_scan_apc_ups_nmc_serial_number" => $old_scan_apc_ups_nmc_serial_number, + "old_scan_apc_ups_nmc_mac_address" => $old_scan_apc_ups_nmc_mac_address, + }}); + + ##################################################################################### + # Battery information # + ##################################################################################### + + my $old_scan_apc_ups_battery_replacement_date = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_battery_replacement_date}; + my $old_scan_apc_ups_battery_health = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_battery_health}; + my $old_scan_apc_ups_battery_model = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_battery_model}; + my $old_scan_apc_ups_battery_percentage_charge = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_battery_percentage_charge}; + my $old_scan_apc_ups_battery_last_replacement_date = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_battery_last_replacement_date}; + my $old_scan_apc_ups_battery_state = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_battery_state}; + my $old_scan_apc_ups_battery_temperature = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_battery_temperature}; + my $old_scan_apc_ups_battery_alarm_temperature = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_battery_alarm_temperature}; + my $old_scan_apc_ups_battery_voltage = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_battery_voltage}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "old_scan_apc_ups_battery_replacement_date" => $old_scan_apc_ups_battery_replacement_date, + "old_scan_apc_ups_battery_health" => $old_scan_apc_ups_battery_health, + "old_scan_apc_ups_battery_model" => $old_scan_apc_ups_battery_model, + "old_scan_apc_ups_battery_percentage_charge" => $old_scan_apc_ups_battery_percentage_charge, + "old_scan_apc_ups_battery_last_replacement_date" => $old_scan_apc_ups_battery_last_replacement_date, + "old_scan_apc_ups_battery_state" => $old_scan_apc_ups_battery_state, + "old_scan_apc_ups_battery_temperature" => $old_scan_apc_ups_battery_temperature, + "old_scan_apc_ups_battery_alarm_temperature" => $old_scan_apc_ups_battery_alarm_temperature, + "old_scan_apc_ups_battery_voltage" => $old_scan_apc_ups_battery_voltage, + }}); + + ##################################################################################### + # Input Information # + ##################################################################################### + + my $old_scan_apc_ups_input_frequency = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_input_frequency}; + my $old_scan_apc_ups_input_sensitivity = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_input_sensitivity}; + my $old_scan_apc_ups_input_voltage = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_input_voltage}; + my $old_scan_apc_ups_input_1m_maximum_input_voltage = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_input_1m_maximum_input_voltage}; + my $old_scan_apc_ups_input_1m_minimum_input_voltage = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_input_1m_minimum_input_voltage}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "old_scan_apc_ups_input_frequency" => $old_scan_apc_ups_input_frequency, + "old_scan_apc_ups_input_sensitivity" => $old_scan_apc_ups_input_sensitivity, + "old_scan_apc_ups_input_voltage" => $old_scan_apc_ups_input_voltage, + "old_scan_apc_ups_input_1m_maximum_input_voltage" => $old_scan_apc_ups_input_1m_maximum_input_voltage, + "old_scan_apc_ups_input_1m_minimum_input_voltage" => $old_scan_apc_ups_input_1m_minimum_input_voltage, + }}); + + ##################################################################################### + # Output Information # + ##################################################################################### + + my $old_scan_apc_ups_output_load_percentage = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_output_load_percentage}; + my $old_scan_apc_ups_output_time_on_batteries = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_output_time_on_batteries}; + my $old_scan_apc_ups_output_estimated_runtime = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_output_estimated_runtime}; + my $old_scan_apc_ups_output_frequency = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_output_frequency}; + my $old_scan_apc_ups_output_voltage = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_output_voltage}; + my $old_scan_apc_ups_output_total_output = $anvil->data->{sql}{$ups_serial_number}{scan_apc_ups_output_total_output}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "old_scan_apc_ups_output_load_percentage" => $old_scan_apc_ups_output_load_percentage, + "old_scan_apc_ups_output_time_on_batteries" => $old_scan_apc_ups_output_time_on_batteries, + "old_scan_apc_ups_output_estimated_runtime" => $old_scan_apc_ups_output_estimated_runtime, + "old_scan_apc_ups_output_frequency" => $old_scan_apc_ups_output_frequency, + "old_scan_apc_ups_output_voltage" => $old_scan_apc_ups_output_voltage, + "old_scan_apc_ups_output_total_output" => $old_scan_apc_ups_output_total_output, + }}); + + ### Start comparing! + # Start with the core values; + if (($new_scan_apc_ups_name ne $old_scan_apc_ups_name) or + ($new_scan_apc_ups_ip ne $old_scan_apc_ups_ip) or + ($new_scan_apc_ups_ac_restore_delay ne $old_scan_apc_ups_ac_restore_delay) or + ($new_scan_apc_ups_shutdown_delay ne $old_scan_apc_ups_shutdown_delay) or + ($new_scan_apc_ups_firmware_version ne $old_scan_apc_ups_firmware_version) or + ($new_scan_apc_ups_health ne $old_scan_apc_ups_health) or + ($new_scan_apc_ups_high_transfer_voltage ne $old_scan_apc_ups_high_transfer_voltage) or + ($new_scan_apc_ups_low_transfer_voltage ne $old_scan_apc_ups_low_transfer_voltage) or + ($new_scan_apc_ups_last_transfer_reason ne $old_scan_apc_ups_last_transfer_reason) or + ($new_scan_apc_ups_manufactured_date ne $old_scan_apc_ups_manufactured_date) or + ($new_scan_apc_ups_model ne $old_scan_apc_ups_model) or + ($new_scan_apc_ups_temperature_units ne $old_scan_apc_ups_temperature_units) or + ($new_scan_apc_ups_nmc_firmware_version ne $old_scan_apc_ups_nmc_firmware_version) or + ($new_scan_apc_ups_nmc_serial_number ne $old_scan_apc_ups_nmc_serial_number) or + ($new_scan_apc_ups_nmc_mac_address ne $old_scan_apc_ups_nmc_mac_address)) + { + # Something in the 'scan_apc_ups' table changed for + # this UPS. + $anvil->Log->entry({log_level => 2, message_key => "scan_apc_ups_log_0001", message_variables => { + table => "scan_apc_ups", + serial_number => $ups_serial_number, + ups_name => $ups_name, + }, file => $THIS_FILE, line => __LINE__}); + + ### Log the variables to help see what actually changed. + # Did the domain name change? + if ($new_scan_apc_ups_name ne $old_scan_apc_ups_name) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_name" => $new_scan_apc_ups_name, + "old_scan_apc_ups_name" => $old_scan_apc_ups_name, + }}); + + # This is a 'notice' level alert. + $anvil->Alert->register_alert({ + alert_level => "notice", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0003", + alert_message_key => "scan_apc_ups_note_0001", + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $new_scan_apc_ups_name, + old_value => $old_scan_apc_ups_name, + }, + }); + } + + # See if the IP address has changed. + if ($new_scan_apc_ups_ip ne $old_scan_apc_ups_ip) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_ip" => $new_scan_apc_ups_ip, + "old_scan_apc_ups_ip" => $old_scan_apc_ups_ip, + }}); + + # This is a 'notice' level alert. + $anvil->Alert->register_alert({ + alert_level => "notice", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0003", + alert_message_key => "scan_apc_ups_note_0002", + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $new_scan_apc_ups_ip, + old_value => $old_scan_apc_ups_ip, + }, + }); + } + + # See if the AC restore delay has changed. + if ($new_scan_apc_ups_ac_restore_delay ne $old_scan_apc_ups_ac_restore_delay) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_ac_restore_delay" => $new_scan_apc_ups_ac_restore_delay, + "old_scan_apc_ups_ac_restore_delay" => $old_scan_apc_ups_ac_restore_delay, + }}); + + # This is a 'notice' level alert. + $anvil->Alert->register_alert({ + alert_level => "notice", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0003", + alert_message_key => "scan_apc_ups_note_0003", + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $new_scan_apc_ups_ac_restore_delay, + old_value => $old_scan_apc_ups_ac_restore_delay, + }, + }); + } + + # See if the shutdown delay has changed. + if ($new_scan_apc_ups_shutdown_delay ne $old_scan_apc_ups_shutdown_delay) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_shutdown_delay" => $new_scan_apc_ups_shutdown_delay, + "old_scan_apc_ups_shutdown_delay" => $old_scan_apc_ups_shutdown_delay, + }}); + + # This is a 'notice' level alert. + $anvil->Alert->register_alert({ + alert_level => "notice", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0003", + alert_message_key => "scan_apc_ups_note_0004", + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $new_scan_apc_ups_shutdown_delay, + old_value => $old_scan_apc_ups_shutdown_delay, + }, + }); + } + + # Has the firmware version changed? + if ($new_scan_apc_ups_firmware_version ne $old_scan_apc_ups_firmware_version) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_firmware_version" => $new_scan_apc_ups_firmware_version, + "old_scan_apc_ups_firmware_version" => $old_scan_apc_ups_firmware_version, + }}); + + # This is a 'notice' level alert. + $anvil->Alert->register_alert({ + alert_level => "notice", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0003", + alert_message_key => "scan_apc_ups_note_0005", + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $new_scan_apc_ups_firmware_version, + old_value => $old_scan_apc_ups_firmware_version, + }, + }); + } + + # Has the health changed? This is fairly complex as there are many possible + # health values. + if ($new_scan_apc_ups_health ne $old_scan_apc_ups_health) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_health" => $new_scan_apc_ups_health, + "old_scan_apc_ups_health" => $old_scan_apc_ups_health, + }}); + + my $level = "notice"; + my $title_key = "an_alert_title_0003"; + + ### There are 20 possible states + ### (* == warning, rest are notice): + # *1 - The UPS's health is in an unknown state. + # 2 - The UPS is operating normally. + # 3 - The UPS is running on its batteries. See below for checks if input voltage remains. If not, this will indicate a power loss event. + # 4 - The UPS is compensating for low input power. This is treated as being on batteries. + # 5 - The UPS is in a timed sleep. It will power back on when the timer has expired. + # 6 - The UPS is in bypass-mode and was placed in this mode by software. Power is passing to downstream devices through a radio frequency interference filter, but is not conditioned in any other way. Batter protection is not available. + # *7 - The UPS is off. No power is being provided to down-stream equipment. + # *8 - The UPS is currently rebooting. + # 9 - The UPS is in bypass-mode and was placed in this mode by a hardware switch. Power is passing to downstream devices through a radio frequency interference filter, but is not conditioned in any other way. Batter protection is not available. + # 10 - The UPS is in bypass-mode because of an internal failure. Power is passing to downstream devices through a radio frequency interference filter, but is not conditioned in any other way. Batter protection is not available. + # *11 - The UPS has lost input power and is sleeping. It will restore output power once input power has been restored. + # 12 - The UPS is compensating for high input voltage. + # 13 - The UPS is operating in low-power mode. In this mode, the UPS is in static bypass mode and it is drawing very little power. If a fault is detected, it will switch to either normal operation or forced static bypass mode. + # 14 - The UPS is operating in hot-standby mode. + # 15 - The UPS is performing a test of its batteries. + # *16 - The UPS has been placed in emergency static bypass mode. Power is passing to downstream devices through a radio frequency interference filter, but is not conditioned in any other way. Batter protection is not available. + # 17 - The UPS is in static bypass standby mode. It is not currently providing power to downstream devices. + # 18 - The UPS is in power saving mode. The front panel display will be off but the UPS is operating normally. + # 19 - The UPS is in SPoT (Self Power Test) operating mode. + # 20 - The UPS is in ECOnversion mode. The UPS is providing power to the downstream devices via the bypass. The UPS's inverter is operational and ready to take over the output load if an input fault occurs. + # ----[ Fake ones ]---- + # 30 - The UPS is running on batteries but there is still input voltage, but it is higher than the high transfer voltage so we're TRIMing + # 31 - The UPS is running on batteries but there is still input voltage, but it is lower than the low transfer voltage so we're BOOSTing + # 32 - The UPS is running on batteries but there is still nominal input voltage, so it is likely a self-test. + + # Make sure we have a valid health integer + my $bad_value = ""; + my $say_new_scan_apc_ups_health = $new_scan_apc_ups_health; + my $say_old_scan_apc_ups_health = $old_scan_apc_ups_health; + if (($new_scan_apc_ups_health =~ /\D/) or (($new_scan_apc_ups_health < 0) or ($new_scan_apc_ups_health > 20))) + { + # We'll use '99' as a generic "this is not known" + $bad_value = $new_scan_apc_ups_health; + $say_new_scan_apc_ups_health = 99; + } + elsif (($old_scan_apc_ups_health =~ /\D/) or (($old_scan_apc_ups_health < 0) or ($old_scan_apc_ups_health > 20))) + { + # We'll use '99' as a generic "this is not known" + $bad_value = $old_scan_apc_ups_health; + $say_old_scan_apc_ups_health = 99; + } + + # If the UPS is on batteries, check the input voltage. If there is + # still power we'll indicate a self-test. + if ($new_scan_apc_ups_health eq "3") + { + if ($new_scan_apc_ups_input_voltage > $new_scan_apc_ups_high_transfer_voltage) + { + $new_scan_apc_ups_health = 30; + } + elsif ($new_scan_apc_ups_input_voltage < 10) + { + # Actual power loss. + $new_scan_apc_ups_health = 3; + $level = "warning"; + $title_key = "an_alert_title_0004"; + } + elsif ($new_scan_apc_ups_input_voltage < $new_scan_apc_ups_low_transfer_voltage) + { + $new_scan_apc_ups_health = 31; + } + else + { + $new_scan_apc_ups_health = 32; + } + } + # Now check if power is back. + if ($old_scan_apc_ups_health eq "3") + { + # Clear the alert. + $level = "warning"; + $title_key = "an_alert_title_0006"; + } + + # If the old or new level is one worthy of a warning, set the alert + # level to warning. + if (($new_scan_apc_ups_health eq "1") or + ($new_scan_apc_ups_health eq "7") or + ($new_scan_apc_ups_health eq "8") or + ($new_scan_apc_ups_health eq "11") or + ($new_scan_apc_ups_health eq "16")) + { + # Entered a warning state. + $level = "warning"; + $title_key = "an_alert_title_0004"; + } + elsif (($old_scan_apc_ups_health eq "1") or + ($old_scan_apc_ups_health eq "7") or + ($old_scan_apc_ups_health eq "8") or + ($old_scan_apc_ups_health eq "11") or + ($old_scan_apc_ups_health eq "16")) + { + # Warning state cleared. + $level = "warning"; + $title_key = "an_alert_title_0006"; + } + + $anvil->Alert->register_alert({ + alert_level => $level, + alert_agent_name => $THIS_FILE, + alert_title_key => $title_key, + alert_message_key => "scan_apc_ups_note_0006", + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => "#!string!scan_apc_ups_health_".sprintf("%04d", $say_new_scan_apc_ups_health)."!#", + old_value => "#!string!scan_apc_ups_health_".sprintf("%04d", $say_old_scan_apc_ups_health)."!#", + bad_value => $bad_value, + }, + }); + } + + # See if the high-voltage transfer to battery value has changed. + if ($new_scan_apc_ups_high_transfer_voltage ne $old_scan_apc_ups_high_transfer_voltage) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_high_transfer_voltage" => $new_scan_apc_ups_high_transfer_voltage, + "old_scan_apc_ups_high_transfer_voltage" => $old_scan_apc_ups_high_transfer_voltage, + }}); + + # This is a 'notice' level alert. Which message we use will depend on + # whether the threshold increased or decreased. + my $message_key = "scan_apc_ups_note_0007"; + if ($new_scan_apc_ups_high_transfer_voltage > $old_scan_apc_ups_high_transfer_voltage) + { + $message_key = "scan_apc_ups_note_0008"; + } + $anvil->Alert->register_alert({ + alert_level => "notice", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0003", + alert_message_key => $message_key, + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $new_scan_apc_ups_high_transfer_voltage, + old_value => $old_scan_apc_ups_high_transfer_voltage, + }, + }); + } + + # See if the low-voltage transfer to battery value has changed. + if ($new_scan_apc_ups_low_transfer_voltage ne $old_scan_apc_ups_low_transfer_voltage) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_low_transfer_voltage" => $new_scan_apc_ups_low_transfer_voltage, + "old_scan_apc_ups_low_transfer_voltage" => $old_scan_apc_ups_low_transfer_voltage, + }}); + + # This is a 'notice' level alert. Which message we use will depend on + # whether the threshold increased or decreased. + my $message_key = "scan_apc_ups_note_0009"; + if ($new_scan_apc_ups_low_transfer_voltage < $old_scan_apc_ups_low_transfer_voltage) + { + $message_key = "scan_apc_ups_note_0010"; + } + $anvil->Alert->register_alert({ + alert_level => "notice", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0003", + alert_message_key => $message_key, + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $new_scan_apc_ups_low_transfer_voltage, + old_value => $old_scan_apc_ups_low_transfer_voltage, + }, + }); + } + + # Has the last transfer reason changed? There are 10 reasons why this might + # happen, some being more critical than others. + if ($new_scan_apc_ups_last_transfer_reason ne $old_scan_apc_ups_last_transfer_reason) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_last_transfer_reason" => $new_scan_apc_ups_last_transfer_reason, + "old_scan_apc_ups_last_transfer_reason" => $old_scan_apc_ups_last_transfer_reason, + }}); + + ### NOTE: This used to be 'warning' level, but it caused way too many + ### false alarms. If a serious issue arises, the input voltage + ### will trigger a 'warning' level alert. + ### There are 10 possible states + # 0 - There is no information on when the UPS last transferred to battery. + # 1 - The UPS has not transferred to battery power since the last time it booted. + # 2 - The UPS last transferred to batteries because of high input voltage. + # 3 - The UPS last transferred to batteries because of a brown out. That is, a prolonged drop in input voltage from the mains circuit. + # 4 - The UPS last transferred to batteries because of a black out. That is, a prolonged loss of input voltage from the mains circuit. + # 5 - The UPS last transferred to batteries because of a brief, minor reduction of input voltage from the mains circuit. + # 6 - The UPS last transferred to batteries because of a brief, significant reduction of input voltage from the mains circuit. + # 7 - The UPS last transferred to batteries because of a brief, minor increase of input voltage from the mains circuit. + # 8 - The UPS last transferred to batteries because of a brief, significant spike of input voltage from the mains circuit. + # 9 - The UPS last transferred to batteries as part of a planned self-test. + # 10 - The UPS last transferred to batteries because of a significant change of input voltage from the mains circuit. + + # Make sure we have a valid health integer + my $bad_value = ""; + my $say_new_scan_apc_ups_last_transfer_reason = $new_scan_apc_ups_last_transfer_reason; + my $say_old_scan_apc_ups_last_transfer_reason = $old_scan_apc_ups_last_transfer_reason; + if (($new_scan_apc_ups_last_transfer_reason =~ /\D/) or (($new_scan_apc_ups_last_transfer_reason < 0) or ($new_scan_apc_ups_last_transfer_reason > 10))) + { + # We'll use '99' as a generic "this is not known" + $bad_value = $new_scan_apc_ups_last_transfer_reason; + $say_new_scan_apc_ups_last_transfer_reason = 99; + } + elsif (($old_scan_apc_ups_last_transfer_reason =~ /\D/) or (($old_scan_apc_ups_last_transfer_reason < 0) or ($old_scan_apc_ups_last_transfer_reason > 10))) + { + # We'll use '99' as a generic "this is not known" + $bad_value = $old_scan_apc_ups_last_transfer_reason; + $say_old_scan_apc_ups_last_transfer_reason = 99; + } + + $anvil->Alert->register_alert({ + alert_level => "notice", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0003", + alert_message_key => "scan_apc_ups_note_0011", + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => "#!string!scan_apc_ups_last_transfer_".sprintf("%04d", $say_new_scan_apc_ups_last_transfer_reason)."!#", + old_value => "#!string!scan_apc_ups_last_transfer_".sprintf("%04d", $say_old_scan_apc_ups_last_transfer_reason)."!#", + bad_value => $bad_value, + }, + }); + } + + # o_O + if ($new_scan_apc_ups_manufactured_date ne $old_scan_apc_ups_manufactured_date) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_manufactured_date" => $new_scan_apc_ups_manufactured_date, + "old_scan_apc_ups_manufactured_date" => $old_scan_apc_ups_manufactured_date, + }}); + + # This is a 'warning' level alert simply because it should never + # happen and it probably a sign of a bigger problem. + $anvil->Alert->register_alert({ + alert_level => "warning", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0004", + alert_message_key => "scan_apc_ups_note_0012", + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $new_scan_apc_ups_manufactured_date, + old_value => $old_scan_apc_ups_manufactured_date, + }, + }); + } + + # Did a deleted UPS return? + if ($new_scan_apc_ups_model ne $old_scan_apc_ups_model) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_model" => $new_scan_apc_ups_model, + "old_scan_apc_ups_model" => $old_scan_apc_ups_model, + }}); + + # Default is a program error. + my $alert_level = "warning"; + my $alert_title = "an_alert_title_0004"; + my $alert_message = "scan_apc_ups_note_0013"; + if ($old_scan_apc_ups_model eq "DELETED") + { + # A UPS returned. + $alert_level = "notice"; + $alert_title = "an_alert_title_0003"; + $alert_message = "scan_apc_ups_note_0065"; + } + + $anvil->Alert->register_alert({ + alert_level => $alert_level, + alert_agent_name => $THIS_FILE, + alert_title_key => $alert_title, + alert_message_key => $alert_message, + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $new_scan_apc_ups_model, + old_value => $old_scan_apc_ups_model, + }, + }); + } + + # See if the core temperature units has changed. + if ($new_scan_apc_ups_temperature_units ne $old_scan_apc_ups_temperature_units) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_temperature_units" => $new_scan_apc_ups_temperature_units, + "old_scan_apc_ups_temperature_units" => $old_scan_apc_ups_temperature_units, + }}); + + # This should not impact the user at all. + $anvil->Alert->register_alert({ + alert_level => "notice", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0003", + alert_message_key => "scan_apc_ups_note_0014", + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $new_scan_apc_ups_temperature_units eq "F" ? "#!string!tools_suffix_0013!#" : "#!string!tools_suffix_0011!#", + old_value => $old_scan_apc_ups_temperature_units eq "F" ? "#!string!tools_suffix_0013!#" : "#!string!tools_suffix_0011!#", + }, + }); + } + + # Has the NMC's firmware changed? + if ($new_scan_apc_ups_nmc_firmware_version ne $old_scan_apc_ups_nmc_firmware_version) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_nmc_firmware_version" => $new_scan_apc_ups_nmc_firmware_version, + "old_scan_apc_ups_nmc_firmware_version" => $old_scan_apc_ups_nmc_firmware_version, + }}); + + # This is a 'notice' level alert. + $anvil->Alert->register_alert({ + alert_level => "notice", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0003", + alert_message_key => "scan_apc_ups_note_0015", + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $new_scan_apc_ups_nmc_firmware_version, + old_value => $old_scan_apc_ups_nmc_firmware_version, + }, + }); + } + + # Has the NMC serial number changed? If the user changed the network card, + # this could be triggered. + if ($new_scan_apc_ups_nmc_serial_number ne $old_scan_apc_ups_nmc_serial_number) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_nmc_serial_number" => $new_scan_apc_ups_nmc_serial_number, + "old_scan_apc_ups_nmc_serial_number" => $old_scan_apc_ups_nmc_serial_number, + }}); + + # This is a 'notice' level alert because the administrator will most + # likely already know about it. + $anvil->Alert->register_alert({ + alert_level => "notice", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0003", + alert_message_key => "scan_apc_ups_note_0016", + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $new_scan_apc_ups_nmc_serial_number, + old_value => $old_scan_apc_ups_nmc_serial_number, + }, + }); + } + + # As with above, if the MAC address changed, it is probably because the NMC + # was replaced. + if ($new_scan_apc_ups_nmc_mac_address ne $old_scan_apc_ups_nmc_mac_address) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_nmc_mac_address" => $new_scan_apc_ups_nmc_mac_address, + "old_scan_apc_ups_nmc_mac_address" => $old_scan_apc_ups_nmc_mac_address, + }}); + + # This is a 'notice' level alert because the administrator will most + # likely already know about it. + $anvil->Alert->register_alert({ + alert_level => "notice", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0003", + alert_message_key => "scan_apc_ups_note_0017", + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $new_scan_apc_ups_nmc_mac_address, + old_value => $old_scan_apc_ups_nmc_mac_address, + }, + }); + } + + # Update the scan_apc_ups record, then we'll handle notifying users. + my $query = " +UPDATE + scan_apc_ups +SET + scan_apc_ups_name = ".$anvil->Database->quote($new_scan_apc_ups_name).", + scan_apc_ups_ip = ".$anvil->Database->quote($new_scan_apc_ups_ip).", + scan_apc_ups_ac_restore_delay = ".$anvil->Database->quote($new_scan_apc_ups_ac_restore_delay).", + scan_apc_ups_shutdown_delay = ".$anvil->Database->quote($new_scan_apc_ups_shutdown_delay).", + scan_apc_ups_firmware_version = ".$anvil->Database->quote($new_scan_apc_ups_firmware_version).", + scan_apc_ups_health = ".$anvil->Database->quote($new_scan_apc_ups_health).", + scan_apc_ups_high_transfer_voltage = ".$anvil->Database->quote($new_scan_apc_ups_high_transfer_voltage).", + scan_apc_ups_low_transfer_voltage = ".$anvil->Database->quote($new_scan_apc_ups_low_transfer_voltage).", + scan_apc_ups_last_transfer_reason = ".$anvil->Database->quote($new_scan_apc_ups_last_transfer_reason).", + scan_apc_ups_manufactured_date = ".$anvil->Database->quote($new_scan_apc_ups_manufactured_date).", + scan_apc_ups_model = ".$anvil->Database->quote($new_scan_apc_ups_model).", + scan_apc_ups_temperature_units = ".$anvil->Database->quote($new_scan_apc_ups_temperature_units).", + scan_apc_ups_nmc_firmware_version = ".$anvil->Database->quote($new_scan_apc_ups_nmc_firmware_version).", + scan_apc_ups_nmc_serial_number = ".$anvil->Database->quote($new_scan_apc_ups_nmc_serial_number).", + scan_apc_ups_nmc_mac_address = ".$anvil->Database->quote($new_scan_apc_ups_nmc_mac_address).", + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +WHERE + scan_apc_ups_uuid = ".$anvil->Database->quote($scan_apc_ups_uuid)." +AND + scan_apc_ups_host_uuid = ".$anvil->Database->quote($anvil->data->{sys}{host_uuid})." +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$anvil->data->{sys}{queries}}, $query; + } + + # Though we use other tables, this is done to conserve disk space. As such, we're not + # going to check individually for the existence of each table. This may prove to be a + # bad decision, so this approach may change later. + + # Check for changes in the battery + if (($new_scan_apc_ups_battery_replacement_date ne $old_scan_apc_ups_battery_replacement_date) or + ($new_scan_apc_ups_battery_health ne $old_scan_apc_ups_battery_health) or + ($new_scan_apc_ups_battery_model ne $old_scan_apc_ups_battery_model) or + ($new_scan_apc_ups_battery_percentage_charge ne $old_scan_apc_ups_battery_percentage_charge) or + ($new_scan_apc_ups_battery_last_replacement_date ne $old_scan_apc_ups_battery_last_replacement_date) or + ($new_scan_apc_ups_battery_state ne $old_scan_apc_ups_battery_state) or + ($new_scan_apc_ups_battery_temperature ne $old_scan_apc_ups_battery_temperature) or + ($new_scan_apc_ups_battery_alarm_temperature ne $old_scan_apc_ups_battery_alarm_temperature) or + ($new_scan_apc_ups_battery_voltage ne $old_scan_apc_ups_battery_voltage)) + { + # Something in the 'scan_apc_ups_battery' table changed for this UPS. + $anvil->Log->entry({log_level => 2, message_key => "scan_apc_ups_log_0001", message_variables => { + table => "scan_apc_ups_battery", + serial_number => $ups_serial_number + }, file => $THIS_FILE, line => __LINE__}); + + ### Log the variables to help see what actually changed. + + # Has the estimated replacement date changed? + if ($new_scan_apc_ups_battery_replacement_date ne $old_scan_apc_ups_battery_replacement_date) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_battery_replacement_date" => $new_scan_apc_ups_battery_replacement_date, + "old_scan_apc_ups_battery_replacement_date" => $old_scan_apc_ups_battery_replacement_date, + }}); + + # This is a 'info' level alert because the administrator will most + # likely already know about it. + $anvil->Alert->register_alert({ + alert_level => "info", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0002", + alert_message_key => "scan_apc_ups_note_0018", + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $new_scan_apc_ups_battery_replacement_date, + old_value => $old_scan_apc_ups_battery_replacement_date, + }, + }); + } + + # Has the battery health changed? + if ($new_scan_apc_ups_battery_health ne $old_scan_apc_ups_battery_health) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_battery_health" => $new_scan_apc_ups_battery_health, + "old_scan_apc_ups_battery_health" => $old_scan_apc_ups_battery_health, + }}); + + # Make sure we have a valid health integer + my $bad_value = ""; + my $say_new_scan_apc_ups_battery_health = $new_scan_apc_ups_battery_health; + my $say_old_scan_apc_ups_battery_health = $old_scan_apc_ups_battery_health; + if (($new_scan_apc_ups_battery_health =~ /\D/) or (($new_scan_apc_ups_battery_health < 0) or ($new_scan_apc_ups_battery_health > 2))) + { + # We'll use '99' as a generic "this is not known" + $bad_value = $new_scan_apc_ups_battery_health; + $new_scan_apc_ups_battery_health = 99; + } + elsif (($old_scan_apc_ups_battery_health =~ /\D/) or (($old_scan_apc_ups_battery_health < 0) or ($old_scan_apc_ups_battery_health > 2))) + { + # We'll use '99' as a generic "this is not known" + $bad_value = $old_scan_apc_ups_battery_health; + $old_scan_apc_ups_battery_health = 99; + } + + my $level = "notice"; + my $title_key = "an_alert_title_0003"; + my $message_key = "scan_apc_ups_note_0061"; + + ### There are 2 possible states (* == warning): + # 1 - The UPS battery is healthy. + # *2 - The UPS battery has failed and needs to be replaced. + if ($new_scan_apc_ups_battery_health eq "2") + { + # Entered a warning state. + $level = "warning"; + $title_key = "an_alert_title_0004"; + $message_key = "scan_apc_ups_note_0019"; + } + elsif ($old_scan_apc_ups_battery_health eq "2") + { + # Warning state cleared. + $level = "warning"; + $title_key = "an_alert_title_0006"; + $message_key = "scan_apc_ups_note_0020"; + } + $anvil->Alert->register_alert({ + alert_level => $level, + alert_agent_name => $THIS_FILE, + alert_title_key => $title_key, + alert_message_key => $message_key, + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => "#!string!scan_apc_ups_battery_health_".sprintf("%04d", $say_new_scan_apc_ups_battery_health)."!#", + old_value => "#!string!scan_apc_ups_battery_health_".sprintf("%04d", $say_old_scan_apc_ups_battery_health)."!#", + battery_model => $new_scan_apc_ups_battery_model, + bad_value => $bad_value, + }, + }); + } + + # Has the battery model changed? + if ($new_scan_apc_ups_battery_model ne $old_scan_apc_ups_battery_model) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_battery_model" => $new_scan_apc_ups_battery_model, + "old_scan_apc_ups_battery_model" => $old_scan_apc_ups_battery_model, + }}); + + # This is a 'notice' level alert because the administrator will most + # likely already know about it. + $anvil->Alert->register_alert({ + alert_level => "notice", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0003", + alert_message_key => "scan_apc_ups_note_0021", + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $new_scan_apc_ups_battery_model, + old_value => $old_scan_apc_ups_battery_model, + }, + }); + } + + # If the battery charge percentage has changed, it will usually be an 'info' + # level event, unless it drops below the warning threshold, climbs above the + # OK threshold or hits 100% + if ($new_scan_apc_ups_battery_percentage_charge ne $old_scan_apc_ups_battery_percentage_charge) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_battery_percentage_charge" => $new_scan_apc_ups_battery_percentage_charge, + "old_scan_apc_ups_battery_percentage_charge" => $old_scan_apc_ups_battery_percentage_charge, + }}); + + # By default, this is a simple 'info' level alert. + my $level = "info"; + my $title_key = "an_alert_title_0002"; + my $message_key = "scan_apc_ups_note_0022"; + my $send_alert = 0; + + # Is the battery charging or discharging? + if ($new_scan_apc_ups_battery_percentage_charge > $old_scan_apc_ups_battery_percentage_charge) + { + # Charging + if ($new_scan_apc_ups_battery_percentage_charge eq "100") + { + # If we say power loss in the last hour, make this a + # 'notice'. Otherwise, leave it as 'info' as the UPS + # frequently fluctuates charge percentage. + my $low_limit = 50; + my $query = " +SELECT + round(extract(epoch from b.modified_date)) +FROM + scan_apc_ups a, + history.scan_apc_ups_input b +WHERE + a.scan_apc_ups_uuid = b.scan_apc_ups_input_scan_apc_ups_uuid +AND + a.scan_apc_ups_host_uuid = ".$anvil->Database->quote($anvil->data->{sys}{host_uuid})." +AND + b.scan_apc_ups_input_1m_minimum_input_voltage < $low_limit +ORDER BY + b.modified_date DESC LIMIT 1; +"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + + # Do the query... + my $last_power_loss = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0]; + my $current_time = time; + my $difference = ($current_time - $last_power_loss); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "last_power_loss" => $last_power_loss, + "current_time" => $current_time, + "difference" => $difference, + }}); + + # if it's less than six hours, well assume it has finished charging. + if ($difference < 21600) + { + $level = "notice"; + $title_key = "an_alert_title_0003"; + } + else + { + $level = "info"; + $title_key = "an_alert_title_0002"; + } + $message_key = "scan_apc_ups_note_0024"; + } + elsif ($new_scan_apc_ups_battery_percentage_charge > $anvil->data->{'scan-apc-ups'}{low_charge_percentage_ok}) + { + # Crossed the "now OK" threshold. Was there an alert? + # If so, tell the user. + my $cleared = $anvil->Alert->check_alert_sent({ + type => "clear", + alert_sent_by => $THIS_FILE, + alert_record_locator => $ups_serial_number, + alert_name => "scan_apc_ups_battery_percentage_charge", + modified_date => $anvil->data->{sys}{database}{timestamp}, + }); + if ($cleared) + { + # There was an alert and it has now been + # cleared. + $level = "warning"; + $title_key = "an_alert_title_0006"; + $message_key = "scan_apc_ups_note_0023"; + } + } + } + else + { + # Discharging + $message_key = "scan_apc_ups_note_0025"; + if ($new_scan_apc_ups_battery_percentage_charge < $anvil->data->{'scan-apc-ups'}{low_charge_percentage_warning}) + { + # Crossed the "oh crap" threshold. Is this the first + # time? 'set' is set to 1 if so. + my $set = $anvil->Alert->check_alert_sent({ + type => "warning", + alert_sent_by => $THIS_FILE, + alert_record_locator => $ups_serial_number, + alert_name => "scan_apc_ups_battery_percentage_charge", + modified_date => $anvil->data->{sys}{database}{timestamp}, + }); + if ($set) + { + # This is the first time we dropped below the + # alert threshold. + $level = "warning"; + $title_key = "an_alert_title_0004"; + $message_key = "scan_apc_ups_note_0026"; + } + } + + ### TODO: If there is a sudden drop in charge (ie: 20% drop + ### in one scan loop), the batteries may be about to + ### blow up. Kill the power. + } + $anvil->Alert->register_alert({ + alert_level => $level, + alert_agent_name => $THIS_FILE, + alert_title_key => $title_key, + alert_message_key => $message_key, + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $anvil->Math->round({places => 1, number => $new_scan_apc_ups_battery_percentage_charge}), + old_value => $anvil->Math->round({places => 1, number => $old_scan_apc_ups_battery_percentage_charge}), + }, + }); + } + + # Has the replacement date changed? + if ($new_scan_apc_ups_battery_last_replacement_date ne $old_scan_apc_ups_battery_last_replacement_date) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_battery_last_replacement_date" => $new_scan_apc_ups_battery_last_replacement_date, + "old_scan_apc_ups_battery_last_replacement_date" => $old_scan_apc_ups_battery_last_replacement_date, + }}); + + # This is a 'notice' level alert because the administrator will most + # likely already know about it. + $anvil->Alert->register_alert({ + alert_level => "notice", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0003", + alert_message_key => "scan_apc_ups_note_0027", + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $new_scan_apc_ups_battery_last_replacement_date, + old_value => $old_scan_apc_ups_battery_last_replacement_date, + }, + }); + } + + # Has the battery state changed? There are four possible battery states. + if ($new_scan_apc_ups_battery_state ne $old_scan_apc_ups_battery_state) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_battery_state" => $new_scan_apc_ups_battery_state, + "old_scan_apc_ups_battery_state" => $old_scan_apc_ups_battery_state, + }}); + + my $level = "notice"; + my $title_key = "an_alert_title_0003"; + my $message_key = "scan_apc_ups_note_0028"; + + ### There are four possible states (* == warning): + # *1 - The UPS battery is in an unknown state. + # 2 - The UPS battery is operating normally. + # 3 - The UPS battery is in a low voltage state. + # *4 - The UPS battery is in a failed state and needs to be replaced. + + # Make sure we have a valid health integer + my $bad_value = ""; + my $say_new_scan_apc_ups_battery_state = $new_scan_apc_ups_battery_state; + my $say_old_scan_apc_ups_battery_state = $old_scan_apc_ups_battery_state; + if (($new_scan_apc_ups_battery_state =~ /\D/) or (($new_scan_apc_ups_battery_state < 0) or ($new_scan_apc_ups_battery_state > 4))) + { + # We'll use '99' as a generic "this is not known" + $bad_value = $new_scan_apc_ups_battery_state; + $say_new_scan_apc_ups_battery_state = 99; + } + elsif (($old_scan_apc_ups_battery_state =~ /\D/) or (($old_scan_apc_ups_battery_state < 0) or ($old_scan_apc_ups_battery_state > 4))) + { + # We'll use '99' as a generic "this is not known" + $bad_value = $old_scan_apc_ups_battery_state; + $say_old_scan_apc_ups_battery_state = 99; + } + + if (($new_scan_apc_ups_battery_state eq "1") or ($new_scan_apc_ups_battery_state eq "4")) + { + # Entered a warning state. + $level = "warning"; + $title_key = "an_alert_title_0004"; + } + elsif (($old_scan_apc_ups_battery_state eq "1") or ($old_scan_apc_ups_battery_state eq "4")) + { + # Warning state cleared. + $level = "warning"; + $title_key = "an_alert_title_0006"; + $message_key = "scan_apc_ups_note_0029"; + } + $anvil->Alert->register_alert({ + alert_level => $level, + alert_agent_name => $THIS_FILE, + alert_title_key => $title_key, + alert_message_key => $message_key, + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => "#!string!scan_apc_ups_battery_state_".sprintf("%04d", $say_new_scan_apc_ups_battery_state)."!#", + old_value => "#!string!scan_apc_ups_battery_state_".sprintf("%04d", $say_old_scan_apc_ups_battery_state)."!#", + battery_model => $new_scan_apc_ups_battery_model, + }, + }); + } + + # Has the battery's temperature changed? If so, check to see if it crossed + # over the warning threshold. + if ($new_scan_apc_ups_battery_temperature ne $old_scan_apc_ups_battery_temperature) + { + ### TODO: Add/clear to the 'temperature' table. + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_battery_temperature" => $new_scan_apc_ups_battery_temperature, + "old_scan_apc_ups_battery_temperature" => $old_scan_apc_ups_battery_temperature, + }}); + + # By default, this is an info level alert. + my $level = "info"; + my $title_key = "an_alert_title_0002"; + my $message_key = "scan_apc_ups_note_0030"; + my $send_alert = 0; + + # These are thresholds + my $alert_temperature = ($new_scan_apc_ups_battery_alarm_temperature - $anvil->data->{'scan-apc-ups'}{temperature_warning_delta}); + my $critical_temperature = $new_scan_apc_ups_battery_alarm_temperature; + my $shutdown_temperature = ($new_scan_apc_ups_battery_alarm_temperature + $anvil->data->{'scan-apc-ups'}{temperature_shutdown_delta}); + my $clear_temperature = ($new_scan_apc_ups_battery_alarm_temperature - $anvil->data->{'scan-apc-ups'}{temperature_clear_delta}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "alert_temperature" => $alert_temperature, + "critical_temperature" => $critical_temperature, + "shutdown_temperature" => $shutdown_temperature, + "clear_temperature" => $clear_temperature, + }}); + + # Is the battery's temperature climbing or dropping? + if ($new_scan_apc_ups_battery_temperature > $old_scan_apc_ups_battery_temperature) + { + # Rising. Has it crossed the shutdown, critical or alert + # thresholds? + if ($new_scan_apc_ups_battery_temperature > $shutdown_temperature) + { + # Crossed the shutdown temperature. Kill the UPS. + my $variable = + my $set = $anvil->Alert->check_alert_sent({ + type => "warning", + alert_sent_by => $THIS_FILE, + alert_record_locator => $ups_serial_number, + alert_name => "scan_apc_ups_battery_temperature_shutdown", + modified_date => $anvil->data->{sys}{database}{timestamp}, + }); + if ($set) + { + # This is the first time we rose above the + # critical threshold. + $level = "critical"; + $title_key = "an_alert_title_0005"; + $message_key = "scan_apc_ups_note_0035"; + } + push @{$shutdown_ups}, $new_scan_apc_ups_ip; + } + elsif ($new_scan_apc_ups_battery_temperature > $critical_temperature) + { + # Crossed the critical threshold. Is this the first + # time? 'set' is set to 1 if so. + my $set = $anvil->Alert->check_alert_sent({ + type => "warning", + alert_sent_by => $THIS_FILE, + alert_record_locator => $ups_serial_number, + alert_name => "scan_apc_ups_battery_temperature_critical", + modified_date => $anvil->data->{sys}{database}{timestamp}, + }); + if ($set) + { + # This is the first time we rose above the + # critical threshold. + $level = "critical"; + $title_key = "an_alert_title_0005"; + $message_key = "scan_apc_ups_note_0034"; + } + } + elsif ($new_scan_apc_ups_battery_temperature > $alert_temperature) + { + # Crossed the "oh crap" threshold. Is this the first + # time? 'set' is set to 1 if so. + my $set = $anvil->Alert->check_alert_sent({ + type => "warning", + alert_sent_by => $THIS_FILE, + alert_record_locator => $ups_serial_number, + alert_name => "scan_apc_ups_battery_temperature_warning", + modified_date => $anvil->data->{sys}{database}{timestamp}, + }); + if ($set) + { + # This is the first time we rose above the + # warning threshold. + $level = "warning"; + $title_key = "an_alert_title_0004"; + $message_key = "scan_apc_ups_note_0031"; + } + } + } + else + { + # Dropping. + $message_key = "scan_apc_ups_note_0032"; + + # If it dropped below the 'clear' threshold, look for and + # clear 'shutdown', 'critical' and 'warning' alerts. + + # If it dropped below the 'warning' threshold, look for and + # clear 'shutdown' and 'critical' alerts. + if ($new_scan_apc_ups_battery_temperature < $clear_temperature) + { + # Clear all alerts, if any exist. + my $cleared_shutdown = $anvil->Alert->check_alert_sent({ + type => "clear", + alert_sent_by => $THIS_FILE, + alert_record_locator => $ups_serial_number, + alert_name => "scan_apc_ups_battery_temperature_shutdown", + modified_date => $anvil->data->{sys}{database}{timestamp}, + }); + my $cleared_critical = $anvil->Alert->check_alert_sent({ + type => "clear", + alert_sent_by => $THIS_FILE, + alert_record_locator => $ups_serial_number, + alert_name => "scan_apc_ups_battery_temperature_critical", + modified_date => $anvil->data->{sys}{database}{timestamp}, + }); + my $cleared_warning = $anvil->Alert->check_alert_sent({ + type => "clear", + alert_sent_by => $THIS_FILE, + alert_record_locator => $ups_serial_number, + alert_name => "scan_apc_ups_battery_temperature_warning", + modified_date => $anvil->data->{sys}{database}{timestamp}, + }); + if (($cleared_shutdown) or ($cleared_critical) or ($cleared_warning)) + { + # The temperature has dropped back down to + # safe levels. + $level = "warning"; + $title_key = "an_alert_title_0006"; + $message_key = "scan_apc_ups_note_0033"; + } + } + elsif ($new_scan_apc_ups_battery_temperature < $alert_temperature) + { + # Clear 'shutdown' and 'critical' alerts. + my $cleared_shutdown = $anvil->Alert->check_alert_sent({ + type => "clear", + alert_sent_by => $THIS_FILE, + alert_record_locator => $ups_serial_number, + alert_name => "scan_apc_ups_battery_temperature_shutdown", + modified_date => $anvil->data->{sys}{database}{timestamp}, + }); + my $cleared_critical = $anvil->Alert->check_alert_sent({ + type => "clear", + alert_sent_by => $THIS_FILE, + alert_record_locator => $ups_serial_number, + alert_name => "scan_apc_ups_battery_temperature_critical", + modified_date => $anvil->data->{sys}{database}{timestamp}, + }); + if (($cleared_shutdown) or ($cleared_critical)) + { + # The temperature has dropped back down to + # warning levels. + $level = "warning"; + $title_key = "an_alert_title_0006"; + $message_key = "scan_apc_ups_note_0036"; + } + } + } + $anvil->Alert->register_alert({ + alert_level => $level, + alert_agent_name => $THIS_FILE, + alert_title_key => $title_key, + alert_message_key => $message_key, + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $anvil->Math->round({places => 1, number => $new_scan_apc_ups_battery_temperature}), + old_value => $anvil->Math->round({places => 1, number => $old_scan_apc_ups_battery_temperature}), + critical_temperature => $new_scan_apc_ups_battery_alarm_temperature, + shutdown_temperature => $shutdown_temperature, + }, + }); + } + + # Has the alarm temperature changed? + if ($new_scan_apc_ups_battery_alarm_temperature ne $old_scan_apc_ups_battery_alarm_temperature) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_battery_alarm_temperature" => $new_scan_apc_ups_battery_alarm_temperature, + "old_scan_apc_ups_battery_alarm_temperature" => $old_scan_apc_ups_battery_alarm_temperature, + }}); + + # This is a 'notice' level alert because the administrator will most + # likely already know about it. + $anvil->Alert->register_alert({ + alert_level => "notice", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0003", + alert_message_key => "scan_apc_ups_note_0037", + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $new_scan_apc_ups_battery_alarm_temperature, + old_value => $old_scan_apc_ups_battery_alarm_temperature, + }, + }); + } + + # Has the UPS battery voltage changed? + if ($new_scan_apc_ups_battery_voltage ne $old_scan_apc_ups_battery_voltage) + { + ### TODO: Determine safe voltages and alarm/kill the UPS if needed. + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_battery_voltage" => $new_scan_apc_ups_battery_voltage, + "old_scan_apc_ups_battery_voltage" => $old_scan_apc_ups_battery_voltage, + }}); + + # This is an 'info' level alert because the voltage is always + # fluctuating. + $anvil->Alert->register_alert({ + alert_level => "info", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0002", + alert_message_key => "scan_apc_ups_note_0038", + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $anvil->Math->round({places => 1, number => $new_scan_apc_ups_battery_voltage}), + old_value => $anvil->Math->round({places => 1, number => $old_scan_apc_ups_battery_voltage}), + }, + }); + } + + # Update the DB table + my $query = " +UPDATE + scan_apc_ups_battery +SET + scan_apc_ups_battery_replacement_date = ".$anvil->Database->quote($new_scan_apc_ups_battery_replacement_date).", + scan_apc_ups_battery_health = ".$anvil->Database->quote($new_scan_apc_ups_battery_health).", + scan_apc_ups_battery_model = ".$anvil->Database->quote($new_scan_apc_ups_battery_model).", + scan_apc_ups_battery_percentage_charge = ".$anvil->Database->quote($new_scan_apc_ups_battery_percentage_charge).", + scan_apc_ups_battery_last_replacement_date = ".$anvil->Database->quote($new_scan_apc_ups_battery_last_replacement_date).", + scan_apc_ups_battery_state = ".$anvil->Database->quote($new_scan_apc_ups_battery_state).", + scan_apc_ups_battery_temperature = ".$anvil->Database->quote($new_scan_apc_ups_battery_temperature).", + scan_apc_ups_battery_alarm_temperature = ".$anvil->Database->quote($new_scan_apc_ups_battery_alarm_temperature).", + scan_apc_ups_battery_voltage = ".$anvil->Database->quote($new_scan_apc_ups_battery_voltage).", + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +WHERE + scan_apc_ups_battery_uuid = + ( + SELECT + b.scan_apc_ups_battery_uuid + FROM + scan_apc_ups a, + scan_apc_ups_battery b + WHERE + a.scan_apc_ups_uuid = b.scan_apc_ups_battery_scan_apc_ups_uuid + AND + a.scan_apc_ups_uuid = ".$anvil->Database->quote($scan_apc_ups_uuid)." + AND + a.scan_apc_ups_host_uuid = ".$anvil->Database->quote($anvil->data->{sys}{host_uuid})." + ) +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$anvil->data->{sys}{queries}}, $query; + + # Now figure out what changed and report it to the user. + } + + # Now check for changes in the input power + if (($new_scan_apc_ups_input_frequency ne $old_scan_apc_ups_input_frequency) or + ($new_scan_apc_ups_input_sensitivity ne $old_scan_apc_ups_input_sensitivity) or + ($new_scan_apc_ups_input_voltage ne $old_scan_apc_ups_input_voltage) or + ($new_scan_apc_ups_input_1m_maximum_input_voltage ne $old_scan_apc_ups_input_1m_maximum_input_voltage) or + ($new_scan_apc_ups_input_1m_minimum_input_voltage ne $old_scan_apc_ups_input_1m_minimum_input_voltage)) + { + # Something in the 'scan_apc_ups_input' table changed for this UPS. + $anvil->Log->entry({log_level => 2, message_key => "scan_apc_ups_log_0001", message_variables => { + table => "scan_apc_ups_input", + serial_number => $ups_serial_number + }, file => $THIS_FILE, line => __LINE__}); + + # Did the input frequency change? + if ($new_scan_apc_ups_input_frequency ne $old_scan_apc_ups_input_frequency) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_input_frequency" => $new_scan_apc_ups_input_frequency, + "old_scan_apc_ups_input_frequency" => $old_scan_apc_ups_input_frequency, + }}); + + # This is an 'info' level alert because the input frequency is always + # fluctuating. + $anvil->Alert->register_alert({ + alert_level => "info", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0002", + alert_message_key => "scan_apc_ups_note_0039", + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $anvil->Math->round({places => 1, number => $new_scan_apc_ups_input_frequency}), + old_value => $anvil->Math->round({places => 1, number => $old_scan_apc_ups_input_frequency}), + }, + }); + } + + # Did the input sensitivity change? + if ($new_scan_apc_ups_input_sensitivity ne $old_scan_apc_ups_input_sensitivity) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_input_sensitivity" => $new_scan_apc_ups_input_sensitivity, + "old_scan_apc_ups_input_sensitivity" => $old_scan_apc_ups_input_sensitivity, + }}); + + # Make sure we have a valid health integer + my $bad_value = ""; + my $say_new_scan_apc_ups_input_sensitivity = $new_scan_apc_ups_input_sensitivity; + my $say_old_scan_apc_ups_input_sensitivity = $old_scan_apc_ups_input_sensitivity; + if (($new_scan_apc_ups_input_sensitivity =~ /\D/) or (($new_scan_apc_ups_input_sensitivity < 0) or ($new_scan_apc_ups_input_sensitivity > 4))) + { + # We'll use '99' as a generic "this is not known" + $bad_value = $new_scan_apc_ups_input_sensitivity; + $say_new_scan_apc_ups_input_sensitivity = 99; + } + elsif (($old_scan_apc_ups_input_sensitivity =~ /\D/) or (($old_scan_apc_ups_input_sensitivity < 0) or ($old_scan_apc_ups_input_sensitivity > 4))) + { + # We'll use '99' as a generic "this is not known" + $bad_value = $old_scan_apc_ups_input_sensitivity; + $say_old_scan_apc_ups_input_sensitivity = 99; + } + + # This is an 'notice' level event because the admin probably made the + # change + $anvil->Alert->register_alert({ + alert_level => "notice", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0003", + alert_message_key => "scan_apc_ups_note_0040", + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => "#!string!scan_apc_ups_sensitivity_".sprintf("%04d", $say_new_scan_apc_ups_input_sensitivity)."!#", + old_value => "#!string!scan_apc_ups_sensitivity_".sprintf("%04d", $say_old_scan_apc_ups_input_sensitivity)."!#", + }, + }); + } + + # Has the input voltage changed? + if ($new_scan_apc_ups_input_voltage ne $old_scan_apc_ups_input_voltage) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_input_voltage" => $new_scan_apc_ups_input_voltage, + "old_scan_apc_ups_input_voltage" => $old_scan_apc_ups_input_voltage, + }}); + + # This is always changing, so normally we will use an 'info' level + # alert. + my $level = "info"; + my $title_key = "an_alert_title_0002"; + my $message_key = "scan_apc_ups_note_0041"; + my $send_alert = 0; + + # These are thresholds + my $high_transfer_voltage = $new_scan_apc_ups_high_transfer_voltage; + my $clear_high_transfer = ($new_scan_apc_ups_high_transfer_voltage - $anvil->data->{'scan-apc-ups'}{transfer_voltage_clear_delta}); + my $low_transfer_voltage = $new_scan_apc_ups_low_transfer_voltage; + my $clear_low_transfer = ($new_scan_apc_ups_low_transfer_voltage + $anvil->data->{'scan-apc-ups'}{transfer_voltage_clear_delta}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "high_transfer_voltage" => $high_transfer_voltage, + "clear_high_transfer" => $clear_high_transfer, + "low_transfer_voltage" => $low_transfer_voltage, + "clear_low_transfer" => $clear_low_transfer, + }}); + + # Is the voltage rising or falling? + if ($new_scan_apc_ups_input_voltage > $old_scan_apc_ups_input_voltage) + { + # It is rising. + + # Has it crossed over the high-voltage transfer threshold? + # If not, has it crossed over the low-voltage clear level? + if ($old_scan_apc_ups_input_voltage == 0) + { + # Power has been restored. + my $cleared = $anvil->Alert->check_alert_sent({ + type => "clear", + alert_sent_by => $THIS_FILE, + alert_record_locator => $ups_serial_number, + alert_name => "scan_apc_ups_input_voltage_low", + modified_date => $anvil->data->{sys}{database}{timestamp}, + }); + if ($cleared) + { + # It is over the clear low-transfer level, + # clear the alert if needed. + $level = "warning"; + $title_key = "an_alert_title_0006"; + $message_key = "scan_apc_ups_note_0057"; + } + } + elsif ($new_scan_apc_ups_input_voltage > $high_transfer_voltage) + { + # See if the alert exists already + my $set = $anvil->Alert->check_alert_sent({ + type => "warning", + alert_sent_by => $THIS_FILE, + alert_record_locator => $ups_serial_number, + alert_name => "scan_apc_ups_input_voltage_high", + modified_date => $anvil->data->{sys}{database}{timestamp}, + }); + if ($set) + { + # Yup, set the alert if needed. + $level = "warning"; + $title_key = "an_alert_title_0004"; + $message_key = "scan_apc_ups_note_0043"; + } + } + elsif ($new_scan_apc_ups_input_voltage > $clear_low_transfer) + { + my $cleared = $anvil->Alert->check_alert_sent({ + type => "clear", + alert_sent_by => $THIS_FILE, + alert_record_locator => $ups_serial_number, + alert_name => "scan_apc_ups_input_voltage_low", + modified_date => $anvil->data->{sys}{database}{timestamp}, + }); + if ($cleared) + { + # It is over the clear low-transfer level, + # clear the alert if needed. + $level = "warning"; + $title_key = "an_alert_title_0006"; + $message_key = "scan_apc_ups_note_0045"; + } + } + } + else + { + # It is falling. + $message_key = "scan_apc_ups_note_0042"; + + # Has it dropped below the low transfer voltage? If not, has + # it dropped below the high-voltage clear level? + if ($new_scan_apc_ups_input_voltage == 0) + { + # Power lost entirely + my $set = $anvil->Alert->check_alert_sent({ + type => "warning", + alert_sent_by => $THIS_FILE, + alert_record_locator => $ups_serial_number, + alert_name => "scan_apc_ups_input_voltage_low", + modified_date => $anvil->data->{sys}{database}{timestamp}, + }); + if ($set) + { + $level = "warning"; + $title_key = "an_alert_title_0004"; + $message_key = "scan_apc_ups_note_0056"; + } + } + elsif ($new_scan_apc_ups_input_voltage < $low_transfer_voltage) + { + # It is below the low-transfer limit, set an alert if + # needed. + my $set = $anvil->Alert->check_alert_sent({ + type => "warning", + alert_sent_by => $THIS_FILE, + alert_record_locator => $ups_serial_number, + alert_name => "scan_apc_ups_input_voltage_low", + modified_date => $anvil->data->{sys}{database}{timestamp}, + }); + if ($set) + { + $level = "warning"; + $title_key = "an_alert_title_0004"; + $message_key = "scan_apc_ups_note_0044"; + } + } + elsif ($new_scan_apc_ups_input_voltage < $clear_high_transfer) + { + my $cleared = $anvil->Alert->check_alert_sent({ + type => "clear", + alert_sent_by => $THIS_FILE, + alert_record_locator => $ups_serial_number, + alert_name => "scan_apc_ups_input_voltage_high", + modified_date => $anvil->data->{sys}{database}{timestamp}, + }); + if ($cleared) + { + # Clear the high-voltage transfer warning. + $level = "warning"; + $title_key = "an_alert_title_0006"; + $message_key = "scan_apc_ups_note_0046"; + } + } + } + $anvil->Alert->register_alert({ + alert_level => $level, + alert_agent_name => $THIS_FILE, + alert_title_key => $title_key, + alert_message_key => $message_key, + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $anvil->Math->round({places => 1, number => $new_scan_apc_ups_input_voltage}), + old_value => $anvil->Math->round({places => 1, number => $old_scan_apc_ups_input_voltage}), + holdup_time => $anvil->Readable->time({'time' => $new_scan_apc_ups_output_estimated_runtime}), + }, + }); + } + + # Has the maximum input voltage seen in the last 60 seconds changed? + if ($new_scan_apc_ups_input_1m_maximum_input_voltage > $old_scan_apc_ups_input_1m_maximum_input_voltage) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_input_1m_maximum_input_voltage" => $new_scan_apc_ups_input_1m_maximum_input_voltage, + "old_scan_apc_ups_input_1m_maximum_input_voltage" => $old_scan_apc_ups_input_1m_maximum_input_voltage, + }}); + + # This is an info level alert as it changes all the time. + $anvil->Alert->register_alert({ + alert_level => "info", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0002", + alert_message_key => "scan_apc_ups_note_0047", + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $anvil->Math->round({places => 1, number => $new_scan_apc_ups_input_1m_maximum_input_voltage}), + old_value => $anvil->Math->round({places => 1, number => $old_scan_apc_ups_input_1m_maximum_input_voltage}), + }, + }); + } + + # Has the minimum input voltage seen in the last 60 seconds changed? + if ($new_scan_apc_ups_input_1m_maximum_input_voltage > $old_scan_apc_ups_input_1m_maximum_input_voltage) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_input_1m_minimum_input_voltage" => $new_scan_apc_ups_input_1m_minimum_input_voltage, + "old_scan_apc_ups_input_1m_minimum_input_voltage" => $old_scan_apc_ups_input_1m_minimum_input_voltage, + }}); + + # This is an info level alert as it changes all the time. + $anvil->Alert->register_alert({ + alert_level => "info", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0002", + alert_message_key => "scan_apc_ups_note_0048", + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $anvil->Math->round({places => 1, number => $new_scan_apc_ups_input_1m_maximum_input_voltage}), + old_value => $anvil->Math->round({places => 1, number => $old_scan_apc_ups_input_1m_maximum_input_voltage}), + }, + }); + } + + # Update the DB table + my $query = " +UPDATE + scan_apc_ups_input +SET + scan_apc_ups_input_frequency = ".$anvil->Database->quote($new_scan_apc_ups_input_frequency).", + scan_apc_ups_input_sensitivity = ".$anvil->Database->quote($new_scan_apc_ups_input_sensitivity).", + scan_apc_ups_input_voltage = ".$anvil->Database->quote($new_scan_apc_ups_input_voltage).", + scan_apc_ups_input_1m_maximum_input_voltage = ".$anvil->Database->quote($new_scan_apc_ups_input_1m_maximum_input_voltage).", + scan_apc_ups_input_1m_minimum_input_voltage = ".$anvil->Database->quote($new_scan_apc_ups_input_1m_minimum_input_voltage).", + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +WHERE + scan_apc_ups_input_uuid = + ( + SELECT + b.scan_apc_ups_input_uuid + FROM + scan_apc_ups a, + scan_apc_ups_input b + WHERE + a.scan_apc_ups_uuid = b.scan_apc_ups_input_scan_apc_ups_uuid + AND + a.scan_apc_ups_uuid = ".$anvil->Database->quote($scan_apc_ups_uuid)." + AND + a.scan_apc_ups_host_uuid = ".$anvil->Database->quote($anvil->data->{sys}{host_uuid})." + ) +"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$anvil->data->{sys}{queries}}, $query; + + # Now figure out what changed and report it to the user. + } + + # Finally, check the output power. + if (($new_scan_apc_ups_output_load_percentage ne $old_scan_apc_ups_output_load_percentage) or + ($new_scan_apc_ups_output_time_on_batteries ne $old_scan_apc_ups_output_time_on_batteries) or + ($new_scan_apc_ups_output_estimated_runtime ne $old_scan_apc_ups_output_estimated_runtime) or + ($new_scan_apc_ups_output_frequency ne $old_scan_apc_ups_output_frequency) or + ($new_scan_apc_ups_output_voltage ne $old_scan_apc_ups_output_voltage) or + ($new_scan_apc_ups_output_total_output ne $old_scan_apc_ups_output_total_output)) + { + # Something in the 'scan_apc_ups_input' table changed for this UPS. + $anvil->Log->entry({log_level => 2, message_key => "scan_apc_ups_log_0001", message_variables => { + table => "scan_apc_ups_output", + serial_number => $ups_serial_number + }, file => $THIS_FILE, line => __LINE__}); + + # Did the output load percentage change? + if ($new_scan_apc_ups_output_load_percentage > $old_scan_apc_ups_output_load_percentage) + { + ### TODO: Determine an alert threshold for max load + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_output_load_percentage" => $new_scan_apc_ups_output_load_percentage, + "old_scan_apc_ups_output_load_percentage" => $old_scan_apc_ups_output_load_percentage, + }}); + + # This is an info level alert as it changes all the time. + $anvil->Alert->register_alert({ + alert_level => "info", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0002", + alert_message_key => "scan_apc_ups_note_0049", + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $new_scan_apc_ups_output_load_percentage, + old_value => $old_scan_apc_ups_output_load_percentage, + }, + }); + } + + # Has the time on batteries value changed? + if ($new_scan_apc_ups_output_time_on_batteries > $old_scan_apc_ups_output_load_percentage) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_output_time_on_batteries" => $new_scan_apc_ups_output_time_on_batteries, + "old_scan_apc_ups_output_time_on_batteries" => $old_scan_apc_ups_output_time_on_batteries, + }}); + + # If the old value was '0', we just switched to batteries. This will + # be a 'notice' level event. Likewise if the new time is '0'. + # Otherwise, it is 'info'. We don't do 'warning' because that is + # caught by the loss of input voltage. + my $level = "info"; + my $title_key = "an_alert_title_0002"; + my $message_key = "scan_apc_ups_note_0052"; + my $send_alert = 0; + + # Is the old time '0'? + if ($new_scan_apc_ups_output_time_on_batteries eq "0") + { + # We're on batteries now. + $level = "notice"; + $title_key = "an_alert_title_0003"; + $message_key = "scan_apc_ups_note_0051"; + } + elsif ($old_scan_apc_ups_output_time_on_batteries eq "0") + { + # We're no longer on batteries. + $level = "notice"; + $title_key = "an_alert_title_0003"; + $message_key = "scan_apc_ups_note_0050"; + } + else + { + # A normal change. + } + + $anvil->Alert->register_alert({ + alert_level => $level, + alert_agent_name => $THIS_FILE, + alert_title_key => $title_key, + alert_message_key => $message_key, + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $anvil->Readable->time({'time' => $new_scan_apc_ups_output_time_on_batteries}), + old_value => $anvil->Readable->time({'time' => $old_scan_apc_ups_output_time_on_batteries}), + }, + }); + } + + # Has the estimated runtime changed? + if ($new_scan_apc_ups_output_estimated_runtime ne $old_scan_apc_ups_output_estimated_runtime) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_output_estimated_runtime" => $new_scan_apc_ups_output_estimated_runtime, + "old_scan_apc_ups_output_estimated_runtime" => $old_scan_apc_ups_output_estimated_runtime, + }}); + + # This is an info level alert. Deciding to shutdown is handled by + # ScanCore. + $anvil->Alert->register_alert({ + alert_level => "info", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0002", + alert_message_key => "scan_apc_ups_note_0053", + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $anvil->Readable->time({'time' => $new_scan_apc_ups_output_estimated_runtime}), + old_value => $anvil->Readable->time({'time' => $old_scan_apc_ups_output_estimated_runtime}), + }, + }); + } + + # Has the output frequency changed? + if ($new_scan_apc_ups_output_frequency ne $old_scan_apc_ups_output_frequency) + { + ### TODO: Determine safe minimum and maximum frequencies + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_output_frequency" => $new_scan_apc_ups_output_frequency, + "old_scan_apc_ups_output_frequency" => $old_scan_apc_ups_output_frequency, + }}); + + # This is an info level alert as it changes frequently + $anvil->Alert->register_alert({ + alert_level => "info", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0002", + alert_message_key => "scan_apc_ups_note_0054", + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $anvil->Math->round({places => 1, number => $new_scan_apc_ups_output_frequency}), + old_value => $anvil->Math->round({places => 1, number => $old_scan_apc_ups_output_frequency}), + }, + }); + } + + # Has the output voltage changed? + if ($new_scan_apc_ups_output_voltage ne $old_scan_apc_ups_output_voltage) + { + ### TODO: Determine if we need to worry about safe minimum and + ### maximum output voltages + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_output_voltage" => $new_scan_apc_ups_output_voltage, + "old_scan_apc_ups_output_voltage" => $old_scan_apc_ups_output_voltage, + }}); + + # This is an info level alert as it changes frequently + $anvil->Alert->register_alert({ + alert_level => "info", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0002", + alert_message_key => "scan_apc_ups_note_0055", + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $anvil->Math->round({places => 1, number => $new_scan_apc_ups_output_voltage}), + old_value => $anvil->Math->round({places => 1, number => $old_scan_apc_ups_output_voltage}), + }, + }); + } + + # This is always changing... + if ($new_scan_apc_ups_output_total_output ne $old_scan_apc_ups_output_total_output) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "new_scan_apc_ups_output_total_output" => $new_scan_apc_ups_output_total_output, + "old_scan_apc_ups_output_total_output" => $old_scan_apc_ups_output_total_output, + }}); + + # This is a debug level alert as it is going to change on just about + # every scan... + $anvil->Alert->register_alert({ + alert_level => "debug", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0001", + alert_message_key => "scan_apc_ups_note_0058", + alert_message_variables => { + ups_name => $new_scan_apc_ups_name, + new_value => $anvil->Math->round({places => 2, number => $new_scan_apc_ups_output_total_output}), + old_value => $anvil->Math->round({places => 2, number => $old_scan_apc_ups_output_total_output}), + }, + }); + } + + # Update the DB table + my $query = " +UPDATE + scan_apc_ups_output +SET + scan_apc_ups_output_load_percentage = ".$anvil->Database->quote($new_scan_apc_ups_output_load_percentage).", + scan_apc_ups_output_time_on_batteries = ".$anvil->Database->quote($new_scan_apc_ups_output_time_on_batteries).", + scan_apc_ups_output_estimated_runtime = ".$anvil->Database->quote($new_scan_apc_ups_output_estimated_runtime).", + scan_apc_ups_output_frequency = ".$anvil->Database->quote($new_scan_apc_ups_output_frequency).", + scan_apc_ups_output_voltage = ".$anvil->Database->quote($new_scan_apc_ups_output_voltage).", + scan_apc_ups_output_total_output = ".$anvil->Database->quote($new_scan_apc_ups_output_total_output).", + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +WHERE + scan_apc_ups_output_id = + ( + SELECT + b.scan_apc_ups_output_id + FROM + scan_apc_ups a, + scan_apc_ups_output b + WHERE + a.scan_apc_ups_uuid = b.scan_apc_ups_output_scan_apc_ups_uuid + AND + a.scan_apc_ups_uuid = ".$anvil->Database->quote($scan_apc_ups_uuid)." + AND + a.scan_apc_ups_host_uuid = ".$anvil->Database->quote($anvil->data->{sys}{host_uuid})." + ) +"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$anvil->data->{sys}{queries}}, $query; + + # Now figure out what changed and report it to the user. + } + } + else + { + ### No record found, INSERT everything. + # I'll need a new UUID + #die "$THIS_FILE ".__LINE__."; UPS: [$new_scan_apc_ups_name] has no serial number and I was about to INSERT..." if $new_scan_apc_ups_nmc_serial_number eq "--"; + my $scan_apc_ups_uuid = $anvil->Get->uuid() or $anvil->Alert->error({title_key => "error_title_0020", message_key => "error_message_0024", code => 2, file => $THIS_FILE, line => __LINE__});; + my $query = " +INSERT INTO + scan_apc_ups +( + scan_apc_ups_uuid, + scan_apc_ups_host_uuid, + scan_apc_ups_name, + scan_apc_ups_ip, + scan_apc_ups_ac_restore_delay, + scan_apc_ups_shutdown_delay, + scan_apc_ups_firmware_version, + scan_apc_ups_health, + scan_apc_ups_high_transfer_voltage, + scan_apc_ups_low_transfer_voltage, + scan_apc_ups_last_transfer_reason, + scan_apc_ups_manufactured_date, + scan_apc_ups_model, + scan_apc_ups_temperature_units, + scan_apc_ups_serial_number, + scan_apc_ups_nmc_firmware_version, + scan_apc_ups_nmc_serial_number, + scan_apc_ups_nmc_mac_address, + modified_date +) VALUES ( + ".$anvil->Database->quote($scan_apc_ups_uuid).", + ".$anvil->Database->quote($anvil->data->{sys}{host_uuid}).", + ".$anvil->Database->quote($new_scan_apc_ups_name).", + ".$anvil->Database->quote($new_scan_apc_ups_ip).", + ".$anvil->Database->quote($new_scan_apc_ups_ac_restore_delay).", + ".$anvil->Database->quote($new_scan_apc_ups_shutdown_delay).", + ".$anvil->Database->quote($new_scan_apc_ups_firmware_version).", + ".$anvil->Database->quote($new_scan_apc_ups_health).", + ".$anvil->Database->quote($new_scan_apc_ups_high_transfer_voltage).", + ".$anvil->Database->quote($new_scan_apc_ups_low_transfer_voltage).", + ".$anvil->Database->quote($new_scan_apc_ups_last_transfer_reason).", + ".$anvil->Database->quote($new_scan_apc_ups_manufactured_date).", + ".$anvil->Database->quote($new_scan_apc_ups_model).", + ".$anvil->Database->quote($new_scan_apc_ups_temperature_units).", + ".$anvil->Database->quote($new_scan_apc_ups_serial_number).", + ".$anvil->Database->quote($new_scan_apc_ups_nmc_firmware_version).", + ".$anvil->Database->quote($new_scan_apc_ups_nmc_serial_number).", + ".$anvil->Database->quote($new_scan_apc_ups_nmc_mac_address).", + ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +);"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$anvil->data->{sys}{queries}}, $query; + + # Now INSERT the battery info. + $query = " +INSERT INTO + scan_apc_ups_battery +( + scan_apc_ups_battery_scan_apc_ups_uuid, + scan_apc_ups_battery_replacement_date, + scan_apc_ups_battery_health, + scan_apc_ups_battery_model, + scan_apc_ups_battery_percentage_charge, + scan_apc_ups_battery_last_replacement_date, + scan_apc_ups_battery_state, + scan_apc_ups_battery_temperature, + scan_apc_ups_battery_alarm_temperature, + scan_apc_ups_battery_voltage, + modified_date +) VALUES ( + ".$anvil->Database->quote($scan_apc_ups_uuid).", + ".$anvil->Database->quote($new_scan_apc_ups_battery_replacement_date).", + ".$anvil->Database->quote($new_scan_apc_ups_battery_health).", + ".$anvil->Database->quote($new_scan_apc_ups_battery_model).", + ".$anvil->Database->quote($new_scan_apc_ups_battery_percentage_charge).", + ".$anvil->Database->quote($new_scan_apc_ups_battery_last_replacement_date).", + ".$anvil->Database->quote($new_scan_apc_ups_battery_state).", + ".$anvil->Database->quote($new_scan_apc_ups_battery_temperature).", + ".$anvil->Database->quote($new_scan_apc_ups_battery_alarm_temperature).", + ".$anvil->Database->quote($new_scan_apc_ups_battery_voltage).", + ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +);"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$anvil->data->{sys}{queries}}, $query; + + # Next, INSERT the input info. + $query = " +INSERT INTO + scan_apc_ups_input +( + scan_apc_ups_input_scan_apc_ups_uuid, + scan_apc_ups_input_frequency, + scan_apc_ups_input_sensitivity, + scan_apc_ups_input_voltage, + scan_apc_ups_input_1m_maximum_input_voltage, + scan_apc_ups_input_1m_minimum_input_voltage, + modified_date +) VALUES ( + ".$anvil->Database->quote($scan_apc_ups_uuid).", + ".$anvil->Database->quote($new_scan_apc_ups_input_frequency).", + ".$anvil->Database->quote($new_scan_apc_ups_input_sensitivity).", + ".$anvil->Database->quote($new_scan_apc_ups_input_voltage).", + ".$anvil->Database->quote($new_scan_apc_ups_input_1m_maximum_input_voltage).", + ".$anvil->Database->quote($new_scan_apc_ups_input_1m_minimum_input_voltage).", + ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +);"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$anvil->data->{sys}{queries}}, $query; + + # And finally, INSERT the output info. + $query = " +INSERT INTO + scan_apc_ups_output +( + scan_apc_ups_output_scan_apc_ups_uuid, + scan_apc_ups_output_load_percentage, + scan_apc_ups_output_time_on_batteries, + scan_apc_ups_output_estimated_runtime, + scan_apc_ups_output_frequency, + scan_apc_ups_output_voltage, + scan_apc_ups_output_total_output, + modified_date +) VALUES ( + ".$anvil->Database->quote($scan_apc_ups_uuid).", + ".$anvil->Database->quote($new_scan_apc_ups_output_load_percentage).", + ".$anvil->Database->quote($new_scan_apc_ups_output_time_on_batteries).", + ".$anvil->Database->quote($new_scan_apc_ups_output_estimated_runtime).", + ".$anvil->Database->quote($new_scan_apc_ups_output_frequency).", + ".$anvil->Database->quote($new_scan_apc_ups_output_voltage).", + ".$anvil->Database->quote($new_scan_apc_ups_output_total_output).", + ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +);"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$anvil->data->{sys}{queries}}, $query; + + ### TODO: Send an email/alert notifying of this new UPS being found. + my $say_time_on_batteries = $new_scan_apc_ups_output_time_on_batteries ? "#!string!scan_apc_ups_message_0007!#" : "#!string!scan_apc_ups_message_0006!#"; + $anvil->Alert->register_alert({ + alert_level => "notice", + alert_agent_name => $THIS_FILE, + alert_title_key => "scan_apc_ups_message_0004", + alert_title_variables => { + name => $new_scan_apc_ups_name, + }, + alert_message_key => "scan_apc_ups_message_0005", + alert_message_variables => { + model => $new_scan_apc_ups_model, + serial_number => $new_scan_apc_ups_serial_number, + manufactured_date => $new_scan_apc_ups_manufactured_date, + name => $new_scan_apc_ups_name, + ip => $new_scan_apc_ups_ip, + firmware_version => $new_scan_apc_ups_firmware_version, + nmc_serial_number => $new_scan_apc_ups_nmc_serial_number, + nmc_firmware_version => $new_scan_apc_ups_nmc_firmware_version, + nmc_mac_address => $new_scan_apc_ups_nmc_mac_address, + battery_model => $new_scan_apc_ups_battery_model, + battery_last_replacement_date => $new_scan_apc_ups_battery_last_replacement_date, + battery_replacement_date => $new_scan_apc_ups_battery_replacement_date, + ac_restore_delay => $new_scan_apc_ups_ac_restore_delay, + shutdown_delay => $new_scan_apc_ups_shutdown_delay, + low_transfer_voltage => $new_scan_apc_ups_low_transfer_voltage, + high_transfer_voltage => $new_scan_apc_ups_high_transfer_voltage, + input_sensitivity => "#!string!scan_apc_ups_sensitivity_".sprintf("%04d", $new_scan_apc_ups_input_sensitivity)."!#", + battery_temperature => $anvil->Math->round({number => $new_scan_apc_ups_battery_temperature, places => 1}), + battery_alarm_temperature => $new_scan_apc_ups_battery_alarm_temperature, + battery_voltage => $anvil->Math->round({number => $new_scan_apc_ups_battery_voltage, places => 1}), + input_frequency => $anvil->Math->round({number => $new_scan_apc_ups_input_frequency, places => 1}), + input_voltage => $anvil->Math->round({number => $new_scan_apc_ups_input_voltage, places => 1}), + input_1m_maximum_input_voltage => $anvil->Math->round({number => $new_scan_apc_ups_input_1m_maximum_input_voltage, places => 1}), + input_1m_minimum_input_voltage => $anvil->Math->round({number => $new_scan_apc_ups_input_1m_minimum_input_voltage, places => 1}), + say_estimated_runtime => $anvil->Readable->time({'time' => $new_scan_apc_ups_output_estimated_runtime}), + output_load_percentage => $anvil->Math->round({number => $new_scan_apc_ups_output_load_percentage, places => 1}), + output_frequency => $anvil->Math->round({number => $new_scan_apc_ups_output_frequency, places => 1}), + output_voltage => $anvil->Math->round({number => $new_scan_apc_ups_output_voltage, places => 1}), + output_total_output => $anvil->Math->round({number => $new_scan_apc_ups_output_total_output, places => 2}), + health => "#!string!scan_apc_ups_health_".sprintf("%04d", $new_scan_apc_ups_health)."!#", + last_transfer_reason => "#!string!scan_apc_ups_last_transfer_".sprintf("%04d", $new_scan_apc_ups_last_transfer_reason)."!#", + battery_health => "#!string!scan_apc_ups_battery_health_".sprintf("%04d", $new_scan_apc_ups_battery_health)."!#", + battery_state => "#!string!scan_apc_ups_battery_state_".sprintf("%04d", $new_scan_apc_ups_battery_state)."!#", + say_time_on_batteries => $say_time_on_batteries, + # This is only used when actually running on batteries. + output_time_on_batteries => $anvil->Readable->time({'time' => $new_scan_apc_ups_output_time_on_batteries}), + }, + }); + } + } + + ### TODO: Redo this, it feels stupid... + + # Now look for any old UPSes that weren't seen this time. + my $query = " +SELECT + scan_apc_ups_uuid, + scan_apc_ups_serial_number, + scan_apc_ups_ip +FROM + scan_apc_ups +WHERE + scan_apc_ups_host_uuid = ".$anvil->Database->quote($anvil->data->{sys}{host_uuid})." +AND + scan_apc_ups_health != 0 +"; + foreach my $serial_number (sort {$a cmp $b} @{$seen_upses}) + { + $query .= " +AND + scan_apc_ups_serial_number != ".$anvil->Database->quote($serial_number)." +"; + } + $query .= ";"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + foreach my $row (@{$results}) + { + my $this_scan_apc_ups_uuid = $row->[0]; + my $this_scan_apc_ups_serial_number = $row->[1]; + my $this_scan_apc_ups_ip = $row->[2]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "this_scan_apc_ups_uuid" => $this_scan_apc_ups_uuid, + "this_scan_apc_ups_serial_number" => $this_scan_apc_ups_serial_number, + "this_scan_apc_ups_ip" => $this_scan_apc_ups_ip, + }}); + + # Skip a UPSes that just recently went missing + next if $anvil->data->{dont_delete}{$this_scan_apc_ups_ip}; + + # Change the 'scan_apc_ups_health' to '0'. + my $query .= " +UPDATE + scan_apc_ups +SET + scan_apc_ups_health = 0, + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +WHERE + scan_apc_ups_host_uuid = ".$anvil->Database->quote($anvil->data->{sys}{host_uuid})." +AND + scan_apc_ups_serial_number = ".$anvil->Database->quote($this_scan_apc_ups_serial_number)."; +"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$anvil->data->{sys}{queries}}, $query; + + # TODO: Send an alert... + } + + # Now commit the changes. + $anvil->DB->commit_sql({source => $THIS_FILE, line => __LINE__}); + + # If one or more UPSed need to be shutdown, do it now. + foreach my $ups (@{$shutdown_ups}) + { + # WARNING: If the user has two different brands of UPSes and both have gone critical, the + # cluster will lose power without gracefully shutting down. This is unavoidable + # because scancore can't (yet) know how to power off a given brand/model of a UPS. + # TODO: If both/all UPSes are to be shutdown, stop servers and withdraw both nodes from the + # cluster before terminating the UPS. + #... + shutdown_ups($an, $ups); + } + + ### Now add entries to the 'power' table. + process_power($anvil); + + # If the power is on, record the charge percentage if needed. + + return(0); +} + +# This adds, updates or removes entries in the 'power' table that ScanCore will use to decide if a given node +# needs to be shutdown. +sub process_power +{ + my ($anvil) = @_; + + # UPSes we found on this scan + foreach my $ups_name (sort {$a cmp $b} keys %{$anvil->data->{'scan-apc-ups'}{ups}}) + { + my $ip = $anvil->data->{'scan-apc-ups'}{ups}{$ups_name}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups_name" => $ups_name, + "ip" => $ip, + }}); + } + + # The main bits we need to know is, for each UPS, is there input power? + my $query = " +SELECT + a.scan_apc_ups_uuid, + a.scan_apc_ups_serial_number, + a.scan_apc_ups_name, + a.scan_apc_ups_health, + c.scan_apc_ups_input_voltage, + d.scan_apc_ups_output_estimated_runtime, + b.scan_apc_ups_battery_percentage_charge, + a.scan_apc_ups_model +FROM + scan_apc_ups a, + scan_apc_ups_battery b, + scan_apc_ups_input c, + scan_apc_ups_output d +WHERE + a.scan_apc_ups_host_uuid = ".$anvil->Database->quote($anvil->data->{sys}{host_uuid})." +AND + a.scan_apc_ups_uuid = b.scan_apc_ups_battery_scan_apc_ups_uuid +AND + a.scan_apc_ups_uuid = c.scan_apc_ups_input_scan_apc_ups_uuid +AND + a.scan_apc_ups_uuid = d.scan_apc_ups_output_scan_apc_ups_uuid +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + name1 => "results", value1 => $results + }}); + foreach my $row (@{$results}) + { + my $scan_apc_ups_uuid = $row->[0]; + my $scan_apc_ups_serial_number = $row->[1]; + my $scan_apc_ups_name = $row->[2]; + my $scan_apc_ups_health = $row->[3]; + my $scan_apc_ups_input_voltage = $row->[4]; + my $scan_apc_ups_output_estimated_runtime = $row->[5]; + my $scan_apc_ups_battery_percentage_charge = $row->[6]; + my $scan_apc_ups_model = $row->[7]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "scan_apc_ups_uuid" => $scan_apc_ups_serial_number, + "scan_apc_ups_serial_number" => $scan_apc_ups_serial_number, + "scan_apc_ups_name" => $scan_apc_ups_name, + "scan_apc_ups_health" => $scan_apc_ups_health, + "scan_apc_ups_input_voltage" => $scan_apc_ups_input_voltage, + "scan_apc_ups_output_estimated_runtime" => $scan_apc_ups_output_estimated_runtime, + "scan_apc_ups_battery_percentage_charge" => $scan_apc_ups_battery_percentage_charge, + "scan_apc_ups_model" => $scan_apc_ups_model, + }}); + + # If the UPS no longer exists under this node, remove it. + if ((not exists $anvil->data->{'scan-apc-ups'}{ups}{$scan_apc_ups_name}) && ($scan_apc_ups_model ne "DELETED")) + { + # Remove it from the scan_apc_ups table and send a notice. + $anvil->Log->entry({log_level => 1, message_key => "scan_apc_ups_note_0063", message_variables => { ups_name => $scan_apc_ups_name }, file => $THIS_FILE, line => __LINE__}); + $anvil->Alert->register_alert({ + alert_level => "notice", + alert_agent_name => $THIS_FILE, + alert_title_key => "an_alert_title_0003", + alert_message_key => "scan_apc_ups_note_0063", + alert_message_variables => { + ups_name => $scan_apc_ups_name, + }, + }); + + my $query = "UPDATE scan_apc_ups SET scan_apc_ups_model = 'DELETED' WHERE scan_apc_ups_uuid = ".$anvil->Database->quote($scan_apc_ups_uuid).";"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$anvil->data->{sys}{queries}}, $query; + } + + # If the UPS health is '4' (boosting for low input voltage), or if there is no input voltage, + # we'll set that we're on batteries. + my $scan_apc_ups_on_battery = 0; + if (($scan_apc_ups_input_voltage eq "0") or ($scan_apc_ups_health eq "4")) + { + $scan_apc_ups_on_battery = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "scan_apc_ups_health" => $scan_apc_ups_health, + "scan_apc_ups_input_voltage" => $scan_apc_ups_input_voltage, + "scan_apc_ups_on_battery" => $scan_apc_ups_on_battery, + }}); + } + + # For each UPS, see if there is an existing entry in the 'power' table. + my $query = " +SELECT + power_uuid, + power_ups_name, + power_on_battery, + power_seconds_left, + power_charge_percentage +FROM + power +WHERE + power_host_uuid = ".$anvil->Database->quote($anvil->data->{sys}{host_uuid})." +AND + power_agent_name = ".$anvil->Database->quote($THIS_FILE)." +AND + power_record_locator = ".$anvil->Database->quote($scan_apc_ups_serial_number).";"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + + my $array_count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "results" => $results, + name2 => "array_count", value2 => $array_count + }}); + + if ($array_count) + { + # I'm not doing the usual 'foreach my $row (@{$results})', so this is a short-cut to + # pull the variables out directly. + my $power_uuid = $results->[0]->[0]; + my $power_ups_name = $results->[0]->[1]; + my $power_on_battery = $results->[0]->[2]; + my $power_seconds_left = $results->[0]->[3]; + my $power_charge_percentage = $results->[0]->[4]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "power_uuid" => $power_uuid, + "power_ups_name" => $power_ups_name, + "power_on_battery" => $power_on_battery, + "power_seconds_left" => $power_seconds_left, + "power_charge_percentage" => $power_charge_percentage, + }}); + + if (not exists $anvil->data->{'scan-apc-ups'}{ups}{$power_ups_name}) + { + # Remove it from the scan_apc_ups table. + $anvil->Log->entry({log_level => 1, message_key => "scan_apc_ups_note_0064", message_variables => { ups_name => $power_ups_name }, file => $THIS_FILE, line => __LINE__}); + + my $query = "DELETE FROM power WHERE power_uuid = ".$anvil->Database->quote($power_uuid).";"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$anvil->data->{sys}{queries}}, $query; + next; + } + + # Existing record; If load shedding is disabled, see if anything changed. If load + # shedding is NOT disabled, we'll update always. + if ((not $anvil->data->{scancore}{disable}{load_shedding}) or + (($power_ups_name ne $scan_apc_ups_name) or + ($power_on_battery ne $scan_apc_ups_on_battery) or + ($power_seconds_left ne $scan_apc_ups_output_estimated_runtime) or + ($power_charge_percentage ne $scan_apc_ups_battery_percentage_charge))) + { + # Something changed if load shedding is disabled. + if ($anvil->data->{scancore}{disable}{load_shedding}) + { + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "power_ups_name" => $power_ups_name, + "scan_apc_ups_name" => $scan_apc_ups_name, + }}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "power_on_battery" => $power_on_battery, + "scan_apc_ups_on_battery" => $scan_apc_ups_on_battery, + }}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "power_seconds_left" => $power_seconds_left, + "scan_apc_ups_output_estimated_runtime" => $scan_apc_ups_output_estimated_runtime, + }}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "power_charge_percentage" => $power_charge_percentage, + "scan_apc_ups_battery_percentage_charge" => $scan_apc_ups_battery_percentage_charge, + }}); + } + else + { + # Load shedding is enabled, so we'll always update. + $anvil->Log->entry({log_level => 2, message_key => "scan_apc_ups_log_0005", message_variables => { ups_name => $scan_apc_ups_name }, file => $THIS_FILE, line => __LINE__}); + } + my $query = " +UPDATE + power +SET + power_ups_name = ".$anvil->Database->quote($scan_apc_ups_name).", + power_on_battery = ".$anvil->Database->quote($scan_apc_ups_on_battery).", + power_seconds_left = ".$anvil->Database->quote($scan_apc_ups_output_estimated_runtime).", + power_charge_percentage = ".$anvil->Database->quote($scan_apc_ups_battery_percentage_charge).", + modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +WHERE + power_uuid = ".$anvil->Database->quote($power_uuid)." +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$anvil->data->{sys}{queries}}, $query; + } + else + { + # No change found, update not needed. + $anvil->Log->entry({log_level => 2, message_key => "scan_apc_ups_log_0004", message_variables => { ups_name => $scan_apc_ups_name }, file => $THIS_FILE, line => __LINE__}); + } + } + else + { + # No existing entry, do an INSERT. + my $power_uuid = $anvil->Get->uuid() or $anvil->Alert->error({title_key => "error_title_0020", message_key => "error_message_0024", code => 2, file => $THIS_FILE, line => __LINE__}); + my $query = " +INSERT INTO + power +( + power_uuid, + power_host_uuid, + power_agent_name, + power_record_locator, + power_ups_name, + power_on_battery, + power_seconds_left, + power_charge_percentage, + modified_date +) VALUES ( + ".$anvil->Database->quote($power_uuid).", + ".$anvil->Database->quote($anvil->data->{sys}{host_uuid}).", + ".$anvil->Database->quote($THIS_FILE).", + ".$anvil->Database->quote($scan_apc_ups_serial_number).", + ".$anvil->Database->quote($scan_apc_ups_name).", + ".$anvil->Database->quote($scan_apc_ups_on_battery).", + ".$anvil->Database->quote($scan_apc_ups_output_estimated_runtime).", + ".$anvil->Database->quote($scan_apc_ups_battery_percentage_charge).", + ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." +); +"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + push @{$anvil->data->{sys}{queries}}, $query; + } + } + + # Now commit the changes. + $anvil->DB->commit_sql({source => $THIS_FILE, line => __LINE__}); + + return(0); +} + +# This sends a command to immediately terminate the UPS. +sub shutdown_ups +{ + my ($anvil, $ups) = @_; + + # There are two possible reasons (at least) for why a UPS's batteries could be hot; + # 1. High load while on battery + # 2. Bad charging circuit + # In theory, switching to Bypass would solve #1 but not #2. For now, determining which case it is + # though is not so easy. Also, given that all Anvil! systems have two UPSes and given the consequence + # getting it is a fire, we'll play it safe and always turn off the UPS. + # + # 2 == Power off without delay + set_oid_integer($an, $ups, $anvil->data->{oids}{ups}{power_off}, 2); + + return(0); +} + +# This sets an OID with to an integer value. +sub set_oid_integer +{ + my ($anvil, $ups, $oid, $value) = @_; + + my $shell_call = $anvil->data->{path}{snmpset}." -v 2c -c ".$anvil->data->{snmp}{community}{'write'}." $ups $oid i $value"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "shell_call" => $shell_call, + }}); + open (my $file_handle, "$shell_call 2>&1 |") or die "$THIS_FILE ".__LINE__."; Failed to call: [$shell_call], error was: $!\n"; + while(<$file_handle>) + { + chomp; + my $line = $_; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "line" => $line, + }}); + if ($line =~ /INTEGER: (\d+)$/i) + { + $value = $1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "value" => $value, + }}); + } + } + close $file_handle; + + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "value" => $value, + }}); + return($value); +} + +# This reads in the last scan's data. +sub read_last_scan +{ + my ($anvil, $new_scan_apc_ups_serial_number) = @_; + + # Read in existing data, if any. + my $query = " +SELECT + scan_apc_ups_uuid, + scan_apc_ups_ups_uuid, + scan_apc_ups_serial_number, + scan_apc_ups_name, + scan_apc_ups_ip, + scan_apc_ups_ac_restore_delay, + scan_apc_ups_shutdown_delay, + scan_apc_ups_firmware_version, + scan_apc_ups_health, + scan_apc_ups_high_transfer_voltage, + scan_apc_ups_low_transfer_voltage, + scan_apc_ups_last_transfer_reason, + scan_apc_ups_manufactured_date, + scan_apc_ups_model, + scan_apc_ups_temperature_units, + scan_apc_ups_nmc_firmware_version, + scan_apc_ups_nmc_serial_number, + scan_apc_ups_nmc_mac_address +FROM + scan_apc_upses +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + my $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + results => $results, + count => $count, + }}); + # One or more records were found. + foreach my $row (@{$results}) + { + my $scan_apc_ups_uuid = $row->[0]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_apc_ups_uuid => $scan_apc_ups_uuid }}); + + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_ups_uuid} = $row->[1]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_serial_number} = $row->[2]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_name} = $row->[3]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_ip} = $row->[4]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_ac_restore_delay} = $row->[5]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_shutdown_delay} = $row->[6]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_firmware_version} = $row->[7]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_health} = $row->[8]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_high_transfer_voltage} = $row->[9]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_low_transfer_voltage} = $row->[10]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_last_transfer_reason} = $row->[11]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_manufactured_date} = $row->[12]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_model} = $row->[13]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_temperature_units} = $row->[14]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_firmware_version} = $row->[15]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_serial_number} = $row->[16]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_mac_address} = $row->[17]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_ups_uuid" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_ups_uuid}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_serial_number" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_serial_number}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_name" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_name}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_ip" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_ip}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_ac_restore_delay" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_ac_restore_delay}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_shutdown_delay" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_shutdown_delay}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_firmware_version" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_firmware_version}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_health" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_health}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_high_transfer_voltage" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_high_transfer_voltage}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_low_transfer_voltage" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_low_transfer_voltage}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_last_transfer_reason" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_last_transfer_reason}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_manufactured_date" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_manufactured_date}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_model" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_model}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_temperature_units" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_temperature_units}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_nmc_firmware_version" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_firmware_version}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_nmc_serial_number" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_serial_number}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_nmc_mac_address" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_mac_address}, + }}); + + $anvil->data->{sql}{ups_uuid_to_apc_ups_uuid}{$scan_apc_ups_ups_uuid} = $scan_apc_ups_uuid; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "sql::ups_uuid_to_apc_ups_uuid::${scan_apc_ups_ups_uuid}" => $anvil->data->{sql}{ups_uuid_to_apc_ups_uuid}{$scan_apc_ups_ups_uuid}, + }}); + } + undef $results; + + # Read in the battery data + $query = " +SELECT + scan_apc_ups_battery_uuid, + scan_apc_ups_battery_scan_apc_ups_uuid, + scan_apc_ups_battery_number, + scan_apc_ups_battery_replacement_date, + scan_apc_ups_battery_health, + scan_apc_ups_battery_model, + scan_apc_ups_battery_percentage_charge, + scan_apc_ups_battery_last_replacement_date, + scan_apc_ups_battery_state, + scan_apc_ups_battery_temperature, + scan_apc_ups_battery_alarm_temperature, + scan_apc_ups_battery_voltage +FROM + scan_apc_ups_batteries +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + + # Do the query against the source DB and loop through the results. + $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + results => $results, + count => $count, + }}); + my $i = 1; + foreach my $row (@{$results}) + { + my $scan_apc_ups_battery_uuid = $row->[0]; + my $scan_apc_ups_uuid = $row->[1]; + my $scan_apc_ups_battery_number = $row->[2]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + scan_apc_ups_battery_uuid => $scan_apc_ups_battery_uuid, + scan_apc_ups_uuid => $scan_apc_ups_uuid, + scan_apc_ups_battery_number => $scan_apc_ups_battery_number, + }}); + + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_battery_uuid}{$scan_apc_ups_battery_uuid}{scan_apc_ups_battery_replacement_date} = $row->[3]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_battery_uuid}{$scan_apc_ups_battery_uuid}{scan_apc_ups_battery_health} = $row->[4]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_battery_uuid}{$scan_apc_ups_battery_uuid}{scan_apc_ups_battery_model} = $row->[5]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_battery_uuid}{$scan_apc_ups_battery_uuid}{scan_apc_ups_battery_percentage_charge} = $row->[6]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_battery_uuid}{$scan_apc_ups_battery_uuid}{scan_apc_ups_battery_last_replacement_date} = $row->[7]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_battery_uuid}{$scan_apc_ups_battery_uuid}{scan_apc_ups_battery_state} = $row->[8]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_battery_uuid}{$scan_apc_ups_battery_uuid}{scan_apc_ups_battery_temperature} = $row->[9]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_battery_uuid}{$scan_apc_ups_battery_uuid}{scan_apc_ups_battery_alarm_temperature} = $row->[10]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_battery_uuid}{$scan_apc_ups_battery_uuid}{scan_apc_ups_battery_voltage} = $row->[11]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_battery_uuid::${scan_apc_ups_battery_uuid}::scan_apc_ups_battery_replacement_date" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_battery_uuid}{$scan_apc_ups_battery_uuid}{scan_apc_ups_battery_replacement_date}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_battery_uuid::${scan_apc_ups_battery_uuid}::scan_apc_ups_battery_health" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_battery_uuid}{$scan_apc_ups_battery_uuid}{scan_apc_ups_battery_health}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_battery_uuid::${scan_apc_ups_battery_uuid}::scan_apc_ups_battery_model" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_battery_uuid}{$scan_apc_ups_battery_uuid}{scan_apc_ups_battery_model}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_battery_uuid::${scan_apc_ups_battery_uuid}::scan_apc_ups_battery_percentage_charge" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_battery_uuid}{$scan_apc_ups_battery_uuid}{scan_apc_ups_battery_percentage_charge}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_battery_uuid::${scan_apc_ups_battery_uuid}::scan_apc_ups_battery_last_replacement_date" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_battery_uuid}{$scan_apc_ups_battery_uuid}{scan_apc_ups_battery_last_replacement_date}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_battery_uuid::${scan_apc_ups_battery_uuid}::scan_apc_ups_battery_state" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_battery_uuid}{$scan_apc_ups_battery_uuid}{scan_apc_ups_battery_state}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_battery_uuid::${scan_apc_ups_battery_uuid}::scan_apc_ups_battery_temperature" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_battery_uuid}{$scan_apc_ups_battery_uuid}{scan_apc_ups_battery_temperature}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_battery_uuid::${scan_apc_ups_battery_uuid}::scan_apc_ups_battery_alarm_temperature" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_battery_uuid}{$scan_apc_ups_battery_uuid}{scan_apc_ups_battery_alarm_temperature}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_battery_uuid::${scan_apc_ups_battery_uuid}::scan_apc_ups_battery_voltage" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_battery_uuid}{$scan_apc_ups_battery_uuid}{scan_apc_ups_battery_voltage}, + }}); + + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery_number}{$i}{scan_apc_ups_battery_uuid} = $scan_apc_ups_battery_uuid; + $i++; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::battery_number::${i}::scan_apc_ups_battery_uuid" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery_number}{$i}{scan_apc_ups_battery_uuid}, + }}); + } + undef $results; + + # Read in the input voltage data + $query = " +SELECT + scan_apc_ups_input_uuid, + scan_apc_ups_input_scan_apc_ups_uuid, + scan_apc_ups_input_frequency, + scan_apc_ups_input_sensitivity, + scan_apc_ups_input_voltage, + scan_apc_ups_input_1m_maximum_input_voltage, + scan_apc_ups_input_1m_minimum_input_voltage +FROM + scan_apc_ups_input +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + + # Do the query against the source DB and loop through the results. + $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + results => $results, + count => $count, + }}); + foreach my $row (@{$results}) + { + my $scan_apc_ups_input_uuid = $row->[0]; + my $scan_apc_ups_uuid = $row->[1]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + scan_apc_ups_input_uuid => $scan_apc_ups_input_uuid, + scan_apc_ups_uuid => $scan_apc_ups_uuid, + }}); + + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_uuid}{$scan_apc_ups_input_uuid}{scan_apc_ups_input_frequency} = $row->[2]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_uuid}{$scan_apc_ups_input_uuid}{scan_apc_ups_input_sensitivity} = $row->[3]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_uuid}{$scan_apc_ups_input_uuid}{scan_apc_ups_input_voltage} = $row->[4]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_uuid}{$scan_apc_ups_input_uuid}{scan_apc_ups_input_1m_maximum_input_voltage} = $row->[5]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_uuid}{$scan_apc_ups_input_uuid}{scan_apc_ups_input_1m_minimum_input_voltage} = $row->[6]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_input_uuid::${scan_apc_ups_input_uuid}::scan_apc_ups_input_frequency" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_uuid}{$scan_apc_ups_input_uuid}{scan_apc_ups_input_frequency}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_input_uuid::${scan_apc_ups_input_uuid}::scan_apc_ups_input_sensitivity" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_uuid}{$scan_apc_ups_input_uuid}{scan_apc_ups_input_sensitivity}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_input_uuid::${scan_apc_ups_input_uuid}::scan_apc_ups_input_voltage" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_uuid}{$scan_apc_ups_input_uuid}{scan_apc_ups_input_voltage}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_input_uuid::${scan_apc_ups_input_uuid}::scan_apc_ups_input_1m_maximum_input_voltage" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_uuid}{$scan_apc_ups_input_uuid}{scan_apc_ups_input_1m_maximum_input_voltage}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_input_uuid::${scan_apc_ups_input_uuid}::scan_apc_ups_input_1m_minimum_input_voltage" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_uuid}{$scan_apc_ups_input_uuid}{scan_apc_ups_input_1m_minimum_input_voltage}, + }}); + } + undef $results; + + # And finally, the output voltage data + $query = " +SELECT + scan_apc_ups_output_uuid, + scan_apc_ups_output_scan_apc_ups_uuid, + scan_apc_ups_output_load_percentage, + scan_apc_ups_output_time_on_batteries, + scan_apc_ups_output_estimated_runtime, + scan_apc_ups_output_frequency, + scan_apc_ups_output_voltage, + scan_apc_ups_output_total_output +FROM + scan_apc_ups_output +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + + # Do the query against the source DB and loop through the results. + $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + results => $results, + count => $count, + }}); + foreach my $row (@{$results}) + { + my $scan_apc_ups_output_uuid = $row->[0]; + my $scan_apc_ups_uuid = $row->[1]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + scan_apc_ups_output_uuid => $scan_apc_ups_output_uuid, + scan_apc_ups_uuid => $scan_apc_ups_uuid, + }}); + + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_uuid}{$scan_apc_ups_output_uuid}{scan_apc_ups_output_load_percentage} = $row->[2]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_uuid}{$scan_apc_ups_output_uuid}{scan_apc_ups_output_time_on_batteries} = $row->[3]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_uuid}{$scan_apc_ups_output_uuid}{scan_apc_ups_output_estimated_runtime} = $row->[4]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_uuid}{$scan_apc_ups_output_uuid}{scan_apc_ups_output_frequency} = $row->[5]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_uuid}{$scan_apc_ups_output_uuid}{scan_apc_ups_output_voltage} = $row->[6]; + $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_uuid}{$scan_apc_ups_output_uuid}{scan_apc_ups_output_total_output} = $row->[7]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_output_uuid::${scan_apc_ups_output_uuid}::scan_apc_ups_output_load_percentage" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_uuid}{$scan_apc_ups_output_uuid}{scan_apc_ups_output_load_percentage}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_output_uuid::${scan_apc_ups_output_uuid}::scan_apc_ups_output_time_on_batteries" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_uuid}{$scan_apc_ups_output_uuid}{scan_apc_ups_output_time_on_batteries}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_output_uuid::${scan_apc_ups_output_uuid}::scan_apc_ups_output_estimated_runtime" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_uuid}{$scan_apc_ups_output_uuid}{scan_apc_ups_output_estimated_runtime}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_output_uuid::${scan_apc_ups_output_uuid}::scan_apc_ups_output_frequency" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_uuid}{$scan_apc_ups_output_uuid}{scan_apc_ups_output_frequency}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_output_uuid::${scan_apc_ups_output_uuid}::scan_apc_ups_output_voltage" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_uuid}{$scan_apc_ups_output_uuid}{scan_apc_ups_output_voltage}, + "sql::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_output_uuid::${scan_apc_ups_output_uuid}::scan_apc_ups_output_total_output" => $anvil->data->{sql}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_uuid}{$scan_apc_ups_output_uuid}{scan_apc_ups_output_total_output}, + }}); + } + undef $results; + + return(0); +} + +# This calls each UPS, first to get the model number and update the OIDs to use if needed, then gathers the +# information from the UPS. +sub gather_ups_data +{ + my ($anvil) = @_; + + ### TODO: If the network with the UPS is congested, it is possible that, despite connecting to the + ### UPS, some OID reads may fail with '#!no_connection!#'. Try to read them a second time in + ### these cases. Regardless, be sure to check all returned OID values for 'no connection' and + ### handle such cases more gracefully. + + # Loop through the UPSes we found in upses (we may miss existing entries from 'scan_apc_upss', but + # we'll watch for that later). + foreach my $ups_uuid (sort {$a cmp $b} keys %{$anvil->data->{upses}{ups_uuid}}) + { + my $ups_ip = $anvil->data->{upses}{ups_uuid}{$ups_uuid}{ip_address}; + my $ups_name = $anvil->data->{upses}{ups_uuid}{$ups_uuid}{name}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + ups_uuid => $ups_uuid, + ups_ip => $ups_ip, + ups_name => $ups_name, + }}); + + # Have we seen this UPS before? + my $scan_apc_ups_uuid = ""; + my $new_ups = 0; + if ((exists $anvil->data->{sql}{ups_uuid_to_apc_ups_uuid}{$ups_uuid}) && (defined $anvil->data->{sql}{ups_uuid_to_apc_ups_uuid}{$ups_uuid})) + { + # Yup! + $scan_apc_ups_uuid = $anvil->data->{sql}{ups_uuid_to_apc_ups_uuid}{$ups_uuid}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + scan_apc_ups_uuid => $scan_apc_ups_uuid, + new_ups => $new_ups, + }}); + } + else + { + # It's new, generate the UUID now. We'll set 'scan_apc_ups_new' so we know to INSERT + # it later. + $scan_apc_ups_uuid = $anvil->Get->uuid(); + $new_ups = 1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + scan_apc_ups_uuid => $scan_apc_ups_uuid, + new_ups => $new_ups, + }}); + } + + # Can I ping it? This returns '1' if it was pingable, '0' if not. + my ($pinged) = $anvil->Network->ping({ping => $ups_ip}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { pinged => $pinged }}); + + if (not $pinged) + { + # Can't reach it. + $anvil->data->{upses}{ups_uuid}{$ups_uuid}{ping} = 0; + $anvil->data->{upses}{ups_uuid}{$ups_uuid}{connected} = 0; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "upses::ups_uuid::${ups_uuid}::ping" => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{ping}, + "upses::ups_uuid::${ups_uuid}::connected" => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{connected}, + }}); + next; + } + + ### NOTE: At this time, we've only seen SNMP v2c, but it's possible we may need to test other + ### versions on a per-UPS bases down the road. + $anvil->data->{upses}{ups_uuid}{$ups_uuid}{ping} = 1; + $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version} = $anvil->data->{snmp}{community}{version}; + $anvil->data->{upses}{ups_uuid}{$ups_uuid}{scan_apc_ups_uuid} = $scan_apc_ups_uuid; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "upses::ups_uuid::${ups_uuid}::ping" => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{ping}, + "upses::ups_uuid::${ups_uuid}::snmp_version" => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + "upses::ups_uuid::${ups_uuid}::scan_apc_ups_uuid" => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{scan_apc_ups_uuid}, + }}); + + my ($scan_apc_ups_serial_number, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{ups}{serial_number}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + scan_apc_ups_serial_number => $scan_apc_ups_serial_number, + data_type => $data_type, + }}); + + # If the serial number is '!!no_connection!!', this isn't the UPS we want or SNMP is disabled. + if ($scan_apc_ups_serial_number eq "!!no_connection!!") + { + $scan_apc_ups_serial_number = ""; + $anvil->data->{upses}{ups_uuid}{$ups_uuid}{connected} = 0; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + scan_apc_ups_serial_number => $scan_apc_ups_serial_number, + "upses::ups_uuid::${ups_uuid}::connected" => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{connected}, + }}); + } + + # These are set to avoid 'undefined' variable warnings later if we fail to reach this PDU. + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{new_ups} = $new_ups; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_upses}{scan_apc_ups_ups_uuid} = $ups_uuid; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_serial_number} = $scan_apc_ups_serial_number; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_name} = $ups_name; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_ip} = $ups_ip; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_ac_restore_delay} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_shutdown_delay} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_firmware_version} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_health} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_high_transfer_voltage} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_low_transfer_voltage} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_last_transfer_reason} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_manufactured_date} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_model} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_temperature_units} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_firmware_version} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_serial_number} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_mac_address} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_1m_minimum_input_voltage} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_1m_maximum_input_voltage} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_sensitivity} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_frequency} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_voltage} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_total_output} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_frequency} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_time_on_batteries} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_load_percentage} = ""; + + # If I got the serial number, I found the PDU. + next if not $scan_apc_ups_serial_number; + + # Now that I can trust my OIDs, lets start gathering data! Keep track of how long it took to + # query the OIDs. + + ############################################################################################# + # Base UPS info # + ############################################################################################# + + # Get the AC restore delay, if set. (High-precision, divide by 10 to get seconds). + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_ac_restore_delay}, my $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{ups}{ac_restore_delay}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_ac_restore_delay" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_ac_restore_delay}, + data_type => $data_type, + }}); + if (($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_ac_restore_delay} eq "--") or ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_ac_restore_delay} =~ /\D/)) + { + $anvil->Log->entry({log_level => 1, message_key => "scan_apc_ups_error_0001", message_variables => { + name => "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_ac_restore_delay", + value => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_ac_restore_delay}, + }, file => $THIS_FILE, line => __LINE__}); + + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_ac_restore_delay} = -1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_ac_restore_delay" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_ac_restore_delay}, + }}); + } + + # Get the shut down delay, if set. (in ticks, divide by 100). + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_shutdown_delay}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{ups}{shutdown_delay}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_shutdown_delay" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_shutdown_delay}, + data_type => $data_type, + }}); + if (($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_shutdown_delay} eq "--") or ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_shutdown_delay} =~ /\D/)) + { + $anvil->Log->entry({log_level => 1, message_key => "scan_apc_ups_error_0001", message_variables => { + name => "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_shutdown_delay", + value => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_shutdown_delay}, + }, file => $THIS_FILE, line => __LINE__}); + + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_shutdown_delay} = -1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_shutdown_delay" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_shutdown_delay}, + }}); + } + + # Get the UPS's firmware version. + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_firmware_version}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{ups}{firmware_version}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_firmware_version" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_firmware_version}, + data_type => $data_type, + }}); + + ### Gather the main UPS data. The returned integer value maps to the appropriate + ### 'scan_apc_ups_health_00xx' string. + # 1 => Unknown, 2 => Online, 3 => On Battery, + # 4 => onSmartBoost, 5 => timedSleeping, 6 => softwareBypass, + # 7 => off, 8 => rebooting, 9 => switchedBypass, + # 10 => hardwareFailureBypass, 11 => sleepingUntilPowerReturn, + # 12 => onSmartTrim, 13 => ecoMode, 14 => hotStandby, + # 15 => onBatteryTest, 15 => emergencyStaticBypass, + # 17 => staticBypassStandby, 18 => powerSavingMode, + # 19 => spotMode, 20 => eConversion + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_health}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{ups}{health}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_health" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_health}, + data_type => $data_type, + }}); + + # Get the high and low transfer voltages. + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_high_transfer_voltage}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{ups}{high_transfer_voltage}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_high_transfer_voltage" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_high_transfer_voltage}, + data_type => $data_type, + }}); + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_low_transfer_voltage}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{ups}{low_transfer_voltage}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_low_transfer_voltage" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_low_transfer_voltage}, + data_type => $data_type, + }}); + + ### Get the last transfer reason. The returned integer maps to the appropriate + ### 'scan_apc_ups_last_transfer_00xx' string. + # 1 => noTransfer, 2 => highLineVoltage, 3 => brownout, + # 4 => blackout, 5 => smallMomentarySag, 6 => deepMomentarySag, + # 7 => smallMomentarySpike, 8 => largeMomentarySpike, + # 9 => selfTest, 10 => rateOfVoltageChange + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_last_transfer_reason}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{ups}{last_transfer_reason}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_last_transfer_reason" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_last_transfer_reason}, + data_type => $data_type, + }}); + + # Get the manufacture date of the UPS. This really should never change, but we need to record + # it at least once. + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_manufactured_date}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{ups}{manufactured_date}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_manufactured_date" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_manufactured_date}, + data_type => $data_type, + }}); + # Convert the format. + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_manufactured_date} = $anvil->Convert->format_mmddyy_to_yymmdd({date => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_manufactured_date}}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_manufactured_date" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_manufactured_date}, + }}); + + # Get the temperature units. 1 == C, 2 == F + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_temperature_units}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{ups}{temperature_units}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_temperature_units" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_temperature_units}, + data_type => $data_type, + }}); + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_temperature_units} = $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_temperature_units} eq "2" ? "F" : "C"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_temperature_units" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_temperature_units}, + }}); + + # If the UPS was replaced, these values could change + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_serial_number}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{ups}{serial_number}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_serial_number" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_serial_number}, + data_type => $data_type, + }}); + + ############################################################################################# + # NMC information # + ############################################################################################# + + # Get the NMC firmware. + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_firmware_version}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{nmc}{firmware_version}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_nmc_firmware_version" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_firmware_version}, + data_type => $data_type, + }}); + + # Get the NMC serial number. + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_firmware_version}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{nmc}{firmware_version}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_nmc_firmware_version" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_firmware_version}, + data_type => $data_type, + }}); + + # Now the MAC address. + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_mac_address}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{nmc}{mac_address}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_nmc_mac_address" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_mac_address}, + data_type => $data_type, + }}); + # Convert the MAC format. + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_mac_address} =~ s/([0-9a-fA-F]{2}) ([0-9a-fA-F]{2}) ([0-9a-fA-F]{2}) ([0-9a-fA-F]{2}) ([0-9a-fA-F]{2}) ([0-9a-fA-F]{2})/$1:$2:$3:$4:$5:$6/g; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_nmc_mac_address" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_nmc_mac_address}, + }}); + + ############################################################################################# + # Battery information # + ############################################################################################# + + # NOTE: In the future, we'll try to read multi-battery UPSes. For now, this is basically a + # place-holder. Also note; There is no unique identifiers for UPS batteries. So we + # simply track them by sequence number (possibly tied to the OID, to be determined...) + foreach my $battery_number (1..1) + { + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_replacement_date} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_health} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_model} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_percentage_charge} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_last_replacement_date} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_state} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_temperature} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_alarm_temperature} = ""; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_voltage} = ""; + + # Read the estimated battery replacement date. This only has value if the user remembered to + # mark the date the battery was replaced. + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_replacement_date}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{battery}{replacement_date}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::battery::${battery_number}::scan_apc_ups_battery_replacement_date" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_replacement_date}, + data_type => $data_type, + }}); + # Convert the date format + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_replacement_date} = $anvil->Convert->format_mmddyy_to_yymmdd({date => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_replacement_date}}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::battery::${battery_number}::scan_apc_ups_battery_replacement_date" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_replacement_date}, + }}); + + # Read the battery's health. 1 == OK, 2 == Failed, replace. + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_health}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{battery}{health}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::battery::${battery_number}::scan_apc_ups_battery_health" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_health}, + data_type => $data_type, + }}); + + # Read the battery's model number. + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_model}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{battery}{model}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::battery::${battery_number}::scan_apc_ups_battery_model" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_model}, + data_type => $data_type, + }}); + + # Read the percentage charge. This is high-precision, so divide by 10 to get the actual + # percentage (ie: 1000 == 100.0%). + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_percentage_charge}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{battery}{percentage_charge_hp}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::battery::${battery_number}::scan_apc_ups_battery_percentage_charge" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_percentage_charge}, + data_type => $data_type, + }}); + if (($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_percentage_charge} eq "--") or ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_percentage_charge} =~ /\D/)) + { + $anvil->Log->entry({log_level => 1, message_key => "scan_apc_ups_error_0001", message_variables => { + name => "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::battery::${battery_number}::scan_apc_ups_battery_percentage_charge", + value => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_percentage_charge}, + }, file => $THIS_FILE, line => __LINE__}); + + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_percentage_charge} = -1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::battery::${battery_number}::scan_apc_ups_battery_percentage_charge" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_percentage_charge}, + }}); + } + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_percentage_charge} /= 10; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::battery::${battery_number}::scan_apc_ups_battery_percentage_charge" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_percentage_charge}, + }}); + + # Get the date that the UPS was last replaced. (mm/dd/yy or yyyy format) + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_last_replacement_date}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{battery}{last_replacement_date}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::battery::${battery_number}::scan_apc_ups_battery_last_replacement_date" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_last_replacement_date}, + data_type => $data_type, + }}); + # Convert the format. + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_last_replacement_date} = $anvil->Convert->format_mmddyy_to_yymmdd({date => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_last_replacement_date}}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::battery::${battery_number}::scan_apc_ups_battery_last_replacement_date" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_last_replacement_date}, + }}); + + ### The battery state is expressed as an integer and maps to 'scan_apc_ups_battery_000x'. + # 1 => unknown, 2 => batteryNormal, 3 => batteryLow, + # 4 => batteryInFaultCondition + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_state}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{battery}{'state'}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::battery::${battery_number}::scan_apc_ups_battery_state" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_state}, + data_type => $data_type, + }}); + + # This is the temperature of the UPS battery. It is high-precision, so divide by 10 to get + # the actual temperature. It is always celsius, so don't convert it. + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_temperature}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{battery}{temperature_hp}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::battery::${battery_number}::scan_apc_ups_battery_temperature" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_temperature}, + data_type => $data_type, + }}); + if (($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_temperature} eq "--") or ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_temperature} =~ /\D/)) + { + $anvil->Log->entry({log_level => 1, message_key => "scan_apc_ups_error_0001", message_variables => { + name => "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::battery::${battery_number}::scan_apc_ups_battery_temperature", + value => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_temperature}, + }, file => $THIS_FILE, line => __LINE__}); + + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_temperature} = -1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::battery::${battery_number}::scan_apc_ups_battery_temperature" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_temperature}, + }}); + } + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_temperature} /= 10; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::battery::${battery_number}::scan_apc_ups_battery_temperature" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_temperature}, + }}); + + # Get the temperature at which the system throws a fit. This is stated as a standard integer, + # no need to divide by 10. + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_alarm_temperature}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{battery}{alarm_temperature}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::battery::${battery_number}::scan_apc_ups_battery_alarm_temperature" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_alarm_temperature}, + data_type => $data_type, + }}); + if (($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_alarm_temperature} eq "--") or ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_alarm_temperature} =~ /\D/)) + { + $anvil->Log->entry({log_level => 1, message_key => "scan_apc_ups_error_0001", message_variables => { + name => "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::battery::${battery_number}::scan_apc_ups_battery_alarm_temperature", + value => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_alarm_temperature}, + }, file => $THIS_FILE, line => __LINE__}); + + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_alarm_temperature} = 40 ; + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_temperature_units} = "C"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::battery::${battery_number}::scan_apc_ups_battery_alarm_temperature" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_alarm_temperature}, + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_temperature_units" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_temperature_units}, + }}); + } + if ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_temperature_units} eq "F") + { + # We live in a metric world! + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_alarm_temperature} = $anvil->Convert->fahrenheit_to_celsius({temperature => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_alarm_temperature}}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::battery::${battery_number}::scan_apc_ups_battery_alarm_temperature" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_alarm_temperature}, + }}); + } + + # Read the current battery voltage (vDC). It is a high-precision value, so divide by 10. + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_voltage}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{battery}{temperature_hp}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::battery::${battery_number}::scan_apc_ups_battery_voltage" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_voltage}, + data_type => $data_type, + }}); + if (($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_voltage} eq "--") or ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_voltage} =~ /\D/)) + { + $anvil->Log->entry({log_level => 1, message_key => "scan_apc_ups_error_0001", message_variables => { + name => "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::battery::${battery_number}::scan_apc_ups_battery_voltage", + value => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_voltage}, + }, file => $THIS_FILE, line => __LINE__}); + + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_voltage} = -1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::battery::${battery_number}::scan_apc_ups_battery_voltage" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_voltage}, + }}); + } + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_voltage} /= 10; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::battery::${battery_number}::scan_apc_ups_battery_voltage" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{battery}{$battery_number}{scan_apc_ups_battery_voltage}, + }}); + } + + ############################################################################################# + # Input Information # + ############################################################################################# + + # This is the current input frequency. It is high-precision, so divide by 10. + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_frequency}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{input}{frequency_hp}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_input_frequency" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_frequency}, + data_type => $data_type, + }}); + if (($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_frequency} eq "--") or ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_frequency} =~ /\D/)) + { + $anvil->Log->entry({log_level => 1, message_key => "scan_apc_ups_error_0001", message_variables => { + name => "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_input_frequency", + value => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_frequency}, + }, file => $THIS_FILE, line => __LINE__}); + + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_frequency} = -1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_input_frequency" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_frequency}, + }}); + } + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_frequency} /= 10; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_input_frequency" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_frequency}, + }}); + + ### The input sensitivity is an integer that maps to 'scan_apc_ups_sensitivity_000x'. If the + ### UPS is an 'online' (dual-conversion model), this will return '0'. + # 1 => Auto, 2 => Low, 3 => Medium, 4 => High + # We set this to '0' if no value was returned to indicate an 'Online' UPS. + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_sensitivity}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{input}{sensitivity}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_input_sensitivity" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_sensitivity}, + data_type => $data_type, + }}); + if (($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_sensitivity} eq "--") or ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_sensitivity} =~ /\D/)) + { + $anvil->Log->entry({log_level => 1, message_key => "scan_apc_ups_error_0001", message_variables => { + name => "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_input_sensitivity", + value => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_sensitivity}, + }, file => $THIS_FILE, line => __LINE__}); + + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_sensitivity} = -1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_input_sensitivity" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_sensitivity}, + }}); + } + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_sensitivity} /= 10; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_input_sensitivity" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_sensitivity}, + }}); + + # High-precision maximum and minimum input voltages (vAC) over the last 60 seconds. + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_1m_maximum_input_voltage}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{input}{sensitivity}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_input_1m_maximum_input_voltage" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_1m_maximum_input_voltage}, + data_type => $data_type, + }}); + if (($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_1m_maximum_input_voltage} eq "--") or ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_1m_maximum_input_voltage} =~ /\D/)) + { + $anvil->Log->entry({log_level => 1, message_key => "scan_apc_ups_error_0001", message_variables => { + name => "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_input_1m_maximum_input_voltage", + value => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_1m_maximum_input_voltage}, + }, file => $THIS_FILE, line => __LINE__}); + + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_1m_maximum_input_voltage} = -1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_input_1m_maximum_input_voltage" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_1m_maximum_input_voltage}, + }}); + } + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_1m_maximum_input_voltage} /= 10; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_input_1m_maximum_input_voltage" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_1m_maximum_input_voltage}, + }}); + + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_1m_minimum_input_voltage}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{input}{sensitivity}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_input_1m_minimum_input_voltage" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_1m_minimum_input_voltage}, + data_type => $data_type, + }}); + if (($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_1m_minimum_input_voltage} eq "--") or ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_1m_minimum_input_voltage} =~ /\D/)) + { + $anvil->Log->entry({log_level => 1, message_key => "scan_apc_ups_error_0001", message_variables => { + name => "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_input_1m_minimum_input_voltage", + value => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_1m_minimum_input_voltage}, + }, file => $THIS_FILE, line => __LINE__}); + + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_1m_minimum_input_voltage} = -1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_input_1m_minimum_input_voltage" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_1m_minimum_input_voltage}, + }}); + } + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_1m_minimum_input_voltage} /= 10; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_input_1m_minimum_input_voltage" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_input_1m_minimum_input_voltage}, + }}); + + + ############################################################################################# + # Output Information # + ############################################################################################# + + # High-precision output load percentage, divide by 10. + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_load_percentage}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{output}{load_percentage_hp}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_output_load_percentage" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_load_percentage}, + data_type => $data_type, + }}); + if (($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_load_percentage} eq "--") or ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_load_percentage} =~ /\D/)) + { + $anvil->Log->entry({log_level => 1, message_key => "scan_apc_ups_error_0001", message_variables => { + name => "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_output_load_percentage", + value => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_load_percentage}, + }, file => $THIS_FILE, line => __LINE__}); + + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_load_percentage} = -1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_output_load_percentage" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_load_percentage}, + }}); + } + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_load_percentage} /= 10; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_output_load_percentage" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_load_percentage}, + }}); + + # The time (in ticks, divide by 100) that the UPS has been running on batteries. '0' + # indicates that it is not on batteries. + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_time_on_batteries}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{output}{time_on_batteries}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_output_time_on_batteries" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_time_on_batteries}, + data_type => $data_type, + }}); + if (($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_time_on_batteries} eq "--") or ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_time_on_batteries} =~ /\D/)) + { + $anvil->Log->entry({log_level => 1, message_key => "scan_apc_ups_error_0001", message_variables => { + name => "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_output_time_on_batteries", + value => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_time_on_batteries}, + }, file => $THIS_FILE, line => __LINE__}); + + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_time_on_batteries} = -1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_output_time_on_batteries" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_time_on_batteries}, + }}); + } + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_time_on_batteries} /= 100; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_output_time_on_batteries" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_time_on_batteries}, + }}); + + + # This is the high-precision output frequency in Hz (divide by 10). + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_frequency}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{output}{frequency_hp}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_output_frequency" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_frequency}, + data_type => $data_type, + }}); + if (($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_frequency} eq "--") or ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_frequency} =~ /\D/)) + { + $anvil->Log->entry({log_level => 1, message_key => "scan_apc_ups_error_0001", message_variables => { + name => "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_output_frequency", + value => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_frequency}, + }, file => $THIS_FILE, line => __LINE__}); + + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_frequency} = -1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_output_frequency" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_frequency}, + }}); + } + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_frequency} /= 10; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_output_frequency" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_frequency}, + }}); + + # High-precision output voltage (vAC, divide by 10). + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_voltage}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{output}{voltage_hp}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_output_voltage" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_voltage}, + data_type => $data_type, + }}); + if (($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_voltage} eq "--") or ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_voltage} =~ /\D/)) + { + $anvil->Log->entry({log_level => 1, message_key => "scan_apc_ups_error_0001", message_variables => { + name => "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_output_voltage", + value => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_voltage}, + }, file => $THIS_FILE, line => __LINE__}); + + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_voltage} = -1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_output_voltage" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_voltage}, + }}); + } + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_voltage} /= 10; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_output_voltage" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_voltage}, + }}); + + # This is the total power outputted since the UPS was created. It is measured in tens of + # watt-hours, so divide be 100 to get kW/hr. + ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_total_output}, $data_type) = $anvil->Remote->read_snmp_oid({ + debug => 2, + target => $ups_ip, + oid => $anvil->data->{oids}{output}{total_output}, + version => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{snmp_version}, + community => $anvil->data->{snmp}{community}{'read'} + }); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_output_total_output" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_total_output}, + data_type => $data_type, + }}); + if (($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_total_output} eq "--") or ($anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_total_output} =~ /\D/)) + { + $anvil->Log->entry({log_level => 1, message_key => "scan_apc_ups_error_0001", message_variables => { + name => "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_output_total_output", + value => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_total_output}, + }, file => $THIS_FILE, line => __LINE__}); + + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_total_output} = -1; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_output_total_output" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_total_output}, + }}); + } + $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_total_output} /= 100; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "ups::scan_apc_ups_uuid::${scan_apc_ups_uuid}::scan_apc_ups_output_total_output" => $anvil->data->{ups}{scan_apc_ups_uuid}{$scan_apc_ups_uuid}{scan_apc_ups_output_total_output}, + }}); + } + + return(0); +} + +# This looks for APC UPSes. +sub find_upses +{ + my ($anvil) = @_; + + # Search in 'upses' for UPSes using this scan agent. These aren't bound to hosts (or even Anvil! + # systems), so not all may be available. + my $query = " +SELECT + ups_uuid, + ups_name, + ups_ip_address +FROM + upses +WHERE + ups_agent = ".$anvil->Database->quote($THIS_FILE)." +ORDER BY + ups_name ASC +;"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); + + my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__}); + my $count = @{$results}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + results => $results, + count => $count, + }}); + foreach my $row (@{$results}) + { + my $ups_uuid = $row->[0]; + my $ups_name = $row->[1]; + my $ups_ip_address = $row->[2]; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + ups_uuid => $ups_uuid, + ups_name => $ups_name, + ups_ip_address => $ups_ip_address, + }}); + + $anvil->data->{upses}{ups_uuid}{$ups_uuid}{name} = $ups_name; + $anvil->data->{upses}{ups_uuid}{$ups_uuid}{ip_address} = $ups_ip_address; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + "upses::ups_uuid::${ups_uuid}::name" => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{name}, + "upses::ups_uuid::${ups_uuid}::ip_address" => $anvil->data->{upses}{ups_uuid}{$ups_uuid}{ip_address}, + }}); + } + + my $ups_count = keys %{$anvil->data->{upses}{ups_uuid}}; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { ups_count => $ups_count }}); + return($ups_count); +} diff --git a/scancore-agents/scan-apc-ups/scan-apc-ups.sql b/scancore-agents/scan-apc-ups/scan-apc-ups.sql new file mode 100644 index 00000000..19e1fcad --- /dev/null +++ b/scancore-agents/scan-apc-ups/scan-apc-ups.sql @@ -0,0 +1,317 @@ +-- This is the database schema for the 'APC UPS Scan Agent'. + +CREATE TABLE scan_apc_upses ( + scan_apc_ups_uuid uuid not null primary key, + scan_apc_ups_ups_uuid uuid not null, + scan_apc_ups_serial_number text not null, + scan_apc_ups_name text not null, + scan_apc_ups_ip text not null, + scan_apc_ups_ac_restore_delay numeric not null, + scan_apc_ups_shutdown_delay numeric not null, + scan_apc_ups_firmware_version text not null, + scan_apc_ups_health numeric not null, + scan_apc_ups_high_transfer_voltage numeric not null, + scan_apc_ups_low_transfer_voltage numeric not null, + scan_apc_ups_last_transfer_reason numeric not null, + scan_apc_ups_manufactured_date text not null, + scan_apc_ups_model text not null, + scan_apc_ups_temperature_units text not null, + scan_apc_ups_nmc_firmware_version text not null, + scan_apc_ups_nmc_serial_number text not null, + scan_apc_ups_nmc_mac_address text not null, + modified_date timestamp with time zone not null, + + FOREIGN KEY(scan_apc_ups_ups_uuid) REFERENCES upses(ups_uuid) +); +ALTER TABLE scan_apc_upses OWNER TO admin; + +CREATE TABLE history.scan_apc_upses ( + history_id bigserial, + scan_apc_ups_uuid uuid, + scan_apc_ups_ups_uuid uuid, + scan_apc_ups_serial_number text, + scan_apc_ups_name text, + scan_apc_ups_ip text, + scan_apc_ups_ac_restore_delay numeric, + scan_apc_ups_shutdown_delay numeric, + scan_apc_ups_firmware_version text, + scan_apc_ups_health numeric, + scan_apc_ups_high_transfer_voltage numeric, + scan_apc_ups_low_transfer_voltage numeric, + scan_apc_ups_last_transfer_reason numeric, + scan_apc_ups_manufactured_date text, + scan_apc_ups_model text, + scan_apc_ups_temperature_units text, + scan_apc_ups_nmc_firmware_version text, + scan_apc_ups_nmc_serial_number text, + scan_apc_ups_nmc_mac_address text, + modified_date timestamp with time zone not null +); +ALTER TABLE history.scan_apc_upses OWNER TO admin; + +CREATE FUNCTION history_scan_apc_upses() RETURNS trigger +AS $$ +DECLARE + history_scan_apc_upses RECORD; +BEGIN + SELECT INTO history_scan_apc_upses * FROM scan_apc_upses WHERE scan_apc_ups_uuid=new.scan_apc_ups_uuid; + INSERT INTO history.scan_apc_upses + (scan_apc_ups_uuid, + scan_apc_ups_serial_number, + scan_apc_ups_name, + scan_apc_ups_ip, + scan_apc_ups_ac_restore_delay, + scan_apc_ups_shutdown_delay, + scan_apc_ups_firmware_version, + scan_apc_ups_health, + scan_apc_ups_high_transfer_voltage, + scan_apc_ups_low_transfer_voltage, + scan_apc_ups_last_transfer_reason, + scan_apc_ups_manufactured_date, + scan_apc_ups_model, + scan_apc_ups_temperature_units, + scan_apc_ups_nmc_firmware_version, + scan_apc_ups_nmc_serial_number, + scan_apc_ups_nmc_mac_address, + modified_date) + VALUES + (history_scan_apc_ups.scan_apc_ups_uuid, + history_scan_apc_ups.scan_apc_ups_serial_number, + history_scan_apc_ups.scan_apc_ups_name, + history_scan_apc_ups.scan_apc_ups_ip, + history_scan_apc_ups.scan_apc_ups_ac_restore_delay, + history_scan_apc_ups.scan_apc_ups_shutdown_delay, + history_scan_apc_ups.scan_apc_ups_firmware_version, + history_scan_apc_ups.scan_apc_ups_health, + history_scan_apc_ups.scan_apc_ups_high_transfer_voltage, + history_scan_apc_ups.scan_apc_ups_low_transfer_voltage, + history_scan_apc_ups.scan_apc_ups_last_transfer_reason, + history_scan_apc_ups.scan_apc_ups_manufactured_date, + history_scan_apc_ups.scan_apc_ups_model, + history_scan_apc_ups.scan_apc_ups_temperature_units, + history_scan_apc_ups.scan_apc_ups_nmc_firmware_version, + history_scan_apc_ups.scan_apc_ups_nmc_serial_number, + history_scan_apc_ups.scan_apc_ups_nmc_mac_address, + history_scan_apc_ups.modified_date); + RETURN NULL; +END; +$$ +LANGUAGE plpgsql; +ALTER FUNCTION history_scan_apc_upses() OWNER TO admin; + +CREATE TRIGGER trigger_scan_apc_upses + AFTER INSERT OR UPDATE ON scan_apc_upses + FOR EACH ROW EXECUTE PROCEDURE history_scan_apc_upses(); + + +-- Battery stuff +CREATE TABLE scan_apc_ups_batteries ( + scan_apc_ups_battery_uuid uuid not null primary key, + scan_apc_ups_battery_scan_apc_ups_uuid uuid not null, + scan_apc_ups_battery_number numeric not null, + scan_apc_ups_battery_replacement_date text not null, + scan_apc_ups_battery_health numeric not null, + scan_apc_ups_battery_model text not null, + scan_apc_ups_battery_percentage_charge numeric not null, + scan_apc_ups_battery_last_replacement_date text not null, + scan_apc_ups_battery_state numeric not null, + scan_apc_ups_battery_temperature numeric not null, + scan_apc_ups_battery_alarm_temperature numeric not null, + scan_apc_ups_battery_voltage numeric not null, + modified_date timestamp with time zone not null, + + FOREIGN KEY(scan_apc_ups_battery_scan_apc_ups_uuid) REFERENCES scan_apc_upses(scan_apc_ups_uuid) +); +ALTER TABLE scan_apc_ups_battery OWNER TO admin; + +CREATE TABLE history.scan_apc_ups_batteries ( + history_id bigserial, + scan_apc_ups_battery_uuid uuid, + scan_apc_ups_battery_scan_apc_ups_uuid uuid, + scan_apc_ups_battery_number numeric, + scan_apc_ups_battery_replacement_date text, + scan_apc_ups_battery_health numeric, + scan_apc_ups_battery_model text, + scan_apc_ups_battery_percentage_charge numeric, + scan_apc_ups_battery_last_replacement_date text, + scan_apc_ups_battery_state numeric, + scan_apc_ups_battery_temperature numeric, + scan_apc_ups_battery_alarm_temperature numeric, + scan_apc_ups_battery_voltage numeric, + modified_date timestamp with time zone not null +); +ALTER TABLE history.scan_apc_ups_batteries OWNER TO admin; + +CREATE FUNCTION history_scan_apc_ups_batteries() RETURNS trigger +AS $$ +DECLARE + history_scan_apc_ups_batteries RECORD; +BEGIN + SELECT INTO history_scan_apc_ups_batteries * FROM scan_apc_ups_batteries WHERE scan_apc_ups_battery_uuid=new.scan_apc_ups_battery_uuid; + INSERT INTO history.scan_apc_ups_batteries + (scan_apc_ups_battery_uuid, + scan_apc_ups_battery_scan_apc_ups_uuid, + scan_apc_ups_battery_number, + scan_apc_ups_battery_replacement_date, + scan_apc_ups_battery_health, + scan_apc_ups_battery_model, + scan_apc_ups_battery_percentage_charge, + scan_apc_ups_battery_last_replacement_date, + scan_apc_ups_battery_state, + scan_apc_ups_battery_temperature, + scan_apc_ups_battery_alarm_temperature, + scan_apc_ups_battery_voltage, + modified_date) + VALUES + (history_scan_apc_ups_battery.scan_apc_ups_battery_uuid, + history_scan_apc_ups_battery.scan_apc_ups_battery_scan_apc_ups_uuid, + history_scan_apc_ups_battery.scan_apc_ups_battery_number, + history_scan_apc_ups_battery.scan_apc_ups_battery_replacement_date, + history_scan_apc_ups_battery.scan_apc_ups_battery_health, + history_scan_apc_ups_battery.scan_apc_ups_battery_model, + history_scan_apc_ups_battery.scan_apc_ups_battery_percentage_charge, + history_scan_apc_ups_battery.scan_apc_ups_battery_last_replacement_date, + history_scan_apc_ups_battery.scan_apc_ups_battery_state, + history_scan_apc_ups_battery.scan_apc_ups_battery_temperature, + history_scan_apc_ups_battery.scan_apc_ups_battery_alarm_temperature, + history_scan_apc_ups_battery.scan_apc_ups_battery_voltage, + history_scan_apc_ups_battery.modified_date); + RETURN NULL; +END; +$$ +LANGUAGE plpgsql; +ALTER FUNCTION history_scan_apc_ups_batteries() OWNER TO admin; + +CREATE TRIGGER trigger_scan_apc_ups_batteries + AFTER INSERT OR UPDATE ON scan_apc_ups_batteries + FOR EACH ROW EXECUTE PROCEDURE history_scan_apc_ups_batteries(); + + +-- Input power +CREATE TABLE scan_apc_ups_input ( + scan_apc_ups_input_uuid uuid not null primary key, + scan_apc_ups_input_scan_apc_ups_uuid uuid not null, + scan_apc_ups_input_frequency numeric not null, + scan_apc_ups_input_sensitivity numeric not null, + scan_apc_ups_input_voltage numeric not null, + scan_apc_ups_input_1m_maximum_input_voltage numeric not null, + scan_apc_ups_input_1m_minimum_input_voltage numeric not null, + modified_date timestamp with time zone not null, + + FOREIGN KEY(scan_apc_ups_input_scan_apc_ups_uuid) REFERENCES scan_apc_upses(scan_apc_ups_uuid) +); +ALTER TABLE scan_apc_ups_input OWNER TO admin; + +CREATE TABLE history.scan_apc_ups_input ( + history_id bigserial, + scan_apc_ups_input_uuid uuid, + scan_apc_ups_input_scan_apc_ups_uuid uuid, + scan_apc_ups_input_frequency numeric, + scan_apc_ups_input_sensitivity numeric, + scan_apc_ups_input_voltage numeric, + scan_apc_ups_input_1m_maximum_input_voltage numeric, + scan_apc_ups_input_1m_minimum_input_voltage numeric + modified_date timestamp with time zone not null +); +ALTER TABLE history.scan_apc_ups_input OWNER TO admin; + +CREATE FUNCTION history_scan_apc_ups_input() RETURNS trigger +AS $$ +DECLARE + history_scan_apc_ups_input RECORD; +BEGIN + SELECT INTO history_scan_apc_ups_input * FROM scan_apc_ups_input WHERE scan_apc_ups_input_uuid=new.scan_apc_ups_input_uuid; + INSERT INTO history.scan_apc_ups_input + (scan_apc_ups_input_uuid, + scan_apc_ups_input_scan_apc_ups_uuid, + scan_apc_ups_input_frequency, + scan_apc_ups_input_sensitivity, + scan_apc_ups_input_voltage, + scan_apc_ups_input_1m_maximum_input_voltage, + scan_apc_ups_input_1m_minimum_input_voltage, + modified_date) + VALUES + (history_scan_apc_ups_input.scan_apc_ups_input_uuid, + history_scan_apc_ups_input.scan_apc_ups_input_scan_apc_ups_uuid, + history_scan_apc_ups_input.scan_apc_ups_input_frequency, + history_scan_apc_ups_input.scan_apc_ups_input_sensitivity, + history_scan_apc_ups_input.scan_apc_ups_input_voltage, + history_scan_apc_ups_input.scan_apc_ups_input_1m_maximum_input_voltage, + history_scan_apc_ups_input.scan_apc_ups_input_1m_minimum_input_voltage, + history_scan_apc_ups_input.modified_date); + RETURN NULL; +END; +$$ +LANGUAGE plpgsql; +ALTER FUNCTION history_scan_apc_ups_input() OWNER TO admin; + +CREATE TRIGGER trigger_scan_apc_ups_input + AFTER INSERT OR UPDATE ON scan_apc_ups_input + FOR EACH ROW EXECUTE PROCEDURE history_scan_apc_ups_input(); + + +-- Output power +CREATE TABLE scan_apc_ups_output ( + scan_apc_ups_output_uuid uuid not null primary key, + scan_apc_ups_output_scan_apc_ups_uuid uuid not null, + scan_apc_ups_output_load_percentage numeric not null, + scan_apc_ups_output_time_on_batteries numeric not null, + scan_apc_ups_output_estimated_runtime numeric not null, + scan_apc_ups_output_frequency numeric not null, + scan_apc_ups_output_voltage numeric not null, + scan_apc_ups_output_total_output numeric not null, + modified_date timestamp with time zone not null, + + FOREIGN KEY(scan_apc_ups_output_scan_apc_ups_uuid) REFERENCES scan_apc_upses(scan_apc_ups_uuid) +); +ALTER TABLE scan_apc_ups_output OWNER TO admin; + +CREATE TABLE history.scan_apc_ups_output ( + history_id bigserial, + scan_apc_ups_output_uuid uuid, + scan_apc_ups_output_scan_apc_ups_uuid uuid, + scan_apc_ups_output_load_percentage numeric, + scan_apc_ups_output_time_on_batteries numeric, + scan_apc_ups_output_estimated_runtime numeric, + scan_apc_ups_output_frequency numeric, + scan_apc_ups_output_voltage numeric, + scan_apc_ups_output_total_output numeric, + modified_date timestamp with time zone not null +); +ALTER TABLE history.scan_apc_ups_output OWNER TO admin; + +CREATE FUNCTION history_scan_apc_ups_output() RETURNS trigger +AS $$ +DECLARE + history_scan_apc_ups_output RECORD; +BEGIN + SELECT INTO history_scan_apc_ups_output * FROM scan_apc_ups_output WHERE scan_apc_ups_output_uuid=new.scan_apc_ups_output_uuid; + INSERT INTO history.scan_apc_ups_output + (scan_apc_ups_output_uuid, + scan_apc_ups_output_scan_apc_ups_uuid, + scan_apc_ups_output_load_percentage, + scan_apc_ups_output_time_on_batteries, + scan_apc_ups_output_estimated_runtime, + scan_apc_ups_output_frequency, + scan_apc_ups_output_voltage, + scan_apc_ups_output_total_output, + modified_date) + VALUES + (history_scan_apc_ups_output.scan_apc_ups_output_uuid, + history_scan_apc_ups_output.scan_apc_ups_output_scan_apc_ups_uuid, + history_scan_apc_ups_output.scan_apc_ups_output_load_percentage, + history_scan_apc_ups_output.scan_apc_ups_output_time_on_batteries, + history_scan_apc_ups_output.scan_apc_ups_output_estimated_runtime, + history_scan_apc_ups_output.scan_apc_ups_output_frequency, + history_scan_apc_ups_output.scan_apc_ups_output_voltage, + history_scan_apc_ups_output.scan_apc_ups_output_total_output, + history_scan_apc_ups_output.modified_date); + RETURN NULL; +END; +$$ +LANGUAGE plpgsql; +ALTER FUNCTION history_scan_apc_ups_output() OWNER TO admin; + +CREATE TRIGGER trigger_scan_apc_ups_output + AFTER INSERT OR UPDATE ON scan_apc_ups_output + FOR EACH ROW EXECUTE PROCEDURE history_scan_apc_ups_output(); diff --git a/scancore-agents/scan-apc-ups/scan-apc-ups.xml b/scancore-agents/scan-apc-ups/scan-apc-ups.xml new file mode 100644 index 00000000..4b04ddd0 --- /dev/null +++ b/scancore-agents/scan-apc-ups/scan-apc-ups.xml @@ -0,0 +1,158 @@ + + + + + + + + + + + The variable: [#!variable!name!#] should have been an integer, but it appears it was not. Read: [#!variable!value!#]. + + + Starting: [#!variable!program!#]. + + + No APC UPSes found as configured UPSes, nothing to do. + A new APC UPS has been found; +-=] UPS Information: + +- UPS Name: ....... [#!variable!name!#] +- Model: .......... [#!variable!model!#] (may not be exact) +- Serial Number: .. [#!variable!serial_number!#] +- Manufactured: ... [#!variable!manufactured_date!#] (yyyy/mm/dd) +- Firmware Version: [#!variable!firmware_version!#] + +-=] Network Interface Information: + +- IP Address: ..... [#!variable!ip_address!#] +- Serial Number: .. [#!variable!nmc_serial_number!#] +- Firmware Version: [#!variable!nmc_firmware_version!#] +- MAC Address: .... [#!variable!nmc_mac_address!#] + +-=] UPS Configuration: + +- AC Restore Delay: .......... [#!variable!ac_restore_delay!#] second(s) +- Shutdown Delay: ............ [#!variable!shutdown_delay!#] second(s) +- Transfer to batteries above: [#!variable!high_transfer_voltage!#] vAC +- Transfer to batteries below: [#!variable!low_transfer_voltage!#] vAC + +-=] UPS Health Information: + +- #!variable!health!# +- #!variable!last_transfer_reason!# + + + + +- #!variable!battery_health!# +- #!variable!battery_state!# +- #!variable!say_time_on_batteries!# +- Delay between shutdown command received and actual shut down: [#!variable!shutdown_delay!#] second(s). +- #!variable!input_sensitivity!# + +-=] Current Sensor Values: + +- Battery Temperature: [#!variable!battery_temperature!#]°C (alarms at: [#!variable!battery_alarm_temperature!#]°C) +- Battery Voltage: [#!variable!battery_voltage!#] vDC +- Input Power Frequency: [#!variable!input_frequency!#] Hz +- Input Power Voltage: [#!variable!input_voltage!#] vAC + - In the last 60 seconds, the input voltage ranged from: [#!variable!input_1m_minimum_input_voltage!#] to: [#!variable!input_1m_maximum_input_voltage!#] vAC. +- Estimated Runtime: [#!variable!say_estimated_runtime!#] +- Output Load Percentage: [#!variable!output_load_percentage!#] % +- Output Power Frequency: [#!variable!output_frequency!#] Hz +- Output Power Voltage: [#!variable!output_voltage!#] vAC +- Lifetime Power Output: [#!variable!output_total_output!#] kWh + + + + Unknown + + + Communication with the UPS has been lost. + The UPS's health is in an unknown state. + The UPS is operating normally. + The UPS is running on its batteries. It is likely that the input power feeding the UPS has failed. Check for an input voltage alert. + The UPS is compensating for low input power. + The UPS is in a timed sleep. It will power back on when the timer has expired. + The UPS is in bypass-mode and was placed in this mode by software. Power is passing to downstream devices through a radio frequency interference filter, but is not conditioned in any other way. Batter protection is not available. + The UPS is off. No power is being provided to down-stream equipment. + The UPS is currently rebooting. + The UPS is in bypass-mode and was placed in this mode by a hardware switch. Power is passing to downstream devices through a radio frequency interference filter, but is not conditioned in any other way. Batter protection is not available. + The UPS is in bypass-mode because of an internal failure. Power is passing to downstream devices through a radio frequency interference filter, but is not conditioned in any other way. Batter protection is not available. + The UPS has lost input power and is sleeping. It will restore output power once input power has been restored. + The UPS is compensating for high input voltage. + The UPS is operating in low-power mode. In this mode, the UPS is in static bypass mode and it is drawing very little power. If a fault is detected, it will switch to either normal operation or forced static bypass mode. + The UPS is operating in hot-standby mode. + The UPS is performing a test of its batteries. + The UPS has been placed in emergency static bypass mode. Power is passing to downstream devices through a radio frequency interference filter, but is not conditioned in any other way. Batter protection is not available. + The UPS is in static bypass standby mode. It is not currently providing power to downstream devices. + The UPS is in power saving mode. The front panel display will be off but the UPS is operating normally. + The UPS is in SPoT (Self Power Test) operating mode. + The UPS is in ECOnversion mode. The UPS is providing power to the downstream devices via the bypass. The UPS's inverter is operational and ready to take over the output load if an input fault occurs. + + The UPS is running on its batteries. The input voltage has risen too high, so the UPS has switched to batteries to trim the output voltage. + The UPS is running on its batteries. The input voltage has dropped too low, so the UPS has switched to batteries to boost the output voltage. + The UPS is running on its batteries. The input voltage is nominal, though, so this is most likely just a self test and not a cause for concern. + + The UPS Reported a health value that isn't recognized. It should have reported an integer between 1~20, but: [#!variable!bad_value!#] was received.. + + + There is no information on when the UPS last transferred to battery. + The UPS has not transferred to battery power since the last time it booted. + The UPS last transferred to batteries because of high input voltage. + The UPS last transferred to batteries because of a brown out. That is, a prolonged drop in input voltage from the mains circuit. + The UPS last transferred to batteries because of a black out. That is, a prolonged loss of input voltage from the mains circuit. + The UPS last transferred to batteries because of a brief, minor reduction of input voltage from the mains circuit. + The UPS last transferred to batteries because of a brief, significant reduction of input voltage from the mains circuit. + The UPS last transferred to batteries because of a brief, minor increase of input voltage from the mains circuit. + The UPS last transferred to batteries because of a brief, significant spike of input voltage from the mains circuit. + The UPS last transferred to batteries as part of a planned self-test. + The UPS last transferred to batteries because of a significant change of input voltage from the mains circuit. + + The UPS Reported a last transfer value that isn't recognized. + + + The UPS battery state was not read. + The UPS battery is in an unknown state. + The UPS battery is operating normally. + The UPS battery is in a low voltage state. + The UPS battery is in a failed state and needs to be replaced. + + The UPS battery's state isn't recognized. It should have reported an integer between 1~4, but: [#!variable!bad_value!#] was received.. + + + The UPS battery health is unknown. + The UPS battery is healthy. + The UPS battery has failed and needs to be replaced. + + The UPS battery health value isn't recognized. It should have reported an integer between 1~2, but: [#!variable!bad_value!#] was received.. + + + The UPS is an 'Online' (dual-conversion) UPS so input sensitivity is not used. + The UPS is automatically determining the input power sensitivity. + The UPS is set to have a low input sensitivity. It will not switch to batteries unless the input power degrades significantly. This mode should only be used if the downstream equipment can handle a wider range of input voltages and frequencies. + The UPS is set to medium input sensitivity. This will cause the UPS to transfer to battery power under moderately distorted input. + The UPS is set to high sensitivity. It will switch to battery power under minor input distortion, providing the best protection for downstream equipment, but will shorten the serviceable life of the batteries if input power distorts frequently. + + The UPS sensitivity value isn't recognized. It should have reported an integer between 1~2, but: [#!variable!bad_value!#] was received.. + + + diff --git a/share/words.xml b/share/words.xml index fab0c98f..bcc4a532 100644 --- a/share/words.xml +++ b/share/words.xml @@ -236,6 +236,8 @@ The error was: Unable to mark the server with UUID: [#!variable!uuid!#] as "deleted" because it doesn't apprear to exist in the database in the first place. The 'anvil_uuid': [#!variable!anvil_uuid!#] in invalid. The MIB file: [#!variable!mib!#] doesn't exist or can't be read. + The date: [#!variable!date!#] is not in either the 'mm/dd/yy' or 'mm/dd/yyyy' formats. Can't convert to 'yyyy/mm/dd'. + The temperature: [#!variable!temperature!#] does not appear to be valid.. Current Network Interfaces and States