#!/usr/bin/perl # # This software was created by Alteeve's Niche! Inc. and has been released # under the terms of the GNU GPL version 2. # # ScanCore Scan Agent for LSI-based RAID controllers using the 'storcli64' command line tool. # # https://alteeve.com # # Exit Codes: # 0 - Success # 1 - storcli64 not installed # 2 - storcli64 is installed but it is not executable. # 3 - No LSI-based controllers found. # 4 - Got data from the wrong controller. # 5 - Unexpecte status when querying adapter # 6 - Failed to parse out the controller's serial number. # 7 - Failed to match a BBU to a host controller's serial number. # 8 - Failed to match a controller's serial number to a controller UUID. # 9 - Failed to match a Cachevault to a host controller's serial number. # 10 - Failed to match a virtual drive ID string to a host controller's serial number. # 11 - Failed to match a drive group ID string to a host controller's serial number. # 12 - Failed to get the controller serial number that a physical drive is connected to. # 13 - Failed to get the serial number of a physical drive. # 14 - Drive group UUID doesn't exist when it should have. # 15 - Controller serial number for an existing drive group wasn't found. # 16 - Non-numeric value in numeric virtual drive variable. # # 255 - The host's UUID isn't in the hosts table yet, ScanCore itself hasn't been run. # # TODO: # - DELETE a remaining_reserve_space variable from a history.scan_storcli_variables on one dashboard and then # trigger a resync. Appears to be causing a duplicate insert error. # # - Check the cache policy and reset it to 'writeback' if the BBU/FBU is healthy and the cache changes to # write-through. # - When two or more drives have errors, 'drive:other_error_count' is only set to '1'. It should be the # number of drives with errors. Also, if the error on one drive got above 100, its weight should be '2' and # above 1000, set to '3'. # # NOTE: # - LSI seems to flip between "Virtual Drive" and "Virtual Disk". We're standardizing on "Virtual Drive". # # NOTE: # - Health values # - Controller - Correctable errors = 1 # - Controller - Uncorrectable errors = 5 # - Controller - Status changes = 5 # - Drive group - partially degraded = 5 # - Drive group - degraded = 10 # - Cachevault - Replacement needed = 5 # - BBU - Replacement needed = 5 # - Temperature - Critical = 2 # - Temperature - Warning = 1 # Use my modules. use strict; use warnings; use Anvil::Tools; use Data::Dumper; use Math::BigInt; 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(); # Make sure we're running as 'root' # $< == real UID, $> == effective UID if (($< != 0) && ($> != 0)) { # Not root print $anvil->Words->string({key => "error_0005"})."\n"; $anvil->nice_exit({exit_code => 1}); } # Here we store data and variables for this agent. $anvil->data->{'scan-storcli'} = { alert_sort => 2, arguments => { adapter_count => "show ctrlcount", alarm_state => "show alarm", # BBU battery or FBU capacitor bank info. bgi_rate => "show bgirate", bbu_data => "/bbu show all", cc_rate => "show ccrate", cachevault_data => "/cv show all", controller_info => "show all", physical_disk_data => "/eall /sall show all", performance_mode => "show perfmode", pr_rate => "show prrate", rebuild_rate => "show rebuildrate", virtual_drive_data => "/vall show all", }, disable => 0, health => { old => {}, new => {}, }, # This will keep track of devices with serial numbers so that it is easy to look up # the UUID from the serial numbers and vice versa. controllers => { by_serial => {}, by_uuid => {}, }, cachevaults => { by_serial => {}, by_uuid => {}, }, bbus => { by_serial => {}, by_uuid => {}, }, physical_drives => { by_serial => {}, by_uuid => {}, }, disable => 0, language => "en_CA", log_level => 1, log_language => "en_CA", log_file => "/var/log/ScanCore.log", log_db_transactions => 0, thresholds => { # 45646-00B_Cache_Backup_Products_SAS_SATA_RAID_Controller_UserGd.pdf -> Specifications for the BBU Models bbu => { normal => { high_warning => 35, high_critical => 40, low_warning => 15, low_critical => 10, jump => 3, buffer => 2, }, discharging => { high_warning => 50, high_critical => 55, low_warning => 15, low_critical => 10, jump => 3, buffer => 2, }, }, # 45646-00B_Cache_Backup_Products_SAS_SATA_RAID_Controller_UserGd.pdf -> Specifications for the Cachevaul Models cachevault => { high_warning => 50, high_critical => 55, low_warning => 5, low_critical => 0, jump => 5, buffer => 3, }, # This is used for unknown sensors and really shouldn't be used at all. 'default' => { high_warning => 50, high_critical => 55, low_warning => 15, low_critical => 10, jump => 5, buffer => 3, }, drives => { # http://storage.toshiba.com/docs/product-datasheets/mk01grrb-r.pdf hdd => { high_warning => 50, high_critical => 55, low_warning => 10, low_critical => 5, jump => 3, buffer => 2, }, # http://toshiba.semicon-storage.com/us/product/storage-products/enterprise-ssd/px02smb-px02smfxxx.html ssd => { high_warning => 50, high_critical => 55, low_warning => 5, low_critical => 0, jump => 3, buffer => 2, }, }, # See email from M. Labrosse on 2014/04/14 @ 12:20; Specs not publicly available raid_on_chip => { high_warning => 108, high_critical => 115, low_warning => 15, low_critical => 10, jump => 10, buffer => 5, }, }, queries => [], }; $anvil->Storage->read_config(); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, 'print' => 1, key => "log_0115", variables => { program => $THIS_FILE }}); # Read switches $anvil->data->{switches}{force} = 0; $anvil->data->{switches}{purge} = 0; $anvil->Get->switches; # If we're disabled and '--force' wasn't used, exit. if (($anvil->data->{scancore}{'scan-storcli'}{disable}) && (not $anvil->data->{switches}{force})) { # Exit. $anvil->nice_exit({exit_code => 0}); } # Handle start-up tasks my $problem = $anvil->ScanCore->agent_startup({agent => $THIS_FILE}); if ($problem) { $anvil->nice_exit({exit_code => 1}); } if ($anvil->data->{switches}{purge}) { # This can be called when doing bulk-database purges. my $schema_file = $anvil->data->{path}{directories}{scan_agents}."/".$THIS_FILE."/".$THIS_FILE.".sql"; $anvil->Database->purge_data({ debug => 2, tables => $anvil->Database->get_tables_from_schema({schema_file => $schema_file}), }); $anvil->nice_exit({exit_code => 0}); } $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "scan_storcli_message_0001"}); # This does two things; It checks to see if storcli64 is installed (exits '1' if not, exits '2' if not # executable) and then checks to see if any controllers are found in the system (exits '3' if not). find_lsi_controllers($anvil); # If we're still alive, start gathering data. gather_data($anvil); # Figure out, other than temperatures, what should be added to or removed from health. pre_process_health($anvil); # Look for changes. find_changes($anvil); # Process temperatures! This also sets health values for warning and critical temperatures so make sure we # always call this before process_health(). process_temperatures($anvil); # Finally, process health weights. process_health($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}); ############################################################################################################# # Function below # ############################################################################################################# # This looks for anything other than temperature sensors that will feed into the health of the node. sub pre_process_health { my ($anvil) = @_; # Is the array degraded or partially degraded? foreach my $id_string (sort {$a cmp $b} keys %{$anvil->data->{virtual_drive}}) { foreach my $drive_group (sort {$a cmp $b} keys %{$anvil->data->{virtual_drive}{$id_string}{drive_group}}) { foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${id_string}::drive_group::${drive_group}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable}, }}); my $value = $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable}; if ($value eq "Partially Degraded") { # Partially degraded array my $health_source_name = "drive_group:".$id_string,"-".$drive_group.":".$variable; $anvil->data->{health}{new}{$health_source_name} = 5; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "health::new::$health_source_name" => $anvil->data->{health}{new}{$health_source_name}, }}); } elsif ($value eq "Degraded") { # Degraded array my $health_source_name = "drive_group:".$id_string,"-".$drive_group.":".$variable; $anvil->data->{health}{new}{$health_source_name} = 10; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "health::new::$health_source_name" => $anvil->data->{health}{new}{$health_source_name}, }}); } } } } # Controllers. foreach my $serial_number (sort {$a cmp $b} keys %{$anvil->data->{controller}{serial_number}}) { foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{controller}{serial_number}{$serial_number}{variable}}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "controller::serial_number::${serial_number}::variable::$variable" => $anvil->data->{controller}{serial_number}{$serial_number}{variable}{$variable}, }}); my $value = $anvil->data->{controller}{serial_number}{$serial_number}{variable}{$variable}; if (($variable eq "alarm_state") && (lc($value) ne "missing") && (lc($value) ne "off")) { my $health_source_name = "controller:".$variable; $anvil->data->{health}{new}{$health_source_name} = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "health::new::controller:$health_source_name" => $anvil->data->{health}{new}{$health_source_name}, }}); } elsif (($variable eq "memory_correctable_errors") && ($value ne "0")) { # TODO: I might want to scale this where 1~X = 1, Y~Z = 3, etc my $health_source_name = "controller:".$variable; $anvil->data->{health}{new}{$health_source_name} = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "health::new::controller:$health_source_name" => $anvil->data->{health}{new}{$health_source_name}, }}); } elsif (($variable eq "memory_uncorrectable_errors") && ($value ne "0")) { # TODO: I might want to scale this where 1~X = 5, Y~Z = 10, etc my $health_source_name = "controller:".$variable; $anvil->data->{health}{new}{$health_source_name} = 5; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "health::new::controller:$health_source_name" => $anvil->data->{health}{new}{$health_source_name}, }}); } elsif (($variable eq "controller_status") && (lc($value) ne "optimal")) { # 'Needs Attention' when array degrades my $health_source_name = "controller:".$variable; $anvil->data->{health}{new}{$health_source_name} = 5; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "health::new::controller:$health_source_name" => $anvil->data->{health}{new}{$health_source_name}, }}); } } } # Physical Drives. foreach my $scan_storcli_virtual_drive_id_string (sort {$a cmp $b} keys %{$anvil->data->{virtual_drive}}) { foreach my $drive_group (sort {$a cmp $b} keys %{$anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}}) { foreach my $enclosure_id (sort {$a cmp $b} keys %{$anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{enclosure_id}}) { foreach my $slot_number (sort {$a cmp $b} keys %{$anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}}) { foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${scan_storcli_virtual_drive_id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::${variable}" => $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable}, }}); my $value = $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable}; if ((($variable eq "media_error_count") or ($variable eq "other_error_count")) && ($value ne "0")) { # If the count is over 50, set to '2'. If over 5, set to 1. my $health_source_name = "drive:".$variable; if ($value > 50) { $anvil->data->{health}{new}{$health_source_name} = 2; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "health::new::controller:alarm-state" => $anvil->data->{health}{new}{'controller:alarm-state'}, }}); } elsif ($value > 5) { $anvil->data->{health}{new}{$health_source_name} = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "health::new::controller:alarm-state" => $anvil->data->{health}{new}{'controller:alarm-state'}, }}); } } elsif (($variable eq "predictive_failure_count") && ($value ne "0")) { # A single tick of this requires immediate replacement. my $health_source_name = "drive:".$variable; $anvil->data->{health}{new}{$health_source_name} = 2; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "health::new::controller:alarm-state" => $anvil->data->{health}{new}{'controller:alarm-state'}, }}); } } } } } } # Cachevaults. foreach my $cachevault_serial_number (sort {$a cmp $b} keys %{$anvil->data->{cachevault}{serial_number}}) { foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{cachevault}{serial_number}{$cachevault_serial_number}{variable}}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cachevault::serial_number::${cachevault_serial_number}::variable::$variable" => $anvil->data->{cachevault}{serial_number}{$cachevault_serial_number}{variable}{$variable}, }}); my $value = $anvil->data->{cachevault}{serial_number}{$cachevault_serial_number}{variable}{$variable}; if (($variable eq "replacement_required") && (lc($value) eq "yes")) { my $health_source_name = "cachevault:".$cachevault_serial_number.":".$variable; $anvil->data->{health}{new}{$health_source_name} = 5; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "health::new::$health_source_name" => $anvil->data->{health}{new}{$health_source_name}, }}); } } } # BBUs foreach my $bbu_serial_number (sort {$a cmp $b} keys %{$anvil->data->{bbu}{serial_number}}) { foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{bbu}{serial_number}{$bbu_serial_number}{variable}}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "bbu::serial_number::${bbu_serial_number}::variable::$variable" => $anvil->data->{bbu}{serial_number}{$bbu_serial_number}{variable}{$variable}, }}); my $value = $anvil->data->{bbu}{serial_number}{$bbu_serial_number}{variable}{$variable}; if (($variable eq "replacement_required") && (lc($value) eq "yes")) { my $health_source_name = "bbu:".$bbu_serial_number.":".$variable; $anvil->data->{health}{new}{$health_source_name} = 5; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "health::new::$health_source_name" => $anvil->data->{health}{new}{$health_source_name}, }}); } } } return(0); } # This reads in all health wieghts previously set, alters ones as needed, INSERTs new ones and DELETEs old # ones. sub process_health { my ($anvil) = @_; # This will hold our updates. $anvil->data->{'scan-storcli'}{queries} = []; # Read in previous health values. my $query = " SELECT health_uuid, health_agent_name, health_source_name, health_source_weight FROM health WHERE health_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." AND health_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__}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "results" => $results, }}); foreach my $row (@{$results}) { my $health_uuid = $row->[0]; my $health_agent_name = $row->[1]; my $health_source_name = $row->[2]; my $health_source_weight = $row->[3]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "health_uuid" => $health_uuid, "health_agent_name" => $health_agent_name, "health_source_name" => $health_source_name, "health_source_weight" => $health_source_weight, }}); $anvil->data->{health}{old}{$health_source_name}{uuid} = $health_uuid; $anvil->data->{health}{old}{$health_source_name}{value} = $health_source_weight; } # Read in the new ones foreach my $health_source_name (sort {$a cmp $b} keys %{$anvil->data->{health}{new}}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "health::new::$health_source_name" => $anvil->data->{health}{new}{$health_source_name}, }}); my $health_uuid = ""; if (exists $anvil->data->{health}{old}{$health_source_name}) { $health_uuid = $anvil->data->{health}{old}{$health_source_name}{uuid}; } $health_uuid = $anvil->Database->insert_or_update_health({ debug => 2, cache => $anvil->data->{'scan-storcli'}{queries}, health_uuid => $health_uuid, health_host_uuid => $anvil->Get->host_uuid, health_agent_name => $THIS_FILE, health_source_name => $health_source_name, health_source_weight => $anvil->data->{health}{new}{$health_source_name}, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { health_uuid => $health_uuid }}); } # Delete any old entries that are left. foreach my $health_source_name (sort {$a cmp $b} keys %{$anvil->data->{health}{old}}) { # Well set the source name to 'DELETED'. my $health_uuid = $anvil->Database->insert_or_update_health({ debug => 2, cache => $anvil->data->{'scan-storcli'}{queries}, 'delete' => 1, health_uuid => $anvil->data->{health}{old}{$health_source_name}{uuid}, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { health_uuid => $health_uuid }}); } # Now commit the changes. $anvil->Database->write({query => $anvil->data->{'scan-storcli'}{queries}, source => $THIS_FILE, line => __LINE__}); $anvil->data->{'scan-storcli'}{queries} = []; return(0); } # 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_value_c, temperature_weight, temperature_state, temperature_is FROM temperature WHERE temperature_host_uuid = ".$anvil->Database->quote($anvil->Get->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_value_c = $row->[3]; my $temperature_weight = $row->[4]; my $temperature_state = $row->[5]; my $temperature_is = $row->[6]; $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_value_c => $temperature_value_c, temperature_weight => $temperature_weight, temperature_state => $temperature_state, temperature_is => $temperature_is, }}); $anvil->data->{old}{temperature}{$temperature_sensor_name}{$temperature_sensor_host} = { temperature_uuid => $temperature_uuid, temperature_value_c => $temperature_value_c, 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_value_c = $anvil->data->{new}{temperature}{$variable}{$serial_number}{temperature_value_c}; 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_value_c => $new_temperature_value_c, 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. my $health_source_name = ""; if ($new_temperature_state eq "warning") { $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") { $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}, }}); } # Store my $temperature_uuid = ""; if (exists $anvil->data->{old}{temperature}{$variable}{$serial_number}) { $temperature_uuid = $anvil->data->{old}{temperature}{$variable}{$serial_number}{temperature_uuid}; delete $anvil->data->{old}{temperature}{$variable}{$serial_number}; } $temperature_uuid = $anvil->Database->insert_or_update_temperature({ cache => $anvil->data->{'scan-storcli'}{queries}, debug => 2, temperature_uuid => $temperature_uuid, temperature_host_uuid => $anvil->Get->host_uuid, temperature_agent_name => $THIS_FILE, temperature_sensor_host => $serial_number, temperature_sensor_name => $variable, temperature_value_c => $new_temperature_value_c, temperature_state => $new_temperature_state, temperature_is => $new_temperature_is, temperature_weight => $anvil->data->{health}{new}{$health_source_name}, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { temperature_uuid => $temperature_uuid }}); } } # 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 $temperature_uuid = $anvil->data->{old}{temperature}{$variable}{$serial_number}{temperature_uuid}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { temperature_uuid => $temperature_uuid }}); $temperature_uuid = $anvil->Database->insert_or_update_temperature({ cache => $anvil->data->{'scan-storcli'}{queries}, debug => 2, 'delete' => 1, temperature_uuid => $temperature_uuid, }); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { temperature_uuid => $temperature_uuid }}); } } # Commit the queries. $anvil->Database->write({query => $anvil->data->{'scan-storcli'}{queries}, source => $THIS_FILE, line => __LINE__}); $anvil->data->{'scan-storcli'}{queries} = []; return(0); } # This returns the value for a given storcli DB variable from this host. sub get_scan_storcli_variable { my ($anvil, $scan_storcli_variable_name, $scan_storcli_variable_source_table, $scan_storcli_variable_source_uuid) = @_; my $query = " SELECT scan_storcli_variable_value FROM scan_storcli_variables WHERE scan_storcli_variable_name = ".$anvil->Database->quote($scan_storcli_variable_name)." AND scan_storcli_variable_source_table = ".$anvil->Database->quote($scan_storcli_variable_source_table)." AND scan_storcli_variable_source_uuid = ".$anvil->Database->quote($scan_storcli_variable_source_uuid)." AND scan_storcli_variable_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." ;"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); my $value = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0]; $value = "" if not defined $value; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { value => $value }}); return($value); } # 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) = @_; # Read in the old data. As we compare and UPDATE if needed, then we'll delete. If any are not found, # then it must be new and will be INSERTed. Any old records left over will have vanished. read_last_scan($anvil); ### NOTE: We will loop through each section of data we scanned, deleting records as we process them ### that existed in the DB, and then marking as removed anything left in the databased data not ### seen in this scan. process_controllers($anvil); process_bbus($anvil); process_cachevaults($anvil); process_virtual_drives($anvil); # This calls process_drive_groups(); process_physical_disks($anvil); return(0); } # Look for added, changed or deleted drive groups. sub process_drive_groups { my ($anvil, $scan_storcli_virtual_drive_id_string, $scan_storcli_drive_group_virtual_drive_uuid, $controller_uuid, $host_controller_serial_number) = @_; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_virtual_drive_id_string => $scan_storcli_virtual_drive_id_string, scan_storcli_drive_group_virtual_drive_uuid => $scan_storcli_drive_group_virtual_drive_uuid, controller_uuid => $controller_uuid, host_controller_serial_number => $host_controller_serial_number, }}); ### NOTE: The parent VD may be VANISHED, in which case this DG is likely VANISHED, too. # Show the drive group and drives. foreach my $drive_group (sort {$a cmp $b} keys %{$anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}}) { my $scan_storcli_drive_group_id_string = $scan_storcli_virtual_drive_id_string."-dg".$drive_group; my $virtual_drive = ($scan_storcli_virtual_drive_id_string =~ /-vd(\d+)$/)[0]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_virtual_drive_id_string => $scan_storcli_virtual_drive_id_string, scan_storcli_drive_group_id_string => $scan_storcli_drive_group_id_string, virtual_drive => $virtual_drive, drive_group => $drive_group, }}); # Make sure I have the host's serial number and UUID. if (not $host_controller_serial_number) { # Error! $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "scan_storcli_error_0011", variables => { id_string => $scan_storcli_virtual_drive_id_string }}); $anvil->nice_exit({exit_code => 11}); } if (not $controller_uuid) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "scan_storcli_error_0008", variables => { serial_number => $host_controller_serial_number }}); $anvil->nice_exit({exit_code => 8}); } ### NOTE: This is here purely for debugging. # Show the basic drive group data foreach my $type ("variable", "temperature") { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { type => $type }}); $anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => 2, prefix => $scan_storcli_drive_group_id_string." (".$drive_group.") - ".$type, list => $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{$type}, }); } # Is this a new Drive Group? my $scan_storcli_drive_group_uuid = ""; my $is_new = 0; if (exists $anvil->data->{'scan-storcli'}{drive_groups}{by_id_string}{$scan_storcli_drive_group_id_string}) { # Yes! $scan_storcli_drive_group_uuid = $anvil->data->{'scan-storcli'}{drive_groups}{by_id_string}{$scan_storcli_drive_group_id_string}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_drive_group_uuid => $scan_storcli_drive_group_uuid }}); } else { # No, this is a new Drive Group. Create a new UUID for it. $scan_storcli_drive_group_uuid = $anvil->Get->uuid(); $is_new = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_drive_group_uuid => $scan_storcli_drive_group_uuid, is_new => $is_new, }}); # Add the keys for looking it up by UUID or serial number. $anvil->data->{'scan-storcli'}{drive_groups}{by_id_string}{$scan_storcli_drive_group_id_string} = $scan_storcli_drive_group_uuid; $anvil->data->{'scan-storcli'}{drive_groups}{by_uuid}{$scan_storcli_drive_group_uuid} = $scan_storcli_virtual_drive_id_string; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "scan-storcli::drive_groups::by_id_string::$scan_storcli_virtual_drive_id_string" => $anvil->data->{'scan-storcli'}{drive_groups}{by_id_string}{$scan_storcli_drive_group_id_string}, "scan-storcli::drive_groups::by_uuid::$scan_storcli_drive_group_uuid" => $anvil->data->{'scan-storcli'}{drive_groups}{by_uuid}{$scan_storcli_drive_group_uuid}, }}); } my $new_drive_group_access = ""; my $new_drive_group_array_size = ""; my $new_drive_group_array_state = ""; my $new_drive_group_cache = ""; my $new_drive_group_cachecade = ""; my $new_drive_group_consistent = ""; my $new_drive_group_disk_cache = ""; my $new_drive_group_raid_type = ""; my $new_drive_group_read_cache = ""; my $new_drive_group_scheduled_cc = ""; my $new_drive_group_write_cache = ""; foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{variable}}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${scan_storcli_virtual_drive_id_string}::drive_group::${drive_group}::variable::${variable}" => $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{variable}{$variable}, }}); # Store and delete the value if ($variable eq "access") { $new_drive_group_access = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_drive_group_access => $new_drive_group_access }}); next; } if ($variable eq "array_size") { $new_drive_group_array_size = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_drive_group_array_size => $new_drive_group_array_size }}); next; } if ($variable eq "array_state") { $new_drive_group_array_state = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_drive_group_array_state => $new_drive_group_array_state }}); next; } if ($variable eq "cache") { $new_drive_group_cache = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_drive_group_cache => $new_drive_group_cache }}); next; } if ($variable eq "cachecade") { $new_drive_group_cachecade = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_drive_group_cachecade => $new_drive_group_cachecade }}); next; } if ($variable eq "consistent") { $new_drive_group_consistent = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_drive_group_consistent => $new_drive_group_consistent }}); next; } if ($variable eq "disk_cache") { $new_drive_group_disk_cache = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_drive_group_disk_cache => $new_drive_group_disk_cache }}); next; } if ($variable eq "raid_type") { $new_drive_group_raid_type = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_drive_group_raid_type => $new_drive_group_raid_type }}); next; } if ($variable eq "read_cache") { $new_drive_group_read_cache = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_drive_group_read_cache => $new_drive_group_read_cache }}); next; } if ($variable eq "scheduled_consistency_check") { $new_drive_group_scheduled_cc = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_drive_group_scheduled_cc => $new_drive_group_scheduled_cc }}); next; } if ($variable eq "write_cache") { $new_drive_group_write_cache = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_drive_group_write_cache => $new_drive_group_write_cache }}); next; } } # Pull out the rest of the variables now. If the Drive Group is new, all variables will be # INSERTed. If the Drive Group exists, each variable will be examined and new ones will be # INSERTed, existing ones will be checked for changes and UPDATEd as needed. If the Drive # Group is NOT new, then variables from the old data will be deleted as we go and any not # found in the current data set will be left over. We'll use this to determine variables that # have vanished. They will not be deleted, but their value will be set to 'VANISHED'. $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { is_new => $is_new }}); if ($is_new) { ### NOTE: DG 9999 will be mostly blank and that is fine $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_drive_group_uuid => $scan_storcli_drive_group_uuid, controller_uuid => $controller_uuid, scan_storcli_drive_group_id_string => $scan_storcli_drive_group_id_string, new_drive_group_access => $new_drive_group_access, new_drive_group_array_size => $new_drive_group_array_size, new_drive_group_array_state => $new_drive_group_array_state, new_drive_group_cache => $new_drive_group_cache, new_drive_group_cachecade => $new_drive_group_cachecade, new_drive_group_consistent => $new_drive_group_consistent, new_drive_group_disk_cache => $new_drive_group_disk_cache, new_drive_group_raid_type => $new_drive_group_raid_type, new_drive_group_read_cache => $new_drive_group_read_cache, new_drive_group_scheduled_cc => $new_drive_group_scheduled_cc, new_drive_group_write_cache => $new_drive_group_write_cache, }}); ### NOTE: The rest of the alerts will be in the format '- Variable: [$value]'. # Send an alert telling the user that we've found a new controller. my $variables = { on_controller => $host_controller_serial_number, id_string => $scan_storcli_drive_group_id_string, access => $new_drive_group_access, array_size => $new_drive_group_array_size, array_state => $new_drive_group_array_state, cache => $new_drive_group_cache, cachecade => $new_drive_group_cachecade, consistent => $new_drive_group_consistent, disk_cache => $new_drive_group_disk_cache, raid_type => $new_drive_group_raid_type, read_cache => $new_drive_group_read_cache, scheduled_cc => $new_drive_group_scheduled_cc, write_cache => $new_drive_group_write_cache, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "scan_storcli_note_0005", variables => $variables}); $anvil->Alert->register({ alert_level => "notice", message => "scan_storcli_note_0005", variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); # INSERT my $query = " INSERT INTO scan_storcli_drive_groups ( scan_storcli_drive_group_uuid, scan_storcli_drive_group_host_uuid, scan_storcli_drive_group_virtual_drive_uuid, scan_storcli_drive_group_id_string, scan_storcli_drive_group_access, scan_storcli_drive_group_array_size, scan_storcli_drive_group_array_state, scan_storcli_drive_group_cache, scan_storcli_drive_group_cachecade, scan_storcli_drive_group_consistent, scan_storcli_drive_group_disk_cache, scan_storcli_drive_group_raid_type, scan_storcli_drive_group_read_cache, scan_storcli_drive_group_scheduled_cc, scan_storcli_drive_group_write_cache, modified_date ) VALUES ( ".$anvil->Database->quote($scan_storcli_drive_group_uuid).", ".$anvil->Database->quote($anvil->Get->host_uuid).", ".$anvil->Database->quote($scan_storcli_drive_group_virtual_drive_uuid).", ".$anvil->Database->quote($scan_storcli_drive_group_id_string).", ".$anvil->Database->quote($new_drive_group_access).", ".$anvil->Database->quote($new_drive_group_array_size).", ".$anvil->Database->quote($new_drive_group_array_state).", ".$anvil->Database->quote($new_drive_group_cache).", ".$anvil->Database->quote($new_drive_group_cachecade).", ".$anvil->Database->quote($new_drive_group_consistent).", ".$anvil->Database->quote($new_drive_group_disk_cache).", ".$anvil->Database->quote($new_drive_group_raid_type).", ".$anvil->Database->quote($new_drive_group_read_cache).", ".$anvil->Database->quote($new_drive_group_scheduled_cc).", ".$anvil->Database->quote($new_drive_group_write_cache).", ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." );"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; # Process the rest of the variables and temperatures now. foreach my $type ("variable", "temperature") { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { type => $type }}); foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{$type}}) { my $value = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{$type}{$variable}; my $temperature = $type eq "temperature" ? "TRUE" : "FALSE"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, temperature => $temperature, }}); # Send an alert telling the user that we've found a new controller. my $variables = { name => $variable, value => $value, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "scan_storcli_note_0002", variables => $variables}); $anvil->Alert->register({ alert_level => "notice", message => "scan_storcli_note_0002", show_header => 0, variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); # INSERT my $query = " INSERT INTO scan_storcli_variables ( scan_storcli_variable_uuid, scan_storcli_variable_host_uuid, scan_storcli_variable_source_table, scan_storcli_variable_source_uuid, scan_storcli_variable_is_temperature, scan_storcli_variable_name, scan_storcli_variable_value, modified_date ) VALUES ( ".$anvil->Database->quote($anvil->Get->uuid()).", ".$anvil->Database->quote($anvil->Get->host_uuid).", 'scan_storcli_drive_groups', ".$anvil->Database->quote($scan_storcli_drive_group_uuid).", ".$anvil->Database->quote($temperature).", ".$anvil->Database->quote($variable).", ".$anvil->Database->quote($value).", ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." );"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } } } else { ### NOTE: The serial number should never change (a changed SN/Drive Group should be ### picked up as a new Drive Group), but we check/update just to be safe. # Look for changes. my $main_table_changed = 0; my $old_drive_group_id_string = $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_id_string}; my $old_drive_group_virtual_drive_uuid = $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_virtual_drive_uuid}; my $old_drive_group_access = $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_access}; my $old_drive_group_array_size = $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_array_size}; my $old_drive_group_array_state = $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_array_state}; my $old_drive_group_cache = $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_cache}; my $old_drive_group_cachecade = $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_cachecade}; my $old_drive_group_consistent = $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_consistent}; my $old_drive_group_disk_cache = $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_disk_cache}; my $old_drive_group_raid_type = $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_raid_type}; my $old_drive_group_read_cache = $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_read_cache}; my $old_drive_group_scheduled_cc = $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_scheduled_cc}; my $old_drive_group_write_cache = $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_write_cache}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_drive_group_id_string => $scan_storcli_drive_group_id_string, old_drive_group_id_string => $old_drive_group_id_string, scan_storcli_drive_group_virtual_drive_uuid => $scan_storcli_drive_group_virtual_drive_uuid, old_drive_group_virtual_drive_uuid => $old_drive_group_virtual_drive_uuid, new_drive_group_access => $new_drive_group_access, old_drive_group_access => $old_drive_group_access, new_drive_group_array_size => $new_drive_group_array_size, old_drive_group_array_size => $old_drive_group_array_size, new_drive_group_array_state => $new_drive_group_array_state, old_drive_group_array_state => $old_drive_group_array_state, new_drive_group_cache => $new_drive_group_cache, old_drive_group_cache => $old_drive_group_cache, new_drive_group_cachecade => $new_drive_group_cachecade, old_drive_group_cachecade => $old_drive_group_cachecade, new_drive_group_consistent => $new_drive_group_consistent, old_drive_group_consistent => $old_drive_group_consistent, new_drive_group_disk_cache => $new_drive_group_disk_cache, old_drive_group_disk_cache => $old_drive_group_disk_cache, new_drive_group_raid_type => $new_drive_group_raid_type, old_drive_group_raid_type => $old_drive_group_raid_type, new_drive_group_read_cache => $new_drive_group_read_cache, old_drive_group_read_cache => $old_drive_group_read_cache, new_drive_group_scheduled_cc => $new_drive_group_scheduled_cc, old_drive_group_scheduled_cc => $old_drive_group_scheduled_cc, new_drive_group_write_cache => $new_drive_group_write_cache, old_drive_group_write_cache => $old_drive_group_write_cache, }}); if (not defined $old_drive_group_virtual_drive_uuid) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "scan_storcli_error_0014", variables => { drive_group => $drive_group }}); $anvil->nice_exit({exit_code => 14}); } if (($scan_storcli_drive_group_id_string ne $old_drive_group_id_string) or ($scan_storcli_drive_group_virtual_drive_uuid ne $old_drive_group_virtual_drive_uuid) or ($new_drive_group_access ne $old_drive_group_access) or ($new_drive_group_array_size ne $old_drive_group_array_size) or ($new_drive_group_array_state ne $old_drive_group_array_state) or ($new_drive_group_cache ne $old_drive_group_cache) or ($new_drive_group_cachecade ne $old_drive_group_cachecade) or ($new_drive_group_consistent ne $old_drive_group_consistent) or ($new_drive_group_disk_cache ne $old_drive_group_disk_cache) or ($new_drive_group_raid_type ne $old_drive_group_raid_type) or ($new_drive_group_read_cache ne $old_drive_group_read_cache) or ($new_drive_group_scheduled_cc ne $old_drive_group_scheduled_cc) or ($new_drive_group_write_cache ne $old_drive_group_write_cache)) { # Send a warning level alert because nothing should change here, ever. my $message_key = "scan_storcli_warning_0024"; if ($new_drive_group_access ne $old_drive_group_access) { if ($new_drive_group_access eq "VANISHED") { # Drive Group has returned. $message_key = "scan_storcli_warning_0025"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { message_key => $message_key }}); } } my $variables = { old_id_string => $old_drive_group_id_string, new_id_string => $scan_storcli_drive_group_id_string, new_access => $new_drive_group_access, old_access => $old_drive_group_access, new_array_size => $new_drive_group_array_size, old_array_size => $old_drive_group_array_size, new_array_state => $new_drive_group_array_state, old_array_state => $old_drive_group_array_state, new_cache => $new_drive_group_cache, old_cache => $old_drive_group_cache, new_cachecade => $new_drive_group_cachecade, old_cachecade => $old_drive_group_cachecade, new_consistent => $new_drive_group_consistent, old_consistent => $old_drive_group_consistent, new_disk_cache => $new_drive_group_disk_cache, old_disk_cache => $old_drive_group_disk_cache, new_raid_type => $new_drive_group_raid_type, old_raid_type => $old_drive_group_raid_type, new_read_cache => $new_drive_group_read_cache, old_read_cache => $old_drive_group_read_cache, new_scheduled_cc => $new_drive_group_scheduled_cc, old_scheduled_cc => $old_drive_group_scheduled_cc, new_write_cache => $new_drive_group_write_cache, old_write_cache => $old_drive_group_write_cache, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => $message_key, variables => $variables}); $anvil->Alert->register({ alert_level => "warning", message => $message_key, show_header => 0, variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); $main_table_changed = 1; my $query = " UPDATE scan_storcli_drive_groups SET scan_storcli_drive_group_virtual_drive_uuid = ".$anvil->Database->quote($scan_storcli_drive_group_virtual_drive_uuid).", scan_storcli_drive_group_id_string = ".$anvil->Database->quote($scan_storcli_drive_group_id_string).", scan_storcli_drive_group_access = ".$anvil->Database->quote($new_drive_group_access).", scan_storcli_drive_group_array_size = ".$anvil->Database->quote($new_drive_group_array_size).", scan_storcli_drive_group_array_state = ".$anvil->Database->quote($new_drive_group_array_state).", scan_storcli_drive_group_cache = ".$anvil->Database->quote($new_drive_group_cache).", scan_storcli_drive_group_cachecade = ".$anvil->Database->quote($new_drive_group_cachecade).", scan_storcli_drive_group_consistent = ".$anvil->Database->quote($new_drive_group_consistent).", scan_storcli_drive_group_disk_cache = ".$anvil->Database->quote($new_drive_group_disk_cache).", scan_storcli_drive_group_raid_type = ".$anvil->Database->quote($new_drive_group_raid_type).", scan_storcli_drive_group_read_cache = ".$anvil->Database->quote($new_drive_group_read_cache).", scan_storcli_drive_group_scheduled_cc = ".$anvil->Database->quote($new_drive_group_scheduled_cc).", scan_storcli_drive_group_write_cache = ".$anvil->Database->quote($new_drive_group_write_cache).", modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." WHERE scan_storcli_drive_group_uuid = ".$anvil->Database->quote($scan_storcli_drive_group_uuid)." ;"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } ### Process the rest of the variables now. foreach my $type ("variable", "temperature") { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { type => $type }}); foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{$type}}) { my $new_variable_value = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{$type}{$variable}; my $temperature = $type eq "temperature" ? "TRUE" : "FALSE"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, temperature => $temperature, }}); if (exists $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_drive_groups'}{source_uuid}{$scan_storcli_drive_group_uuid}{$type}{$variable}{scan_storcli_variable_uuid}) { # Look for changes my $old_variable_value = $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_drive_groups'}{source_uuid}{$scan_storcli_drive_group_uuid}{$type}{$variable}{scan_storcli_variable_value}; my $variable_uuid = $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_drive_groups'}{source_uuid}{$scan_storcli_drive_group_uuid}{$type}{$variable}{scan_storcli_variable_uuid}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, new_variable_value => $new_variable_value, old_variable_value => $old_variable_value, variable_uuid => $variable_uuid, }}); # Delete it so that we know it has been processed. delete $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_drive_groups'}{source_uuid}{$scan_storcli_drive_group_uuid}{$type}{$variable}; if (($new_variable_value ne $old_variable_value)) { # Changed! If the old value was 'VANISHED', then a # sensor or variable returned. Otherwise, for now, we # treat everything as 'warning' and step down # explicitely anything not of concern that proves # noisey later (better safe than sorry). $anvil->data->{'scan-storcli'}{alert_sort}++; my $message_key = "scan_storcli_warning_0026"; if ($old_variable_value eq "VANISHED") { $message_key = "scan_storcli_warning_0027"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { message_key => $message_key }}); } my $variables = { id_string => $scan_storcli_drive_group_id_string, name => $variable, old_value => $old_variable_value, new_value => $new_variable_value, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => $message_key, variables => $variables}); $anvil->Alert->register({ alert_level => "warning", message => $message_key, show_header => $main_table_changed ? 0 : 1, variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); my $query = " UPDATE scan_storcli_variables SET scan_storcli_variable_value = ".$anvil->Database->quote($new_variable_value).", modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." WHERE scan_storcli_variable_uuid = ".$anvil->Database->quote($variable_uuid)." ;"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } } else { # New variable, record it. This is a 'warning' level as # variables aren't expected to spawn into existence. my $variables = { id_string => $scan_storcli_drive_group_id_string, name => $variable, value => $new_variable_value, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_storcli_warning_0043", variables => $variables}); $anvil->Alert->register({ alert_level => "warning", message => "scan_storcli_warning_0043", show_header => $main_table_changed ? 0 : 1, variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); # INSERT my $query = " INSERT INTO scan_storcli_variables ( scan_storcli_variable_uuid, scan_storcli_variable_host_uuid, scan_storcli_variable_source_table, scan_storcli_variable_source_uuid, scan_storcli_variable_is_temperature, scan_storcli_variable_name, scan_storcli_variable_value, modified_date ) VALUES ( ".$anvil->Database->quote($anvil->Get->uuid()).", ".$anvil->Database->quote($anvil->Get->host_uuid).", 'scan_storcli_drive_groups', ".$anvil->Database->quote($scan_storcli_drive_group_uuid).", ".$anvil->Database->quote($temperature).", ".$anvil->Database->quote($variable).", ".$anvil->Database->quote($new_variable_value).", ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." );"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } } } # Now look for any variables left from the previous scan that we didn't match up (and # delete) this pass. foreach my $type ("variable", "temperature") { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { type => $type }}); foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_drive_groups'}{source_uuid}{$scan_storcli_drive_group_uuid}{$type}}) { # This variable has vanished my $old_variable_value = $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_drive_groups'}{source_uuid}{$scan_storcli_drive_group_uuid}{$type}{$variable}{scan_storcli_variable_value}; my $variable_uuid = $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_drive_groups'}{source_uuid}{$scan_storcli_drive_group_uuid}{$type}{$variable}{scan_storcli_variable_uuid}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_variable_value => $old_variable_value, variable_uuid => $variable_uuid, }}); # Delete it so that we know it has been processed. delete $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_drive_groups'}{source_uuid}{$scan_storcli_drive_group_uuid}{$type}{$variable}; # If the old value is already 'VANISHED', ignore it. next if $old_variable_value eq "VANISHED"; ### NOTE: For now, we're going to use warning level because variables ### shouldn't vanish, but under an-cm, it did happen for ### reasons that we never figured out. So later, we may drop ### the alert level in some cases. # Still here? Alert and UPDATE. my $variables = { id_string => $scan_storcli_drive_group_id_string, name => $variable, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_storcli_warning_0028", variables => $variables}); $anvil->Alert->register({ alert_level => "warning", message => "scan_storcli_warning_0028", show_header => $main_table_changed ? 0 : 1, variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); my $query = " UPDATE scan_storcli_variables SET scan_storcli_variable_value = 'VANISHED', modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." WHERE scan_storcli_variable_uuid = ".$anvil->Database->quote($variable_uuid)." ;"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } } } # Now commit the changes. We do this here so that the VD is in place before processing drive # groups under it. $anvil->Database->write({query => $anvil->data->{'scan-storcli'}{queries}, source => $THIS_FILE, line => __LINE__}); $anvil->data->{'scan-storcli'}{queries} = []; # Delete the Drive Group from the last scan so that we can find controllers that have been removed. if (exists $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "sql::scan_storcli_drive_groups::scan_storcli_drive_group_uuid::$scan_storcli_drive_group_uuid" => $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}, }}); delete $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}; } } ### NOTE: We can't check for vanished drive groups here as we're called by each virtual drive. return(0); } # Look for added, changed or deleted virtual drives. sub process_virtual_drives { my ($anvil) = @_; # This method is a little different because as we process virtual drives, we will also process and # drive groups under them. $anvil->data->{'scan-storcli'}{queries} = []; foreach my $scan_storcli_virtual_drive_id_string (sort {$a cmp $b} keys %{$anvil->data->{virtual_drive}}) { # The identifying string '-vd' where 'x' is the virtual drive number. my $host_controller_serial_number = $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{variable}{on_controller}; my $controller_uuid = $anvil->data->{'scan-storcli'}{controllers}{by_serial}{$host_controller_serial_number}; my $virtual_drive = ($scan_storcli_virtual_drive_id_string =~ /-vd(\d+)$/)[0]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_virtual_drive_id_string => $scan_storcli_virtual_drive_id_string, host_controller_serial_number => $host_controller_serial_number, controller_uuid => $controller_uuid, virtual_drive => $virtual_drive, }}); # Make sure I have the host's serial number and UUID. if (not $host_controller_serial_number) { # Error! $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "scan_storcli_error_0010", variables => { id_string => $scan_storcli_virtual_drive_id_string }}); $anvil->nice_exit({exit_code => 10}); } if (not $controller_uuid) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "scan_storcli_error_0008", variables => { serial_number => $host_controller_serial_number }}); $anvil->nice_exit({exit_code => 8}); } ### NOTE: This is here purely for debugging. # Show the basic virtual drive data. foreach my $type ("variable", "temperature") { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${scan_storcli_virtual_drive_id_string}::${type}" => $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{$type}, }}); next if not defined $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{$type}; # Log the hash with a prefix $anvil->Log->variables({ source => $THIS_FILE, line => __LINE__, level => 2, prefix => "VD ".$virtual_drive." - ".$type, list => $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{$type}, }); } # Is this a new Virtual Drive? my $scan_storcli_virtual_drive_uuid = ""; my $is_new = 0; if (exists $anvil->data->{'scan-storcli'}{virtual_drives}{by_id_string}{$scan_storcli_virtual_drive_id_string}) { # Yes! $scan_storcli_virtual_drive_uuid = $anvil->data->{'scan-storcli'}{virtual_drives}{by_id_string}{$scan_storcli_virtual_drive_id_string}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_virtual_drive_uuid => $scan_storcli_virtual_drive_uuid }}); } else { # No, this is a new Virtual Drive. Create a new UUID for it. $scan_storcli_virtual_drive_uuid = $anvil->Get->uuid(); $is_new = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_virtual_drive_uuid => $scan_storcli_virtual_drive_uuid, is_new => $is_new, }}); # Add the keys for looking it up by UUID or serial number. $anvil->data->{'scan-storcli'}{virtual_drives}{by_id_string}{$scan_storcli_virtual_drive_id_string} = $scan_storcli_virtual_drive_uuid; $anvil->data->{'scan-storcli'}{virtual_drives}{by_uuid}{$scan_storcli_virtual_drive_uuid} = $scan_storcli_virtual_drive_id_string; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "scan-storcli::virtual_drives::by_id_string::$scan_storcli_virtual_drive_id_string" => $anvil->data->{'scan-storcli'}{virtual_drives}{by_id_string}{$scan_storcli_virtual_drive_id_string}, "scan-storcli::virtual_drives::by_uuid::$scan_storcli_virtual_drive_uuid" => $anvil->data->{'scan-storcli'}{virtual_drives}{by_uuid}{$scan_storcli_virtual_drive_uuid}, }}); } ### These are the values for the main table. Anything else will go in the variables table. # Creation date is a combination of two variables. my $creation_date = ""; my $creation_time = ""; my $new_creation_date = ""; my $new_data_protection = ""; my $new_disk_cache_policy = ""; my $new_emulation_type = ""; my $new_encryption = ""; my $new_blocks = ""; my $new_strip_size = ""; my $new_drives_per_span = ""; my $new_span_depth = ""; my $new_scsi_naa_id = ""; if ($virtual_drive eq "9999") { # This is the fake VD $creation_date = "2020/01/01"; $creation_time = "00:00:00"; $new_creation_date = "2020/01/01 00:00:00"; $new_data_protection = "na"; $new_disk_cache_policy = "na"; $new_emulation_type = "na"; $new_encryption = "na"; $new_blocks = 0; $new_strip_size = 0; $new_drives_per_span = 0; $new_span_depth = 0; $new_scsi_naa_id = "placeholder"; } foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{variable}}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${scan_storcli_virtual_drive_id_string}::variable::${variable}" => $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{variable}{$variable}, }}); # Store and delete the value if ($variable eq "creation_date") { # The creation date we store is a combination of two variables. That's why # these first two entries are a little odd. $creation_date = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { creation_date => $creation_date, creation_time => $creation_time, }}); # If I have the time, assemble. if ($creation_time) { $new_creation_date = $creation_date." ".$creation_time; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_creation_date => $new_creation_date }}); } next; } if ($variable eq "creation_time") { $creation_time = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { creation_date => $creation_date, creation_time => $creation_time, }}); # If I have the date, assemble. if ($creation_date) { $new_creation_date = $creation_date." ".$creation_time; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_creation_date => $new_creation_date }}); } next; } # Back to our regularly scheduled programming... if ($variable eq "data_protection") { $new_data_protection = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_data_protection => $new_data_protection }}); next; } if ($variable eq "disk_cache_policy") { $new_disk_cache_policy = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_disk_cache_policy => $new_disk_cache_policy }}); next; } if ($variable eq "emulation_type") { $new_emulation_type = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_emulation_type => $new_emulation_type }}); next; } if ($variable eq "encryption") { $new_encryption = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_encryption => $new_encryption }}); next; } if ($variable eq "number_of_blocks") { $new_blocks = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_blocks => $new_blocks }}); next; } if ($variable eq "strip_size") { $new_strip_size = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_strip_size => $new_strip_size }}); next; } if ($variable eq "number_of_drives_per_span") { $new_drives_per_span = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_drives_per_span => $new_drives_per_span }}); next; } if ($variable eq "span_depth") { $new_span_depth = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_span_depth => $new_span_depth }}); next; } if ($variable eq "scsi_naa_id") { $new_scsi_naa_id = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_scsi_naa_id => $new_scsi_naa_id }}); next; } } # Pull out the rest of the variables now. If the Virtual Drive is new, all variables will be # INSERTed. If the Virtual Drive exists, each variable will be examined and new ones will be # INSERTed, existing ones will be checked for changes and UPDATEd as needed. If the # Virtual Drive is NOT new, then variables from the old data will be deleted as we go and any # not found in the current data set will be left over. We'll use this to determine variables # that have vanished. They will not be deleted, but their value will be set to 'VANISHED'. $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { is_new => $is_new }}); if ($is_new) { ### NOTE: VD 9999 will be mostly blank and that is fine $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_virtual_drive_uuid => $scan_storcli_virtual_drive_uuid, controller_uuid => $controller_uuid, scan_storcli_virtual_drive_id_string => $scan_storcli_virtual_drive_id_string, new_creation_date => $new_creation_date, new_data_protection => $new_data_protection, new_disk_cache_policy => $new_disk_cache_policy, new_emulation_type => $new_emulation_type, new_encryption => $new_encryption, new_blocks => $new_blocks, new_strip_size => $new_strip_size, new_drives_per_span => $new_drives_per_span, new_span_depth => $new_span_depth, new_scsi_naa_id => $new_scsi_naa_id, }}); ### NOTE: The rest of the alerts will be in the format '- Variable: [$value]'. my $variables = { on_controller => $host_controller_serial_number, id_string => $scan_storcli_virtual_drive_id_string, creation_date => $new_creation_date, data_protection => $new_data_protection, disk_cache_policy => $new_disk_cache_policy, emulation_type => $new_emulation_type, encryption => $new_encryption, blocks => $new_blocks, strip_size => $new_strip_size, drives_per_span => $new_drives_per_span, span_depth => $new_span_depth, scsi_naa_id => $new_scsi_naa_id, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "scan_storcli_note_0004", variables => $variables}); $anvil->Alert->register({ alert_level => "notice", message => "scan_storcli_note_0004", variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); # INSERT my $query = " INSERT INTO scan_storcli_virtual_drives ( scan_storcli_virtual_drive_uuid, scan_storcli_virtual_drive_host_uuid, scan_storcli_virtual_drive_controller_uuid, scan_storcli_virtual_drive_id_string, scan_storcli_virtual_drive_creation_date, scan_storcli_virtual_drive_data_protection, scan_storcli_virtual_drive_disk_cache_policy, scan_storcli_virtual_drive_emulation_type, scan_storcli_virtual_drive_encryption, scan_storcli_virtual_drive_blocks, scan_storcli_virtual_drive_strip_size, scan_storcli_virtual_drive_drives_per_span, scan_storcli_virtual_drive_span_depth, scan_storcli_virtual_drive_scsi_naa_id, modified_date ) VALUES ( ".$anvil->Database->quote($scan_storcli_virtual_drive_uuid).", ".$anvil->Database->quote($anvil->Get->host_uuid).", ".$anvil->Database->quote($controller_uuid).", ".$anvil->Database->quote($scan_storcli_virtual_drive_id_string).", ".$anvil->Database->quote($new_creation_date).", ".$anvil->Database->quote($new_data_protection).", ".$anvil->Database->quote($new_disk_cache_policy).", ".$anvil->Database->quote($new_emulation_type).", ".$anvil->Database->quote($new_encryption).", ".$anvil->Database->quote($new_blocks).", ".$anvil->Database->quote($new_strip_size).", ".$anvil->Database->quote($new_drives_per_span).", ".$anvil->Database->quote($new_span_depth).", ".$anvil->Database->quote($new_scsi_naa_id).", ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." );"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; # Process the rest of the variables and temperatures now. foreach my $type ("variable", "temperature") { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { type => $type }}); foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{$type}}) { my $value = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{$type}{$variable}; my $temperature = $type eq "temperature" ? "TRUE" : "FALSE"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, temperature => $temperature, }}); # Send an alert telling the user that we've found a new controller. my $variables = { name => $variable, value => $value, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "scan_storcli_note_0004", variables => $variables}); $anvil->Alert->register({ alert_level => "notice", show_header => 0, message => "scan_storcli_note_0002", variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); # INSERT my $query = " INSERT INTO scan_storcli_variables ( scan_storcli_variable_uuid, scan_storcli_variable_host_uuid, scan_storcli_variable_source_table, scan_storcli_variable_source_uuid, scan_storcli_variable_is_temperature, scan_storcli_variable_name, scan_storcli_variable_value, modified_date ) VALUES ( ".$anvil->Database->quote($anvil->Get->uuid()).", ".$anvil->Database->quote($anvil->Get->host_uuid).", 'scan_storcli_virtual_drives', ".$anvil->Database->quote($scan_storcli_virtual_drive_uuid).", ".$anvil->Database->quote($temperature).", ".$anvil->Database->quote($variable).", ".$anvil->Database->quote($value).", ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." );"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } } } else { ### NOTE: The serial number should never change (a changed SN/Virtual Drive should be ### picked up as a new Virtual Drive), but we check/update just to be safe. # Look for changes. my $main_table_changed = 0; my $old_scan_storcli_virtual_drive_id_string = $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}{scan_storcli_virtual_drive_id_string}; my $old_controller_uuid = $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}{scan_storcli_virtual_drive_controller_uuid}; my $old_creation_date = $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}{scan_storcli_virtual_drive_creation_date}; my $old_data_protection = $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}{scan_storcli_virtual_drive_data_protection}; my $old_disk_cache_policy = $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}{scan_storcli_virtual_drive_disk_cache_policy}; my $old_emulation_type = $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}{scan_storcli_virtual_drive_emulation_type}; my $old_encryption = $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}{scan_storcli_virtual_drive_encryption}; my $old_blocks = $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}{scan_storcli_virtual_drive_blocks}; my $old_strip_size = $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}{scan_storcli_virtual_drive_strip_size}; my $old_drives_per_span = $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}{scan_storcli_virtual_drive_drives_per_span}; my $old_span_depth = $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}{scan_storcli_virtual_drive_span_depth}; my $old_scsi_naa_id = $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}{scan_storcli_virtual_drive_scsi_naa_id}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_virtual_drive_id_string => $scan_storcli_virtual_drive_id_string, old_scan_storcli_virtual_drive_id_string => $old_scan_storcli_virtual_drive_id_string, controller_uuid => $controller_uuid, old_controller_uuid => $old_controller_uuid, new_creation_date => $new_creation_date, old_creation_date => $old_creation_date, new_data_protection => $new_data_protection, old_data_protection => $old_data_protection, new_disk_cache_policy => $new_disk_cache_policy, old_disk_cache_policy => $old_disk_cache_policy, new_emulation_type => $new_emulation_type, old_emulation_type => $old_emulation_type, new_encryption => $new_encryption, old_encryption => $old_encryption, new_blocks => $new_blocks, old_blocks => $old_blocks, new_strip_size => $new_strip_size, old_strip_size => $old_strip_size, new_drives_per_span => $new_drives_per_span, old_drives_per_span => $old_drives_per_span, new_span_depth => $new_span_depth, old_span_depth => $old_span_depth, new_scsi_naa_id => $new_scsi_naa_id, old_scsi_naa_id => $old_scsi_naa_id, }}); if (($scan_storcli_virtual_drive_id_string ne $old_scan_storcli_virtual_drive_id_string) or ($controller_uuid ne $old_controller_uuid) or ($new_creation_date ne $old_creation_date) or ($new_data_protection ne $old_data_protection) or ($new_disk_cache_policy ne $old_disk_cache_policy) or ($new_emulation_type ne $old_emulation_type) or ($new_encryption ne $old_encryption) or ($new_blocks ne $old_blocks) or ($new_strip_size ne $old_strip_size) or ($new_drives_per_span ne $old_drives_per_span) or ($new_span_depth ne $old_span_depth) or ($new_scsi_naa_id ne $old_scsi_naa_id)) { # I need the serial numbers for the controller UUIDs. my $new_controller_serial_number = $anvil->data->{'scan-storcli'}{controllers}{by_uuid}{$controller_uuid}; my $old_controller_serial_number = $anvil->data->{'scan-storcli'}{controllers}{by_uuid}{$old_controller_uuid}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_controller_serial_number => $new_controller_serial_number, old_controller_serial_number => $old_controller_serial_number, }}); # Send a warning level alert because nothing should change here, ever. my $cleared = 0; my $message_key = "scan_storcli_warning_0017"; if ($new_creation_date ne $old_creation_date) { if ($old_creation_date eq "VANISHED") { # Virtual Drive has returned. $cleared = 1; $message_key = "scan_storcli_warning_0018"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared => $cleared, message_key => $message_key, }}); } } my $variables = { old_on_controller => $old_controller_serial_number, new_on_controller => $new_controller_serial_number, old_id_string => $old_scan_storcli_virtual_drive_id_string, new_id_string => $scan_storcli_virtual_drive_id_string, old_creation_date => $old_creation_date, new_creation_date => $new_creation_date, old_data_protection => $old_data_protection, new_data_protection => $new_data_protection, old_disk_cache_policy => $old_disk_cache_policy, new_disk_cache_policy => $new_disk_cache_policy, old_emulation_type => $old_emulation_type, new_emulation_type => $new_emulation_type, old_encryption => $old_encryption, new_encryption => $new_encryption, old_blocks => $old_blocks, new_blocks => $new_blocks, old_strip_size => $old_strip_size, new_strip_size => $new_strip_size, old_drives_per_span => $old_drives_per_span, new_drives_per_span => $new_drives_per_span, old_span_depth => $old_span_depth, new_span_depth => $new_span_depth, old_scsi_naa_id => $old_scsi_naa_id, new_scsi_naa_id => $new_scsi_naa_id, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => $message_key, variables => $variables}); $anvil->Alert->register({ alert_level => "warning", clear_alert => $cleared, message => $message_key, variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); $main_table_changed = 1; my $query = " UPDATE scan_storcli_virtual_drives SET scan_storcli_virtual_drive_controller_uuid = ".$anvil->Database->quote($controller_uuid).", scan_storcli_virtual_drive_id_string = ".$anvil->Database->quote($scan_storcli_virtual_drive_id_string).", scan_storcli_virtual_drive_creation_date = ".$anvil->Database->quote($new_creation_date).", scan_storcli_virtual_drive_data_protection = ".$anvil->Database->quote($new_data_protection).", scan_storcli_virtual_drive_disk_cache_policy = ".$anvil->Database->quote($new_disk_cache_policy).", scan_storcli_virtual_drive_emulation_type = ".$anvil->Database->quote($new_emulation_type).", scan_storcli_virtual_drive_encryption = ".$anvil->Database->quote($new_encryption).", scan_storcli_virtual_drive_blocks = ".$anvil->Database->quote($new_blocks).", scan_storcli_virtual_drive_strip_size = ".$anvil->Database->quote($new_strip_size).", scan_storcli_virtual_drive_drives_per_span = ".$anvil->Database->quote($new_drives_per_span).", scan_storcli_virtual_drive_span_depth = ".$anvil->Database->quote($new_span_depth).", scan_storcli_virtual_drive_scsi_naa_id = ".$anvil->Database->quote($new_scsi_naa_id).", modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." WHERE scan_storcli_virtual_drive_uuid = ".$anvil->Database->quote($scan_storcli_virtual_drive_uuid)." ;"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } ### Process the rest of the variables now. foreach my $type ("variable", "temperature") { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { type => $type }}); foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{$type}}) { my $new_variable_value = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{$type}{$variable}; my $temperature = $type eq "temperature" ? "TRUE" : "FALSE"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, temperature => $temperature, }}); if (exists $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_virtual_drives'}{source_uuid}{$scan_storcli_virtual_drive_uuid}{$type}{$variable}{scan_storcli_variable_uuid}) { # Look for changes my $old_variable_value = $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_virtual_drives'}{source_uuid}{$scan_storcli_virtual_drive_uuid}{$type}{$variable}{scan_storcli_variable_value}; my $variable_uuid = $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_virtual_drives'}{source_uuid}{$scan_storcli_virtual_drive_uuid}{$type}{$variable}{scan_storcli_variable_uuid}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, new_variable_value => $new_variable_value, old_variable_value => $old_variable_value, variable_uuid => $variable_uuid, }}); # Delete it so that we know it has been processed. delete $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_virtual_drives'}{source_uuid}{$scan_storcli_virtual_drive_uuid}{$type}{$variable}; if (($new_variable_value ne $old_variable_value)) { # Changed! If the old value was 'VANISHED', then a # sensor or variable returned. Otherwise, for now, we # treat everything as 'warning' and step down # explicitely anything not of concern that proves # noisey later (better safe than sorry). $anvil->data->{'scan-storcli'}{alert_sort}++; my $cleared = 0; my $level = "warning"; my $message_key = "scan_storcli_warning_0019"; if ($old_variable_value eq "VANISHED") { $message_key = "scan_storcli_warning_0020"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { message_key => $message_key }}); } # If the 'name' is 'active operations' and the new is # 'Background Initialization', we're in the middle of # a BGI and this is not a warning. If the new is # 'None', the CGI completed. if ($variable =~ /Active Operations/i) { $level = "notice"; if ($new_variable_value =~ /None/i) { # BGI done! $message_key = "scan_storcli_note_0069"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { message_key => $message_key }}); } else { # BGI in progress still, pull out the # percentage. my $percentage = ($new_variable_value =~ /\(\d+%\)/)[0]; if ($percentage) { # Successfully pulled the new # percentage complete value. $new_variable_value = $percentage; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_variable_value => $new_variable_value }}); } $message_key = "scan_storcli_note_0068"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { message_key => $message_key }}); } } my $variables = { id_string => $scan_storcli_virtual_drive_id_string, name => $variable, old_value => $old_variable_value, new_value => $new_variable_value, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => $message_key, variables => $variables}); $anvil->Alert->register({ alert_level => $level, clear_alert => $cleared, show_header => $main_table_changed ? 0 : 1, message => $message_key, variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); my $query = " UPDATE scan_storcli_variables SET scan_storcli_variable_value = ".$anvil->Database->quote($new_variable_value).", modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." WHERE scan_storcli_variable_uuid = ".$anvil->Database->quote($variable_uuid)." ;"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } } else { # New variable, record it. This is a 'warning' level as # variables aren't expected to spawn into existence. my $variables = { id_string => $scan_storcli_virtual_drive_id_string, name => $variable, value => $new_variable_value, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_storcli_warning_0022", variables => $variables}); $anvil->Alert->register({ alert_level => "warning", clear_alert => 0, show_header => $main_table_changed ? 0 : 1, message => "scan_storcli_warning_0022", variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); # INSERT my $query = " INSERT INTO scan_storcli_variables ( scan_storcli_variable_uuid, scan_storcli_variable_host_uuid, scan_storcli_variable_source_table, scan_storcli_variable_source_uuid, scan_storcli_variable_is_temperature, scan_storcli_variable_name, scan_storcli_variable_value, modified_date ) VALUES ( ".$anvil->Database->quote($anvil->Get->uuid()).", ".$anvil->Database->quote($anvil->Get->host_uuid).", 'scan_storcli_virtual_drives', ".$anvil->Database->quote($scan_storcli_virtual_drive_uuid).", ".$anvil->Database->quote($temperature).", ".$anvil->Database->quote($variable).", ".$anvil->Database->quote($new_variable_value).", ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." );"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } } } # Now look for any variables left from the previous scan that we didn't match up (and # delete) this pass. foreach my $type ("variable", "temperature") { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { type => $type }}); foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_virtual_drives'}{source_uuid}{$scan_storcli_virtual_drive_uuid}{$type}}) { # This variable has vanished my $old_variable_value = $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_virtual_drives'}{source_uuid}{$scan_storcli_virtual_drive_uuid}{$type}{$variable}{scan_storcli_variable_value}; my $variable_uuid = $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_virtual_drives'}{source_uuid}{$scan_storcli_virtual_drive_uuid}{$type}{$variable}{scan_storcli_variable_uuid}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_variable_value => $old_variable_value, variable_uuid => $variable_uuid, }}); # Delete it so that we know it has been processed. delete $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_virtual_drives'}{source_uuid}{$scan_storcli_virtual_drive_uuid}{$type}{$variable}; # If the old value is already 'VANISHED', ignore it. next if $old_variable_value eq "VANISHED"; ### NOTE: For now, we're going to use warning level because variables ### shouldn't vanish, but under an-cm, it did happen for ### reasons that we never figured out. So later, we may drop ### the alert level in some cases. # Still here? Alert and UPDATE. my $variables = { id_string => $scan_storcli_virtual_drive_id_string, name => $variable, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_storcli_warning_0021", variables => $variables}); $anvil->Alert->register({ alert_level => "warning", clear_alert => 0, show_header => $main_table_changed ? 0 : 1, message => "scan_storcli_warning_0021", variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); my $query = " UPDATE scan_storcli_variables SET scan_storcli_variable_value = 'VANISHED', modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." WHERE scan_storcli_variable_uuid = ".$anvil->Database->quote($variable_uuid)." ;"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } } } # Now commit the changes. We do this here so that the VD is in place before processing drive # groups under it. $anvil->Database->write({query => $anvil->data->{'scan-storcli'}{queries}, source => $THIS_FILE, line => __LINE__}); $anvil->data->{'scan-storcli'}{queries} = []; # Process drive groups under this virtual drive. They'll likely have vanished, too. process_drive_groups($anvil, $scan_storcli_virtual_drive_id_string, $scan_storcli_virtual_drive_uuid, $controller_uuid, $host_controller_serial_number); # Delete the Virtual Drive from the last scan so that we can find controllers that have been removed. if (exists $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}) { delete $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}; } } # See if any Virtual Drives have vanished. foreach my $scan_storcli_virtual_drive_uuid (keys %{$anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}}) { # Virtual Drive vanished! my $scan_storcli_virtual_drive_id_string = $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}{scan_storcli_virtual_drive_id_string}; my $controller_uuid = $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}{scan_storcli_virtual_drive_controller_uuid}; my $creation_date = $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}{scan_storcli_virtual_drive_creation_date}; my $host_controller_serial_number = $anvil->data->{'scan-storcli'}{controllers}{by_uuid}{$controller_uuid}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_virtual_drive_id_string => $scan_storcli_virtual_drive_id_string, controller_uuid => $controller_uuid, creation_date => $creation_date, host_controller_serial_number => $host_controller_serial_number, }}); # Delete it so that we know it has been processed. delete $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}; # If the old creation date is already 'VANISHED', ignore it. next if $creation_date eq "VANISHED"; # Still here? Alert and UPDATE. ### NOTE: For now, we're going to use warning level because controllers should never vanish ### unless one failed. If that is the case, the admin already knows, but this will let ### other notification targets know that the change has happened. my $variables = { id_string => $scan_storcli_virtual_drive_id_string, controller_serial_number => $host_controller_serial_number, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_storcli_warning_0023", variables => $variables}); $anvil->Alert->register({ alert_level => "warning", clear_alert => 0, show_header => 1, message => "scan_storcli_warning_0023", variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); my $query = " UPDATE scan_storcli_virtual_drives SET scan_storcli_virtual_drive_creation_date = 'VANISHED', modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." WHERE scan_storcli_virtual_drive_uuid = ".$anvil->Database->quote($scan_storcli_virtual_drive_uuid)." ;"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; # Now commit the changes. We do this here so that the VD is in place before processing drive # groups under it. $anvil->Database->write({query => $anvil->data->{'scan-storcli'}{queries}, source => $THIS_FILE, line => __LINE__}); $anvil->data->{'scan-storcli'}{queries} = []; # Process drive groups under this virtual drive. They'll likely have vanished, too. process_drive_groups($anvil, $scan_storcli_virtual_drive_id_string, $scan_storcli_virtual_drive_uuid, $controller_uuid, $host_controller_serial_number); } ### Now that we've processed virtual drives, see if in stray drive groups are left. foreach my $scan_storcli_drive_group_uuid (keys %{$anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}}) { # Drive Group vanished! my $scan_storcli_drive_group_id_string = $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_id_string}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "scan_storcli_drive_group_id_string" => $scan_storcli_drive_group_id_string, }}); # If this drive group is '9999', we won't care if it vanishes because it was a pseudo DG # anyway. next if $scan_storcli_drive_group_id_string =~ /-dg9999$/; # Still here? Gather the rest of the data. my $controller_uuid = $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_controller_uuid}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { controller_uuid => $controller_uuid }}); # If I don't have the controller ID, see if I can parse it from the if ((not $controller_uuid) && ($scan_storcli_drive_group_id_string =~ /^(.*?)-vd/)) { my $serial_number = $1; $controller_uuid = $anvil->data->{'scan-storcli'}{controllers}{by_serial}{$serial_number} ? $anvil->data->{'scan-storcli'}{controllers}{by_serial}{$serial_number} : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { serial_number => $serial_number, controller_uuid => $controller_uuid, }}); } # If I still don't have a controller UUID, then something serious went wrong. if (not $controller_uuid) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "scan_storcli_error_0015", variables => { drive_group_uuid => $scan_storcli_drive_group_uuid }}); $anvil->nice_exit({exit_code => 15}); } my $access = $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_access}; my $host_controller_serial_number = $anvil->data->{'scan-storcli'}{controllers}{by_uuid}{$controller_uuid}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_drive_group_id_string => $scan_storcli_drive_group_id_string, access => $access, host_controller_serial_number => $host_controller_serial_number, }}); # Delete it so that we know it has been processed. delete $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}; # If the old creation date is already 'VANISHED', ignore it. next if $access eq "VANISHED"; # Still here? Alert and UPDATE. ### NOTE: For now, we're going to use warning level because controllers should never vanish ### unless one failed. If that is the case, the admin already knows, but this will let ### other notification targets know that the change has happened. my $variables = { id_string => $scan_storcli_drive_group_id_string, controller_serial_number => $host_controller_serial_number, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_storcli_warning_0028", variables => $variables}); $anvil->Alert->register({ alert_level => "warning", clear_alert => 0, show_header => 1, message => "scan_storcli_warning_0028", variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); my $query = " UPDATE scan_storcli_drive_groups SET scan_storcli_drive_group_access = 'VANISHED', modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." WHERE scan_storcli_drive_group_uuid = ".$anvil->Database->quote($scan_storcli_drive_group_uuid)." ;"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; # Now commit the changes. We do this here so that the VD is in place before processing drive # groups under it. $anvil->Database->write({query => $anvil->data->{'scan-storcli'}{queries}, source => $THIS_FILE, line => __LINE__}); $anvil->data->{'scan-storcli'}{queries} = []; } return(0); } # Look for added, changed or deleted physical drives. sub process_physical_disks { my ($anvil) = @_; foreach my $scan_storcli_virtual_drive_id_string (sort {$a cmp $b} keys %{$anvil->data->{virtual_drive}}) { my $virtual_drive = ($scan_storcli_virtual_drive_id_string =~ /-vd(\d+)$/)[0]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_virtual_drive_id_string => $scan_storcli_virtual_drive_id_string, virtual_drive => $virtual_drive, }}); foreach my $drive_group (sort {$a cmp $b} keys %{$anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { drive_group => $drive_group }}); foreach my $enclosure_id (sort {$a cmp $b} keys %{$anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{enclosure_id}}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { enclosure_id => $enclosure_id }}); foreach my $slot_number (sort {$a cmp $b} keys %{$anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { slot_number => $slot_number }}); # This function is really just to help keep code clean and avoid massive indenting. process_a_physical_disk($anvil, $scan_storcli_virtual_drive_id_string, $drive_group, $enclosure_id, $slot_number); } } } } # See if any Physical Disks have vanished. foreach my $scan_storcli_physical_drive_uuid (keys %{$anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}}) { # Physical Disk vanished! my $old_physical_disk_serial_number = $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}{scan_storcli_physical_drive_serial_number}; my $old_controller_uuid = $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}{scan_storcli_physical_drive_controller_uuid}; my $old_model = $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}{scan_storcli_physical_drive_model}; my $old_controller_serial_number = $anvil->data->{'scan-storcli'}{controllers}{by_uuid}{$old_controller_uuid}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_physical_disk_serial_number => $old_physical_disk_serial_number, old_controller_uuid => $old_controller_uuid, old_model => $old_model, old_controller_serial_number => $old_controller_serial_number, }}); # Delete it so that we know it has been processed. delete $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}; # If the old alarm state is already 'VANISHED', ignore it. next if $old_model eq "VANISHED"; # Still here? Alert and UPDATE. ### NOTE: For now, we're going to use warning level because controllers should never vanish ### unless one failed. If that is the case, the admin already knows, but this will let ### other notification targets know that the change has happened. my $variables = { physical_disk_serial_number => $old_physical_disk_serial_number, controller_serial_number => $old_controller_serial_number, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "scan_storcli_warning_0035", variables => $variables}); $anvil->Alert->register({ alert_level => "notice", show_header => 1, message => "scan_storcli_warning_0035", variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); my $query = " UPDATE scan_storcli_physical_drives SET scan_storcli_physical_drive_model = 'VANISHED', modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." WHERE scan_storcli_physical_drive_uuid = ".$anvil->Database->quote($scan_storcli_physical_drive_uuid)." ;"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } # Now commit the changes. $anvil->Database->write({query => $anvil->data->{'scan-storcli'}{queries}, source => $THIS_FILE, line => __LINE__}); $anvil->data->{'scan-storcli'}{queries} = []; return(0); } ### NOTE: I don't loop by drive serial number, like in the other process_X() functions, so this one is ### designed a little different. # This does the work of processing a specific physical disk. sub process_a_physical_disk { my ($anvil, $scan_storcli_virtual_drive_id_string, $drive_group, $enclosure_id, $slot_number) = @_; my ($vd_controller_serial_number, $virtual_drive) = ($scan_storcli_virtual_drive_id_string =~ /^(.*?)-vd(\d+)$/); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { vd_controller_serial_number => $vd_controller_serial_number, virtual_drive => $virtual_drive, }}); # I need the drive's serial number right off the bat. # = virtual_drive::0000000051622771-vd0 ::drive_group::0:: enclosure_id::7:: slot_number::1:: variable::serial_number: [S7M1RD440000E722PB9N] my $scan_storcli_physical_drive_serial_number = $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{serial_number}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_physical_drive_serial_number => $scan_storcli_physical_drive_serial_number }}); if ($scan_storcli_physical_drive_serial_number) { # Got it, delete it now. delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{serial_number}; } else { # Error! $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "scan_storcli_error_0013", variables => { virtual_drive => $scan_storcli_virtual_drive_id_string, drive_group => $drive_group, enclosure_id => $enclosure_id, slot_number => $slot_number, }}); $anvil->nice_exit({exit_code => 13}); } # Make sure I have a controller serial number. $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${scan_storcli_virtual_drive_id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::on_controller" => $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{on_controller}, }}); if (not $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{on_controller}) { # Error! $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "scan_storcli_error_0012", variables => { serial_number => $scan_storcli_physical_drive_serial_number }}); $anvil->nice_exit({exit_code => 12}); } my $host_controller_serial_number = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{on_controller}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_controller_serial_number => $host_controller_serial_number }}); # Now make sure I can translate this to a UUID. if (not $anvil->data->{'scan-storcli'}{controllers}{by_serial}{$host_controller_serial_number}) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "scan_storcli_error_0008", variables => { serial_number => $host_controller_serial_number }}); $anvil->nice_exit({exit_code => 8}); } my $controller_uuid = $anvil->data->{'scan-storcli'}{controllers}{by_serial}{$host_controller_serial_number}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { controller_uuid => $controller_uuid }}); # Not gather the rest of the core table's values. my $new_size = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{drive_size}; my $new_sector_size = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{sector_size}; my $new_vendor = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{manufacturer_identification}; my $new_model = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{drive_model}; my $new_self_encrypting_drive = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{self_encrypting_drive}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_size => $new_size, new_sector_size => $new_sector_size, new_vendor => $new_vendor, new_model => $new_model, new_self_encrypting_drive => $new_self_encrypting_drive, }}); # Get the temperature ranges, which depends on whether this is an SSD or HDD. my $drive_temperature_key = "hdd"; my $drive_media = $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{drive_media}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { drive_media => $drive_media }}); if (lc($drive_media) eq "ssd") { $drive_temperature_key = "ssd"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { drive_temperature_key => $drive_temperature_key }}); } # Setup our thermal thresholds my $high_critical = $anvil->data->{'scan-storcli'}{thresholds}{drives}{$drive_temperature_key}{high_critical}; my $high_warning = $anvil->data->{'scan-storcli'}{thresholds}{drives}{$drive_temperature_key}{high_warning}; my $low_warning = $anvil->data->{'scan-storcli'}{thresholds}{drives}{$drive_temperature_key}{low_warning}; my $low_critical = $anvil->data->{'scan-storcli'}{thresholds}{drives}{$drive_temperature_key}{low_critical}; my $jump = $anvil->data->{'scan-storcli'}{thresholds}{drives}{$drive_temperature_key}{jump}; my $buffer = $anvil->data->{'scan-storcli'}{thresholds}{drives}{$drive_temperature_key}{buffer}; my $clear_high_critical = $high_critical - $buffer; my $clear_high_warning = $high_warning - $buffer; my $clear_low_critical = $low_critical - $buffer; my $clear_low_warning = $low_warning - $buffer; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { high_critical => $high_critical, high_warning => $high_warning, low_warning => $low_warning, low_critical => $low_critical, jump => $jump, buffer => $buffer, clear_high_critical => $clear_high_critical, clear_high_warning => $clear_high_warning, clear_low_critical => $clear_low_critical, clear_low_warning => $clear_low_warning, }}); # Fine-tune the alert thresholds if ($clear_high_critical < $high_warning) { $clear_high_critical = $high_warning + 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { clear_high_critical => $clear_high_critical }}); } if ($clear_low_critical > $low_warning) { $clear_low_critical = $low_warning - 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { clear_low_critical => $clear_low_critical }}); } # Is this a new physical disk? my $is_new = 0; my $scan_storcli_physical_drive_uuid = ""; if (exists $anvil->data->{'scan-storcli'}{physical_drives}{by_serial}{$scan_storcli_physical_drive_serial_number}) { # Yup! $scan_storcli_physical_drive_uuid = $anvil->data->{'scan-storcli'}{physical_drives}{by_serial}{$scan_storcli_physical_drive_serial_number}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_physical_drive_uuid => $scan_storcli_physical_drive_uuid }}); } else { $is_new = 1; $scan_storcli_physical_drive_uuid = $anvil->Get->uuid(); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { is_new => $is_new, scan_storcli_physical_drive_uuid => $scan_storcli_physical_drive_uuid, }}); $anvil->data->{'scan-storcli'}{physical_drives}{by_serial}{$scan_storcli_physical_drive_serial_number} = $scan_storcli_physical_drive_uuid; $anvil->data->{'scan-storcli'}{physical_drives}{by_uuid}{$scan_storcli_physical_drive_uuid} = $scan_storcli_physical_drive_serial_number; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "scan-storcli::physical_drives::by_serial::$scan_storcli_physical_drive_serial_number" => $anvil->data->{'scan-storcli'}{physical_drives}{by_serial}{$scan_storcli_physical_drive_serial_number}, "scan-storcli::physical_drives::by_uuid::$scan_storcli_physical_drive_uuid" => $anvil->data->{'scan-storcli'}{physical_drives}{by_uuid}{$scan_storcli_physical_drive_uuid}, }}); } # Pull out the rest of the variables now. If the physical disk is new, all variables will be # INSERTed. If the physical drive exists, each variable will be examined and new ones will be # INSERTed, existing ones will be checked for changes and UPDATEd as needed. If the physical disk is # NOT new, then variables from the old data will be deleted as we go and any not found in the current # data set will be left over. We'll use this to determine variables that have vanished. They will not # be deleted, but their value will be set to 'VANISHED'. if ($is_new) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_physical_drive_serial_number => $scan_storcli_physical_drive_serial_number, scan_storcli_physical_drive_uuid => $scan_storcli_physical_drive_uuid, host_controller_serial_number => $host_controller_serial_number, virtual_drive => $virtual_drive, drive_group => $drive_group, controller_uuid => $controller_uuid, new_size => $new_size, new_sector_size => $new_sector_size, new_vendor => $new_vendor, new_model => $new_model, new_self_encrypting_drive => $new_self_encrypting_drive, }}); ### NOTE: The rest of the alerts will be in the format '- Variable: [$value]'. # Send an alert telling the user that we've found a new controller. my $variables = { on_controller => $host_controller_serial_number, virtual_drive => $virtual_drive, drive_group => $drive_group, serial_number => $scan_storcli_physical_drive_serial_number, size => $new_size, sector_size => $new_sector_size, vendor => $new_vendor, model => $new_model, self_encrypting_drive => $new_self_encrypting_drive, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "scan_storcli_note_0006", variables => $variables}); $anvil->Alert->register({ alert_level => "notice", show_header => 1, message => "scan_storcli_note_0006", variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); # INSERT my $query = " INSERT INTO scan_storcli_physical_drives ( scan_storcli_physical_drive_uuid, scan_storcli_physical_drive_host_uuid, scan_storcli_physical_drive_controller_uuid, scan_storcli_physical_drive_virtual_drive, scan_storcli_physical_drive_drive_group, scan_storcli_physical_drive_enclosure_id, scan_storcli_physical_drive_slot_number, scan_storcli_physical_drive_serial_number, scan_storcli_physical_drive_size, scan_storcli_physical_drive_sector_size, scan_storcli_physical_drive_vendor, scan_storcli_physical_drive_model, scan_storcli_physical_drive_self_encrypting_drive, modified_date ) VALUES ( ".$anvil->Database->quote($scan_storcli_physical_drive_uuid).", ".$anvil->Database->quote($anvil->Get->host_uuid).", ".$anvil->Database->quote($controller_uuid).", ".$anvil->Database->quote($virtual_drive).", ".$anvil->Database->quote($drive_group).", ".$anvil->Database->quote($enclosure_id).", ".$anvil->Database->quote($slot_number).", ".$anvil->Database->quote($scan_storcli_physical_drive_serial_number).", ".$anvil->Database->quote($new_size).", ".$anvil->Database->quote($new_sector_size).", ".$anvil->Database->quote($new_vendor).", ".$anvil->Database->quote($new_model).", ".$anvil->Database->quote($new_self_encrypting_drive).", ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." );"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; # Process the rest of the variables and temperatures now. foreach my $type ("variable", "temperature") { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { type => $type }}); foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{$type}}) { my $value = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{$type}{$variable}; my $temperature = $type eq "temperature" ? "TRUE" : "FALSE"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, temperature => $temperature, }}); # If this is a thermal sensor, we need to see if it is OK or not. my $cleared = 0; my $message_key = "scan_storcli_note_0002"; my $alert_level = "notice"; my $temperature_state = "ok"; my $temperature_is = "nominal"; if ($type eq "temperature") { # This is a temperature, so see if the temperature outside of the # warning or critical thresholds. This is a new sensor, so nothing # to compare against. my $temperature_state = "ok"; my $temperature_is = "nominal"; if ($value > $high_critical) { # Crossed the critical threshold. This should # always be unset because it is a new # variable, but check anyway. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $scan_storcli_physical_drive_serial_number.":".$variable."_high_critical", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first time we rose above the critical # threshold. $alert_level = "critical"; $message_key = "scan_storcli_note_0012"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } } elsif ($value > $high_warning) { # Crossed the warning threshold. This should always be unset # because it is a new variable, but check anyway. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $scan_storcli_physical_drive_serial_number.":".$variable."_high_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first time we rose above the critical # threshold. $alert_level = "warning"; $message_key = "scan_storcli_note_0013"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } } elsif ($value < $low_critical) { # Dropped below the critical threshold. This should always be # unset because it is a new variable, but check anyway. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $scan_storcli_physical_drive_serial_number.":".$variable."_low_critical", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first time we rose above the critical # threshold. $alert_level = "critical"; $message_key = "scan_storcli_note_0014"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } } elsif ($value < $low_warning) { # Crossed the warning threshold. This should always be unset # because it is a new variable, but check anyway. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $scan_storcli_physical_drive_serial_number.":".$variable."_low_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first time we rose above the critical # threshold. $alert_level = "warning"; $message_key = "scan_storcli_note_0015"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } } # Record this for later processing into the 'temperature' table. my $sensor_host_key = "pd:".$scan_storcli_physical_drive_serial_number; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { sensor_host_key => $sensor_host_key }}); $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_value_c} = $value; $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_state} = $temperature_state; $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_is} = $temperature_is; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "new::temperature::${variable}::${sensor_host_key}::temperature_value_c" => $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_value_c}, "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}, }}); } # Send an alert telling the user that we've found a new physical drive sensor. my $variables = { serial_number => $scan_storcli_physical_drive_serial_number, name => $variable, value => $value, high_critical_temperature => $high_critical, high_warning_temperature => $high_warning, low_critical_temperature => $low_critical, low_warning_temperature => $low_warning, }; my $log_level = $alert_level eq "notice" ? 3 : 2; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $log_level, key => $message_key, variables => $variables}); $anvil->Alert->register({ alert_level => $alert_level, show_header => 0, message => $message_key, variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); # INSERT my $query = " INSERT INTO scan_storcli_variables ( scan_storcli_variable_uuid, scan_storcli_variable_host_uuid, scan_storcli_variable_source_table, scan_storcli_variable_source_uuid, scan_storcli_variable_is_temperature, scan_storcli_variable_name, scan_storcli_variable_value, modified_date ) VALUES ( ".$anvil->Database->quote($anvil->Get->uuid()).", ".$anvil->Database->quote($anvil->Get->host_uuid).", 'scan_storcli_physical_drives', ".$anvil->Database->quote($scan_storcli_physical_drive_uuid).", ".$anvil->Database->quote($temperature).", ".$anvil->Database->quote($variable).", ".$anvil->Database->quote($value).", ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." );"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } } } else { ### NOTE: The serial number should never change, but we check/update just to be safe. # Look for changes. my $main_table_changed = 0; my $old_controller_uuid = $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}{scan_storcli_physical_drive_controller_uuid}; my $old_virtual_drive = $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}{scan_storcli_physical_drive_virtual_drive}; my $old_drive_group = $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}{scan_storcli_physical_drive_drive_group}; my $old_enclosure_id = $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}{scan_storcli_physical_drive_enclosure_id}; my $old_slot_number = $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}{scan_storcli_physical_drive_slot_number}; my $old_physical_drive_serial_number = $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}{scan_storcli_physical_drive_serial_number}; my $old_size = $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}{scan_storcli_physical_drive_size}; my $old_sector_size = $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}{scan_storcli_physical_drive_sector_size}; my $old_vendor = $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}{scan_storcli_physical_drive_vendor}; my $old_model = $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}{scan_storcli_physical_drive_model}; my $old_self_encrypting_drive = $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}{scan_storcli_physical_drive_self_encrypting_drive}; $old_controller_uuid = "" if not defined $old_controller_uuid; $old_physical_drive_serial_number = "" if not defined $old_physical_drive_serial_number; $old_model = "" if not defined $old_model; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_physical_drive_serial_number => $scan_storcli_physical_drive_serial_number, old_physical_drive_serial_number => $old_physical_drive_serial_number, controller_uuid => $controller_uuid, old_controller_uuid => $old_controller_uuid, virtual_drive => $virtual_drive, old_virtual_drive => $old_virtual_drive, drive_group => $drive_group, old_drive_group => $old_drive_group, enclosure_id => $enclosure_id, old_enclosure_id => $old_enclosure_id, slot_number => $slot_number, old_slot_number => $old_slot_number, new_size => $new_size, old_size => $old_size, new_sector_size => $new_sector_size, old_sector_size => $old_sector_size, new_vendor => $new_vendor, old_vendor => $old_vendor, new_model => $new_model, old_model => $old_model, new_self_encrypting_drive => $new_self_encrypting_drive, old_self_encrypting_drive => $old_self_encrypting_drive, }}); if (($scan_storcli_physical_drive_serial_number ne $old_physical_drive_serial_number) or ($controller_uuid ne $old_controller_uuid) or ($virtual_drive ne $old_virtual_drive) or ($drive_group ne $old_drive_group) or ($enclosure_id ne $old_enclosure_id) or ($slot_number ne $old_slot_number) or ($new_size ne $old_size) or ($new_sector_size ne $old_sector_size) or ($new_vendor ne $old_vendor) or ($new_model ne $old_model) or ($new_self_encrypting_drive ne $old_self_encrypting_drive)) { # I'll need the old controller's serial number. my $old_host_controller_serial_number = $anvil->data->{'scan-storcli'}{controllers}{by_uuid}{$old_controller_uuid}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_host_controller_serial_number => $old_host_controller_serial_number }}); # Send a warning level alert because these really should never change. my $cleared = 0; my $message_key = "scan_storcli_warning_0029"; if ($new_model ne $old_model) { if ($old_model eq "VANISHED") { # Physical drive has returned. $message_key = "scan_storcli_warning_0030"; } } my $variables = { new_on_controller => $host_controller_serial_number, old_on_controller => $old_host_controller_serial_number, new_virtual_drive => $virtual_drive, old_virtual_drive => $old_virtual_drive, new_drive_group => $drive_group, old_drive_group => $old_drive_group, new_enclosure_id => $enclosure_id, old_enclosure_id => $old_enclosure_id, new_slot_number => $slot_number, old_slot_number => $old_slot_number, new_serial_number => $scan_storcli_physical_drive_serial_number, old_serial_number => $old_physical_drive_serial_number, new_vendor => $new_vendor, old_vendor => $old_vendor, new_model => $new_model, old_model => $old_model, new_size => $new_size, old_size => $old_size, new_sector_size => $new_sector_size, old_sector_size => $old_sector_size, new_self_encrypting_drive => $new_self_encrypting_drive, old_self_encrypting_drive => $old_self_encrypting_drive, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => $message_key, variables => $variables}); $anvil->Alert->register({ alert_level => "warning", show_header => 0, message => $message_key, variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); $main_table_changed = 1; my $query = " UPDATE scan_storcli_physical_drives SET scan_storcli_physical_drive_controller_uuid = ".$anvil->Database->quote($controller_uuid).", scan_storcli_physical_drive_virtual_drive = ".$anvil->Database->quote($virtual_drive).", scan_storcli_physical_drive_drive_group = ".$anvil->Database->quote($drive_group).", scan_storcli_physical_drive_enclosure_id = ".$anvil->Database->quote($enclosure_id).", scan_storcli_physical_drive_slot_number = ".$anvil->Database->quote($slot_number).", scan_storcli_physical_drive_serial_number = ".$anvil->Database->quote($scan_storcli_physical_drive_serial_number).", scan_storcli_physical_drive_size = ".$anvil->Database->quote($new_size).", scan_storcli_physical_drive_sector_size = ".$anvil->Database->quote($new_sector_size).", scan_storcli_physical_drive_vendor = ".$anvil->Database->quote($new_vendor).", scan_storcli_physical_drive_model = ".$anvil->Database->quote($new_model).", scan_storcli_physical_drive_self_encrypting_drive = ".$anvil->Database->quote($new_self_encrypting_drive).", modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." WHERE scan_storcli_physical_drive_uuid = ".$anvil->Database->quote($scan_storcli_physical_drive_uuid)." ;"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } ### Process the rest of the variables now. foreach my $type ("variable", "temperature") { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { type => $type }}); foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{$type}}) { my $new_variable_value = delete $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{$type}{$variable}; $new_variable_value = "" if not defined $new_variable_value; my $temperature = $type eq "temperature" ? "TRUE" : "FALSE"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, temperature => $temperature, }}); if (exists $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_physical_drives'}{source_uuid}{$scan_storcli_physical_drive_uuid}{$type}{$variable}{scan_storcli_variable_uuid}) { # Look for changes my $old_variable_value = $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_physical_drives'}{source_uuid}{$scan_storcli_physical_drive_uuid}{$type}{$variable}{scan_storcli_variable_value}; my $variable_uuid = $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_physical_drives'}{source_uuid}{$scan_storcli_physical_drive_uuid}{$type}{$variable}{scan_storcli_variable_uuid}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, new_variable_value => $new_variable_value, old_variable_value => $old_variable_value, variable_uuid => $variable_uuid, }}); # Delete it so that we know it has been processed. delete $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_physical_drives'}{source_uuid}{$scan_storcli_physical_drive_uuid}{$type}{$variable}; # If there is no change, I still want to record the temperature # (where applicable). So I setup like I will send an alert from the # start, but will only actually send if something has changed. my $cleared = 0; my $message_key = "scan_storcli_warning_0031"; my $alert_level = "info"; my $temperature_state = "ok"; my $temperature_is = "nominal"; if ($type eq "temperature") { # It's a temperature, so change the default message. $message_key = "scan_storcli_note_0064"; if (($new_variable_value ne $old_variable_value)) { # If the temperature is rising, see if we need to set # a high warning or critical, or, clear a low warning # or critical. Check for the reverse if the # temperature is dropping. if ($old_variable_value eq "VANISHED") { ### NOTE: We don't (yet) check to see if the ### drive is overheating or freezing ### here. That would require new logic ### and is unlikely to be needed. # Temperature is back. $message_key = "scan_storcli_warning_0032"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { message_key => $message_key }}); } elsif ($new_variable_value > $old_variable_value) { # Rising my $jumped = $new_variable_value - $old_variable_value; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { jumped => $jumped }}); if ($new_variable_value > $high_critical) { # Crossed the critical threshold. See # if this is the first time. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $scan_storcli_physical_drive_serial_number.":".$variable."_high_critical", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first time we # rose above the critical # threshold. $alert_level = "critical"; $message_key = "scan_storcli_note_0008"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } $temperature_state = "critical"; $temperature_is = "high"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { temperature_state => $temperature_state, temperature_is => $temperature_is, }}); } elsif ($new_variable_value > $high_warning) { # Crossed the warning threshold. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $scan_storcli_physical_drive_serial_number.":".$variable."_high_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first time we rose above the # critical threshold. $alert_level = "warning"; $message_key = "scan_storcli_note_0009"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } $temperature_state = "warning"; $temperature_is = "high"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "temperature_state" => $temperature_state, "temperature_is" => $temperature_is, }}); } elsif ($new_variable_value > $clear_low_warning) { # Risen into the clear, make sure # both warning and critical are # cleared. my $cleared_critical = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $scan_storcli_physical_drive_serial_number.":".$variable."_low_critical", set_by => $THIS_FILE}); my $cleared_warning = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $scan_storcli_physical_drive_serial_number.":".$variable."_low_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared_critical => $cleared_critical, cleared_warning => $cleared_warning, }}); if ($cleared_critical) { $cleared = 1; $alert_level = "warning"; $message_key = "scan_storcli_note_0016"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared => $cleared, alert_level => $alert_level, message_key => $message_key, }}); } elsif ($cleared_warning) { # The temperature has dropped back down to # safe levels. $cleared = 1; $alert_level = "warning"; $message_key = "scan_storcli_note_0016"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared => $cleared, alert_level => $alert_level, message_key => $message_key, }}); } } elsif ($new_variable_value > $clear_low_critical) { # Risen above critical, but not in # the clear. my $cleared_critical = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $scan_storcli_physical_drive_serial_number.":".$variable."_low_critical", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared_critical => $cleared_critical }}); if ($cleared_critical) { # Set the warning. my $changed = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $scan_storcli_physical_drive_serial_number.":".$variable."_low_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); $cleared = 1; $alert_level = "warning"; $message_key = "scan_storcli_note_0017"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared => $cleared, alert_level => $alert_level, message_key => $message_key, }}); } $temperature_state = "warning"; $temperature_is = "low"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "temperature_state" => $temperature_state, "temperature_is" => $temperature_is, }}); } elsif ($jumped > $jump) { # The temperature jumped a large # amount. $alert_level = "warning"; $message_key = "scan_storcli_note_0018"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } } else { # Falling my $jumped = $old_variable_value - $new_variable_value; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { jumped => $jumped }}); if ($new_variable_value < $low_critical) { # Dropped below the critical threshold. This # should always be unset because it is a new # variable, but check anyway. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $scan_storcli_physical_drive_serial_number.":".$variable."_low_critical", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first time we rose above the # critical threshold. $alert_level = "critical"; $message_key = "scan_storcli_note_0010"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } $temperature_state = "critical"; $temperature_is = "low"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "temperature_state" => $temperature_state, "temperature_is" => $temperature_is, }}); } elsif ($new_variable_value < $low_warning) { # Crossed the warning threshold. This should # always be unset because it is a new # variable, but check anyway. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $scan_storcli_physical_drive_serial_number.":".$variable."_low_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first time we rose above the # critical threshold. $alert_level = "warning"; $message_key = "scan_storcli_note_0011"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } $temperature_state = "warning"; $temperature_is = "low"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "temperature_state" => $temperature_state, "temperature_is" => $temperature_is, }}); } elsif ($new_variable_value < $clear_high_warning) { # Dropped into the clear my $cleared_critical = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $scan_storcli_physical_drive_serial_number.":".$variable."_high_critical", set_by => $THIS_FILE}); my $cleared_warning = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $scan_storcli_physical_drive_serial_number.":".$variable."_high_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared_critical => $cleared_critical, cleared_warning => $cleared_warning, }}); if ($cleared_critical) { $cleared = 1; $alert_level = "warning"; $message_key = "scan_storcli_note_0019"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared => $cleared, alert_level => $alert_level, message_key => $message_key, }}); } elsif ($cleared_warning) { # The temperature has dropped back down to # safe levels. $cleared = 1; $alert_level = "warning"; $message_key = "scan_storcli_note_0019"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared => $cleared, alert_level => $alert_level, message_key => $message_key, }}); } } elsif ($new_variable_value < $clear_high_critical) { # Dropped below critical, but not in # the clear. my $cleared_critical = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $scan_storcli_physical_drive_serial_number.":".$variable."_high_critical", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared_critical => $cleared_critical }}); if ($cleared_critical) { # Set the warning. my $changed = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $scan_storcli_physical_drive_serial_number.":".$variable."_high_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); $cleared = 1; $alert_level = "warning"; $message_key = "scan_storcli_note_0020"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared => $cleared, alert_level => $alert_level, message_key => $message_key, }}); } $temperature_state = "warning"; $temperature_is = "high"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { temperature_state => $temperature_state, temperature_is => $temperature_is, }}); } elsif ($jumped > $jump) { # The temperature dropped a large # amount. $alert_level = "warning"; $message_key = "scan_storcli_note_0021"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } } } # Record this for later processing into the 'temperature' # table. my $sensor_host_key = "pd:".$scan_storcli_physical_drive_serial_number; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { sensor_host_key => $sensor_host_key }}); $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_value_c} = $new_variable_value; $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_state} = $temperature_state; $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_is} = $temperature_is; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "new::temperature::${variable}::${sensor_host_key}::temperature_value_c" => $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_value_c}, "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}, }}); } # Now actually generate an alert and save the changes if something # changed. if (($new_variable_value ne $old_variable_value)) { # Changed! If the old value was 'VANISHED', then a # sensor or variable returned. Otherwise, for now, we # treat everything as 'warning' and step down # explicitely anything not of concern that proves # noisey later (better safe than sorry). $anvil->data->{'scan-storcli'}{alert_sort}++; if ($type ne "temperature") { if ($old_variable_value eq "VANISHED") { $message_key = "scan_storcli_warning_0032"; } # We want to watch for the 'error' counters. If they # change, we make this a warning. if ($variable =~ /error_count/) { # If we're over 6, it's a warning. If its # under 6, a notice. If it's 0, clear. if ($new_variable_value > 6) { $alert_level = "warning"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level }}); } elsif ($new_variable_value > 0) { $alert_level = "notice"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level }}); } $message_key = "scan_storcli_warning_0044"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { message_key => $message_key }}); } elsif ($variable =~ /fail/) { $alert_level = "warning"; $message_key = "scan_storcli_warning_0045"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } } my $variables = { serial_number => $scan_storcli_physical_drive_serial_number, name => $variable, old_value => $old_variable_value ? $old_variable_value : "--", new_value => $new_variable_value, high_critical_temperature => $high_critical, high_warning_temperature => $high_warning, low_critical_temperature => $low_critical, low_warning_temperature => $low_warning, jump => $jump, }; my $log_level = $alert_level eq "notice" ? 3 : 2; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $log_level, key => $message_key, variables => $variables}); $anvil->Alert->register({ alert_level => $alert_level, show_header => $main_table_changed ? 0 : 1, message => $message_key, variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); my $query = " UPDATE scan_storcli_variables SET scan_storcli_variable_value = ".$anvil->Database->quote($new_variable_value).", modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." WHERE scan_storcli_variable_uuid = ".$anvil->Database->quote($variable_uuid)." ;"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } } else { # New variable, record it. This is a 'warning' level as # variables aren't expected to spawn into existence. my $variables = { serial_number => $scan_storcli_physical_drive_serial_number, name => $variable, value => $new_variable_value, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "scan_storcli_warning_0033", variables => $variables}); $anvil->Alert->register({ alert_level => "warning", show_header => $main_table_changed ? 0 : 1, message => "scan_storcli_warning_0033", variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); # INSERT my $query = " INSERT INTO scan_storcli_variables ( scan_storcli_variable_uuid, scan_storcli_variable_host_uuid, scan_storcli_variable_source_table, scan_storcli_variable_source_uuid, scan_storcli_variable_is_temperature, scan_storcli_variable_name, scan_storcli_variable_value, modified_date ) VALUES ( ".$anvil->Database->quote($anvil->Get->uuid()).", ".$anvil->Database->quote($anvil->Get->host_uuid).", 'scan_storcli_physical_drives', ".$anvil->Database->quote($scan_storcli_physical_drive_uuid).", ".$anvil->Database->quote($temperature).", ".$anvil->Database->quote($variable).", ".$anvil->Database->quote($new_variable_value).", ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." );"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } } } # Now look for any variables left from the previous scan that we didn't match up (and delete) # this pass. foreach my $type ("variable", "temperature") { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { type => $type }}); foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_physical_drives'}{source_uuid}{$scan_storcli_physical_drive_uuid}{$type}}) { # This variable has vanished next if not defined $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_physical_drives'}{source_uuid}{$scan_storcli_physical_drive_uuid}{$type}{$variable}{scan_storcli_variable_value}; my $old_variable_value = $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_physical_drives'}{source_uuid}{$scan_storcli_physical_drive_uuid}{$type}{$variable}{scan_storcli_variable_value}; my $variable_uuid = $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_physical_drives'}{source_uuid}{$scan_storcli_physical_drive_uuid}{$type}{$variable}{scan_storcli_variable_uuid}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_variable_value => $old_variable_value, variable_uuid => $variable_uuid, }}); # Delete it so that we know it has been processed. delete $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_physical_drives'}{source_uuid}{$scan_storcli_physical_drive_uuid}{$type}{$variable}; # If the old value is already 'VANISHED', ignore it. next if $old_variable_value eq "VANISHED"; ### NOTE: For now, we're going to use warning level because variables ### shouldn't vanish, but under an-cm, it did happen for ### reasons that we never figured out. So later, we may drop ### the alert level in some cases. # Still here? Alert and UPDATE. my $variables = { serial_number => $scan_storcli_physical_drive_serial_number, name => $variable, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "scan_storcli_warning_0034", variables => $variables}); $anvil->Alert->register({ alert_level => "warning", show_header => $main_table_changed ? 0 : 1, message => "scan_storcli_warning_0034", variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); my $query = " UPDATE scan_storcli_variables SET scan_storcli_variable_value = 'VANISHED', modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." WHERE scan_storcli_variable_uuid = ".$anvil->Database->quote($variable_uuid)." ;"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } } } # Delete the Physical Drive from the last scan so that we can find disks that have been removed. if (exists $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}) { delete $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}; } return(0); } # Look for added, changed or deleted cachevaults (FBUs). sub process_cachevaults { my ($anvil) = @_; # Setup our thermal thresholds my $high_critical = $anvil->data->{'scan-storcli'}{thresholds}{cachevault}{high_critical}; my $high_warning = $anvil->data->{'scan-storcli'}{thresholds}{cachevault}{high_warning}; my $low_warning = $anvil->data->{'scan-storcli'}{thresholds}{cachevault}{low_warning}; my $low_critical = $anvil->data->{'scan-storcli'}{thresholds}{cachevault}{low_critical}; my $jump = $anvil->data->{'scan-storcli'}{thresholds}{cachevault}{jump}; my $buffer = $anvil->data->{'scan-storcli'}{thresholds}{cachevault}{buffer}; my $clear_high_critical = $high_critical - $buffer; my $clear_high_warning = $high_warning - $buffer; my $clear_low_critical = $low_critical - $buffer; my $clear_low_warning = $low_warning - $buffer; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { high_critical => $high_critical, high_warning => $high_warning, low_warning => $low_warning, low_critical => $low_critical, jump => $jump, buffer => $buffer, clear_high_critical => $clear_high_critical, clear_high_warning => $clear_high_warning, clear_low_critical => $clear_low_critical, clear_low_warning => $clear_low_warning, }}); # Find new, changed and replaced Cachevaults (FBUs). $anvil->data->{'scan-storcli'}{queries} = []; foreach my $cachevault_serial_number (sort {$a cmp $b} keys %{$anvil->data->{cachevault}{serial_number}}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cachevault_serial_number => $cachevault_serial_number }}); # Is this a new Cachevault? my $cachevault_uuid = ""; my $is_new = 0; if (exists $anvil->data->{'scan-storcli'}{cachevaults}{by_serial}{$cachevault_serial_number}) { # Yes! $cachevault_uuid = $anvil->data->{'scan-storcli'}{cachevaults}{by_serial}{$cachevault_serial_number}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cachevault_uuid => $cachevault_uuid }}); } else { # No, this is a new Cachevault. Create a new UUID for it. $cachevault_uuid = $anvil->Get->uuid(); $is_new = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cachevault_uuid => $cachevault_uuid, is_new => $is_new, }}); # Add the keys for looking it up by UUID or serial number. $anvil->data->{'scan-storcli'}{cachevaults}{by_serial}{$cachevault_serial_number} = $cachevault_uuid; $anvil->data->{'scan-storcli'}{cachevaults}{by_uuid}{$cachevault_uuid} = $cachevault_serial_number; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "scan-storcli::cachevaults::by_serial::$cachevault_serial_number" => $anvil->data->{'scan-storcli'}{cachevaults}{by_serial}{$cachevault_serial_number}, "scan-storcli::cachevaults::by_uuid::$cachevault_uuid" => $anvil->data->{'scan-storcli'}{cachevaults}{by_uuid}{$cachevault_uuid}, }}); } # First up, get the controller serial number and translate that to a controller_uuid. if (not $anvil->data->{cachevault}{serial_number}{$cachevault_serial_number}{host_controller_serial_number}) { # Error! $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "scan_storcli_error_0009", variables => { serial_number => $cachevault_serial_number }}); $anvil->nice_exit({exit_code => 9}); } my $host_controller_serial_number = $anvil->data->{cachevault}{serial_number}{$cachevault_serial_number}{host_controller_serial_number}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_controller_serial_number => $host_controller_serial_number }}); if (not $anvil->data->{'scan-storcli'}{controllers}{by_serial}{$host_controller_serial_number}) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "scan_storcli_error_0008", variables => { serial_number => $host_controller_serial_number }}); $anvil->nice_exit({exit_code => 8}); } my $controller_uuid = $anvil->data->{'scan-storcli'}{controllers}{by_serial}{$host_controller_serial_number}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { controller_uuid => $controller_uuid }}); # These are the values for the main table. Anything else will go in the variables table. my $new_type = ""; my $new_model = ""; my $new_state = ""; my $new_manufacture_date = ""; my $new_design_capacity = ""; my $new_replacement_needed = ""; foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{cachevault}{serial_number}{$cachevault_serial_number}{variable}}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable }}); # Store and delete the value if ($variable eq "type") { $new_type = delete $anvil->data->{cachevault}{serial_number}{$cachevault_serial_number}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_type => $new_type }}); next; } if ($variable eq "device_name") { $new_model = delete $anvil->data->{cachevault}{serial_number}{$cachevault_serial_number}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_model => $new_model }}); next; } if ($variable eq "state") { $new_state = delete $anvil->data->{cachevault}{serial_number}{$cachevault_serial_number}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_state => $new_state }}); next; } if ($variable eq "date_of_manufacture") { $new_manufacture_date = delete $anvil->data->{cachevault}{serial_number}{$cachevault_serial_number}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_manufacture_date => $new_manufacture_date }}); next; } if ($variable eq "design_capacity") { $new_design_capacity = delete $anvil->data->{cachevault}{serial_number}{$cachevault_serial_number}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_design_capacity => $new_design_capacity }}); next; } if ($variable eq "replacement_required") { $new_replacement_needed = delete $anvil->data->{cachevault}{serial_number}{$cachevault_serial_number}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_replacement_needed => $new_replacement_needed }}); next; } } # Pull out the rest of the variables now. If the Cachevault is new, all variables will be # INSERTed. If the Cachevault exists, each variable will be examined and new ones will be # INSERTed, existing ones will be checked for changes and UPDATEd as needed. If the # Cachevault is NOT new, then variables from the old data will be deleted as we go and any # not found in the current data set will be left over. We'll use this to determine variables # that have vanished. They will not be deleted, but their value will be set to 'VANISHED'. if ($is_new) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cachevault_uuid => $cachevault_uuid, controller_uuid => $controller_uuid, cachevault_serial_number => $cachevault_serial_number, new_type => $new_type, new_model => $new_model, new_state => $new_state, new_design_capacity => $new_design_capacity, new_manufacture_date => $new_manufacture_date, new_replacement_needed => $new_replacement_needed, }}); ### NOTE: The rest of the alerts will be in the format '- Variable: [$value]'. # Send an alert telling the user that we've found a new controller. my $variables = { on_controller => $host_controller_serial_number, serial_number => $cachevault_serial_number, type => $new_type, model => $new_model, 'state' => $new_state, design_capacity => $new_design_capacity, manufacture_date => $new_manufacture_date, replacement_needed => $new_replacement_needed, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "scan_storcli_note_0007", variables => $variables}); $anvil->Alert->register({ alert_level => "notice", message => "scan_storcli_note_0007", variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); # INSERT my $query = " INSERT INTO scan_storcli_cachevaults ( scan_storcli_cachevault_uuid, scan_storcli_cachevault_host_uuid, scan_storcli_cachevault_controller_uuid, scan_storcli_cachevault_serial_number, scan_storcli_cachevault_type, scan_storcli_cachevault_model, scan_storcli_cachevault_state, scan_storcli_cachevault_design_capacity, scan_storcli_cachevault_manufacture_date, scan_storcli_cachevault_replacement_needed, modified_date ) VALUES ( ".$anvil->Database->quote($cachevault_uuid).", ".$anvil->Database->quote($anvil->Get->host_uuid).", ".$anvil->Database->quote($controller_uuid).", ".$anvil->Database->quote($cachevault_serial_number).", ".$anvil->Database->quote($new_type).", ".$anvil->Database->quote($new_model).", ".$anvil->Database->quote($new_state).", ".$anvil->Database->quote($new_design_capacity).", ".$anvil->Database->quote($new_manufacture_date).", ".$anvil->Database->quote($new_replacement_needed).", ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." );"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; # Process the rest of the variables and temperatures now. foreach my $type ("variable", "temperature") { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { type => $type }}); foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{cachevault}{serial_number}{$cachevault_serial_number}{$type}}) { my $value = delete $anvil->data->{cachevault}{serial_number}{$cachevault_serial_number}{$type}{$variable}; my $temperature = $type eq "temperature" ? "TRUE" : "FALSE"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, temperature => $temperature, }}); my $cleared = 0; my $message_key = "scan_storcli_note_0002"; my $alert_level = "info"; my $temperature_state = "ok"; my $temperature_is = "nominal"; if ($type eq "temperature") { # This is a temperature, so see if the temperature outside of # the warning or critical thresholds. This is a new sensor, # so nothing to compare against. my $temperature_state = "ok"; my $temperature_is = "nominal"; if ($value > $high_critical) { # Crossed the critical threshold. This should # always be unset because it is a new # variable, but check anyway. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $cachevault_serial_number.":".$variable."_high_critical", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first time we rose above the # critical threshold. $alert_level = "critical"; $message_key = "scan_storcli_note_0036"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } } elsif ($value > $high_warning) { # Crossed the warning threshold. This should always # be unset because it is a new variable, but check # anyway. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $cachevault_serial_number.":".$variable."_high_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first time we rose above the # critical threshold. $alert_level = "warning"; $message_key = "scan_storcli_note_0037"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } } elsif ($value < $low_critical) { # Dropped below the critical threshold. This should # always be unset because it is a new variable, but # check anyway. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $cachevault_serial_number.":".$variable."_low_critical", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first time we rose above the # critical threshold. $alert_level = "critical"; $message_key = "scan_storcli_note_0038"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } } elsif ($value < $low_warning) { # Crossed the warning threshold. This should always be unset # because it is a new variable, but check anyway. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $cachevault_serial_number.":".$variable."_low_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first time we rose above the critical # threshold. $alert_level = "warning"; $message_key = "scan_storcli_note_0039"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } } # Record this for later processing into the 'temperature' table. my $sensor_host_key = "cachevault:".$cachevault_serial_number; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { sensor_host_key => $sensor_host_key }}); $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_value_c} = $value; $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_state} = $temperature_state; $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_is} = $temperature_is; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "new::temperature::${variable}::${sensor_host_key}::temperature_value_c" => $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_value_c}, "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}, }}); } # Send an alert telling the user that we've found a new controller. my $variables = { serial_number => $cachevault_serial_number, name => $variable, value => $value, high_critical_temperature => $high_critical, high_warning_temperature => $high_warning, low_critical_temperature => $low_critical, low_warning_temperature => $low_warning, }; my $log_level = (($alert_level eq "warning") or ($alert_level eq "critical")) ? 1 : 2; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $log_level, key => $message_key, variables => $variables}); $anvil->Alert->register({ clear_alert => $cleared, alert_level => $alert_level, message => $message_key, variables => $variables, show_header => 0, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); # INSERT my $query = " INSERT INTO scan_storcli_variables ( scan_storcli_variable_uuid, scan_storcli_variable_host_uuid, scan_storcli_variable_source_table, scan_storcli_variable_source_uuid, scan_storcli_variable_is_temperature, scan_storcli_variable_name, scan_storcli_variable_value, modified_date ) VALUES ( ".$anvil->Database->quote($anvil->Get->uuid()).", ".$anvil->Database->quote($anvil->Get->host_uuid).", 'scan_storcli_cachevaults', ".$anvil->Database->quote($cachevault_uuid).", ".$anvil->Database->quote($temperature).", ".$anvil->Database->quote($variable).", ".$anvil->Database->quote($value).", ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." );"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } } } else { ### NOTE: The serial number should never change (a changed SN/Cachevault should be ### picked up as a new Cachevault), but we check/update just to be safe. # Look for changes. my $main_table_changed = 0; my $old_cachevault_serial_number = $anvil->data->{sql}{scan_storcli_cachevaults}{scan_storcli_cachevault_uuid}{$cachevault_uuid}{scan_storcli_cachevault_serial_number}; my $old_controller_uuid = $anvil->data->{sql}{scan_storcli_cachevaults}{scan_storcli_cachevault_uuid}{$cachevault_uuid}{scan_storcli_cachevault_controller_uuid}; my $old_type = $anvil->data->{sql}{scan_storcli_cachevaults}{scan_storcli_cachevault_uuid}{$cachevault_uuid}{scan_storcli_cachevault_type}; my $old_model = $anvil->data->{sql}{scan_storcli_cachevaults}{scan_storcli_cachevault_uuid}{$cachevault_uuid}{scan_storcli_cachevault_model}; my $old_state = $anvil->data->{sql}{scan_storcli_cachevaults}{scan_storcli_cachevault_uuid}{$cachevault_uuid}{scan_storcli_cachevault_state}; my $old_design_capacity = $anvil->data->{sql}{scan_storcli_cachevaults}{scan_storcli_cachevault_uuid}{$cachevault_uuid}{scan_storcli_cachevault_design_capacity}; my $old_manufacture_date = $anvil->data->{sql}{scan_storcli_cachevaults}{scan_storcli_cachevault_uuid}{$cachevault_uuid}{scan_storcli_cachevault_manufacture_date}; my $old_replacement_needed = $anvil->data->{sql}{scan_storcli_cachevaults}{scan_storcli_cachevault_uuid}{$cachevault_uuid}{scan_storcli_cachevault_replacement_needed}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cachevault_serial_number => $cachevault_serial_number, old_cachevault_serial_number => $old_cachevault_serial_number, controller_uuid => $controller_uuid, old_controller_uuid => $old_controller_uuid, new_type => $new_type, old_type => $old_type, new_model => $new_model, old_model => $old_model, new_state => $new_state, old_state => $old_state, new_design_capacity => $new_design_capacity, old_design_capacity => $old_design_capacity, new_manufacture_date => $new_manufacture_date, old_manufacture_date => $old_manufacture_date, new_replacement_needed => $new_replacement_needed, old_replacement_needed => $old_replacement_needed, }}); if (($cachevault_serial_number ne $old_cachevault_serial_number) or ($controller_uuid ne $old_controller_uuid) or ($new_type ne $old_type) or ($new_model ne $old_model) or ($new_state ne $old_state) or ($new_design_capacity ne $old_design_capacity) or ($new_manufacture_date ne $old_manufacture_date) or ($new_replacement_needed ne $old_replacement_needed)) { # I need the serial numbers for the controller UUIDs. my $new_controller_serial_number = $anvil->data->{'scan-storcli'}{controllers}{by_uuid}{$controller_uuid}; my $old_controller_serial_number = $anvil->data->{'scan-storcli'}{controllers}{by_uuid}{$old_controller_uuid}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_controller_serial_number => $new_controller_serial_number, old_controller_serial_number => $old_controller_serial_number, }}); # Send a warning level alert because the most likely change is 'state' or # 'replacement_needed'. my $cleared = 0; my $message_key = "scan_storcli_warning_0036"; if ($new_state ne $old_state) { if ($old_state eq "VANISHED") { # Cachevault has returned. $message_key = "scan_storcli_warning_0037"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { message_key => $message_key }}); } ### TODO: Verify this is the expected string on Cachevaults. if ($new_state =~ /optimal/i) { $cleared = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared => $cleared }}); } } my $variables = { new_on_controller => $new_controller_serial_number, old_on_controller => $old_controller_serial_number, new_model => $new_model, old_model => $old_model, new_serial_number => $cachevault_serial_number, old_serial_number => $old_cachevault_serial_number, new_type => $new_type, old_type => $old_type, new_state => $new_state, old_state => $old_state, new_design_capacity => $new_design_capacity, old_design_capacity => $old_design_capacity, new_manufacture_date => $new_manufacture_date, old_manufacture_date => $old_manufacture_date, new_replacement_needed => $new_replacement_needed, old_replacement_needed => $old_replacement_needed, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => $message_key, variables => $variables}); $anvil->Alert->register({ clear_alert => $cleared, alert_level => "warning", message => $message_key, variables => $variables, show_header => 0, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); $main_table_changed = 1; my $query = " UPDATE scan_storcli_cachevaults SET scan_storcli_cachevault_controller_uuid = ".$anvil->Database->quote($controller_uuid).", scan_storcli_cachevault_serial_number = ".$anvil->Database->quote($cachevault_serial_number).", scan_storcli_cachevault_type = ".$anvil->Database->quote($new_type).", scan_storcli_cachevault_model = ".$anvil->Database->quote($new_model).", scan_storcli_cachevault_state = ".$anvil->Database->quote($new_state).", scan_storcli_cachevault_manufacture_date = ".$anvil->Database->quote($new_manufacture_date).", scan_storcli_cachevault_design_capacity = ".$anvil->Database->quote($new_design_capacity).", scan_storcli_cachevault_replacement_needed = ".$anvil->Database->quote($new_replacement_needed).", modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." WHERE scan_storcli_cachevault_uuid = ".$anvil->Database->quote($cachevault_uuid)." ;"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } ### Process the rest of the variables now. foreach my $type ("variable", "temperature") { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { type => $type }}); foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{cachevault}{serial_number}{$cachevault_serial_number}{$type}}) { my $new_variable_value = delete $anvil->data->{cachevault}{serial_number}{$cachevault_serial_number}{$type}{$variable}; my $temperature = $type eq "temperature" ? "TRUE" : "FALSE"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, new_variable_value => $new_variable_value, temperature => $temperature, }}); if (exists $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_cachevaults'}{source_uuid}{$cachevault_uuid}{$type}{$variable}{scan_storcli_variable_uuid}) { # Look for changes my $old_variable_value = $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_cachevaults'}{source_uuid}{$cachevault_uuid}{$type}{$variable}{scan_storcli_variable_value}; my $variable_uuid = $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_cachevaults'}{source_uuid}{$cachevault_uuid}{$type}{$variable}{scan_storcli_variable_uuid}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, new_variable_value => $new_variable_value, old_variable_value => $old_variable_value, variable_uuid => $variable_uuid, }}); # Delete it so that we know it has been processed. delete $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_cachevaults'}{source_uuid}{$cachevault_uuid}{$type}{$variable}; # If there is no change, I still want to record the # temperature (where applicable). So I setup like I will send # an alert from the start, but will only actually send if # something has changed. my $cleared = 1; my $message_key = "scan_storcli_warning_0038"; my $alert_level = "info"; my $temperature_state = "ok"; my $temperature_is = "nominal"; if ($type eq "temperature") { # It's a temperature, so change the default message. $message_key = "scan_storcli_note_0065"; if (($new_variable_value ne $old_variable_value)) { # If the temperature is rising, see if we # need to set a high warning or critical, or, # clear a low warning or critical. Check for # the reverse if the temperature is dropping. if ($old_variable_value eq "VANISHED") { ### NOTE: We don't (yet) check to see ### if the drive is overheating ### or freezing here. That ### would require new logic and ### is unlikely to be needed. # Temperature is back. $cleared = 1; $message_key = "scan_storcli_warning_0039"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared => $cleared, message_key => $message_key, }}); } elsif ($old_variable_value > $new_variable_value) { # Rising my $jumped = $new_variable_value - $old_variable_value; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { jumped => $jumped }}); if ($new_variable_value > $high_critical) { # Crossed the critical # threshold. See if this is # the first time. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $cachevault_serial_number.":".$variable."_high_critical", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first # time we rose above # the critical # threshold. $alert_level = "critical"; $message_key = "scan_storcli_note_0040"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } $temperature_state = "critical"; $temperature_is = "high"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { temperature_state => $temperature_state, temperature_is => $temperature_is, }}); } elsif ($new_variable_value > $high_warning) { # Crossed the warning # threshold. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $cachevault_serial_number.":".$variable."_high_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first # time we rose above # the critical # threshold. $alert_level = "warning"; $message_key = "scan_storcli_note_0041"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } $temperature_state = "warning"; $temperature_is = "high"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { temperature_state => $temperature_state, temperature_is => $temperature_is, }}); } elsif ($new_variable_value > $clear_low_warning) { # Risen into the clear, make # sure both warning and # critical are cleared. my $cleared_critical = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $cachevault_serial_number.":".$variable."_low_critical", set_by => $THIS_FILE}); my $cleared_warning = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $cachevault_serial_number.":".$variable."_low_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared_critical => $cleared_critical, cleared_warning => $cleared_warning, }}); if ($cleared_critical) { $cleared = 1; $alert_level = "warning"; $message_key = "scan_storcli_note_0042"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared => $cleared, alert_level => $alert_level, message_key => $message_key, }}); } elsif ($cleared_warning) { # The temperature has # dropped back down # to safe levels. $cleared = 1; $alert_level = "warning"; $message_key = "scan_storcli_note_0042"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared => $cleared, alert_level => $alert_level, message_key => $message_key, }}); } } elsif ($new_variable_value > $clear_low_critical) { # Risen above critical, but # not in the clear. my $cleared_critical = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $cachevault_serial_number.":".$variable."_low_critical", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared_critical => $cleared_critical }}); if ($cleared_critical) { # Set the warning. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $cachevault_serial_number.":".$variable."_low_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); $cleared = 1; $alert_level = "warning"; $message_key = "scan_storcli_note_0043"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared => $cleared, alert_level => $alert_level, message_key => $message_key, }}); } $temperature_state = "warning"; $temperature_is = "low"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { temperature_state => $temperature_state, temperature_is => $temperature_is, }}); } elsif ($jumped > $jump) { # The temperature jumped a # large amount. $alert_level = "warning"; $message_key = "scan_storcli_note_0044"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } } else { # Falling my $jumped = $old_variable_value - $new_variable_value; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { jumped => $jumped }}); if ($new_variable_value < $low_critical) { # Dropped below the critical # threshold. This should # always be unset because it # is a new variable, but # check anyway. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $cachevault_serial_number.":".$variable."_low_critical", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first # time we rose above # the critical # threshold. $alert_level = "critical"; $message_key = "scan_storcli_note_0045"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } $temperature_state = "critical"; $temperature_is = "low"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { temperature_state => $temperature_state, temperature_is => $temperature_is, }}); } elsif ($new_variable_value < $low_warning) { # Crossed the warning # threshold. This should # always be unset because it # is a new variable, but # check anyway. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $cachevault_serial_number.":".$variable."_low_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first # time we rose above # the critical # threshold. $alert_level = "warning"; $message_key = "scan_storcli_note_0046"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } $temperature_state = "warning"; $temperature_is = "low"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { temperature_state => $temperature_state, temperature_is => $temperature_is, }}); } elsif ($new_variable_value < $clear_high_warning) { # Dropped into the clear my $cleared_critical = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $cachevault_serial_number.":".$variable."_high_critical", set_by => $THIS_FILE}); my $cleared_warning = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $cachevault_serial_number.":".$variable."_high_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared_critical => $cleared_critical, cleared_warning => $cleared_warning, }}); if ($cleared_critical) { $cleared = 1; $alert_level = "warning"; $message_key = "scan_storcli_note_0047"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared => $cleared, alert_level => $alert_level, message_key => $message_key, }}); } elsif ($cleared_warning) { # The temperature has # dropped back down # to safe levels. $cleared = 1; $alert_level = "warning"; $message_key = "scan_storcli_note_0047"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared => $cleared, alert_level => $alert_level, message_key => $message_key, }}); } } elsif ($new_variable_value < $clear_high_critical) { # Dropped below critical, but # not in the clear. my $cleared_critical = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $cachevault_serial_number.":".$variable."_high_critical", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared_critical => $cleared_critical }}); if ($cleared_critical) { # Set the warning. my $changed = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $cachevault_serial_number.":".$variable."_high_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); $cleared = 1; $alert_level = "warning"; $message_key = "scan_storcli_note_0048"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared => $cleared, alert_level => $alert_level, message_key => $message_key, }}); } $temperature_state = "warning"; $temperature_is = "high"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { temperature_state => $temperature_state, temperature_is => $temperature_is, }}); } elsif ($jumped > $jump) { # The temperature dropped a # large amount. $alert_level = "warning"; $message_key = "scan_storcli_note_0049"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } } } # Record this for later processing into the # 'temperature' table. my $sensor_host_key = "cachevault:".$cachevault_serial_number; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { sensor_host_key => $sensor_host_key }}); $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_value_c} = $new_variable_value; $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_state} = $temperature_state; $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_is} = $temperature_is; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "new::temperature::${variable}::${sensor_host_key}::temperature_value_c" => $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_value_c}, "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}, }}); } # Now actually generate an alert and save the changes if something # changed. if (($new_variable_value ne $old_variable_value)) { # Changed! If the old value was 'VANISHED', then a # sensor or variable returned. Otherwise, for now, we # treat everything as 'warning' and step down # explicitely anything not of concern that proves # noisey later (better safe than sorry). $anvil->data->{'scan-storcli'}{alert_sort}++; if ($type ne "temperature") { if ($old_variable_value eq "VANISHED") { $message_key = "scan_storcli_warning_0039"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { message_key => $message_key }}); } # Check for some important issues #if ($variable =~ /memory_correctable_errors/) # Nothing seems to be of special interest... See: SELECT a.host_name, b.scan_storcli_cachevault_serial_number AS sn, b.scan_storcli_cachevault_type, scan_storcli_cachevault_model, c.scan_storcli_variable_name AS variable, c.scan_storcli_variable_value AS value, c.scan_storcli_variable_is_temperature AS temp, c.modified_date FROM hosts a, scan_storcli_cachevaults b, scan_storcli_variables c WHERE a.host_uuid = b.scan_storcli_cachevault_host_uuid AND b.scan_storcli_cachevault_uuid = c.scan_storcli_variable_source_uuid AND a.host_name = 'an-a07n01.alteeve.com'; } my $variables = { serial_number => $cachevault_serial_number, name => $variable, old_value => $old_variable_value ? $old_variable_value : "--", new_value => $new_variable_value, high_critical_temperature => $high_critical, high_warning_temperature => $high_warning, low_critical_temperature => $low_critical, low_warning_temperature => $low_warning, jump => $jump, }; my $log_level = (($alert_level eq "warning") or ($alert_level eq "critical")) ? 1 : 2; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $log_level, key => $message_key, variables => $variables}); $anvil->Alert->register({ clear_alert => $cleared, alert_level => $alert_level, message => $message_key, variables => $variables, show_header => $main_table_changed ? 0 : 1, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); my $query = " UPDATE scan_storcli_variables SET scan_storcli_variable_value = ".$anvil->Database->quote($new_variable_value).", modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." WHERE scan_storcli_variable_uuid = ".$anvil->Database->quote($variable_uuid)." ;"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } } else { # New variable, record it. This is a 'warning' level as # variables aren't expected to spawn into existence. my $variables = { serial_number => $cachevault_serial_number, name => $variable, value => $new_variable_value, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_storcli_warning_0041", variables => $variables}); $anvil->Alert->register({ alert_level => "warning", message => "scan_storcli_warning_0041", variables => $variables, show_header => $main_table_changed ? 0 : 1, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); # INSERT my $query = " INSERT INTO scan_storcli_variables ( scan_storcli_variable_uuid, scan_storcli_variable_host_uuid, scan_storcli_variable_source_table, scan_storcli_variable_source_uuid, scan_storcli_variable_is_temperature, scan_storcli_variable_name, scan_storcli_variable_value, modified_date ) VALUES ( ".$anvil->Database->quote($anvil->Get->uuid()).", ".$anvil->Database->quote($anvil->Get->host_uuid).", 'scan_storcli_cachevaults', ".$anvil->Database->quote($cachevault_uuid).", ".$anvil->Database->quote($temperature).", ".$anvil->Database->quote($variable).", ".$anvil->Database->quote($new_variable_value).", ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." );"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } } } # Now look for any variables left from the previous scan that we didn't match up (and # delete) this pass. foreach my $type ("variable", "temperature") { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { type => $type }}); foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_cachevaults'}{source_uuid}{$cachevault_uuid}{$type}}) { # This variable has vanished my $old_variable_value = $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_cachevaults'}{source_uuid}{$cachevault_uuid}{$type}{$variable}{scan_storcli_variable_value}; my $variable_uuid = $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_cachevaults'}{source_uuid}{$cachevault_uuid}{$type}{$variable}{scan_storcli_variable_uuid}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_variable_value => $old_variable_value, variable_uuid => $variable_uuid, }}); # Delete it so that we know it has been processed. delete $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_cachevaults'}{source_uuid}{$cachevault_uuid}{$type}{$variable}; # If the old value is already 'VANISHED', ignore it. next if $old_variable_value eq "VANISHED"; ### NOTE: For now, we're going to use warning level because variables ### shouldn't vanish, but under an-cm, it did happen for ### reasons that we never figured out. So later, we may drop ### the alert level in some cases. # Still here? Alert and UPDATE. my $variables = { serial_number => $cachevault_serial_number, name => $variable, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_storcli_warning_0040", variables => $variables}); $anvil->Alert->register({ clear_alert => 0, alert_level => "warning", message => "scan_storcli_warning_0040", variables => $variables, show_header => $main_table_changed ? 0 : 1, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); my $query = " UPDATE scan_storcli_variables SET scan_storcli_variable_value = 'VANISHED', modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." WHERE scan_storcli_variable_uuid = ".$anvil->Database->quote($variable_uuid)." ;"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } } } # Delete the Cachevault from the last scan so that we can find controllers that have been removed. if (exists $anvil->data->{sql}{scan_storcli_cachevaults}{scan_storcli_cachevault_uuid}{$cachevault_uuid}) { delete $anvil->data->{sql}{scan_storcli_cachevaults}{scan_storcli_cachevault_uuid}{$cachevault_uuid}; } } # See if any Cachevaults have vanished. foreach my $cachevault_uuid (keys %{$anvil->data->{sql}{scan_storcli_cachevaults}{scan_storcli_cachevault_uuid}}) { # Cachevault vanished! my $old_cachevault_serial_number = $anvil->data->{sql}{scan_storcli_cachevaults}{scan_storcli_cachevault_uuid}{$cachevault_uuid}{scan_storcli_cachevault_serial_number}; my $old_controller_uuid = $anvil->data->{sql}{scan_storcli_cachevaults}{scan_storcli_cachevault_uuid}{$cachevault_uuid}{scan_storcli_cachevault_controller_uuid}; my $old_state = $anvil->data->{sql}{scan_storcli_cachevaults}{scan_storcli_cachevault_uuid}{$cachevault_uuid}{scan_storcli_cachevault_state}; my $old_controller_serial_number = $anvil->data->{'scan-storcli'}{controllers}{by_uuid}{$old_controller_uuid}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_cachevault_serial_number => $old_cachevault_serial_number, old_controller_uuid => $old_controller_uuid, old_state => $old_state, old_controller_serial_number => $old_controller_serial_number, }}); # Delete it so that we know it has been processed. delete $anvil->data->{sql}{scan_storcli_cachevaults}{scan_storcli_cachevault_uuid}{$cachevault_uuid}; # If the old alarm state is already 'VANISHED', ignore it. next if $old_state eq "VANISHED"; # Still here? Alert and UPDATE. ### NOTE: For now, we're going to use warning level because controllers should never vanish ### unless one failed. If that is the case, the admin already knows, but this will let ### other notification targets know that the change has happened. my $variables = { cachevault_serial_number => $old_cachevault_serial_number, controller_serial_number => $old_controller_serial_number, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_storcli_warning_0042", variables => $variables}); $anvil->Alert->register({ clear_alert => 0, alert_level => "warning", message => "scan_storcli_warning_0042", variables => $variables, show_header => 1, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); my $query = " UPDATE scan_storcli_cachevaults SET scan_storcli_cachevault_state = 'VANISHED', modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." WHERE scan_storcli_cachevault_uuid = ".$anvil->Database->quote($cachevault_uuid)." ;"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } # Now commit the changes. $anvil->Database->write({query => $anvil->data->{'scan-storcli'}{queries}, source => $THIS_FILE, line => __LINE__}); $anvil->data->{'scan-storcli'}{queries} = []; return(0); } # Look for added, changed or deleted battery backup units (BBUs). sub process_bbus { my ($anvil) = @_; # Find new, changed and replaced BBUs. $anvil->data->{'scan-storcli'}{queries} = []; foreach my $bbu_serial_number (sort {$a cmp $b} keys %{$anvil->data->{bbu}{serial_number}}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bbu_serial_number => $bbu_serial_number }}); # Is this a new BBU? my $bbu_uuid = ""; my $is_new = 0; if (exists $anvil->data->{'scan-storcli'}{bbus}{by_serial}{$bbu_serial_number}) { # Yes! $bbu_uuid = $anvil->data->{'scan-storcli'}{bbus}{by_serial}{$bbu_serial_number}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bbu_uuid => $bbu_uuid }}); } else { # No, this is a new BBU. Create a new UUID for it. $bbu_uuid = $anvil->Get->uuid(); $is_new = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bbu_uuid => $bbu_uuid, is_new => $is_new, }}); # Add the keys for looking it up by UUID or serial number. $anvil->data->{'scan-storcli'}{bbus}{by_serial}{$bbu_serial_number} = $bbu_uuid; $anvil->data->{'scan-storcli'}{bbus}{by_uuid}{$bbu_uuid} = $bbu_serial_number; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "scan-storcli::bbus::by_serial::$bbu_serial_number" => $anvil->data->{'scan-storcli'}{bbus}{by_serial}{$bbu_serial_number}, "scan-storcli::bbus::by_uuid::$bbu_uuid" => $anvil->data->{'scan-storcli'}{bbus}{by_uuid}{$bbu_uuid}, }}); } # The temperature range of the BBU depends on whether it is charging or discharging. my $bbu_temperature_key = "normal"; # Get the charge state. # - None # - Discharging # - Charging my $charging = $anvil->data->{bbu}{serial_number}{$bbu_serial_number}{variable}{charging_status}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { charging => $charging }}); if ($charging =~ /^discharging$/i) { $bbu_temperature_key = "discharging"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bbu_temperature_key => $bbu_temperature_key }}); } # Setup our thermal thresholds my $high_critical = $anvil->data->{'scan-storcli'}{thresholds}{bbu}{$bbu_temperature_key}{high_critical}; my $high_warning = $anvil->data->{'scan-storcli'}{thresholds}{bbu}{$bbu_temperature_key}{high_warning}; my $low_warning = $anvil->data->{'scan-storcli'}{thresholds}{bbu}{$bbu_temperature_key}{low_warning}; my $low_critical = $anvil->data->{'scan-storcli'}{thresholds}{bbu}{$bbu_temperature_key}{low_critical}; my $jump = $anvil->data->{'scan-storcli'}{thresholds}{bbu}{$bbu_temperature_key}{jump}; my $buffer = $anvil->data->{'scan-storcli'}{thresholds}{bbu}{$bbu_temperature_key}{buffer}; my $clear_high_critical = $high_critical - $buffer; my $clear_high_warning = $high_warning - $buffer; my $clear_low_critical = $low_critical - $buffer; my $clear_low_warning = $low_warning - $buffer; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { high_critical => $high_critical, high_warning => $high_warning, low_warning => $low_warning, low_critical => $low_critical, jump => $jump, buffer => $buffer, clear_high_critical => $clear_high_critical, clear_high_warning => $clear_high_warning, clear_low_critical => $clear_low_critical, clear_low_warning => $clear_low_warning, }}); # Fine-tune the alert thresholds if ($clear_high_critical < $high_warning) { $clear_high_critical = $high_warning + 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { clear_high_critical => $clear_high_critical }}); } if ($clear_low_critical > $low_warning) { $clear_low_critical = $low_warning - 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { clear_low_critical => $clear_low_critical }}); } # First up, get the controller serial number and translate that to a controller_uuid. if (not $anvil->data->{bbu}{serial_number}{$bbu_serial_number}{host_controller_serial_number}) { # Error! $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "scan_storcli_error_0007", variables => { serial_number => $bbu_serial_number }}); $anvil->nice_exit({exit_code => 7}); } my $host_controller_serial_number = $anvil->data->{bbu}{serial_number}{$bbu_serial_number}{host_controller_serial_number}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_controller_serial_number => $host_controller_serial_number }}); if (not $anvil->data->{'scan-storcli'}{controllers}{by_serial}{$host_controller_serial_number}) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "scan_storcli_error_0008", variables => { serial_number => $host_controller_serial_number }}); $anvil->nice_exit({exit_code => 8}); } my $controller_uuid = $anvil->data->{'scan-storcli'}{controllers}{by_serial}{$host_controller_serial_number}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { controller_uuid => $controller_uuid }}); # These are the values for the main table. Anything else will go in the variables table. my $new_type = ""; my $new_model = ""; my $new_state = ""; my $new_manufacture_date = ""; my $new_design_capacity = ""; my $new_replacement_needed = ""; foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{bbu}{serial_number}{$bbu_serial_number}{variable}}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable }}); # Store and delete the value if ($variable eq "type") { $new_type = delete $anvil->data->{bbu}{serial_number}{$bbu_serial_number}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_type => $new_type }}); next; } if ($variable eq "manufacture_name") { $new_model = delete $anvil->data->{bbu}{serial_number}{$bbu_serial_number}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_model => $new_model }}); next; } if ($variable eq "battery_state") { $new_state = delete $anvil->data->{bbu}{serial_number}{$bbu_serial_number}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_model => $new_model }}); next; } if ($variable eq "date_of_manufacture") { $new_manufacture_date = delete $anvil->data->{bbu}{serial_number}{$bbu_serial_number}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_manufacture_date => $new_manufacture_date }}); next; } if ($variable eq "design_capacity") { $new_design_capacity = delete $anvil->data->{bbu}{serial_number}{$bbu_serial_number}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_design_capacity => $new_design_capacity }}); next; } if ($variable eq "replacement_required") { $new_replacement_needed = delete $anvil->data->{bbu}{serial_number}{$bbu_serial_number}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_replacement_needed => $new_replacement_needed }}); next; } } # Pull out the rest of the variables now. If the BBU is new, all variables will be INSERTed. # If the BBU exists, each variable will be examined and new ones will be INSERTed, existing # ones will be checked for changes and UPDATEd as needed. If the BBU is NOT new, then # variables from the old data will be deleted as we go and any not found in the current data # set will be left over. We'll use this to determine variables that have vanished. They will # not be deleted, but their value will be set to 'VANISHED'. if ($is_new) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bbu_uuid => $bbu_uuid, controller_uuid => $controller_uuid, bbu_serial_number => $bbu_serial_number, new_type => $new_type, new_model => $new_model, new_state => $new_state, new_design_capacity => $new_design_capacity, new_manufacture_date => $new_manufacture_date, new_replacement_needed => $new_replacement_needed, }}); ### NOTE: The rest of the alerts will be in the format '- Variable: [$value]'. # Send an alert telling the user that we've found a new controller. my $variables = { on_controller => $host_controller_serial_number, serial_number => $bbu_serial_number, type => $new_type, model => $new_model, 'state' => $new_state, design_capacity => $new_design_capacity, manufacture_date => $new_manufacture_date, replacement_needed => $new_replacement_needed, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "scan_storcli_note_0003", variables => $variables}); $anvil->Alert->register({ alert_level => "notice", message => "scan_storcli_note_0003", variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); # INSERT my $query = " INSERT INTO scan_storcli_bbus ( scan_storcli_bbu_uuid, scan_storcli_bbu_host_uuid, scan_storcli_bbu_controller_uuid, scan_storcli_bbu_serial_number, scan_storcli_bbu_type, scan_storcli_bbu_model, scan_storcli_bbu_state, scan_storcli_bbu_design_capacity, scan_storcli_bbu_manufacture_date, scan_storcli_bbu_replacement_needed, modified_date ) VALUES ( ".$anvil->Database->quote($bbu_uuid).", ".$anvil->Database->quote($anvil->Get->host_uuid).", ".$anvil->Database->quote($controller_uuid).", ".$anvil->Database->quote($bbu_serial_number).", ".$anvil->Database->quote($new_type).", ".$anvil->Database->quote($new_model).", ".$anvil->Database->quote($new_state).", ".$anvil->Database->quote($new_design_capacity).", ".$anvil->Database->quote($new_manufacture_date).", ".$anvil->Database->quote($new_replacement_needed).", ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." );"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; # Process the rest of the variables and temperatures now. foreach my $type ("variable", "temperature") { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { type => $type }}); foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{bbu}{serial_number}{$bbu_serial_number}{$type}}) { my $value = delete $anvil->data->{bbu}{serial_number}{$bbu_serial_number}{$type}{$variable}; my $temperature = $type eq "temperature" ? "TRUE" : "FALSE"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, temperature => $temperature, }}); my $message_key = "scan_storcli_note_0002"; my $alert_level = "notice"; my $temperature_state = "ok"; my $temperature_is = "nominal"; if ($type eq "temperature") { # This is a temperature, so see if the temperature outside of # the warning or critical thresholds. This is a new sensor, # so nothing to compare against. my $temperature_state = "ok"; my $temperature_is = "nominal"; if ($value > $high_critical) { # Crossed the critical threshold. This should # always be unset because it is a new # variable, but check anyway. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $bbu_serial_number.":".$variable."_high_critical", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first time we rose above the # critical threshold. $alert_level = "critical"; $message_key = "scan_storcli_note_0050"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } } elsif ($value > $high_warning) { # Crossed the warning threshold. This should always # be unset because it is a new variable, but check # anyway. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $bbu_serial_number.":".$variable."_high_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first time we rose above the # critical threshold. $alert_level = "warning"; $message_key = "scan_storcli_note_0051"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } } elsif ($value < $low_critical) { # Dropped below the critical threshold. This should # always be unset because it is a new variable, but # check anyway. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $bbu_serial_number.":".$variable."_low_critical", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first time we rose above the # critical threshold. $alert_level = "critical"; $message_key = "scan_storcli_note_0052"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } } elsif ($value < $low_warning) { # Crossed the warning threshold. This should always be unset # because it is a new variable, but check anyway. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $bbu_serial_number.":".$variable."_low_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first time we rose above the critical # threshold. $alert_level = "warning"; $message_key = "scan_storcli_note_0053"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } } # Record this for later processing into the 'temperature' table. my $sensor_host_key = "bbu:".$bbu_serial_number; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { sensor_host_key => $sensor_host_key }}); $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_value_c} = $value; $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_state} = $temperature_state; $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_is} = $temperature_is; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "new::temperature::${variable}::${sensor_host_key}::temperature_value_c" => $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_value_c}, "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}, }}); } # Send an alert telling the user that we've found a new controller. my $variables = { serial_number => $bbu_serial_number, name => $variable, value => $value, high_critical_temperature => $high_critical, high_warning_temperature => $high_warning, low_critical_temperature => $low_critical, low_warning_temperature => $low_warning, }; my $log_level = (($alert_level eq "warning") or ($alert_level eq "critical")) ? 1 : 2; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $log_level, key => $message_key, variables => $variables}); $anvil->Alert->register({ clear_alert => 0, alert_level => $alert_level, message => $message_key, variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); # INSERT my $query = " INSERT INTO scan_storcli_variables ( scan_storcli_variable_uuid, scan_storcli_variable_host_uuid, scan_storcli_variable_source_table, scan_storcli_variable_source_uuid, scan_storcli_variable_is_temperature, scan_storcli_variable_name, scan_storcli_variable_value, modified_date ) VALUES ( ".$anvil->Database->quote($anvil->Get->uuid()).", ".$anvil->Database->quote($anvil->Get->host_uuid).", 'scan_storcli_bbus', ".$anvil->Database->quote($bbu_uuid).", ".$anvil->Database->quote($temperature).", ".$anvil->Database->quote($variable).", ".$anvil->Database->quote($value).", ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." );"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } } } else { ### NOTE: The serial number should never change (a changed SN/BBU should be picked up ### as a new BBU), but we check/update just to be safe. # Look for changes. my $main_table_changed = 0; my $old_bbu_serial_number = $anvil->data->{sql}{scan_storcli_bbus}{scan_storcli_bbu_uuid}{$bbu_uuid}{scan_storcli_bbu_serial_number}; my $old_controller_uuid = $anvil->data->{sql}{scan_storcli_bbus}{scan_storcli_bbu_uuid}{$bbu_uuid}{scan_storcli_bbu_controller_uuid}; my $old_type = $anvil->data->{sql}{scan_storcli_bbus}{scan_storcli_bbu_uuid}{$bbu_uuid}{scan_storcli_bbu_type}; my $old_model = $anvil->data->{sql}{scan_storcli_bbus}{scan_storcli_bbu_uuid}{$bbu_uuid}{scan_storcli_bbu_model}; my $old_state = $anvil->data->{sql}{scan_storcli_bbus}{scan_storcli_bbu_uuid}{$bbu_uuid}{scan_storcli_bbu_state}; my $old_design_capacity = $anvil->data->{sql}{scan_storcli_bbus}{scan_storcli_bbu_uuid}{$bbu_uuid}{scan_storcli_bbu_design_capacity}; my $old_manufacture_date = $anvil->data->{sql}{scan_storcli_bbus}{scan_storcli_bbu_uuid}{$bbu_uuid}{scan_storcli_bbu_manufacture_date}; my $old_replacement_needed = $anvil->data->{sql}{scan_storcli_bbus}{scan_storcli_bbu_uuid}{$bbu_uuid}{scan_storcli_bbu_replacement_needed}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bbu_serial_number => $bbu_serial_number, old_bbu_serial_number => $old_bbu_serial_number, controller_uuid => $controller_uuid, old_controller_uuid => $old_controller_uuid, new_type => $new_type, old_type => $old_type, new_model => $new_model, old_model => $old_model, new_state => $new_state, old_state => $old_state, new_design_capacity => $new_design_capacity, old_design_capacity => $old_design_capacity, new_manufacture_date => $new_manufacture_date, old_manufacture_date => $old_manufacture_date, new_replacement_needed => $new_replacement_needed, old_replacement_needed => $old_replacement_needed, }}); if (($bbu_serial_number ne $old_bbu_serial_number) or ($controller_uuid ne $old_controller_uuid) or ($new_type ne $old_type) or ($new_model ne $old_model) or ($new_state ne $old_state) or ($new_design_capacity ne $old_design_capacity) or ($new_manufacture_date ne $old_manufacture_date) or ($new_replacement_needed ne $old_replacement_needed)) { # I need the serial numbers for the controller UUIDs. my $new_controller_serial_number = $anvil->data->{'scan-storcli'}{controllers}{by_uuid}{$controller_uuid}; my $old_controller_serial_number = $anvil->data->{'scan-storcli'}{controllers}{by_uuid}{$old_controller_uuid}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_controller_serial_number => $new_controller_serial_number, old_controller_serial_number => $old_controller_serial_number, }}); # Send a warning level alert because the most likely change is 'state' or # 'replacement_needed'. my $cleared = 0; my $message_key = "scan_storcli_warning_0010"; if ($new_state ne $old_state) { if ($old_state eq "VANISHED") { # BBU has returned. $message_key = "scan_storcli_warning_0011"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { message_key => $message_key }}); } if ($new_state =~ /optimal/i) { $cleared = 1; } } my $variables = { new_on_controller => $new_controller_serial_number, old_on_controller => $old_controller_serial_number, new_model => $new_model, old_model => $old_model, new_serial_number => $bbu_serial_number, old_serial_number => $old_bbu_serial_number, new_type => $new_type, old_type => $old_type, new_state => $new_state, old_state => $old_state, new_design_capacity => $new_design_capacity, old_design_capacity => $old_design_capacity, new_manufacture_date => $new_manufacture_date, old_manufacture_date => $old_manufacture_date, new_replacement_needed => $new_replacement_needed, old_replacement_needed => $old_replacement_needed, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => $message_key, variables => $variables}); $anvil->Alert->register({ alert_level => "warning", clear_alert => $cleared, message => $message_key, variables => $variables, show_header => 0, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); $main_table_changed = 1; my $query = " UPDATE scan_storcli_bbus SET scan_storcli_bbu_controller_uuid = ".$anvil->Database->quote($controller_uuid).", scan_storcli_bbu_serial_number = ".$anvil->Database->quote($bbu_serial_number).", scan_storcli_bbu_type = ".$anvil->Database->quote($new_type).", scan_storcli_bbu_model = ".$anvil->Database->quote($new_model).", scan_storcli_bbu_state = ".$anvil->Database->quote($new_state).", scan_storcli_bbu_manufacture_date = ".$anvil->Database->quote($new_manufacture_date).", scan_storcli_bbu_design_capacity = ".$anvil->Database->quote($new_design_capacity).", scan_storcli_bbu_replacement_needed = ".$anvil->Database->quote($new_replacement_needed).", modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." WHERE scan_storcli_bbu_uuid = ".$anvil->Database->quote($bbu_uuid)." ;"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } ### Process the rest of the variables now. foreach my $type ("variable", "temperature") { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { type => $type }}); foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{bbu}{serial_number}{$bbu_serial_number}{$type}}) { my $new_variable_value = delete $anvil->data->{bbu}{serial_number}{$bbu_serial_number}{$type}{$variable}; my $temperature = $type eq "temperature" ? "TRUE" : "FALSE"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, temperature => $temperature, }}); if (exists $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_bbus'}{source_uuid}{$bbu_uuid}{$type}{$variable}{scan_storcli_variable_uuid}) { # Look for changes my $old_variable_value = $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_bbus'}{source_uuid}{$bbu_uuid}{$type}{$variable}{scan_storcli_variable_value}; my $variable_uuid = $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_bbus'}{source_uuid}{$bbu_uuid}{$type}{$variable}{scan_storcli_variable_uuid}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_variable_value => $new_variable_value, old_variable_value => $old_variable_value, variable_uuid => $variable_uuid, }}); # Delete it so that we know it has been processed. delete $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_bbus'}{source_uuid}{$bbu_uuid}{$type}{$variable}; # If there is no change, I still want to record the # temperature (where applicable). So I setup like I will send # an alert from the start, but will only actually send if # something has changed. my $cleared = 0; my $message_key = "scan_storcli_warning_0012"; my $alert_level = "info"; my $temperature_state = "ok"; my $temperature_is = "nominal"; if ($type eq "temperature") { # It's a temperature, so change the default message # and drop the default alert level. $message_key = "scan_storcli_note_0066"; $alert_level = "info"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { message_key => $message_key, alert_level => $alert_level, }}); if (($new_variable_value ne $old_variable_value)) { # If the temperature is rising, see if we # need to set a high warning or critical, or, # clear a low warning or critical. Check for # the reverse if the temperature is dropping. if ($old_variable_value eq "VANISHED") { ### NOTE: We don't (yet) check to see ### if the drive is overheating ### or freezing here. That ### would require new logic and ### is unlikely to be needed. # Temperature is back. $message_key = "scan_storcli_warning_0013"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { message_key => $message_key }}); } elsif ($new_variable_value > $old_variable_value) { # Rising my $jumped = $new_variable_value - $old_variable_value; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { jumped => $jumped }}); if ($new_variable_value > $high_critical) { # Crossed the critical # threshold. See if this is # the first time. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $bbu_serial_number.":".$variable."_high_critical", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first # time we rose above # the critical # threshold. $alert_level = "critical"; $message_key = "scan_storcli_note_0054"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { message_key => $message_key, alert_level => $alert_level, }}); } $temperature_state = "critical"; $temperature_is = "high"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { temperature_state => $temperature_state, temperature_is => $temperature_is, }}); } elsif ($new_variable_value > $high_warning) { # Crossed the warning # threshold. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $bbu_serial_number.":".$variable."_high_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first # time we rose above # the critical # threshold. $alert_level = "warning"; $message_key = "scan_storcli_note_0055"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { message_key => $message_key, alert_level => $alert_level, }}); } $temperature_state = "warning"; $temperature_is = "high"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "temperature_state" => $temperature_state, "temperature_is" => $temperature_is, }}); } elsif ($new_variable_value > $clear_low_warning) { # Risen into the clear, make # sure both warning and # critical are cleared. my $cleared_critical = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $bbu_serial_number.":".$variable."_low_critical", set_by => $THIS_FILE}); my $cleared_warning = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $bbu_serial_number.":".$variable."_low_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared_critical => $cleared_critical, cleared_warning => $cleared_warning, }}); if ($cleared_critical) { $cleared = 1; $alert_level = "warning"; $message_key = "scan_storcli_note_0056"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared => $cleared, message_key => $message_key, alert_level => $alert_level, }}); } elsif ($cleared_warning) { # The temperature has # dropped back down # to safe levels. $cleared = 1; $alert_level = "warning"; $message_key = "scan_storcli_note_0056"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared => $cleared, message_key => $message_key, alert_level => $alert_level, }}); } } elsif ($new_variable_value > $clear_low_critical) { # Risen above critical, but # not in the clear. my $cleared_critical = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $bbu_serial_number.":".$variable."_low_critical", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared_critical => $cleared_critical }}); if ($cleared_critical) { # Set the warning. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $bbu_serial_number.":".$variable."_low_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); $cleared = 1; $alert_level = "warning"; $message_key = "scan_storcli_note_0057"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared => $cleared, message_key => $message_key, alert_level => $alert_level, }}); } $temperature_state = "warning"; $temperature_is = "low"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { temperature_state => $temperature_state, temperature_is => $temperature_is, }}); } elsif ($jumped > $jump) { # The temperature jumped a # large amount. $alert_level = "warning"; $message_key = "scan_storcli_note_0058"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { message_key => $message_key, alert_level => $alert_level, }}); } } else { # Falling my $jumped = $old_variable_value - $new_variable_value; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { jumped => $jumped }}); if ($new_variable_value < $low_critical) { # Dropped below the critical # threshold. This should # always be unset because it # is a new variable, but # check anyway. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $bbu_serial_number.":".$variable."_low_critical", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first # time we rose above # the critical # threshold. $alert_level = "critical"; $message_key = "scan_storcli_note_0059"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { message_key => $message_key, alert_level => $alert_level, }}); } $temperature_state = "critical"; $temperature_is = "low"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { temperature_state => $temperature_state, temperature_is => $temperature_is, }}); } elsif ($new_variable_value < $low_warning) { # Crossed the warning # threshold. This should # always be unset because it # is a new variable, but # check anyway. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $bbu_serial_number.":".$variable."_low_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first # time we rose above # the critical # threshold. $alert_level = "warning"; $message_key = "scan_storcli_note_0060"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { message_key => $message_key, alert_level => $alert_level, }}); } $temperature_state = "warning"; $temperature_is = "low"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { temperature_state => $temperature_state, temperature_is => $temperature_is, }}); } elsif ($new_variable_value < $clear_high_warning) { # Dropped into the clear my $cleared_critical = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $bbu_serial_number.":".$variable."_high_critical", set_by => $THIS_FILE}); my $cleared_warning = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $bbu_serial_number.":".$variable."_high_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared_critical => $cleared_critical, cleared_warning => $cleared_warning, }}); if ($cleared_critical) { $cleared = 1; $alert_level = "warning"; $message_key = "scan_storcli_note_0061"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared => $cleared, message_key => $message_key, alert_level => $alert_level, }}); } elsif ($cleared_warning) { # The temperature has # dropped back down # to safe levels. $cleared = 1; $alert_level = "warning"; $message_key = "scan_storcli_note_0061"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared => $cleared, message_key => $message_key, alert_level => $alert_level, }}); } } elsif ($new_variable_value < $clear_high_critical) { # Dropped below critical, but # not in the clear. my $cleared_critical = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $bbu_serial_number.":".$variable."_high_critical", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared_critical => $cleared_critical }}); if ($cleared_critical) { # Set the warning. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $bbu_serial_number.":".$variable."_high_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); $cleared = 1; $alert_level = "warning"; $message_key = "scan_storcli_note_0062"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared => $cleared, message_key => $message_key, alert_level => $alert_level, }}); } $temperature_state = "warning"; $temperature_is = "high"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { temperature_state => $temperature_state, temperature_is => $temperature_is, }}); } elsif ($jumped > $jump) { # The temperature dropped a # large amount. $alert_level = "warning"; $message_key = "scan_storcli_note_0063"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { message_key => $message_key, alert_level => $alert_level, }}); } } } # Record this for later processing into the # 'temperature' table. my $sensor_host_key = "bbu:$bbu_serial_number"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { sensor_host_key => $sensor_host_key }}); $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_value_c} = $new_variable_value; $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_state} = $temperature_state; $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_is} = $temperature_is; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "new::temperature::${variable}::${sensor_host_key}::temperature_value_c" => $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_value_c}, "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}, }}); } # Now actually generate an alert and save the changes if something # changed. if (($new_variable_value ne $old_variable_value)) { # Changed! If the old value was 'VANISHED', then a # sensor or variable returned. Otherwise, for now, we # treat everything as 'warning' and step down # explicitely anything not of concern that proves # noisey later (better safe than sorry). if ($type ne "temperature") { if ($old_variable_value eq "VANISHED") { $message_key = "scan_storcli_warning_0013"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { message_key => $message_key }}); } # Check for some important issues if ($variable =~ /over_charged/) { $alert_level = "critical"; $message_key = "scan_storcli_warning_0049"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { message_key => $message_key, alert_level => $alert_level, }}); } if ($variable =~ /over_temperature/) { $alert_level = "warning"; $message_key = "scan_storcli_warning_0050"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { message_key => $message_key, alert_level => $alert_level, }}); } if ($variable =~ /pack_is_about_to_fail_and_should_be_replaced/) { $alert_level = "warning"; $message_key = "scan_storcli_warning_0051"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { message_key => $message_key, alert_level => $alert_level, }}); } # Other things that might be of interest... See: SELECT a.host_name, b.scan_storcli_bbu_serial_number AS sn, b.scan_storcli_bbu_type, b.scan_storcli_bbu_model, c.scan_storcli_variable_name AS variable, c.scan_storcli_variable_value AS value, c.scan_storcli_variable_is_temperature AS temp, c.modified_date FROM hosts a, scan_storcli_bbus b, scan_storcli_variables c WHERE a.host_uuid = b.scan_storcli_bbu_host_uuid AND b.scan_storcli_bbu_uuid = c.scan_storcli_variable_source_uuid AND a.host_name = 'an-a05n01.alteeve.com'; } my $variables = { serial_number => $bbu_serial_number, name => $variable, old_value => $old_variable_value ? $old_variable_value : "--", new_value => $new_variable_value, high_critical_temperature => $high_critical, high_warning_temperature => $high_warning, low_critical_temperature => $low_critical, low_warning_temperature => $low_warning, jump => $jump, }; my $show_header = $main_table_changed ? 0 : 1; my $log_level = (($alert_level eq "warning") or ($alert_level eq "critical")) ? 1 : 2; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $log_level, key => $message_key, variables => $variables}); $anvil->Alert->register({ clear_alert => $cleared, alert_level => $alert_level, message => $message_key, variables => $variables, show_header => $show_header, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); my $query = " UPDATE scan_storcli_variables SET scan_storcli_variable_value = ".$anvil->Database->quote($new_variable_value).", modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." WHERE scan_storcli_variable_uuid = ".$anvil->Database->quote($variable_uuid)." ;"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } } else { # New variable, record it. This is a 'warning' level as # variables aren't expected to spawn into existence. my $variables = { serial_number => $bbu_serial_number, name => $variable, value => $new_variable_value, }; my $show_header = $main_table_changed ? 0 : 1; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "scan_storcli_warning_0015", variables => $variables}); $anvil->Alert->register({ alert_level => "warning", message => "scan_storcli_warning_0015", variables => $variables, show_header => $show_header, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); # INSERT my $query = " INSERT INTO scan_storcli_variables ( scan_storcli_variable_uuid, scan_storcli_variable_host_uuid, scan_storcli_variable_source_table, scan_storcli_variable_source_uuid, scan_storcli_variable_is_temperature, scan_storcli_variable_name, scan_storcli_variable_value, modified_date ) VALUES ( ".$anvil->Database->quote($anvil->Get->uuid()).", ".$anvil->Database->quote($anvil->Get->host_uuid).", 'scan_storcli_bbus', ".$anvil->Database->quote($bbu_uuid).", ".$anvil->Database->quote($temperature).", ".$anvil->Database->quote($variable).", ".$anvil->Database->quote($new_variable_value).", ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." );"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } } } # Now look for any variables left from the previous scan that we didn't match up (and # delete) this pass. foreach my $type ("variable", "temperature") { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { type => $type }}); foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_bbus'}{source_uuid}{$bbu_uuid}{$type}}) { # This variable has vanished my $old_variable_value = $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_bbus'}{source_uuid}{$bbu_uuid}{$type}{$variable}{scan_storcli_variable_value}; my $variable_uuid = $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_bbus'}{source_uuid}{$bbu_uuid}{$type}{$variable}{scan_storcli_variable_uuid}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_variable_value => $old_variable_value, variable_uuid => $variable_uuid, }}); # Delete it so that we know it has been processed. delete $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_bbus'}{source_uuid}{$bbu_uuid}{$type}{$variable}; # If the old value is already 'VANISHED', ignore it. next if $old_variable_value eq "VANISHED"; ### NOTE: For now, we're going to use warning level because variables ### shouldn't vanish, but under an-cm, it did happen for ### reasons that we never figured out. So later, we may drop ### the alert level in some cases. # Still here? Alert and UPDATE. my $variables = { serial_number => $bbu_serial_number, name => $variable, }; my $show_header = $main_table_changed ? 0 : 1; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "scan_storcli_warning_0014", variables => $variables}); $anvil->Alert->register({ alert_level => "warning", message => "scan_storcli_warning_0014", variables => $variables, show_header => $show_header, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); my $query = " UPDATE scan_storcli_variables SET scan_storcli_variable_value = 'VANISHED', modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." WHERE scan_storcli_variable_uuid = ".$anvil->Database->quote($variable_uuid)." ;"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } } } # Delete the BBU from the last scan so that we can find controllers that have been removed. if (exists $anvil->data->{sql}{scan_storcli_bbus}{scan_storcli_bbu_uuid}{$bbu_uuid}) { delete $anvil->data->{sql}{scan_storcli_bbus}{scan_storcli_bbu_uuid}{$bbu_uuid}; } } # See if any BBUs have vanished. foreach my $bbu_uuid (keys %{$anvil->data->{sql}{scan_storcli_bbus}{scan_storcli_bbu_uuid}}) { # BBU vanished! my $old_bbu_serial_number = $anvil->data->{sql}{scan_storcli_bbus}{scan_storcli_bbu_uuid}{$bbu_uuid}{scan_storcli_bbu_serial_number}; my $old_controller_uuid = $anvil->data->{sql}{scan_storcli_bbus}{scan_storcli_bbu_uuid}{$bbu_uuid}{scan_storcli_bbu_controller_uuid}; my $old_state = $anvil->data->{sql}{scan_storcli_bbus}{scan_storcli_bbu_uuid}{$bbu_uuid}{scan_storcli_bbu_state}; my $old_controller_serial_number = $anvil->data->{'scan-storcli'}{controllers}{by_uuid}{$old_controller_uuid}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_bbu_serial_number => $old_bbu_serial_number, old_controller_uuid => $old_controller_uuid, old_state => $old_state, old_controller_serial_number => $old_controller_serial_number, }}); # Delete it so that we know it has been processed. delete $anvil->data->{sql}{scan_storcli_bbus}{scan_storcli_bbu_uuid}{$bbu_uuid}; # If the old alarm state is already 'VANISHED', ignore it. next if $old_state eq "VANISHED"; # Still here? Alert and UPDATE. ### NOTE: For now, we're going to use warning level because controllers should never vanish ### unless one failed. If that is the case, the admin already knows, but this will let ### other notification targets know that the change has happened. my $variables = { bbu_serial_number => $old_bbu_serial_number, controller_serial_number => $old_controller_serial_number, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_storcli_warning_0016", variables => $variables}); $anvil->Alert->register({ alert_level => "warning", message => "scan_storcli_warning_0016", variables => $variables, show_header => 1, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); my $query = " UPDATE scan_storcli_bbus SET scan_storcli_bbu_state = 'VANISHED', modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." WHERE scan_storcli_bbu_uuid = ".$anvil->Database->quote($bbu_uuid)." ;"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } # Now commit the changes. $anvil->Database->write({query => $anvil->data->{'scan-storcli'}{queries}, source => $THIS_FILE, line => __LINE__}); $anvil->data->{'scan-storcli'}{queries} = []; return(0); } # Look for added, changed or deleted controllers. sub process_controllers { my ($anvil) = @_; # Setup our thermal thresholds my $high_critical = $anvil->data->{'scan-storcli'}{thresholds}{raid_on_chip}{high_critical}; my $high_warning = $anvil->data->{'scan-storcli'}{thresholds}{raid_on_chip}{high_warning}; my $low_warning = $anvil->data->{'scan-storcli'}{thresholds}{raid_on_chip}{low_warning}; my $low_critical = $anvil->data->{'scan-storcli'}{thresholds}{raid_on_chip}{low_critical}; my $jump = $anvil->data->{'scan-storcli'}{thresholds}{raid_on_chip}{jump}; my $buffer = $anvil->data->{'scan-storcli'}{thresholds}{raid_on_chip}{buffer}; my $clear_high_critical = $high_critical - $buffer; my $clear_high_warning = $high_warning - $buffer; my $clear_low_critical = $low_critical - $buffer; my $clear_low_warning = $low_warning - $buffer; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { high_critical => $high_critical, high_warning => $high_warning, low_warning => $low_warning, low_critical => $low_critical, jump => $jump, buffer => $buffer, clear_high_critical => $clear_high_critical, clear_high_warning => $clear_high_warning, clear_low_critical => $clear_low_critical, clear_low_warning => $clear_low_warning, }}); # Fine-tune the alert thresholds if ($clear_high_critical < $high_warning) { $clear_high_critical = $high_warning + 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { clear_high_critical => $clear_high_critical }}); } if ($clear_low_critical > $low_warning) { $clear_low_critical = $low_warning - 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { clear_low_critical => $clear_low_critical }}); } # Look for new, changed or deleted controllers. $anvil->data->{'scan-storcli'}{queries} = []; foreach my $serial_number (sort {$a cmp $b} keys %{$anvil->data->{controller}{serial_number}}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { serial_number => $serial_number, "scan-storcli::controllers::by_serial::$serial_number" => $anvil->data->{'scan-storcli'}{controllers}{by_serial}{$serial_number}, }}); # Is this controller in the database yet? my $controller_uuid = ""; my $is_new = 0; if ($anvil->data->{'scan-storcli'}{controllers}{by_serial}{$serial_number}) { # Yes! $controller_uuid = $anvil->data->{'scan-storcli'}{controllers}{by_serial}{$serial_number}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { controller_uuid => $controller_uuid }}); } else { # No, this is a new controller. Create a new UUID for it. $controller_uuid = $anvil->Get->uuid(); $is_new = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { controller_uuid => $controller_uuid, is_new => $is_new, }}); # Add the keys for looking it up by UUID or serial number. $anvil->data->{'scan-storcli'}{controllers}{by_serial}{$serial_number} = $controller_uuid; $anvil->data->{'scan-storcli'}{controllers}{by_uuid}{$controller_uuid} = $serial_number; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "scan-storcli::controllers::by_serial::$serial_number" => $anvil->data->{'scan-storcli'}{controllers}{by_serial}{$serial_number}, "scan-storcli::controllers::by_uuid::$controller_uuid" => $anvil->data->{'scan-storcli'}{controllers}{by_uuid}{$controller_uuid}, }}); } # These are the values for the main table. Anything else will go in the variables table which # will be processed after the controller. my $new_model = ""; my $new_alarm_state = ""; my $new_cache_size = ""; foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{controller}{serial_number}{$serial_number}{variable}}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable }}); if ($variable eq "model") { # Store and delete the value $new_model = delete $anvil->data->{controller}{serial_number}{$serial_number}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_model => $new_model }}); next; } elsif ($variable eq "alarm_state") { # Store and delete the value $new_alarm_state = delete $anvil->data->{controller}{serial_number}{$serial_number}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_alarm_state => $new_alarm_state }}); next; } elsif ($variable eq "on_board_memory_size") { # Store and delete the value $new_cache_size = delete $anvil->data->{controller}{serial_number}{$serial_number}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_cache_size => $new_cache_size }}); next; } } # Pull out the rest of the variables now. If the controller is new, all variables will be # INSERTed. If the controller exists, each variable will be examined and new ones will be # INSERTed, existing ones will be checked for changes and UPDATEd as needed. If the # controller is NOT new, then variables from the old data will be deleted as we go and any # not found in the current data set will be left over. We'll use this to determine variables # that have vanished. They will not be deleted, but their value will be set to 'VANISHED'. if ($is_new) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { serial_number => $serial_number, new_model => $new_model, new_alarm_state => $new_alarm_state, new_cache_size => $new_cache_size, }}); ### NOTE: The rest of the alerts will be in the format '- Variable: [$value]'. # Send an alert telling the user that we've found a new controller. my $variables = { model => $new_model, serial_number => $serial_number, say_cache_size => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_cache_size}), alarm_state => $new_alarm_state, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_storcli_note_0001", variables => $variables}); $anvil->Alert->register({ alert_level => "notice", message => "scan_storcli_note_0001", variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); # INSERT my $query = " INSERT INTO scan_storcli_controllers ( scan_storcli_controller_host_uuid, scan_storcli_controller_uuid, scan_storcli_controller_serial_number, scan_storcli_controller_model, scan_storcli_controller_alarm_state, scan_storcli_controller_cache_size, modified_date ) VALUES ( ".$anvil->Database->quote($anvil->Get->host_uuid).", ".$anvil->Database->quote($controller_uuid).", ".$anvil->Database->quote($serial_number).", ".$anvil->Database->quote($new_model).", ".$anvil->Database->quote($new_alarm_state).", ".$anvil->Database->quote($new_cache_size).", ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." );"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; # Process the rest of the variables and temperatures now. foreach my $type ("variable", "temperature") { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { type => $type }}); foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{controller}{serial_number}{$serial_number}{$type}}) { my $value = delete $anvil->data->{controller}{serial_number}{$serial_number}{$type}{$variable}; my $temperature = $type eq "temperature" ? "TRUE" : "FALSE"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, temperature => $temperature, }}); my $message_key = "scan_storcli_note_0002"; my $alert_level = "notice"; my $temperature_state = "ok"; my $temperature_is = "nominal"; if ($type eq "temperature") { # This is a temperature, so see if the temperature outside of # the warning or critical thresholds. This is a new sensor, # so nothing to compare against. my $temperature_state = "ok"; my $temperature_is = "nominal"; if ($value > $high_critical) { # Crossed the high critical threshold. This should # always be unset because it is a new variable, but # check anyway. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $serial_number.":".$variable."_high_critical", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { $alert_level = "critical"; $message_key = "scan_storcli_note_0022"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } } elsif ($value > $high_warning) { # Crossed the high warning threshold. This should # always be unset because it is a new variable, but # check anyway. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $serial_number.":".$variable."_high_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { $alert_level = "warning"; $message_key = "scan_storcli_note_0023"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } } elsif ($value < $low_critical) { # Dropped below the low critical threshold. This # should always be unset because it is a new # variable, but check anyway. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $serial_number.":".$variable."_low_critical", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { $alert_level = "critical"; $message_key = "scan_storcli_note_0024"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } } elsif ($value < $low_warning) { # Dropped below the low warning threshold. This # should always be unset because it is a new # variable, but check anyway. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $serial_number.":".$variable."_low_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { $alert_level = "warning"; $message_key = "scan_storcli_note_0025"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } } # Record this for later processing into the 'temperature' table. my $sensor_host_key = "controller:".$serial_number; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { sensor_host_key => $sensor_host_key }}); $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_value_c} = $value; $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_state} = $temperature_state; $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_is} = $temperature_is; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "new::temperature::${variable}::${sensor_host_key}::temperature_value_c" => $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_value_c}, "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}, }}); } # Send an alert telling the user that we've found a new controller. my $variables = { serial_number => $serial_number, name => $variable, value => $value, high_critical_temperature => $high_critical, high_warning_temperature => $high_warning, low_critical_temperature => $low_critical, low_warning_temperature => $low_warning, }; my $log_level = (($alert_level eq "warning") or ($alert_level eq "critical")) ? 1 : 2; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $log_level, key => $message_key, variables => $variables}); $anvil->Alert->register({ clear_alert => 0, alert_level => $alert_level, message => $message_key, variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); # INSERT my $query = " INSERT INTO scan_storcli_variables ( scan_storcli_variable_uuid, scan_storcli_variable_host_uuid, scan_storcli_variable_source_table, scan_storcli_variable_source_uuid, scan_storcli_variable_is_temperature, scan_storcli_variable_name, scan_storcli_variable_value, modified_date ) VALUES ( ".$anvil->Database->quote($anvil->Get->uuid()).", ".$anvil->Database->quote($anvil->Get->host_uuid).", 'scan_storcli_controllers', ".$anvil->Database->quote($controller_uuid).", ".$anvil->Database->quote($temperature).", ".$anvil->Database->quote($variable).", ".$anvil->Database->quote($value).", ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." );"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } } } else { ### NOTE: The serial number should never change (a changed SN/controller should be ### picked up as a new controller), but we check/update just to be safe. # Look for changes. my $main_table_changed = 0; my $old_serial_number = $anvil->data->{sql}{scan_storcli_controllers}{scan_storcli_controller_uuid}{$controller_uuid}{scan_storcli_controller_serial_number}; my $old_model = $anvil->data->{sql}{scan_storcli_controllers}{scan_storcli_controller_uuid}{$controller_uuid}{scan_storcli_controller_model}; my $old_alarm_state = $anvil->data->{sql}{scan_storcli_controllers}{scan_storcli_controller_uuid}{$controller_uuid}{scan_storcli_controller_alarm_state}; my $old_cache_size = $anvil->data->{sql}{scan_storcli_controllers}{scan_storcli_controller_uuid}{$controller_uuid}{scan_storcli_controller_cache_size}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { serial_number => $serial_number, old_serial_number => $old_serial_number, new_model => $new_model, old_model => $old_model, new_alarm_state => $new_alarm_state, old_alarm_state => $old_alarm_state, new_cache_size => $new_cache_size, old_cache_size => $old_cache_size, }}); if (($serial_number ne $old_serial_number) or ($new_model ne $old_model) or ($new_alarm_state ne $old_alarm_state) or ($new_cache_size ne $old_cache_size)) { # Send a warning level alert because the most likely change is 'alarm_state'. # If, however, the alarm state is now 'off', then we'll clear the alert. my $cleared = 0; my $message_key = "scan_storcli_warning_0002"; if ($new_alarm_state ne $old_alarm_state) { if ($old_alarm_state eq "VANISHED") { # Controller has returned. $message_key = "scan_storcli_warning_0009"; } if ($new_alarm_state =~ /off/i) { $cleared = 1; } } my $variables = { new_serial_number => $serial_number, old_serial_number => $old_serial_number, new_model => $new_model, old_model => $old_model, new_alarm_state => $new_alarm_state, old_alarm_state => $old_alarm_state, new_cache_size => $new_cache_size, old_cache_size => $old_cache_size, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => $message_key, variables => $variables}); $anvil->Alert->register({ clear_alert => $cleared, alert_level => "warning", message => $message_key, variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE}); $main_table_changed = 1; my $query = " UPDATE scan_storcli_controllers SET scan_storcli_controller_serial_number = ".$anvil->Database->quote($serial_number).", scan_storcli_controller_model = ".$anvil->Database->quote($new_model).", scan_storcli_controller_alarm_state = ".$anvil->Database->quote($new_alarm_state).", scan_storcli_controller_cache_size = ".$anvil->Database->quote($new_cache_size).", modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." WHERE scan_storcli_controller_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." AND scan_storcli_controller_uuid = ".$anvil->Database->quote($controller_uuid)." ;"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } ### Process the rest of the variables now. foreach my $type ("variable", "temperature") { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { type => $type }}); foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{controller}{serial_number}{$serial_number}{$type}}) { # Being in the 'variable' hash, 'is_temperature' is 'FALSE'. my $new_variable_value = delete $anvil->data->{controller}{serial_number}{$serial_number}{$type}{$variable}; my $temperature = $type eq "temperature" ? "TRUE" : "FALSE"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, temperature => $temperature, }}); if (exists $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_controllers'}{source_uuid}{$controller_uuid}{$type}{$variable}{scan_storcli_variable_uuid}) { # Look for changes my $old_variable_value = $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_controllers'}{source_uuid}{$controller_uuid}{$type}{$variable}{scan_storcli_variable_value}; my $variable_uuid = $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_controllers'}{source_uuid}{$controller_uuid}{$type}{$variable}{scan_storcli_variable_uuid}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_variable_value => $new_variable_value, old_variable_value => $old_variable_value, variable_uuid => $variable_uuid, }}); # Delete it so that we know it has been processed. delete $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_controllers'}{source_uuid}{$controller_uuid}{$type}{$variable}; # If there is no change, I still want to record the # temperature (where applicable). So I setup like I will send # an alert from the start, but will only actually send if # something has changed. my $message_key = "scan_storcli_warning_0003"; my $alert_level = "info"; my $temperature_state = "ok"; my $temperature_is = "nominal"; my $cleared = 0; if ($type eq "temperature") { # It's a temperature, so change the default message. $message_key = "scan_storcli_note_0067"; if (($new_variable_value ne $old_variable_value)) { # If the temperature is rising, see if we # need to set a high warning or critical, or, # clear a low warning or critical. Check for # the reverse if the temperature is dropping. if ($old_variable_value eq "VANISHED") { ### NOTE: We don't (yet) check to see ### if the drive is overheating ### or freezing here. That ### would require new logic and ### is unlikely to be needed. # Temperature is back. $message_key = "scan_storcli_warning_0006"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { message_key => $message_key }}); } elsif ($old_variable_value > $new_variable_value) { # Rising my $jumped = ($new_variable_value - $old_variable_value); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { jumped => $jumped }}); if ($new_variable_value > $high_critical) { # Crossed the critical # threshold. See if this is # the first time. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $serial_number.":".$variable."_high_critical", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first # time we rose above # the critical # threshold. $alert_level = "critical"; $message_key = "scan_storcli_note_0026"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } $temperature_state = "critical"; $temperature_is = "high"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { temperature_state => $temperature_state, temperature_is => $temperature_is, }}); } elsif ($new_variable_value > $high_warning) { # Crossed the warning # threshold. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $serial_number.":".$variable."_high_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first # time we rose above # the critical # threshold. $alert_level = "warning"; $message_key = "scan_storcli_note_0027"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "alert_level" => $alert_level, "message_key" => $message_key, }}); } $temperature_state = "warning"; $temperature_is = "high"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { temperature_state => $temperature_state, temperature_is => $temperature_is, }}); } elsif ($new_variable_value > $clear_low_warning) { # Risen into the clear, make # sure both warning and # critical are cleared. my $cleared_critical = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $serial_number.":".$variable."_low_critical", set_by => $THIS_FILE}); my $cleared_warning = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $serial_number.":".$variable."_low_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared_critical => $cleared_critical, cleared_warning => $cleared_warning, }}); if ($cleared_critical) { $cleared = 1; $alert_level = "warning"; $message_key = "scan_storcli_note_0028"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared => $cleared, alert_level => $alert_level, message_key => $message_key, }}); } elsif ($cleared_warning) { # The temperature has # dropped back down # to safe levels. $cleared = 1; $alert_level = "warning"; $message_key = "scan_storcli_note_0028"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared => $cleared, alert_level => $alert_level, message_key => $message_key, }}); } } elsif ($new_variable_value > $clear_low_critical) { # Risen above critical, but # not in the clear. my $cleared_critical = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $serial_number.":".$variable."_low_critical", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared_critical => $cleared_critical }}); if ($cleared_critical) { # Set the warning. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $serial_number.":".$variable."_low_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); $cleared = 1; $alert_level = "warning"; $message_key = "scan_storcli_note_0029"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared => $cleared, alert_level => $alert_level, message_key => $message_key, }}); } $temperature_state = "warning"; $temperature_is = "low"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { temperature_state => $temperature_state, temperature_is => $temperature_is, }}); } elsif ($jumped > $jump) { # The temperature jumped a # large amount. $alert_level = "warning"; $message_key = "scan_storcli_note_0030"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "alert_level" => $alert_level, "message_key" => $message_key, }}); } } else { # Falling my $jumped = $old_variable_value - $new_variable_value; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { jumped => $jumped }}); if ($new_variable_value < $low_critical) { # Dropped below the critical # threshold. This should # always be unset because it # is a new variable, but # check anyway. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $serial_number.":".$variable."_low_critical", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first # time we rose above # the critical # threshold. $alert_level = "critical"; $message_key = "scan_storcli_note_0031"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } $temperature_state = "critical"; $temperature_is = "low"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { temperature_state => $temperature_state, temperature_is => $temperature_is, }}); } elsif ($new_variable_value < $low_warning) { # Crossed the warning # threshold. This should # always be unset because it # is a new variable, but # check anyway. my $changed = $anvil->Alert->check_alert_sent({clear => 0, record_locator => $serial_number.":".$variable."_low_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }}); if ($changed) { # This is the first # time we rose above # the critical # threshold. $alert_level = "warning"; $message_key = "scan_storcli_note_0032"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } $temperature_state = "warning"; $temperature_is = "low"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { temperature_state => $temperature_state, temperature_is => $temperature_is, }}); } elsif ($new_variable_value < $clear_high_warning) { # Dropped into the clear my $cleared_critical = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $serial_number.":".$variable."_high_critical", set_by => $THIS_FILE}); my $cleared_warning = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $serial_number.":".$variable."_high_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared_critical => $cleared_critical, cleared_warning => $cleared_warning, }}); if ($cleared_critical) { $cleared = 1; $alert_level = "warning"; $message_key = "scan_storcli_note_0033"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared => $cleared, alert_level => $alert_level, message_key => $message_key, }}); } elsif ($cleared_warning) { # The temperature has # dropped back down # to safe levels. $cleared = 1; $alert_level = "warning"; $message_key = "scan_storcli_note_0033"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared => $cleared, alert_level => $alert_level, message_key => $message_key, }}); } } elsif ($new_variable_value < $clear_high_critical) { # Dropped below critical, but # not in the clear. my $cleared_critical = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $serial_number.":".$variable."_high_critical", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared_critical => $cleared_critical }}); if ($cleared_critical) { # Set the warning. my $cleared_warning = $anvil->Alert->check_alert_sent({clear => 1, record_locator => $serial_number.":".$variable."_high_warning", set_by => $THIS_FILE}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared_warning => $cleared_warning }}); $cleared = 1; $alert_level = "warning"; $message_key = "scan_storcli_note_0034"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cleared => $cleared, alert_level => $alert_level, message_key => $message_key, }}); } $temperature_state = "warning"; $temperature_is = "high"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { temperature_state => $temperature_state, temperature_is => $temperature_is, }}); } elsif ($jumped > $jump) { # The temperature dropped a # large amount. $alert_level = "warning"; $message_key = "scan_storcli_note_0035"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } } } # Record this for later processing into the # 'temperature' table. my $sensor_host_key = "controller:$serial_number"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { sensor_host_key => $sensor_host_key }}); $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_value_c} = $new_variable_value; $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_state} = $temperature_state; $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_is} = $temperature_is; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "new::temperature::${variable}::${sensor_host_key}::temperature_value_c" => $anvil->data->{new}{temperature}{$variable}{$sensor_host_key}{temperature_value_c}, "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}, }}); } ### TODO: Check to see if the new state of certain key ### variables are worthy of setting a health weight. ### We can't check just on changes. # Now actually generate an alert and save the changes if something # changed. if (($new_variable_value ne $old_variable_value)) { # Changed! If the old value was 'VANISHED', then a # sensor or variable returned. Otherwise, for now, we # treat everything as 'warning' and step down # explicitely anything not of concern that proves # noisey later (better safe than sorry). # The 'Safe ID' seems to change frequently, so we # drop this to an 'info' level alert. if ($variable eq "safe_id") { $alert_level = "info"; $message_key = "scan_storcli_note_0070"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } # If it is a temperature, we may want to make some # changes to the alert level/message. if ($type ne "temperature") { if ($old_variable_value eq "VANISHED") { $message_key = "scan_storcli_warning_0006"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { message_key => $message_key }}); } # Check for some important issues if ($variable =~ /memory_correctable_errors/) { $alert_level = "warning"; $message_key = "scan_storcli_warning_0046"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } elsif ($variable =~ /memory_uncorrectable_errors/) { $alert_level = "critical"; $message_key = "scan_storcli_warning_0047"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } elsif ($variable =~ /controller_status/) { $alert_level = "critical"; $message_key = "scan_storcli_warning_0048"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { alert_level => $alert_level, message_key => $message_key, }}); } # There more of interest... See: SELECT a.host_name, b.scan_storcli_controller_serial_number AS sn, b.scan_storcli_controller_model, c.scan_storcli_variable_name AS variable, c.scan_storcli_variable_value AS value, c.scan_storcli_variable_is_temperature AS temp, c.modified_date FROM hosts a, scan_storcli_controllers b, scan_storcli_variables c WHERE a.host_uuid = b.scan_storcli_controller_host_uuid AND b.scan_storcli_controller_uuid = c.scan_storcli_variable_source_uuid AND a.host_name = 'an-a07n01.alteeve.com'; } # Send an alert telling the user that we've found a new controller. my $variables = { serial_number => $serial_number, name => $variable, old_value => $old_variable_value ? $old_variable_value : "--", new_value => $new_variable_value, high_critical_temperature => $high_critical, high_warning_temperature => $high_warning, low_critical_temperature => $low_critical, low_warning_temperature => $low_warning, jump => $jump, }; my $log_level = (($alert_level eq "warning") or ($alert_level eq "critical")) ? 1 : 2; my $alert_header = $main_table_changed ? 0 : 1; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $log_level, key => $message_key, variables => $variables}); $anvil->Alert->register({ clear_alert => $cleared, alert_level => $alert_level, message => $message_key, variables => $variables, show_header => $alert_header, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); my $query = " UPDATE scan_storcli_variables SET scan_storcli_variable_value = ".$anvil->Database->quote($new_variable_value).", modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." WHERE scan_storcli_variable_uuid = ".$anvil->Database->quote($variable_uuid)." ;"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } } else { # New variable, record it. This is a 'warning' level as # variables aren't expected to spawn into existence. my $variables = { serial_number => $serial_number, name => $variable, value => $new_variable_value, }; my $alert_header = $main_table_changed ? 0 : 1; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_storcli_warning_0004", variables => $variables}); $anvil->Alert->register({ alert_level => "warning", message => "scan_storcli_warning_0004", variables => $variables, show_header => $alert_header, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); # INSERT my $query = " INSERT INTO scan_storcli_variables ( scan_storcli_variable_uuid, scan_storcli_variable_host_uuid, scan_storcli_variable_source_table, scan_storcli_variable_source_uuid, scan_storcli_variable_is_temperature, scan_storcli_variable_name, scan_storcli_variable_value, modified_date ) VALUES ( ".$anvil->Database->quote($anvil->Get->uuid()).", ".$anvil->Database->quote($anvil->Get->host_uuid).", 'scan_storcli_controllers', ".$anvil->Database->quote($controller_uuid).", ".$anvil->Database->quote($temperature).", ".$anvil->Database->quote($variable).", ".$anvil->Database->quote($new_variable_value).", ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." );"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } } } # Now look for any variables left from the previous scan that we didn't match up (and # delete) this pass. foreach my $type ("variable", "temperature") { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { type => $type }}); foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_controllers'}{source_uuid}{$controller_uuid}{$type}}) { # This variable has vanished my $old_variable_value = $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_controllers'}{source_uuid}{$controller_uuid}{$type}{$variable}{scan_storcli_variable_value}; my $variable_uuid = $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_controllers'}{source_uuid}{$controller_uuid}{$type}{$variable}{scan_storcli_variable_uuid}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_variable_value => $old_variable_value, variable_uuid => $variable_uuid, }}); # Delete it so that we know it has been processed. delete $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{'scan_storcli_controllers'}{source_uuid}{$controller_uuid}{$type}{$variable}; # If the old value is already 'VANISHED', ignore it. next if $old_variable_value eq "VANISHED"; ### NOTE: For now, we're going to use warning level because variables ### shouldn't vanish, but under an-cm, it did happen for ### reasons that we never figured out. So later, we may drop ### the alert level in some cases. # Still here? Alert and UPDATE. my $variables = { serial_number => $serial_number, name => $variable, }; my $alert_header = $main_table_changed ? 0 : 1; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_storcli_warning_0007", variables => $variables}); $anvil->Alert->register({ alert_level => "warning", message => "scan_storcli_warning_0007", variables => $variables, show_header => $alert_header, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); my $query = " UPDATE scan_storcli_variables SET scan_storcli_variable_value = 'VANISHED', modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." WHERE scan_storcli_variable_uuid = ".$anvil->Database->quote($variable_uuid)." ;"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } } } # Delete the controller from the last scan so that we can find controllers that have been # removed. if (exists $anvil->data->{sql}{scan_storcli_controllers}{scan_storcli_controller_uuid}{$controller_uuid}) { delete $anvil->data->{sql}{scan_storcli_controllers}{scan_storcli_controller_uuid}{$controller_uuid}; } } # See if any controllers vanished. foreach my $controller_uuid (keys %{$anvil->data->{sql}{scan_storcli_controllers}{scan_storcli_controller_uuid}}) { # Controller vanished! my $old_serial_number = $anvil->data->{sql}{scan_storcli_controllers}{scan_storcli_controller_uuid}{$controller_uuid}{scan_storcli_controller_serial_number}; my $old_model = $anvil->data->{sql}{scan_storcli_controllers}{scan_storcli_controller_uuid}{$controller_uuid}{scan_storcli_controller_model}; my $old_alarm_state = $anvil->data->{sql}{scan_storcli_controllers}{scan_storcli_controller_uuid}{$controller_uuid}{scan_storcli_controller_alarm_state}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { controller_uuid => $controller_uuid, old_serial_number => $old_serial_number, old_model => $old_model, old_alarm_state => $old_alarm_state, }}); # Delete it so that we know it has been processed. delete $anvil->data->{sql}{scan_storcli_controllers}{scan_storcli_controller_uuid}{$controller_uuid}; # If the old alarm state is already 'VANISHED', ignore it. next if $old_alarm_state eq "VANISHED"; # Still here? Alert and UPDATE. ### NOTE: For now, we're going to use warning level because controllers should never vanish ### unless one failed. If that is the case, the admin already knows, but this will let ### other notification targets know that the change has happened. my $variables = { serial_number => $old_serial_number, model => $old_model, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_storcli_warning_0008", variables => $variables}); $anvil->Alert->register({ alert_level => "warning", message => "scan_storcli_warning_0008", variables => $variables, show_header => 1, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); my $query = " UPDATE scan_storcli_controllers SET scan_storcli_controller_alarm_state = 'VANISHED', modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." WHERE scan_storcli_controller_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." AND scan_storcli_controller_uuid = ".$anvil->Database->quote($controller_uuid)." ;"; # Now record the query in the array $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); push @{$anvil->data->{'scan-storcli'}{queries}}, $query; } # Now commit the changes. $anvil->Database->write({query => $anvil->data->{'scan-storcli'}{queries}, source => $THIS_FILE, line => __LINE__}); $anvil->data->{'scan-storcli'}{queries} = []; return(0); } # In some weird (and so far unreproducable) case, a controller will get recorded twice. If this happens, the # system will think it keeps finding and losing controllers. So this check purges duplicates. sub clear_duplicate_controllers { my ($anvil) = @_; # Get a list of controllers on this host and count them by their serial numbers. my $query = " SELECT scan_storcli_controller_uuid, scan_storcli_controller_serial_number, scan_storcli_controller_alarm_state FROM scan_storcli_controllers WHERE scan_storcli_controller_host_uuid = ".$anvil->Database->quote($anvil->Get->host_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__}); my $count = @{$results}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { results => $results, count => $count, }}); if ($count > 1) { ### NOTE: This works a little differently from originally intended, but it actually works ### better. This should track 'uuid::serial_number' as the UUIDs are unique, the serial ### numbers aren't. As it is now, only the first seen UUID is deleted. In most cases, ### there are only two entries, so this deletes one and leaves the other, so everything ### isn't discovered again new. When we re-do this for M3, swap the logic but skip the ### most recently updated one. # We'll loop through and track controllers by their serial numbers. If any controller serial # number is seen 2 or more times, it will be purged and re-added on the next scan. my $duplicate_controllers = {}; foreach my $row (@{$results}) { my $scan_storcli_controller_uuid = $row->[0]; my $scan_storcli_controller_serial_number = $row->[1]; my $scan_storcli_controller_alarm_state = $row->[2]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_controller_uuid => $scan_storcli_controller_uuid, scan_storcli_controller_serial_number => $scan_storcli_controller_serial_number, scan_storcli_controller_alarm_state => $scan_storcli_controller_alarm_state, }}); # Add this to the controllers we know about if (not exists $duplicate_controllers->{$scan_storcli_controller_serial_number}) { $duplicate_controllers->{$scan_storcli_controller_serial_number}{count} = 1; $duplicate_controllers->{$scan_storcli_controller_serial_number}{controller_uuid} = $scan_storcli_controller_uuid; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "duplicate_controllers->${scan_storcli_controller_serial_number}::count" => $duplicate_controllers->{$scan_storcli_controller_serial_number}{count}, "duplicate_controllers->${scan_storcli_controller_serial_number}::controller_uuid" => $duplicate_controllers->{$scan_storcli_controller_serial_number}{controller_uuid}, }}); } else { $duplicate_controllers->{$scan_storcli_controller_serial_number}{count}++; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "duplicate_controllers->${scan_storcli_controller_serial_number}" => $duplicate_controllers->{$scan_storcli_controller_serial_number}, }}); } } foreach my $scan_storcli_controller_serial_number (sort {$a cmp $b} keys %{$duplicate_controllers}) { my $count = $duplicate_controllers->{$scan_storcli_controller_serial_number}{count}; my $scan_storcli_controller_uuid = $duplicate_controllers->{$scan_storcli_controller_serial_number}{controller_uuid}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_controller_serial_number => $scan_storcli_controller_serial_number, count => $count, scan_storcli_controller_uuid => $scan_storcli_controller_uuid, }}); if ($count > 1) { # Duplicate. Send an alert and then purge. my $variables = { serial_number => $scan_storcli_controller_serial_number, count => $count, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_storcli_warning_0052", variables => $variables}); $anvil->Alert->register({ alert_level => "warning", show_header => 1, message => "scan_storcli_warning_0052", variables => $variables, sort_position => $anvil->data->{'scan-storcli'}{alert_sort}++, set_by => $THIS_FILE, }); # We'll commit this batch of queries per controller my $queries = []; # Delete all physical drives my $query = "SELECT scan_storcli_physical_drive_uuid FROM scan_storcli_physical_drives WHERE scan_storcli_physical_drive_controller_uuid = ".$anvil->Database->quote($scan_storcli_controller_uuid).";"; 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 $scan_storcli_physical_drive_uuid = $row->[0]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_physical_drive_uuid => $scan_storcli_physical_drive_uuid }}); push @{$queries}, "DELETE FROM history.scan_storcli_variables WHERE scan_storcli_variable_source_table = 'scan_storcli_physical_drives' AND scan_storcli_variable_source_uuid = ".$anvil->Database->quote($scan_storcli_physical_drive_uuid).";"; push @{$queries}, "DELETE FROM scan_storcli_variables WHERE scan_storcli_variable_source_table = 'scan_storcli_physical_drives' AND scan_storcli_variable_source_uuid = ".$anvil->Database->quote($scan_storcli_physical_drive_uuid).";"; push @{$queries}, "DELETE FROM history.scan_storcli_physical_drives WHERE scan_storcli_physical_drive_uuid = ".$anvil->Database->quote($scan_storcli_physical_drive_uuid).";"; push @{$queries}, "DELETE FROM scan_storcli_physical_drives WHERE scan_storcli_physical_drive_uuid = ".$anvil->Database->quote($scan_storcli_physical_drive_uuid).";"; } # The scan_storcli_drive_groups are linked to the virtual drives, so we need to # pull up all the VDs on this controller and use their UUIDs to delete # down-stream DGs $query = "SELECT scan_storcli_virtual_drive_uuid FROM scan_storcli_virtual_drives WHERE scan_storcli_virtual_drive_controller_uuid = ".$anvil->Database->quote($scan_storcli_controller_uuid).";"; $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_storcli_virtual_drive_uuid = $row->[0]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_virtual_drive_uuid => $scan_storcli_virtual_drive_uuid }}); # Select the DGs under this VD. my $query = "SELECT scan_storcli_drive_group_uuid FROM scan_storcli_drive_groups WHERE scan_storcli_drive_group_virtual_drive_uuid = ".$anvil->Database->quote($scan_storcli_virtual_drive_uuid).";"; 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 $scan_storcli_drive_group_uuid = $row->[0]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_drive_group_uuid => $scan_storcli_drive_group_uuid }}); push @{$queries}, "DELETE FROM history.scan_storcli_variables WHERE scan_storcli_variable_source_table = 'scan_storcli_drive_groups' AND scan_storcli_variable_source_uuid = ".$anvil->Database->quote($scan_storcli_drive_group_uuid).";"; push @{$queries}, "DELETE FROM scan_storcli_variables WHERE scan_storcli_variable_source_table = 'scan_storcli_drive_groups' AND scan_storcli_variable_source_uuid = ".$anvil->Database->quote($scan_storcli_drive_group_uuid).";"; push @{$queries}, "DELETE FROM history.scan_storcli_drive_groups WHERE scan_storcli_drive_group_uuid = ".$anvil->Database->quote($scan_storcli_drive_group_uuid).";"; push @{$queries}, "DELETE FROM scan_storcli_drive_groups WHERE scan_storcli_drive_group_uuid = ".$anvil->Database->quote($scan_storcli_drive_group_uuid).";"; } push @{$queries}, "DELETE FROM history.scan_storcli_variables WHERE scan_storcli_variable_source_table = 'scan_storcli_virtual_drives' AND scan_storcli_variable_source_uuid = ".$anvil->Database->quote($scan_storcli_virtual_drive_uuid).";"; push @{$queries}, "DELETE FROM scan_storcli_variables WHERE scan_storcli_variable_source_table = 'scan_storcli_virtual_drives' AND scan_storcli_variable_source_uuid = ".$anvil->Database->quote($scan_storcli_virtual_drive_uuid).";"; push @{$queries}, "DELETE FROM history.scan_storcli_virtual_drives WHERE scan_storcli_virtual_drive_uuid = ".$anvil->Database->quote($scan_storcli_virtual_drive_uuid).";"; push @{$queries}, "DELETE FROM scan_storcli_virtual_drives WHERE scan_storcli_virtual_drive_uuid = ".$anvil->Database->quote($scan_storcli_virtual_drive_uuid).";"; } # Delete all cachevaults (FBUs) $query = "SELECT scan_storcli_cachevault_uuid FROM scan_storcli_cachevaults WHERE scan_storcli_cachevault_controller_uuid = ".$anvil->Database->quote($scan_storcli_controller_uuid).";"; $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_storcli_cachevault_uuid = $row->[0]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_cachevault_uuid => $scan_storcli_cachevault_uuid }}); push @{$queries}, "DELETE FROM history.scan_storcli_variables WHERE scan_storcli_variable_source_table = 'scan_storcli_cachevaults' AND scan_storcli_variable_source_uuid = ".$anvil->Database->quote($scan_storcli_cachevault_uuid).";"; push @{$queries}, "DELETE FROM scan_storcli_variables WHERE scan_storcli_variable_source_table = 'scan_storcli_cachevaults' AND scan_storcli_variable_source_uuid = ".$anvil->Database->quote($scan_storcli_cachevault_uuid).";"; push @{$queries}, "DELETE FROM history.scan_storcli_cachevaults WHERE scan_storcli_cachevault_uuid = ".$anvil->Database->quote($scan_storcli_cachevault_uuid).";"; push @{$queries}, "DELETE FROM scan_storcli_cachevaults WHERE scan_storcli_cachevault_uuid = ".$anvil->Database->quote($scan_storcli_cachevault_uuid).";"; } # Delete all BBUs $query = "SELECT scan_storcli_bbu_uuid FROM scan_storcli_bbus WHERE scan_storcli_bbu_controller_uuid = ".$anvil->Database->quote($scan_storcli_controller_uuid).";"; $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_storcli_bbu_uuid = $row->[0]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_bbu_uuid => $scan_storcli_bbu_uuid }}); push @{$queries}, "DELETE FROM history.scan_storcli_variables WHERE scan_storcli_variable_source_table = 'scan_storcli_bbus' AND scan_storcli_variable_source_uuid = ".$anvil->Database->quote($scan_storcli_bbu_uuid).";"; push @{$queries}, "DELETE FROM scan_storcli_variables WHERE scan_storcli_variable_source_table = 'scan_storcli_bbus' AND scan_storcli_variable_source_uuid = ".$anvil->Database->quote($scan_storcli_bbu_uuid).";"; push @{$queries}, "DELETE FROM history.scan_storcli_bbus WHERE scan_storcli_bbu_uuid = ".$anvil->Database->quote($scan_storcli_bbu_uuid).";"; push @{$queries}, "DELETE FROM scan_storcli_bbus WHERE scan_storcli_bbu_uuid = ".$anvil->Database->quote($scan_storcli_bbu_uuid).";"; } # Finally, delete the controller. push @{$queries}, "DELETE FROM history.scan_storcli_variables WHERE scan_storcli_variable_source_table = 'scan_storcli_controllers' AND scan_storcli_variable_source_uuid = ".$anvil->Database->quote($scan_storcli_controller_uuid).";"; push @{$queries}, "DELETE FROM scan_storcli_variables WHERE scan_storcli_variable_source_table = 'scan_storcli_controllers' AND scan_storcli_variable_source_uuid = ".$anvil->Database->quote($scan_storcli_controller_uuid).";"; push @{$queries}, "DELETE FROM history.scan_storcli_controllers WHERE scan_storcli_controller_uuid = ".$anvil->Database->quote($scan_storcli_controller_uuid).";"; push @{$queries}, "DELETE FROM scan_storcli_controllers WHERE scan_storcli_controller_uuid = ".$anvil->Database->quote($scan_storcli_controller_uuid).";"; # Commit. Because it's an array, it'll be done as a single transaction foreach my $query (@{$queries}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); } $anvil->Database->write({query => $queries, source => $THIS_FILE, line => __LINE__}); } } } return(0); } # This reads in the last scan's data. sub read_last_scan { my ($anvil) = @_; clear_duplicate_controllers($anvil); # Read in the controller(s) my $query = " SELECT scan_storcli_controller_uuid, scan_storcli_controller_serial_number, scan_storcli_controller_model, scan_storcli_controller_alarm_state, scan_storcli_controller_cache_size FROM scan_storcli_controllers WHERE scan_storcli_controller_host_uuid = ".$anvil->Database->quote($anvil->Get->host_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__}); my $count = @{$results}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { results => $results, count => $count, }}); foreach my $row (@{$results}) { my $scan_storcli_controller_uuid = $row->[0]; my $scan_storcli_controller_serial_number = $row->[1]; my $scan_storcli_controller_model = $row->[2]; my $scan_storcli_controller_alarm_state = $row->[3]; my $scan_storcli_controller_cache_size = $row->[4]; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_controller_uuid => $scan_storcli_controller_uuid, scan_storcli_controller_serial_number => $scan_storcli_controller_serial_number, scan_storcli_controller_model => $scan_storcli_controller_model, scan_storcli_controller_alarm_state => $scan_storcli_controller_alarm_state, scan_storcli_controller_cache_size => $scan_storcli_controller_cache_size, }}); # Store the information about this controllers $anvil->data->{'scan-storcli'}{controllers}{by_serial}{$scan_storcli_controller_serial_number} = $scan_storcli_controller_uuid; $anvil->data->{'scan-storcli'}{controllers}{by_uuid}{$scan_storcli_controller_uuid} = $scan_storcli_controller_serial_number; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "scan-storcli::controllers::by_serial::${scan_storcli_controller_serial_number}" => $anvil->data->{'scan-storcli'}{controllers}{by_serial}{$scan_storcli_controller_serial_number}, "scan-storcli::controllers::by_uuid::${scan_storcli_controller_uuid}" => $anvil->data->{'scan-storcli'}{controllers}{by_uuid}{$scan_storcli_controller_uuid}, }}); $anvil->data->{sql}{scan_storcli_controllers}{scan_storcli_controller_uuid}{$scan_storcli_controller_uuid} = { scan_storcli_controller_serial_number => $scan_storcli_controller_serial_number, scan_storcli_controller_model => $scan_storcli_controller_model, scan_storcli_controller_alarm_state => $scan_storcli_controller_alarm_state, scan_storcli_controller_cache_size => $scan_storcli_controller_cache_size, }; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "scan-storcli::controllers::by_serial::$scan_storcli_controller_serial_number" => $anvil->data->{'scan-storcli'}{controllers}{by_serial}{$scan_storcli_controller_serial_number}, "scan-storcli::controllers::by_uuid::$scan_storcli_controller_uuid" => $anvil->data->{'scan-storcli'}{controllers}{by_uuid}{$scan_storcli_controller_uuid}, "sql::scan_storcli_controllers::scan_storcli_controller_uuid::${scan_storcli_controller_uuid}::scan_storcli_controller_serial_number" => $anvil->data->{sql}{scan_storcli_controllers}{scan_storcli_controller_uuid}{$scan_storcli_controller_uuid}{scan_storcli_controller_serial_number}, "sql::scan_storcli_controllers::scan_storcli_controller_uuid::${scan_storcli_controller_uuid}::scan_storcli_controller_model" => $anvil->data->{sql}{scan_storcli_controllers}{scan_storcli_controller_uuid}{$scan_storcli_controller_uuid}{scan_storcli_controller_model}, "sql::scan_storcli_controllers::scan_storcli_controller_uuid::${scan_storcli_controller_uuid}::scan_storcli_controller_alarm_state" => $anvil->data->{sql}{scan_storcli_controllers}{scan_storcli_controller_uuid}{$scan_storcli_controller_uuid}{scan_storcli_controller_alarm_state}, "sql::scan_storcli_controllers::scan_storcli_controller_uuid::${scan_storcli_controller_uuid}::scan_storcli_controller_cache_size" => $anvil->data->{sql}{scan_storcli_controllers}{scan_storcli_controller_uuid}{$scan_storcli_controller_uuid}{scan_storcli_controller_cache_size}, }}); } undef $results; # Now load the cachevault data $query = " SELECT scan_storcli_cachevault_uuid, scan_storcli_cachevault_controller_uuid, scan_storcli_cachevault_serial_number, scan_storcli_cachevault_state, scan_storcli_cachevault_design_capacity, scan_storcli_cachevault_replacement_needed, scan_storcli_cachevault_type, scan_storcli_cachevault_model, scan_storcli_cachevault_manufacture_date FROM scan_storcli_cachevaults WHERE scan_storcli_cachevault_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." ;"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); $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_storcli_cachevault_uuid = $row->[0]; my $scan_storcli_cachevault_controller_uuid = $row->[1]; my $scan_storcli_cachevault_serial_number = $row->[2]; my $scan_storcli_cachevault_state = defined $row->[3] ? $row->[3] : ""; my $scan_storcli_cachevault_design_capacity = defined $row->[4] ? $row->[4] : ""; my $scan_storcli_cachevault_replacement_needed = defined $row->[5] ? $row->[5] : ""; my $scan_storcli_cachevault_type = defined $row->[6] ? $row->[6] : ""; my $scan_storcli_cachevault_model = defined $row->[7] ? $row->[7] : ""; my $scan_storcli_cachevault_manufacture_date = defined $row->[8] ? $row->[8] : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_cachevault_uuid => $scan_storcli_cachevault_uuid, scan_storcli_cachevault_controller_uuid => $scan_storcli_cachevault_controller_uuid, scan_storcli_cachevault_serial_number => $scan_storcli_cachevault_serial_number, scan_storcli_cachevault_state => $scan_storcli_cachevault_state, scan_storcli_cachevault_design_capacity => $scan_storcli_cachevault_design_capacity, scan_storcli_cachevault_replacement_needed => $scan_storcli_cachevault_replacement_needed, scan_storcli_cachevault_type => $scan_storcli_cachevault_type, scan_storcli_cachevault_model => $scan_storcli_cachevault_model, scan_storcli_cachevault_manufacture_date => $scan_storcli_cachevault_manufacture_date, }}); # Store the information about this cachevault $anvil->data->{'scan-storcli'}{cachevaults}{by_serial}{$scan_storcli_cachevault_serial_number} = $scan_storcli_cachevault_uuid; $anvil->data->{'scan-storcli'}{cachevaults}{by_uuid}{$scan_storcli_cachevault_uuid} = $scan_storcli_cachevault_serial_number; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "scan-storcli::cachevaults::by_serial::${scan_storcli_cachevault_serial_number}" => $anvil->data->{'scan-storcli'}{cachevaults}{by_serial}{$scan_storcli_cachevault_serial_number}, "scan-storcli::cachevaults::by_uuid::${scan_storcli_cachevault_uuid}" => $anvil->data->{'scan-storcli'}{cachevaults}{by_uuid}{$scan_storcli_cachevault_uuid}, }}); $anvil->data->{sql}{scan_storcli_cachevaults}{scan_storcli_cachevault_uuid}{$scan_storcli_cachevault_uuid} = { scan_storcli_cachevault_controller_uuid => $scan_storcli_cachevault_controller_uuid, scan_storcli_cachevault_serial_number => $scan_storcli_cachevault_serial_number, scan_storcli_cachevault_state => $scan_storcli_cachevault_state, scan_storcli_cachevault_design_capacity => $scan_storcli_cachevault_design_capacity, scan_storcli_cachevault_replacement_needed => $scan_storcli_cachevault_replacement_needed, scan_storcli_cachevault_type => $scan_storcli_cachevault_type, scan_storcli_cachevault_model => $scan_storcli_cachevault_model, scan_storcli_cachevault_manufacture_date => $scan_storcli_cachevault_manufacture_date, }; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "sql::scan_storcli_cachevaults::scan_storcli_cachevault_uuid::${scan_storcli_cachevault_uuid}::scan_storcli_cachevault_controller_uuid" => $anvil->data->{sql}{scan_storcli_cachevaults}{scan_storcli_cachevault_uuid}{$scan_storcli_cachevault_uuid}{scan_storcli_cachevault_controller_uuid}, "sql::scan_storcli_cachevaults::scan_storcli_cachevault_uuid::${scan_storcli_cachevault_uuid}::scan_storcli_cachevault_serial_number" => $anvil->data->{sql}{scan_storcli_cachevaults}{scan_storcli_cachevault_uuid}{$scan_storcli_cachevault_uuid}{scan_storcli_cachevault_serial_number}, "sql::scan_storcli_cachevaults::scan_storcli_cachevault_uuid::${scan_storcli_cachevault_uuid}::scan_storcli_cachevault_state" => $anvil->data->{sql}{scan_storcli_cachevaults}{scan_storcli_cachevault_uuid}{$scan_storcli_cachevault_uuid}{scan_storcli_cachevault_state}, "sql::scan_storcli_cachevaults::scan_storcli_cachevault_uuid::${scan_storcli_cachevault_uuid}::scan_storcli_cachevault_design_capacity" => $anvil->data->{sql}{scan_storcli_cachevaults}{scan_storcli_cachevault_uuid}{$scan_storcli_cachevault_uuid}{scan_storcli_cachevault_design_capacity}, "sql::scan_storcli_cachevaults::scan_storcli_cachevault_uuid::${scan_storcli_cachevault_uuid}::scan_storcli_cachevault_replacement_needed" => $anvil->data->{sql}{scan_storcli_cachevaults}{scan_storcli_cachevault_uuid}{$scan_storcli_cachevault_uuid}{scan_storcli_cachevault_replacement_needed}, "sql::scan_storcli_cachevaults::scan_storcli_cachevault_uuid::${scan_storcli_cachevault_uuid}::scan_storcli_cachevault_type" => $anvil->data->{sql}{scan_storcli_cachevaults}{scan_storcli_cachevault_uuid}{$scan_storcli_cachevault_uuid}{scan_storcli_cachevault_type}, "sql::scan_storcli_cachevaults::scan_storcli_cachevault_uuid::${scan_storcli_cachevault_uuid}::scan_storcli_cachevault_model" => $anvil->data->{sql}{scan_storcli_cachevaults}{scan_storcli_cachevault_uuid}{$scan_storcli_cachevault_uuid}{scan_storcli_cachevault_model}, "sql::scan_storcli_cachevaults::scan_storcli_cachevault_uuid::${scan_storcli_cachevault_uuid}::scan_storcli_cachevault_manufacture_date" => $anvil->data->{sql}{scan_storcli_cachevaults}{scan_storcli_cachevault_uuid}{$scan_storcli_cachevault_uuid}{scan_storcli_cachevault_manufacture_date}, }}); } undef $results; # The BBU data... $query = " SELECT scan_storcli_bbu_uuid, scan_storcli_bbu_controller_uuid, scan_storcli_bbu_serial_number, scan_storcli_bbu_type, scan_storcli_bbu_model, scan_storcli_bbu_state, scan_storcli_bbu_manufacture_date, scan_storcli_bbu_design_capacity, scan_storcli_bbu_replacement_needed FROM scan_storcli_bbus WHERE scan_storcli_bbu_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." ;"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); $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_storcli_bbu_uuid = $row->[0]; my $scan_storcli_bbu_controller_uuid = $row->[1]; my $scan_storcli_bbu_serial_number = $row->[2]; my $scan_storcli_bbu_type = defined $row->[3] ? $row->[3] : ""; my $scan_storcli_bbu_model = defined $row->[4] ? $row->[4] : ""; my $scan_storcli_bbu_state = defined $row->[5] ? $row->[5] : ""; my $scan_storcli_bbu_manufacture_date = defined $row->[6] ? $row->[6] : ""; my $scan_storcli_bbu_design_capacity = defined $row->[7] ? $row->[7] : ""; my $scan_storcli_bbu_replacement_needed = defined $row->[8] ? $row->[8] : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_bbu_uuid => $scan_storcli_bbu_uuid, scan_storcli_bbu_controller_uuid => $scan_storcli_bbu_controller_uuid, scan_storcli_bbu_serial_number => $scan_storcli_bbu_serial_number, scan_storcli_bbu_type => $scan_storcli_bbu_type, scan_storcli_bbu_model => $scan_storcli_bbu_model, scan_storcli_bbu_state => $scan_storcli_bbu_state, scan_storcli_bbu_manufacture_date => $scan_storcli_bbu_manufacture_date, scan_storcli_bbu_design_capacity => $scan_storcli_bbu_design_capacity, scan_storcli_bbu_replacement_needed => $scan_storcli_bbu_replacement_needed, }}); # Store the information about this BBU $anvil->data->{'scan-storcli'}{bbus}{by_serial}{$scan_storcli_bbu_serial_number} = $scan_storcli_bbu_uuid; $anvil->data->{'scan-storcli'}{bbus}{by_uuid}{$scan_storcli_bbu_uuid} = $scan_storcli_bbu_serial_number; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "scan-storcli::bbus::by_serial::${scan_storcli_bbu_serial_number}" => $anvil->data->{'scan-storcli'}{bbus}{by_serial}{$scan_storcli_bbu_serial_number}, "scan-storcli::bbus::by_uuid::${scan_storcli_bbu_uuid}" => $anvil->data->{'scan-storcli'}{bbus}{by_uuid}{$scan_storcli_bbu_uuid}, }}); $anvil->data->{sql}{scan_storcli_bbus}{scan_storcli_bbu_uuid}{$scan_storcli_bbu_uuid} = { scan_storcli_bbu_controller_uuid => $scan_storcli_bbu_controller_uuid, scan_storcli_bbu_serial_number => $scan_storcli_bbu_serial_number, scan_storcli_bbu_type => $scan_storcli_bbu_type, scan_storcli_bbu_model => $scan_storcli_bbu_model, scan_storcli_bbu_state => $scan_storcli_bbu_state, scan_storcli_bbu_manufacture_date => $scan_storcli_bbu_manufacture_date, scan_storcli_bbu_design_capacity => $scan_storcli_bbu_design_capacity, scan_storcli_bbu_replacement_needed => $scan_storcli_bbu_replacement_needed, }; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "sql::scan_storcli_bbus::scan_storcli_bbu_uuid::${scan_storcli_bbu_uuid}::scan_storcli_bbu_controller_uuid" => $anvil->data->{sql}{scan_storcli_bbus}{scan_storcli_bbu_uuid}{$scan_storcli_bbu_uuid}{scan_storcli_bbu_controller_uuid}, "sql::scan_storcli_bbus::scan_storcli_bbu_uuid::${scan_storcli_bbu_uuid}::scan_storcli_bbu_serial_number" => $anvil->data->{sql}{scan_storcli_bbus}{scan_storcli_bbu_uuid}{$scan_storcli_bbu_uuid}{scan_storcli_bbu_serial_number}, "sql::scan_storcli_bbus::scan_storcli_bbu_uuid::${scan_storcli_bbu_uuid}::scan_storcli_bbu_type" => $anvil->data->{sql}{scan_storcli_bbus}{scan_storcli_bbu_uuid}{$scan_storcli_bbu_uuid}{scan_storcli_bbu_type}, "sql::scan_storcli_bbus::scan_storcli_bbu_uuid::${scan_storcli_bbu_uuid}::scan_storcli_bbu_model" => $anvil->data->{sql}{scan_storcli_bbus}{scan_storcli_bbu_uuid}{$scan_storcli_bbu_uuid}{scan_storcli_bbu_model}, "sql::scan_storcli_bbus::scan_storcli_bbu_uuid::${scan_storcli_bbu_uuid}::scan_storcli_bbu_state" => $anvil->data->{sql}{scan_storcli_bbus}{scan_storcli_bbu_uuid}{$scan_storcli_bbu_uuid}{scan_storcli_bbu_state}, "sql::scan_storcli_bbus::scan_storcli_bbu_uuid::${scan_storcli_bbu_uuid}::scan_storcli_bbu_manufacture_date" => $anvil->data->{sql}{scan_storcli_bbus}{scan_storcli_bbu_uuid}{$scan_storcli_bbu_uuid}{scan_storcli_bbu_manufacture_date}, "sql::scan_storcli_bbus::scan_storcli_bbu_uuid::${scan_storcli_bbu_uuid}::scan_storcli_bbu_design_capacity" => $anvil->data->{sql}{scan_storcli_bbus}{scan_storcli_bbu_uuid}{$scan_storcli_bbu_uuid}{scan_storcli_bbu_design_capacity}, "sql::scan_storcli_bbus::scan_storcli_bbu_uuid::${scan_storcli_bbu_uuid}::scan_storcli_bbu_replacement_needed" => $anvil->data->{sql}{scan_storcli_bbus}{scan_storcli_bbu_uuid}{$scan_storcli_bbu_uuid}{scan_storcli_bbu_replacement_needed}, }}); } undef $results; # The virtual drives data... $query = " SELECT scan_storcli_virtual_drive_uuid, scan_storcli_virtual_drive_controller_uuid, scan_storcli_virtual_drive_id_string, scan_storcli_virtual_drive_creation_date, scan_storcli_virtual_drive_data_protection, scan_storcli_virtual_drive_disk_cache_policy, scan_storcli_virtual_drive_emulation_type, scan_storcli_virtual_drive_encryption, scan_storcli_virtual_drive_blocks, scan_storcli_virtual_drive_strip_size, scan_storcli_virtual_drive_drives_per_span, scan_storcli_virtual_drive_span_depth, scan_storcli_virtual_drive_scsi_naa_id FROM scan_storcli_virtual_drives WHERE scan_storcli_virtual_drive_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." ;"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); $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_storcli_virtual_drive_uuid = $row->[0]; my $scan_storcli_virtual_drive_controller_uuid = $row->[1]; my $scan_storcli_virtual_drive_id_string = $row->[2]; my $scan_storcli_virtual_drive_creation_date = defined $row->[3] ? $row->[3] : ""; my $scan_storcli_virtual_drive_data_protection = defined $row->[4] ? $row->[4] : ""; my $scan_storcli_virtual_drive_disk_cache_policy = defined $row->[5] ? $row->[5] : ""; my $scan_storcli_virtual_drive_emulation_type = defined $row->[6] ? $row->[6] : ""; my $scan_storcli_virtual_drive_encryption = defined $row->[7] ? $row->[7] : ""; my $scan_storcli_virtual_drive_blocks = defined $row->[8] ? $row->[8] : ""; my $scan_storcli_virtual_drive_strip_size = defined $row->[9] ? $row->[9] : ""; my $scan_storcli_virtual_drive_drives_per_span = defined $row->[10] ? $row->[10] : ""; my $scan_storcli_virtual_drive_span_depth = defined $row->[11] ? $row->[11] : ""; my $scan_storcli_virtual_drive_scsi_naa_id = defined $row->[12] ? $row->[12] : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_virtual_drive_uuid => $scan_storcli_virtual_drive_uuid, scan_storcli_virtual_drive_controller_uuid => $scan_storcli_virtual_drive_controller_uuid, scan_storcli_virtual_drive_id_string => $scan_storcli_virtual_drive_id_string, scan_storcli_virtual_drive_creation_date => $scan_storcli_virtual_drive_creation_date, scan_storcli_virtual_drive_data_protection => $scan_storcli_virtual_drive_data_protection, scan_storcli_virtual_drive_disk_cache_policy => $scan_storcli_virtual_drive_disk_cache_policy, scan_storcli_virtual_drive_emulation_type => $scan_storcli_virtual_drive_emulation_type, scan_storcli_virtual_drive_encryption => $scan_storcli_virtual_drive_encryption, scan_storcli_virtual_drive_blocks => $scan_storcli_virtual_drive_blocks, scan_storcli_virtual_drive_strip_size => $scan_storcli_virtual_drive_strip_size, scan_storcli_virtual_drive_drives_per_span => $scan_storcli_virtual_drive_drives_per_span, scan_storcli_virtual_drive_span_depth => $scan_storcli_virtual_drive_span_depth, scan_storcli_virtual_drive_scsi_naa_id => $scan_storcli_virtual_drive_scsi_naa_id, }}); # Store the information about this virtual drive. $anvil->data->{'scan-storcli'}{virtual_drives}{by_id_string}{$scan_storcli_virtual_drive_id_string} = $scan_storcli_virtual_drive_uuid; $anvil->data->{'scan-storcli'}{virtual_drives}{by_uuid}{$scan_storcli_virtual_drive_uuid} = $scan_storcli_virtual_drive_id_string; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "scan-storcli::virtual_drives::by_id_string::${scan_storcli_virtual_drive_id_string}" => $anvil->data->{'scan-storcli'}{virtual_drives}{by_id_string}{$scan_storcli_virtual_drive_id_string}, "scan-storcli::virtual_drives::by_uuid::${scan_storcli_virtual_drive_uuid}" => $anvil->data->{'scan-storcli'}{virtual_drives}{by_uuid}{$scan_storcli_virtual_drive_uuid}, }}); # Store the drive group data. (Drive groups have no SN) $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid} = { scan_storcli_virtual_drive_controller_uuid => $scan_storcli_virtual_drive_controller_uuid, scan_storcli_virtual_drive_id_string => $scan_storcli_virtual_drive_id_string, scan_storcli_virtual_drive_creation_date => $scan_storcli_virtual_drive_creation_date, scan_storcli_virtual_drive_data_protection => $scan_storcli_virtual_drive_data_protection, scan_storcli_virtual_drive_disk_cache_policy => $scan_storcli_virtual_drive_disk_cache_policy, scan_storcli_virtual_drive_emulation_type => $scan_storcli_virtual_drive_emulation_type, scan_storcli_virtual_drive_encryption => $scan_storcli_virtual_drive_encryption, scan_storcli_virtual_drive_blocks => $scan_storcli_virtual_drive_blocks, scan_storcli_virtual_drive_strip_size => $scan_storcli_virtual_drive_strip_size, scan_storcli_virtual_drive_drives_per_span => $scan_storcli_virtual_drive_drives_per_span, scan_storcli_virtual_drive_span_depth => $scan_storcli_virtual_drive_span_depth, scan_storcli_virtual_drive_scsi_naa_id => $scan_storcli_virtual_drive_scsi_naa_id, }; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "sql::scan_storcli_virtual_drives::scan_storcli_virtual_drive_uuid::${scan_storcli_virtual_drive_uuid}::scan_storcli_virtual_drive_controller_uuid" => $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}{scan_storcli_virtual_drive_controller_uuid}, "sql::scan_storcli_virtual_drives::scan_storcli_virtual_drive_uuid::${scan_storcli_virtual_drive_uuid}::scan_storcli_virtual_drive_id_string" => $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}{scan_storcli_virtual_drive_id_string}, "sql::scan_storcli_virtual_drives::scan_storcli_virtual_drive_uuid::${scan_storcli_virtual_drive_uuid}::scan_storcli_virtual_drive_creation_date" => $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}{scan_storcli_virtual_drive_creation_date}, "sql::scan_storcli_virtual_drives::scan_storcli_virtual_drive_uuid::${scan_storcli_virtual_drive_uuid}::scan_storcli_virtual_drive_data_protection" => $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}{scan_storcli_virtual_drive_data_protection}, "sql::scan_storcli_virtual_drives::scan_storcli_virtual_drive_uuid::${scan_storcli_virtual_drive_uuid}::scan_storcli_virtual_drive_disk_cache_policy" => $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}{scan_storcli_virtual_drive_disk_cache_policy}, "sql::scan_storcli_virtual_drives::scan_storcli_virtual_drive_uuid::${scan_storcli_virtual_drive_uuid}::scan_storcli_virtual_drive_emulation_type" => $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}{scan_storcli_virtual_drive_emulation_type}, "sql::scan_storcli_virtual_drives::scan_storcli_virtual_drive_uuid::${scan_storcli_virtual_drive_uuid}::scan_storcli_virtual_drive_encryption" => $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}{scan_storcli_virtual_drive_encryption}, "sql::scan_storcli_virtual_drives::scan_storcli_virtual_drive_uuid::${scan_storcli_virtual_drive_uuid}::scan_storcli_virtual_drive_blocks" => $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}{scan_storcli_virtual_drive_blocks}, "sql::scan_storcli_virtual_drives::scan_storcli_virtual_drive_uuid::${scan_storcli_virtual_drive_uuid}::scan_storcli_virtual_drive_strip_size" => $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}{scan_storcli_virtual_drive_strip_size}, "sql::scan_storcli_virtual_drives::scan_storcli_virtual_drive_uuid::${scan_storcli_virtual_drive_uuid}::scan_storcli_virtual_drive_drives_per_span" => $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}{scan_storcli_virtual_drive_drives_per_span}, "sql::scan_storcli_virtual_drives::scan_storcli_virtual_drive_uuid::${scan_storcli_virtual_drive_uuid}::scan_storcli_virtual_drive_span_depth" => $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}{scan_storcli_virtual_drive_span_depth}, "sql::scan_storcli_virtual_drives::scan_storcli_virtual_drive_uuid::${scan_storcli_virtual_drive_uuid}::scan_storcli_virtual_drive_scsi_naa_id" => $anvil->data->{sql}{scan_storcli_virtual_drives}{scan_storcli_virtual_drive_uuid}{$scan_storcli_virtual_drive_uuid}{scan_storcli_virtual_drive_scsi_naa_id}, }}); } undef $results; # The drive group data... $query = " SELECT scan_storcli_drive_group_uuid, scan_storcli_drive_group_virtual_drive_uuid, scan_storcli_drive_group_id_string, scan_storcli_drive_group_access, scan_storcli_drive_group_array_size, scan_storcli_drive_group_array_state, scan_storcli_drive_group_cache, scan_storcli_drive_group_cachecade, scan_storcli_drive_group_consistent, scan_storcli_drive_group_disk_cache, scan_storcli_drive_group_raid_type, scan_storcli_drive_group_read_cache, scan_storcli_drive_group_scheduled_cc, scan_storcli_drive_group_write_cache FROM scan_storcli_drive_groups WHERE scan_storcli_drive_group_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." ;"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); $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_storcli_drive_group_uuid = $row->[0]; my $scan_storcli_drive_group_virtual_drive_uuid = $row->[1]; my $scan_storcli_drive_group_id_string = $row->[2]; my $scan_storcli_drive_group_access = defined $row->[3] ? $row->[3] : ""; my $scan_storcli_drive_group_array_size = defined $row->[4] ? $row->[4] : ""; my $scan_storcli_drive_group_array_state = defined $row->[5] ? $row->[5] : ""; my $scan_storcli_drive_group_cache = defined $row->[6] ? $row->[6] : ""; my $scan_storcli_drive_group_cachecade = defined $row->[7] ? $row->[7] : ""; my $scan_storcli_drive_group_consistent = defined $row->[8] ? $row->[8] : ""; my $scan_storcli_drive_group_disk_cache = defined $row->[9] ? $row->[9] : ""; my $scan_storcli_drive_group_raid_type = defined $row->[10] ? $row->[10] : ""; my $scan_storcli_drive_group_read_cache = defined $row->[11] ? $row->[11] : ""; my $scan_storcli_drive_group_scheduled_cc = defined $row->[12] ? $row->[12] : ""; my $scan_storcli_drive_group_write_cache = defined $row->[13] ? $row->[13] : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_drive_group_uuid => $scan_storcli_drive_group_uuid, scan_storcli_drive_group_virtual_drive_uuid => $scan_storcli_drive_group_virtual_drive_uuid, scan_storcli_drive_group_id_string => $scan_storcli_drive_group_id_string, scan_storcli_drive_group_access => $scan_storcli_drive_group_access, scan_storcli_drive_group_array_size => $scan_storcli_drive_group_array_size, scan_storcli_drive_group_array_state => $scan_storcli_drive_group_array_state, scan_storcli_drive_group_cache => $scan_storcli_drive_group_cache, scan_storcli_drive_group_cachecade => $scan_storcli_drive_group_cachecade, scan_storcli_drive_group_consistent => $scan_storcli_drive_group_consistent, scan_storcli_drive_group_disk_cache => $scan_storcli_drive_group_disk_cache, scan_storcli_drive_group_raid_type => $scan_storcli_drive_group_raid_type, scan_storcli_drive_group_read_cache => $scan_storcli_drive_group_read_cache, scan_storcli_drive_group_scheduled_cc => $scan_storcli_drive_group_scheduled_cc, scan_storcli_drive_group_write_cache => $scan_storcli_drive_group_write_cache, }}); # Store the information about this virtual drive. $anvil->data->{'scan-storcli'}{drive_groups}{by_id_string}{$scan_storcli_drive_group_id_string} = $scan_storcli_drive_group_uuid; $anvil->data->{'scan-storcli'}{drive_groups}{by_uuid}{$scan_storcli_drive_group_uuid} = $scan_storcli_drive_group_id_string; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "scan-storcli::drive_groups::by_id_string::${scan_storcli_drive_group_id_string}" => $anvil->data->{'scan-storcli'}{drive_groups}{by_id_string}{$scan_storcli_drive_group_id_string}, "scan-storcli::drive_groups::by_uuid::${scan_storcli_drive_group_uuid}" => $anvil->data->{'scan-storcli'}{drive_groups}{by_uuid}{$scan_storcli_drive_group_uuid}, }}); # Store the drive group data. (Drive groups have no SN) $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid} = { scan_storcli_drive_group_virtual_drive_uuid => $scan_storcli_drive_group_virtual_drive_uuid, scan_storcli_drive_group_id_string => $scan_storcli_drive_group_id_string, scan_storcli_drive_group_access => $scan_storcli_drive_group_access, scan_storcli_drive_group_array_size => $scan_storcli_drive_group_array_size, scan_storcli_drive_group_array_state => $scan_storcli_drive_group_array_state, scan_storcli_drive_group_cache => $scan_storcli_drive_group_cache, scan_storcli_drive_group_cachecade => $scan_storcli_drive_group_cachecade, scan_storcli_drive_group_consistent => $scan_storcli_drive_group_consistent, scan_storcli_drive_group_disk_cache => $scan_storcli_drive_group_disk_cache, scan_storcli_drive_group_raid_type => $scan_storcli_drive_group_raid_type, scan_storcli_drive_group_read_cache => $scan_storcli_drive_group_read_cache, scan_storcli_drive_group_scheduled_cc => $scan_storcli_drive_group_scheduled_cc, scan_storcli_drive_group_write_cache => $scan_storcli_drive_group_write_cache, }; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "sql::scan_storcli_drive_groups::scan_storcli_drive_group_uuid::${scan_storcli_drive_group_uuid}::scan_storcli_drive_group_virtual_drive_uuid" => $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_virtual_drive_uuid}, "sql::scan_storcli_drive_groups::scan_storcli_drive_group_uuid::${scan_storcli_drive_group_uuid}::scan_storcli_drive_group_id_string" => $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_id_string}, "sql::scan_storcli_drive_groups::scan_storcli_drive_group_uuid::${scan_storcli_drive_group_uuid}::scan_storcli_drive_group_access" => $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_access}, "sql::scan_storcli_drive_groups::scan_storcli_drive_group_uuid::${scan_storcli_drive_group_uuid}::scan_storcli_drive_group_array_size" => $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_array_size}, "sql::scan_storcli_drive_groups::scan_storcli_drive_group_uuid::${scan_storcli_drive_group_uuid}::scan_storcli_drive_group_array_state" => $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_array_state}, "sql::scan_storcli_drive_groups::scan_storcli_drive_group_uuid::${scan_storcli_drive_group_uuid}::scan_storcli_drive_group_cache" => $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_cache}, "sql::scan_storcli_drive_groups::scan_storcli_drive_group_uuid::${scan_storcli_drive_group_uuid}::scan_storcli_drive_group_cachecade" => $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_cachecade}, "sql::scan_storcli_drive_groups::scan_storcli_drive_group_uuid::${scan_storcli_drive_group_uuid}::scan_storcli_drive_group_consistent" => $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_consistent}, "sql::scan_storcli_drive_groups::scan_storcli_drive_group_uuid::${scan_storcli_drive_group_uuid}::scan_storcli_drive_group_disk_cache" => $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_disk_cache}, "sql::scan_storcli_drive_groups::scan_storcli_drive_group_uuid::${scan_storcli_drive_group_uuid}::scan_storcli_drive_group_raid_type" => $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_raid_type}, "sql::scan_storcli_drive_groups::scan_storcli_drive_group_uuid::${scan_storcli_drive_group_uuid}::scan_storcli_drive_group_read_cache" => $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_read_cache}, "sql::scan_storcli_drive_groups::scan_storcli_drive_group_uuid::${scan_storcli_drive_group_uuid}::scan_storcli_drive_group_scheduled_cc" => $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_scheduled_cc}, "sql::scan_storcli_drive_groups::scan_storcli_drive_group_uuid::${scan_storcli_drive_group_uuid}::scan_storcli_drive_group_write_cache" => $anvil->data->{sql}{scan_storcli_drive_groups}{scan_storcli_drive_group_uuid}{$scan_storcli_drive_group_uuid}{scan_storcli_drive_group_write_cache}, }}); } undef $results; # And now, the physical drives. $query = " SELECT scan_storcli_physical_drive_uuid, scan_storcli_physical_drive_controller_uuid, scan_storcli_physical_drive_virtual_drive, scan_storcli_physical_drive_drive_group, scan_storcli_physical_drive_enclosure_id, scan_storcli_physical_drive_slot_number, scan_storcli_physical_drive_serial_number, scan_storcli_physical_drive_size, scan_storcli_physical_drive_sector_size, scan_storcli_physical_drive_vendor, scan_storcli_physical_drive_model, scan_storcli_physical_drive_self_encrypting_drive FROM scan_storcli_physical_drives WHERE scan_storcli_physical_drive_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." ;"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); $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_storcli_physical_drive_uuid = $row->[0]; my $scan_storcli_physical_drive_controller_uuid = $row->[1]; my $scan_storcli_physical_drive_virtual_drive = defined $row->[2] ? $row->[2] : ""; my $scan_storcli_physical_drive_drive_group = defined $row->[3] ? $row->[3] : ""; my $scan_storcli_physical_drive_enclosure_id = defined $row->[4] ? $row->[4] : ""; my $scan_storcli_physical_drive_slot_number = defined $row->[5] ? $row->[5] : ""; my $scan_storcli_physical_drive_serial_number = defined $row->[6] ? $row->[6] : ""; my $scan_storcli_physical_drive_size = defined $row->[7] ? $row->[7] : ""; my $scan_storcli_physical_drive_sector_size = defined $row->[8] ? $row->[8] : ""; my $scan_storcli_physical_drive_vendor = defined $row->[9] ? $row->[9] : ""; my $scan_storcli_physical_drive_model = defined $row->[10] ? $row->[10] : ""; my $scan_storcli_physical_drive_self_encrypting_drive = defined $row->[11] ? $row->[11] : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_physical_drive_uuid => $scan_storcli_physical_drive_uuid, scan_storcli_physical_drive_controller_uuid => $scan_storcli_physical_drive_controller_uuid, scan_storcli_physical_drive_virtual_drive => $scan_storcli_physical_drive_virtual_drive, scan_storcli_physical_drive_drive_group => $scan_storcli_physical_drive_drive_group, scan_storcli_physical_drive_enclosure_id => $scan_storcli_physical_drive_enclosure_id, scan_storcli_physical_drive_slot_number => $scan_storcli_physical_drive_slot_number, scan_storcli_physical_drive_serial_number => $scan_storcli_physical_drive_serial_number, scan_storcli_physical_drive_size => $scan_storcli_physical_drive_size, scan_storcli_physical_drive_sector_size => $scan_storcli_physical_drive_sector_size, scan_storcli_physical_drive_vendor => $scan_storcli_physical_drive_vendor, scan_storcli_physical_drive_model => $scan_storcli_physical_drive_model, scan_storcli_physical_drive_self_encrypting_drive => $scan_storcli_physical_drive_self_encrypting_drive, }}); # Make it so that we can look up the serial number from the drive's UUID and vice versa $anvil->data->{'scan-storcli'}{physical_drives}{by_serial}{$scan_storcli_physical_drive_serial_number} = $scan_storcli_physical_drive_uuid; $anvil->data->{'scan-storcli'}{physical_drives}{by_uuid}{$scan_storcli_physical_drive_uuid} = $scan_storcli_physical_drive_serial_number; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "scan-storcli::physical_drives::by_serial::${scan_storcli_physical_drive_serial_number}" => $anvil->data->{'scan-storcli'}{physical_drives}{by_serial}{$scan_storcli_physical_drive_serial_number}, "scan-storcli::physical_drives::by_uuid::${scan_storcli_physical_drive_uuid}" => $anvil->data->{'scan-storcli'}{physical_drives}{by_uuid}{$scan_storcli_physical_drive_uuid}, }}); # Store the information about this physical drive $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid} = { scan_storcli_physical_drive_controller_uuid => $scan_storcli_physical_drive_controller_uuid, scan_storcli_physical_drive_virtual_drive => $scan_storcli_physical_drive_virtual_drive, scan_storcli_physical_drive_drive_group => $scan_storcli_physical_drive_drive_group, scan_storcli_physical_drive_enclosure_id => $scan_storcli_physical_drive_enclosure_id, scan_storcli_physical_drive_slot_number => $scan_storcli_physical_drive_slot_number, scan_storcli_physical_drive_serial_number => $scan_storcli_physical_drive_serial_number, scan_storcli_physical_drive_size => $scan_storcli_physical_drive_size, scan_storcli_physical_drive_sector_size => $scan_storcli_physical_drive_sector_size, scan_storcli_physical_drive_vendor => $scan_storcli_physical_drive_vendor, scan_storcli_physical_drive_model => $scan_storcli_physical_drive_model, scan_storcli_physical_drive_self_encrypting_drive => $scan_storcli_physical_drive_self_encrypting_drive, }; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "sql::scan_storcli_physical_drives::scan_storcli_physical_drive_uuid::${scan_storcli_physical_drive_uuid}::scan_storcli_physical_drive_controller_uuid" => $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}{scan_storcli_physical_drive_controller_uuid}, "sql::scan_storcli_physical_drives::scan_storcli_physical_drive_uuid::${scan_storcli_physical_drive_uuid}::scan_storcli_physical_drive_virtual_drive" => $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}{scan_storcli_physical_drive_virtual_drive}, "sql::scan_storcli_physical_drives::scan_storcli_physical_drive_uuid::${scan_storcli_physical_drive_uuid}::scan_storcli_physical_drive_drive_group" => $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}{scan_storcli_physical_drive_drive_group}, "sql::scan_storcli_physical_drives::scan_storcli_physical_drive_uuid::${scan_storcli_physical_drive_uuid}::scan_storcli_physical_drive_enclosure_id" => $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}{scan_storcli_physical_drive_enclosure_id}, "sql::scan_storcli_physical_drives::scan_storcli_physical_drive_uuid::${scan_storcli_physical_drive_uuid}::scan_storcli_physical_drive_slot_number" => $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}{scan_storcli_physical_drive_slot_number}, "sql::scan_storcli_physical_drives::scan_storcli_physical_drive_uuid::${scan_storcli_physical_drive_uuid}::scan_storcli_physical_drive_serial_number" => $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}{scan_storcli_physical_drive_serial_number}, "sql::scan_storcli_physical_drives::scan_storcli_physical_drive_uuid::${scan_storcli_physical_drive_uuid}::scan_storcli_physical_drive_size" => $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}{scan_storcli_physical_drive_size}, "sql::scan_storcli_physical_drives::scan_storcli_physical_drive_uuid::${scan_storcli_physical_drive_uuid}::scan_storcli_physical_drive_sector_size" => $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}{scan_storcli_physical_drive_sector_size}, "sql::scan_storcli_physical_drives::scan_storcli_physical_drive_uuid::${scan_storcli_physical_drive_uuid}::scan_storcli_physical_drive_vendor" => $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}{scan_storcli_physical_drive_vendor}, "sql::scan_storcli_physical_drives::scan_storcli_physical_drive_uuid::${scan_storcli_physical_drive_uuid}::scan_storcli_physical_drive_model" => $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}{scan_storcli_physical_drive_model}, "sql::scan_storcli_physical_drives::scan_storcli_physical_drive_uuid::${scan_storcli_physical_drive_uuid}::scan_storcli_physical_drive_self_encrypting_drive" => $anvil->data->{sql}{scan_storcli_physical_drives}{scan_storcli_physical_drive_uuid}{$scan_storcli_physical_drive_uuid}{scan_storcli_physical_drive_self_encrypting_drive}, }}); } undef $results; # Lastly, the variables. $query = " SELECT scan_storcli_variable_uuid, scan_storcli_variable_source_table, scan_storcli_variable_source_uuid, scan_storcli_variable_is_temperature, scan_storcli_variable_name, scan_storcli_variable_value FROM scan_storcli_variables WHERE scan_storcli_variable_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." ;"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); $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_storcli_variable_uuid = $row->[0]; my $scan_storcli_variable_source_table = $row->[1]; my $scan_storcli_variable_source_uuid = $row->[2]; my $scan_storcli_variable_is_temperature = $row->[3]; my $scan_storcli_variable_name = $row->[4]; my $scan_storcli_variable_value = defined $row->[5] ? $row->[5] : ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_storcli_variable_uuid => $scan_storcli_variable_uuid, scan_storcli_variable_source_table => $scan_storcli_variable_source_table, scan_storcli_variable_source_uuid => $scan_storcli_variable_source_uuid, scan_storcli_variable_is_temperature => $scan_storcli_variable_is_temperature, scan_storcli_variable_name => $scan_storcli_variable_name, scan_storcli_variable_value => $scan_storcli_variable_value, }}); # We store these differently for easier reference. my $type = $scan_storcli_variable_is_temperature eq "1" ? "temperature" : "variable"; $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{$scan_storcli_variable_source_table}{source_uuid}{$scan_storcli_variable_source_uuid}{$type}{$scan_storcli_variable_name} = { scan_storcli_variable_uuid => $scan_storcli_variable_uuid, scan_storcli_variable_is_temperature => $scan_storcli_variable_is_temperature, scan_storcli_variable_value => $scan_storcli_variable_value, }; # Entries are so long that we log the one per variable. $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "sql::scan_storcli_variables::scan_storcli_variable_uuid::source_table::${scan_storcli_variable_source_table}::source_uuid::${scan_storcli_variable_source_uuid}::${type}::${scan_storcli_variable_name}::scan_storcli_variable_uuid" => $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{$scan_storcli_variable_source_table}{source_uuid}{$scan_storcli_variable_source_uuid}{$type}{$scan_storcli_variable_name}{scan_storcli_variable_uuid}, }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "sql::scan_storcli_variables::scan_storcli_variable_uuid::source_table::${scan_storcli_variable_source_table}::source_uuid::${scan_storcli_variable_source_uuid}::${type}::${scan_storcli_variable_name}::scan_storcli_variable_is_temperature" => $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{$scan_storcli_variable_source_table}{source_uuid}{$scan_storcli_variable_source_uuid}{$type}{$scan_storcli_variable_name}{scan_storcli_variable_is_temperature}, }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "sql::scan_storcli_variables::scan_storcli_variable_uuid::source_table::${scan_storcli_variable_source_table}::source_uuid::${scan_storcli_variable_source_uuid}::${type}::${scan_storcli_variable_name}::scan_storcli_variable_value" => $anvil->data->{sql}{scan_storcli_variables}{scan_storcli_variable_uuid}{source_table}{$scan_storcli_variable_source_table}{source_uuid}{$scan_storcli_variable_source_uuid}{$type}{$scan_storcli_variable_name}{scan_storcli_variable_value}, }}); } undef $results; # Return the number return(0); } # This gathers the various data from the controller(s). sub gather_data { my ($anvil) = @_; ### TODO: This assumes the controllers go 0, 1, ... n. If this is wrong, we'll need to call ### 'storcli64 show all' and parse 'System Overview'. # Loops through reach found controller. foreach my $controller (1..$anvil->data->{'scan-storcli'}{adapter_count}) { # We drop the number by 1 because the '/cX' starts at '0' where the controller count starts # at '1'. my $adapter = ($controller - 1); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { controller => $controller, adapter => $adapter, }}); # Read in controller data. my $serial_number = get_controller_info($anvil, $adapter); # We use dummy VDs and DGs to store drives not allocated to either yet. The drives will # reference their parent controller, but the VDs and DGs won't. To deal with this, we need to # allocate the pseudo DG and VG to something, so we'll use the first controller's SN that we # see. if ($controller eq "1") { my $scan_storcli_virtual_drive_id_string = $serial_number."-vd9999"; $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{variable}{on_controller} = $serial_number; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${scan_storcli_virtual_drive_id_string}::variable::on_controller" => $anvil->data->{virtual_drive}{$scan_storcli_virtual_drive_id_string}{variable}{on_controller}, }}); } # Read in cachevault (FBU) data (if it exists). get_cachevault_data($anvil, $adapter, $serial_number); # Read in BBU data (if it exists). get_bbu_data($anvil, $adapter, $serial_number); # Read in virtual drive information. get_virtual_drive_data($anvil, $adapter, $serial_number); # Read in the physical disk information. get_physical_disk_data($anvil, $adapter, $serial_number); } return(0); } # This looks for physical disks on the controller. sub get_physical_disk_data { my ($anvil, $adapter, $serial_number) = @_; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { adapter => $adapter, serial_number => $serial_number, }}); my $virtual_drive = $serial_number."-vd9999"; my $drive_group = 9999; my $enclosure_id = ""; my $slot_number = ""; my $device_id = ""; my $state = ""; my $drive_size = ""; my $interface = ""; my $drive_media = ""; my $self_encrypting_drive = ""; my $protection_info = ""; my $drive_model = ""; my $spun_up = ""; my $in_drive_header = 0; my $in_port_status = 0; my $start_break = 0; my $sector_size = 0; my $sector_variables = []; my $shell_call = $anvil->data->{path}{exe}{storcli64}." /c".$adapter." ".$anvil->data->{'scan-storcli'}{arguments}{physical_disk_data}; my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code, }}); foreach my $line (split/\n/, $output) { $line = $anvil->Words->clean_spaces({string => $line}); $line =~ s/\s+:/:/; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); ### TODO: Make sure we don't hit a case where we don't find the virtual drive ID string ### before we start recording data. If we do, we'll have to artificially create the ID ### from the controller's serial number (which is pretty safe). # If I am in the drive header, look for this drive data # EID :Slt DID State DG Size Intf Med SED PI SeSz Model Sp # 252 :0 9 Onln 0 136.218 GB SAS HDD N N 512B MK1401GRRB U if (($in_drive_header) && ($line =~ /^$enclosure_id:$slot_number\s+(\d+)\s+(.*?)\s+(.*?)\s+(.*?B)\s+(.*?)\s+(.*?)\s+(.*?)\s+(.*?)\s+(.*?B)\s+(.*?)\s+(.*)$/)) { $device_id = $1; $state = $2; $drive_group = $3; $drive_size = $4; $interface = $5; $drive_media = $6; $self_encrypting_drive = $7; $protection_info = $8; $sector_size = $9; $drive_model = $10; $spun_up = $11; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { enclosure_id => $enclosure_id, slot_number => $slot_number, device_id => $device_id, 'state' => $state, drive_group => $drive_group, drive_size => $drive_size, interface => $interface, drive_media => $drive_media, self_encrypting_drive => $self_encrypting_drive, protection_info => $protection_info, sector_size => $sector_size, drive_model => $drive_model, spun_up => $spun_up, }}); # If it isn't in a drive group, it also won't be in a virtual drive. if ($drive_group eq "-") { $drive_group = 9999 ; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { drive_group => $drive_group }}); } elsif ($drive_group =~ /^\d+$/) { # Find the virtual drive this drive is connected to, if any. foreach my $this_virtual_drive (sort {$a cmp $b} keys %{$anvil->data->{virtual_drive}}) { # This avoids auto-vivication of the drive group under the virtual drive next if not exists $anvil->data->{virtual_drive}{$this_virtual_drive}{drive_group}{$drive_group}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${this_virtual_drive}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::drive_size" => $anvil->data->{virtual_drive}{$this_virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{drive_size}, }}); if ($anvil->data->{virtual_drive}{$this_virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{drive_size}) { # Found it. $virtual_drive = $this_virtual_drive; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { virtual_drive => $virtual_drive }}); last; } } } else { # Not sure that this should ever happen... $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1 , key => "scan_storcli_warning_0005", variables => { line => $line, drive_group => $drive_group, }}); } $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { virtual_drive => $virtual_drive }}); # We need to record this here because drives not in a drive group will be missed # later in process_vg_pd_list_data(). Drives processed there may overwrite this data, # which is fine. $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{device_id} = $device_id; $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{'state'} = $state; $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{drive_size} = $drive_size; $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{interface} = $interface; $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{drive_media} = $drive_media; $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{self_encrypting_drive} = $self_encrypting_drive; $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{protection_info} = $protection_info; $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{sector_size} = $sector_size; $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{drive_model} = $drive_model; $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{spun_up} = $spun_up; $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{on_controller} = $serial_number; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${virtual_drive}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::device_id" => $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{device_id}, "virtual_drive::${virtual_drive}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::state" => $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{'state'}, "virtual_drive::${virtual_drive}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::drive_size" => $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{drive_size}, "virtual_drive::${virtual_drive}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::interface" => $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{interface}, "virtual_drive::${virtual_drive}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::drive_media" => $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{drive_media}, "virtual_drive::${virtual_drive}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::self_encrypting_drive" => $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{self_encrypting_drive}, "virtual_drive::${virtual_drive}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::protection_info" => $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{protection_info}, "virtual_drive::${virtual_drive}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::sector_size" => $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{sector_size}, "virtual_drive::${virtual_drive}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::drive_model" => $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{drive_model}, "virtual_drive::${virtual_drive}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::spun_up" => $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{spun_up}, "virtual_drive::${virtual_drive}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::on_controller" => $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{on_controller}, }}); next; } # See if I am entering or exiting a section. if ($line =~ /Drive \/c$adapter\/e(\d+)\/s(\d+):/i) { $enclosure_id = $1; $slot_number = $2; #$slot_number = sprintf("%02d", $slot_number); $in_drive_header = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { enclosure_id => $enclosure_id, slot_number => $slot_number, in_drive_header => $in_drive_header, }}); next; } if ($line =~ /Drive \/c$adapter\/e(\d+)\/s(\d+) State/i) { $enclosure_id = $1; $slot_number = $2; #$slot_number = sprintf("%02d", $slot_number); $in_drive_header = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { enclosure_id => $enclosure_id, slot_number => $slot_number, in_drive_header => $in_drive_header, }}); next; } if ($line =~ /^Port Status /) { $in_port_status = 1; $start_break = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_port_status => $in_port_status, start_break => $start_break, }}); } if (($line =~ /^--------/) && ($in_port_status)) { if (not $start_break) { # Split point set, must be the start break $start_break = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { start_break => $start_break }}); next; } else { # In 'port status' and start break set, must be end break. $in_port_status = 0; $start_break = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_port_status => $in_port_status, start_break => $start_break, }}); ### NOTE: So far as I can tell, there is only ever two SAS ports on hard ### drives. The way the system parses them handles N-number of ports, ### though. For now, we'll squeeze these into the top layer variables ### to save having to have another table to process. my $sas_port_0_port_status = $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{sas_port}{0}{port_status}; my $sas_port_0_link_speed = $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{sas_port}{0}{link_speed}; my $sas_port_0_sas_address = $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{sas_port}{0}{sas_address}; my $sas_port_1_port_status = $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{sas_port}{1}{port_status}; my $sas_port_1_link_speed = $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{sas_port}{1}{link_speed}; my $sas_port_1_sas_address = $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{sas_port}{1}{sas_address}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { sas_port_0_port_status => $sas_port_0_port_status, sas_port_0_link_speed => $sas_port_0_link_speed, sas_port_0_sas_address => $sas_port_0_sas_address, sas_port_1_port_status => $sas_port_1_port_status, sas_port_1_link_speed => $sas_port_1_link_speed, sas_port_1_sas_address => $sas_port_1_sas_address, }}); $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{sas_port_0_port_status} = $sas_port_0_port_status; $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{sas_port_0_link_speed} = $sas_port_0_link_speed; $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{sas_port_0_sas_address} = $sas_port_0_sas_address; $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{sas_port_1_port_status} = $sas_port_1_port_status; $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{sas_port_1_link_speed} = $sas_port_1_link_speed; $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{sas_port_1_sas_address} = $sas_port_1_sas_address; next; } } # We stop processing a given drive when we see 'Inquiry Data'. if ($line =~ /^Inquiry Data =/) { # Process and variables that need their size calculated from sectors to bytes. # These are always variables. foreach my $variable (sort {$a cmp $b} @{$sector_variables}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${virtual_drive}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::$variable" => $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable}, }}); next if not $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable}; my $value = $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, }}); if ($value =~ /^(\d+) bytes, (\d+) sectors/) { my $size = $1; my $sectors = $2; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { size => $size, sectors => $sectors, sector_size => $sector_size, }}); if ($sector_size) { $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable} = ($sectors * $sector_size); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${virtual_drive}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::$variable" => $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable}, }}); } else { $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable} = $size; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${virtual_drive}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::$variable" => $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable}, }}); } } } # Record the drive as being on this controller. $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{on_controller} = $serial_number; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${virtual_drive}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::on_controller" => $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{on_controller}, }}); $virtual_drive = $serial_number."-vd9999"; $drive_group = 9999; $enclosure_id = ""; $slot_number = ""; $in_drive_header = 0; $in_port_status = 0; $start_break = 0; $sector_size = 0; $sector_variables = []; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { virtual_drive => $virtual_drive }}); } next if $enclosure_id eq ""; # If I am in the port status, parse the port info. if (($in_port_status) && ($line =~ /^(\d+)\s+(.*?)\s+(\d.*?)Gb\/s\s+(0x.*)$/)) { my $sas_port = $1; my $port_status = $2; my $link_speed = $3." #!variable!scan_storcli_unit_0013!#"; my $sas_address = $4; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { sas_port => $sas_port, port_status => $port_status, link_speed => $link_speed, sas_address => $sas_address, }}); $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{sas_port}{$sas_port}{port_status} = $port_status; $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{sas_port}{$sas_port}{link_speed} = $link_speed; $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{sas_port}{$sas_port}{sas_address} = $sas_address; # These are so flipping long that we print them as three separate log entries so that # they're easier to read in the logs. $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${virtual_drive}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::sas_port::${sas_port}::port_status" => $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{sas_port}{$sas_port}{port_status}, }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${virtual_drive}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::sas_port::${sas_port}::link_speed" => $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{sas_port}{$sas_port}{link_speed}, }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${virtual_drive}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::sas_port::${sas_port}::sas_address" => $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{sas_port}{$sas_port}{sas_address}, }}); next; } ### If I have a 'variable = value' pair split and parse. # The 'Drive position' isn't set for drives not in an array/drive group. if ($line =~ /^Drive position = DriveGroup:(\d+), Span:(\d+), Row:(\d+)/i) { my $this_drive_group = $1; my $span = $2; my $row = $3; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { this_drive_group => $this_drive_group, span => $span, row => $row, }}); # I don't pick up this until well into the scan, but the drive group should match # what we found earlier. Just to be safe though, if not, throw a warning. if ($this_drive_group ne $drive_group) { # Report this error $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1 , key => "scan_storcli_warning_0001", variables => { adapter => $adapter, serial_number => $serial_number, virtual_drive => $virtual_drive, enclosure_id => $enclosure_id, slot_number => $slot_number, span => $span, row => $row, old_drive_group => $drive_group, new_drive_group => $this_drive_group, shell_call => $shell_call, }}); $drive_group = $this_drive_group; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { drive_group => $drive_group }}); } $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{span} = $span; $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{row} = $row; # These are so flipping long that we print them as three separate log entries so that # they're easier to read in the logs. $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${virtual_drive}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::span" => $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{span}, }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${virtual_drive}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::row" => $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{row}, }}); next; } # Split and process all 'variable = value' lines we've not already handled. if ($line =~ /^(.*?)\s+=\s+(.*)$/) { my $variable = $1; my $value = $2; my $type = "variable"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, }}); # Process some variable names. if ($variable =~ /^SN$/i) { $variable = "Serial Number"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable }}); } if ($variable =~ /^WWN$/i) { $variable = "World Wide Name"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable }}); } if ($variable =~ /^PI /i) { $variable =~ s/^PI /Protection Information /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable }}); } if ($variable =~ / Id$/i) { $variable =~ s/ Id$/ Identification/; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable }}); } if ($variable =~ /^Sector Size$/i) { # Convert to bytes and record. $value = $anvil->Convert->human_readable_to_bytes({base2 => 1, size => $value})." #!string!scan_storcli_unit_0001!#"; $sector_size = $value; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { value => $value, sector_size => $sector_size, }}); } if ($variable =~ / EKM/i) { # De-TLA it $variable =~ s/ EKM/ External Key Management/; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable }}); } if ($variable =~ /S\.M\.A\.R\.T\./i) { $variable =~ s/S\.M\.A\.R\.T\./SMART/; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable }}); } if ($variable =~ /S\.M\.A\.R\.T/i) { $variable =~ s/S\.M\.A\.R\.T/SMART/; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable }}); } # Process some values. if ($value =~ /^(\d.*?B) \[(0x.*?) Sectors\]/) { my $size = $1; my $hex_sectors = $2; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { size => $size, hex_sectors => $hex_sectors, }}); my $sectors = Math::BigInt->new($hex_sectors); my $bytes = $anvil->Convert->human_readable_to_bytes({base2 => 1, size => $size}); $value = $bytes." #!variable!scan_storcli_unit_0001!#, ".$sectors." #!variable!scan_storcli_unit_0012!#"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { size => $size, 'bytes' => $bytes, hex_sectors => $hex_sectors, value => $value, variable => $variable, }}); # Mark this for sector size calculation later. push @{$sector_variables}, $variable; } if (($value =~ /^(\d+\s*[A-Za-z]+B)$/i) or ($value =~ /^(\d+\.\d+\s*[A-Za-z]+B)$/i)) { my $size = $1; $value = $anvil->Convert->human_readable_to_bytes({base2 => 1, size => $size})." #!string!scan_storcli_unit_0001!#"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { size => $size, value => $value, }}); } if ($value =~ /^(\d.*?)C \(\d.*? F\)/i) { $value = $1; $type = "temperature"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { value => $value, type => $type, }}); } if (($value =~ /^(\d+)C$/i) or ($value =~ /^(\d+\.\d+)C$/i)) { $value = $1; $type = "temperature"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { value => $value, type => $type, }}); } if (($value =~ /^(\d+)F$/i) or ($value =~ /^(\d+\.\d+)F$/i)) { $value = $anvil->Convert->fahrenheit_to_celsius({temperature => $1}); $type = "temperature"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { value => $value, type => $type, }}); } if ($value =~ /^(\d.*?)Gb\/s$/) { $value = $1." #!variable!scan_storcli_unit_0013!#"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { value => $value }}); } if ($value eq "NA") { $value = "N/A"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { value => $value }}); } # Process/standardize the variable. $variable = process_variable_name($anvil, $variable); # Record $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{$type}{$variable} = $value; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${virtual_drive}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::${type}::$variable" => $anvil->data->{virtual_drive}{$virtual_drive}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{$type}{$variable}, }}); } } return(0); } # This looks for virtual drives on the controller. sub get_virtual_drive_data { my ($anvil, $adapter, $serial_number) = @_; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { adapter => $adapter, serial_number => $serial_number, }}); my $virtual_drive = ""; my $id_string = ""; my $in_overview = 0; my $in_pd_list = 0; my $in_vd_data = 0; my $start_break = 0; my ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{storcli64}." /c".$adapter." ".$anvil->data->{'scan-storcli'}{arguments}{virtual_drive_data}}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code, }}); foreach my $line (split/\n/, $output) { $line = $anvil->Words->clean_spaces({string => $line}); $line =~ s/\s+:/:/; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); if ($line =~ /\/c$adapter\/v(\d+):/) { $virtual_drive = $1; $id_string = $serial_number."-vd".$virtual_drive; $anvil->data->{virtual_drive}{$id_string}{variable}{on_controller} = $serial_number; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { virtual_drive => $virtual_drive, id_string => $id_string, "virtual_drive::${id_string}::variable::on_controller" => $anvil->data->{virtual_drive}{$id_string}{variable}{on_controller}, }}); # We set the VD '9999' to the same SN so that we can find unallocated disks later. my $unallocated_id_string = $serial_number."-vd9999"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { unallocated_id_string => $unallocated_id_string }}); if (not $anvil->data->{virtual_drive}{$unallocated_id_string}{variable}{on_controller}) { $anvil->data->{virtual_drive}{$unallocated_id_string}{variable}{on_controller} = $serial_number; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${unallocated_id_string}::variable::on_controller" => $anvil->data->{virtual_drive}{$unallocated_id_string}{variable}{on_controller}, }}); } } if ($line =~ /PDs for VD (\d+) :/) { $virtual_drive = $1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { virtual_drive => $virtual_drive }}); } if ($line =~ /VD(\d+) Properties/) { $virtual_drive = $1; $in_vd_data = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { virtual_drive => $virtual_drive, in_vd_data => $in_vd_data, }}); } next if $virtual_drive eq ""; # See if I am entering or exiting the overview chunk. if ($line =~ /^DG\/VD/) { $in_overview = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_overview => $in_overview }}); next; } if (($line =~ /^--------/) && ($in_overview)) { if (not $start_break) { # Split point set, must be the start break $start_break = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { start_break => $start_break }}); next; } else { # Split point and start break set, must be end break. $in_overview = 0; $start_break = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_overview => $in_overview, start_break => $start_break, }}); next; } } if ($in_overview) { process_vg_overview_data($anvil, $line, $serial_number); } # See if I am entering or exiting the physical drive chunk. if ($line =~ /^EID:Slt/) { $in_pd_list = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_pd_list => $in_pd_list }}); next; } if (($line =~ /^--------/) && ($in_pd_list)) { if (not $start_break) { # Split point set, must be the start break $start_break = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { start_break => $start_break }}); next; } else { # Split point and start break set, must be end break. $in_pd_list = 0; $start_break = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_pd_list => $in_pd_list, start_break => $start_break, }}); next; } } if ($in_pd_list) { process_vg_pd_list_data($anvil, $line, $virtual_drive, $serial_number, $id_string); } if ($in_vd_data) { if ($line =~ /^(.*?) = (.*)$/) { my $variable = $1; my $value = $2; my $type = "variable"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, }}); # Convert some formatting. if (($value =~ /^(\d+\s*[A-Za-z]+B)$/i) or ($value =~ /^(\d+\.\d+\s*[A-Za-z]+B)$/i)) { # Convert to bytes $value = $anvil->Convert->human_readable_to_bytes({base2 => 1, size => $value})." #!string!scan_storcli_unit_0001!#"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { value => $value }}); } elsif ($value =~ /^(\d\d)-(\d\d)-(\d\d\d\d)$/) { # Convert dd-mm-yyyy to yyyy/mm/dd $value = $3."/".$2."/".$1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { value => $value }}); } elsif ($value =~ /^(\d\d):(\d\d):(\d\d) (\wM)$/) { # Convert AM/PM -> 24h my $hour = $1; my $minute = $2; my $second = $3; my $suffix = $4; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { hour => $hour, minute => $minute, second => $second, suffix => $suffix, }}); if ($suffix eq "PM") { $hour += 12; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { hour => $hour }}); } $value = "$hour:$minute:$second"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { value => $value }}); } # Record $variable = process_variable_name($anvil, $variable); $anvil->data->{virtual_drive}{$id_string}{$type}{$variable} = $value; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${id_string}::${type}::$variable" => $anvil->data->{virtual_drive}{$id_string}{$type}{$variable}, }}); } } } return(0); } # This parses a virtual drive's physical disk overview line sub process_vg_pd_list_data { my ($anvil, $line, $virtual_drive, $serial_number, $id_string) = @_; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "line" => $line, }}); ### Sector size is either '512B' or '4 KB' # EID:Slt DID State DG Size Intf Med SED PI SeSz Model Sp # 252:0 9 Onln 0 136.218 GB SAS HDD N N 512B MK1401GRRB U if ($line =~ /^(\d+):(\d+)\s+(\d+)\s+(.*?)\s+(.*?)\s+(.*?B)\s+(.*?)\s+(.*?)\s+(.*?)\s+(.*?)\s+(.*?B)\s+(.*?)\s+(.*)$/) { my $enclosure_id = $1; my $slot_number = $2; my $device_id = $3; my $state = $4; my $drive_group = $5; my $drive_size = $6; my $interface = $7; my $drive_media = $8; my $self_encrypting_drive = $9; my $protection_info = $10; my $sector_size = $11; my $drive_model = $12; my $spun_up = $13; $drive_group = 9999 if $drive_group eq "-"; $protection_info = $protection_info =~ /n/i ? "No" : "Yes"; $self_encrypting_drive = $self_encrypting_drive =~ /n/i ? "No" : "Yes"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "enclosure_id" => $enclosure_id, "slot_number" => $slot_number, "device_id" => $device_id, "state" => $state, "drive_group" => $drive_group, "drive_size" => $drive_size, "interface" => $interface, "drive_media" => $drive_media, "self_encrypting_drive" => $self_encrypting_drive, "protection_info" => $protection_info, "sector_size" => $sector_size, "drive_model" => $drive_model, "spun_up" => $spun_up, }}); # Convert the sector and drive sizes into bytes. The controller uses 'xB' but uses base2 values. $sector_size = $anvil->Convert->human_readable_to_bytes({base2 => 1, size => $sector_size})." #!string!scan_storcli_unit_0001!#"; $drive_size = $anvil->Convert->human_readable_to_bytes({base2 => 1, size => $drive_size})." #!string!scan_storcli_unit_0001!#"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "sector_size" => $sector_size, "drive_size" => $drive_size, }}); ### Long hashes are long... x_x # Store the data (we'll convert it in a minute. $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{device_id} = $device_id; $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{'state'} = $state; $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{drive_size} = $drive_size; $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{interface} = $interface; $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{drive_media} = $drive_media; $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{self_encrypting_drive} = $self_encrypting_drive; $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{protection_info} = $protection_info; $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{sector_size} = $sector_size; $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{drive_model} = $drive_model; $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{spun_up} = $spun_up; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::device_id" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{device_id}, "virtual_drive::${id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::state" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{'state'}, "virtual_drive::${id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::drive_size" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{drive_size}, "virtual_drive::${id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::interface" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{interface}, "virtual_drive::${id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::drive_media" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{drive_media}, "virtual_drive::${id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::self_encrypting_drive" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{self_encrypting_drive}, "virtual_drive::${id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::protection_info" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{protection_info}, "virtual_drive::${id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::sector_size" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{sector_size}, "virtual_drive::${id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::drive_model" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{drive_model}, "virtual_drive::${id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::spun_up" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{spun_up}, }}); ### WARNING: The strings we set here will be parsed later, so don't change them without also ### changing where they're checked for elsewhere in this agent. ### NOTE: We don't use a function for this because the glossary for the block of data is ### specific for the table above (it would appear). # Translate the weird short form to useable strings foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}}) { my $value = $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { ">> virtual_drive::${id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable}, }}); if ($value =~ /^DHS$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable} = "Dedicated Hot Spare"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable}, }}); } if ($value =~ /^UGood$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable} = "Unconfigured Good"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable}, }}); } if ($value =~ /^GHS$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable} = "Global Hotspare"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable}, }}); } if ($value =~ /^UBad$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable} = "Unconfigured Bad"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable}, }}); } if ($value =~ /^Onln$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable} = "Online"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable}, }}); } if ($value =~ /^Offln$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable} = "Offline"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable}, }}); } if ($value =~ /^U$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable} = "Spun Up"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable}, }}); } if ($value =~ /^D$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable} = "Spun Down"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable}, }}); } if ($value =~ /^T$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable} = "Transition"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable}, }}); } if ($value =~ /^F$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable} = "Foreign"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable}, }}); } if ($value =~ /^UGUnsp$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable} = "Unsupported"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable}, }}); } if ($value =~ /^UGShld$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable} = "Unconfigured shielded"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable}, }}); } if ($value =~ /^HSPShld$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable} = "Hotspare shielded"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable}, }}); } if ($value =~ /^CFShld$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable} = "Configured shielded"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable}, }}); } if ($value =~ /^Cpybck$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable} = "Copyback"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable}, }}); } if ($value =~ /^CBShld$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable} = "Copyback Shielded"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::enclosure_id::${enclosure_id}::slot_number::${slot_number}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{enclosure_id}{$enclosure_id}{slot_number}{$slot_number}{variable}{$variable}, }}); } } } else { # Unparsed line $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "? line" => $line, }}); } return(0); } # This parses a virtual drive overview line sub process_vg_overview_data { my ($anvil, $line, $serial_number) = @_; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line, serial_number => $serial_number, }}); my $drive_group = ""; my $virtual_drive = ""; my $raid_type = ""; my $array_state = ""; my $access = ""; my $consistent = ""; my $cache = ""; my $cachecade = ""; my $scheduled_consistency_check = ""; my $array_size = ""; my $name = ""; my $id_string = ""; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "line" => $line, }}); # Get the line when there is no name # 0 / 0 RAID5 Optl RW Yes NRWBD - OFF 953.531 GB if ($line =~ /^(\d+)\/(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+ \wB)$/i) { $drive_group = $1; $virtual_drive = $2; $raid_type = $3; $array_state = $4; $access = $5; $consistent = $6; $cache = $7; $cachecade = $8; $scheduled_consistency_check = $9; $array_size = $10; $id_string = $serial_number."-vd".$virtual_drive; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "drive_group" => $drive_group, "virtual_drive" => $virtual_drive, "raid_type" => $raid_type, "array_state" => $array_state, "access" => $access, "consistent" => $consistent, "cache" => $cache, "cachecade" => $cachecade, "scheduled_consistency_check" => $scheduled_consistency_check, "array_size" => $array_size, "id_string" => $id_string, }}); } # Get the line when there is a name # 0 / 0 RAID5 Optl RW Yes RWBD - OFF 3.271 TB VD0 # 1 / 1 RAID5 Optl RW Yes RWBD - OFF 744.187 GB VD1 # 2 / 2 RAID0 Optl RW Yes RWBD - OFF 1.636 TB VD2 elsif ($line =~ /^(\d+)\/(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+ \wB)\s+(.*)$/i) { $drive_group = $1; $virtual_drive = $2; $raid_type = $3; $array_state = $4; $access = $5; $consistent = $6; $cache = $7; $cachecade = $8; $scheduled_consistency_check = $9; $array_size = $10; $name = $11; $id_string = $serial_number."-vd".$virtual_drive; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "drive_group" => $drive_group, "virtual_drive" => $virtual_drive, "raid_type" => $raid_type, "array_state" => $array_state, "access" => $access, "consistent" => $consistent, "cache" => $cache, "cachecade" => $cachecade, "scheduled_consistency_check" => $scheduled_consistency_check, "array_size" => $array_size, "name" => $name, "id_string" => $id_string, }}); } else { # Unmatched line... $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "? line" => $line, }}); next; } # Convert the array size into bytes. The controller uses 'xB' but uses base2 values. $array_size = $anvil->Convert->human_readable_to_bytes({base2 => 1, size => $array_size})." #!string!scan_storcli_unit_0001!#"; # Store the data. $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{on_controller} = $serial_number; $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{raid_type} = $raid_type; $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{array_state} = $array_state; $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{access} = $access; $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{consistent} = $consistent; $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{cache} = $cache; $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{cachecade} = $cachecade; $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{scheduled_consistency_check} = $scheduled_consistency_check; $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{array_size} = $array_size; $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{name} = $name; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${id_string}::drive_group::${drive_group}::variable::on_controller" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{on_controller}, "virtual_drive::${id_string}::drive_group::${drive_group}::variable::raid_type" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{raid_type}, "virtual_drive::${id_string}::drive_group::${drive_group}::variable::array_state" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{array_state}, "virtual_drive::${id_string}::drive_group::${drive_group}::variable::access" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{access}, "virtual_drive::${id_string}::drive_group::${drive_group}::variable::consistent" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{consistent}, "virtual_drive::${id_string}::drive_group::${drive_group}::variable::cache" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{cache}, "virtual_drive::${id_string}::drive_group::${drive_group}::variable::cachecade" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{cachecade}, "virtual_drive::${id_string}::drive_group::${drive_group}::variable::scheduled_consistency_check" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{scheduled_consistency_check}, "virtual_drive::${id_string}::drive_group::${drive_group}::variable::array_size" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{array_size}, "virtual_drive::${id_string}::drive_group::${drive_group}::variable::name" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{name}, }}); ### WARNING: The strings we set here will be parsed later, so don't change them without also changing ### where they're checked for elsewhere in this agent. ### NOTE: We don't use a function for this because the glossary for the block of data is specific for ### the table above (it would appear). # Translate the weird short form to useable strings foreach my $variable (sort {$a cmp $b} keys %{$anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}}) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { ">> virtual_drive::${id_string}::drive_group::${drive_group}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable}, }}); if ($anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} eq "-") { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} = "No"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable}, }}); } elsif ($anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} =~ /^Cac$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} = "CacheCade"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable}, }}); } elsif ($anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} =~ /^Rec$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} = "Recovery"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable}, }}); } elsif ($anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} =~ /^OfLn$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} = "OffLine"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable}, }}); } elsif ($anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} =~ /^Pdgd$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} = "Partially Degraded"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable}, }}); } elsif ($anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} =~ /^dgrd$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} = "Degraded"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable}, }}); } elsif ($anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} =~ /^Optl$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} = "Optimal"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable}, }}); } elsif ($anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} =~ /^RO$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} = "Read Only"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable}, }}); } elsif ($anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} =~ /^RW$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} = "Read Write"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable}, }}); } elsif ($anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} =~ /^HD$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} = "Hidden"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable}, }}); } elsif ($anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} =~ /^TRANS$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} = "Transport Ready"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable}, }}); } elsif ($anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} =~ /^B$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} = "Blocked"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable}, }}); } elsif ($anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} =~ /^Consist$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} = "Consistent"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable}, }}); } elsif ($anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} =~ /^sCC$/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable} = "Scheduled Check Consistency"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< virtual_drive::${id_string}::drive_group::${drive_group}::variable::$variable" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable}, }}); } # Because they hate programmers, the cache is a combination of a few of the # above strings. if ($variable eq "cache") { my $cache = $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{$variable}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cache" => $cache, }}); # Prep some blanks $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{read_cache} = ""; $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{write_cache} = ""; $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{disk_cache} = ""; # Read cache if ($cache =~ /NR/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{read_cache} = "No Read-Ahead"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${id_string}::drive_group::${drive_group}::variable::read_cache" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{read_cache}, }}); } elsif ($cache =~ /R/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{read_cache} = "Always Read-Ahead"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${id_string}::drive_group::${drive_group}::variable::read_cache" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{read_cache}, }}); } # Write cache if ($cache =~ /AWB/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{write_cache} = "Always Write-Back"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${id_string}::drive_group::${drive_group}::variable::write_cache" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{write_cache}, }}); } elsif ($cache =~ /WB/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{write_cache} = "Write-Back"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${id_string}::drive_group::${drive_group}::variable::write_cache" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{write_cache}, }}); } elsif ($cache =~ /WT/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{write_cache} = "Write-Through"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${id_string}::drive_group::${drive_group}::variable::write_cache" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{write_cache}, }}); } # Disk cache if ($cache =~ /C/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{disk_cache} = "Cached IO"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${id_string}::drive_group::${drive_group}::variable::disk_cache" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{disk_cache}, }}); } elsif ($cache =~ /D/i) { $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{disk_cache} = "Direct IO"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "virtual_drive::${id_string}::drive_group::${drive_group}::variable::disk_cache" => $anvil->data->{virtual_drive}{$id_string}{drive_group}{$drive_group}{variable}{disk_cache}, }}); } } } return(0); } # This looks for a BBU and, if it finds one, parses the output for it. sub get_bbu_data { my ($anvil, $adapter, $serial_number) = @_; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { adapter => $adapter, serial_number => $serial_number, }}); my $bbu_serial_number = ""; my $bbu_data = []; my ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{storcli64}." /c".$adapter." ".$anvil->data->{'scan-storcli'}{arguments}{bbu_data}}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code, }}); foreach my $line (split/\n/, $output) { $line = $anvil->Words->clean_spaces({string => $line}); $line =~ s/\s+:/:/; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); last if $line =~ /$adapter Failed /i; if ($line =~ /^Serial Number\s+(\S.*)?/i) { $bbu_serial_number = $1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bbu_serial_number => $bbu_serial_number }}); next; } push @{$bbu_data}, $line; } # If I didn't find a serial number, then I probably don't have an FBU. return(0) if not $bbu_serial_number; # Record the controller the bbu is on. $anvil->data->{bbu}{serial_number}{$bbu_serial_number}{host_controller_serial_number} = $serial_number; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "bbu::serial_number::${bbu_serial_number}::host_controller_serial_number" => $anvil->data->{bbu}{serial_number}{$bbu_serial_number}{host_controller_serial_number}, }}); # Still alive? Good, time to parse the most annoying output ever... >_< my $split_point = 0; my $start_break = 0; my $end_break = 0; foreach my $line (@{$bbu_data}) { next if not $line; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); if ($line =~ /Property(\s+)Value/) { $split_point = length($1) + 8; $start_break = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { split_point => $split_point }}); next; } # See if I am entering or exiting a data chunk. if (($line =~ /^--------/) && ($split_point)) { if (not $start_break) { # Split point set, must be the start break $start_break = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { start_break => $start_break }}); next; } else { # Split point and start break set, must be end break. $split_point = 0; $start_break = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { split_point => $split_point, start_break => $start_break, }}); next; } } # If I have a split point, break the string. if ($split_point) { # Elegant? I think not! my $variable = ""; my $value = ""; my $type = "variable"; if ($split_point >= length($line)) { # Variable with no value $variable = $line; $variable =~ s/\s+$//; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, }}); } else { ($variable, $value) = ($line =~ /^(.{$split_point})(.*)$/); $variable =~ s/\s+$//; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, }}); # Because LSI is either evil or amazingly incompetent, we have the variable # "Absolute State of charge" listed twice, but once with 'State' and the # other with 'state' (capital versus small 'S'). We fix it because it is the # same data and we don't want two records, so the second copy will now just # overwrite the first one. if ($variable eq "Absolute State of charge") { $variable = "Absolute state of charge"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable }}); } # They use 'Temperature' twice; once for the temperature and once to show if # it is OK or not. We'll avoid one clobbering the other by renaming the # status one. if (($variable eq "Temperature") && ($value !~ /^\d/)) { $variable = "Temperature Status"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable }}); } if (($variable eq "Temperature") && ($value =~ /^\d/)) { $variable = "BBU Temperature"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable }}); } # *sigh* - Typo on their part, but also a re-use of the variable name # "Remaining Time Alarm". The Status one we will rename the variable and the # later we'll fix the type. if ((lc($variable) eq "remaining time alarm") && ($value !~ /\d/)) { # No digit in the value, so this is the status. $variable = "Remaining Time Alarm Status"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable }}); } if ($variable =~ /remining /) { $variable =~ s/emining /emaining /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable }}); } if ($variable =~ /\(initial setting\)/i) { $variable =~ s/\(initial setting\)//i; $variable = "Initial ".$variable." Setting"; $variable =~ s/\s+//; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable }}); } } # Is this a temperature or something else we want to pre-process? if ($value =~ /^(\d+) C$/i) { # Yup $value = $1; $type = "temperature"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, }}); } elsif ($value =~ /^(\d+) F$/i) { # Yup, but translate $value = $anvil->Convert->fahrenheit_to_celsius({temperature => $1}); $type = "temperature"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, }}); } elsif ($value =~ /\d+d \((\d+) seconds\)$/i) { # This is a static time span (like the time between learn cycles. $value = $1; $value .= " #!variable!scan_storcli_unit_0006!#"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, }}); } elsif ($value =~ /^(\d\d\d\d)\/(\d\d)\/(\d\d)\s+(\d\d:\d\d:\d\d)\s+\(\d+ seconds\)$/i) { ### NOTE: The 'X seconds' seems to not change and be a useless number... # This is a specific time in the future, properly formatted $value = $1."/".$2."/".$3.", ".$4; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, }}); } elsif ($value =~ /^(\d\d)\/(\d\d)\/(\d\d\d\d)$/i) { # 'Murica! mm/dd/yyyy -> yyyy/mm/dd $value = $3."/".$2."/".$1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, }}); } elsif ($value =~ /^(\d+) hour\(s\)$/i) { # 'Murica! mm/dd/yyyy -> yyyy/mm/dd $value = $1 * 3600; $value .= " #!variable!scan_storcli_unit_0006!#"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, }}); } # Record $variable = process_variable_name($anvil, $variable); # 'Auto-Learn' and 'Auto_Learn' are both used because screw consistency, right? $variable =~ s/auto-learn/auto_learn/; $anvil->data->{bbu}{serial_number}{$bbu_serial_number}{$type}{$variable} = $value; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "bbu:serial_number::${bbu_serial_number}::${type}::$variable" => $anvil->data->{bbu}{serial_number}{$bbu_serial_number}{$type}{$variable}, }}); } } return(0); } # This looks for a cachevault (flash-backup unit) and, if one is found, parses the output. sub get_cachevault_data { my ($anvil, $adapter, $serial_number) = @_; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { adapter => $adapter, serial_number => $serial_number, }}); my $cachevault_serial_number = ""; my $cachevault_data = []; my ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{storcli64}." /c".$adapter." ".$anvil->data->{'scan-storcli'}{arguments}{cachevault_data}}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code, }}); foreach my $line (split/\n/, $output) { $line = $anvil->Words->clean_spaces({string => $line}); $line =~ s/\s+:/:/; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); last if $line =~ /Cachevault doesn't exist/i; if ($line =~ /^Serial Number\s+(\S.*)?/i) { $cachevault_serial_number = $1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { cachevault_serial_number => $cachevault_serial_number }}); next; } push @{$cachevault_data}, $line; } # If I didn't find a serial number, then I probably don't have an FBU. return(0) if not $cachevault_serial_number; # Record the controller the cachevault is on. $anvil->data->{cachevault}{serial_number}{$cachevault_serial_number}{host_controller_serial_number} = $serial_number; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cachevault::serial_number::${cachevault_serial_number}::host_controller_serial_number" => $anvil->data->{cachevault}{serial_number}{$cachevault_serial_number}{host_controller_serial_number}, }}); # Still alive? Good, time to parse the most annoying output ever... >_< my $split_point = 0; my $start_break = 0; my $end_break = 0; foreach my $line (@{$cachevault_data}) { next if not $line; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); if ($line =~ /Property(\s+)Value/) { $split_point = length($1) + 8; $start_break = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { split_point => $split_point }}); next; } # See if I am entering or exiting a data chunk. if (($line =~ /^--------/) && ($split_point)) { if (not $start_break) { # Split point set, must be the start break $start_break = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { start_break => $start_break }}); next; } else { # Split point and start break set, must be end break. $split_point = 0; $start_break = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { split_point => $split_point, start_break => $start_break, }}); next; } } # If I have a split point, break the string. if ($split_point) { my $variable = ""; my $value = ""; my $type = "variable"; if ($split_point >= length($line)) { # Variable with no value $variable = $line; $variable =~ s/\s+$//; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, }}); } else { ($variable, $value) = ($line =~ /^(.{$split_point})(.*)$/); $variable =~ s/\s+$//; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, }}); } # With BBUs, they use 'Temperature' twice; once for status and once for the current # temperature. They don't currently do this with Cachevaults, but in case they do # later, we'll rename the temperature variable. if (($variable eq "Temperature") && ($value =~ /^\d/)) { $variable = "Cachevault Temperature"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable }}); } # Is this a temperature or something else we want to pre-process? if ($value =~ /^(\d+) C$/i) { # Yup $value = $1; $type = "temperature"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, }}); } elsif ($value =~ /^(\d+) F$/i) { # Yup, but translate $value = $anvil->Convert->fahrenheit_to_celsius({temperature => $1}); $type = "temperature"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, }}); } elsif ($value =~ /\d+d \((\d+) seconds\)$/i) { # This is a static time span (like the time between learn cycles. $value = $1; $value .= " #!variable!scan_storcli_unit_0006!#"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, }}); } elsif ($value =~ /^(\d\d\d\d)\/(\d\d)\/(\d\d)\s+(\d\d:\d\d:\d\d)\s+\(\d+ seconds\)$/i) { ### NOTE: The 'X seconds' seems to not change and be a useless number... # This is a specific time in the future, properly formatted # yyyy/mm/dd -> $value = $1."/".$2."/".$3.", ".$4; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, }}); } elsif ($value =~ /^(\d\d)\/(\d\d)\/(\d\d\d\d)$/i) { # 'Murica! mm/dd/yyyy -> yyyy/mm/dd $value = $3."/".$2."/".$1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, }}); } elsif ($value =~ /^(\d+) hour\(s\)$/i) { # 'Murica! mm/dd/yyyy -> yyyy/mm/dd $value = $1 * 3600; $value .= " #!variable!scan_storcli_unit_0006!#"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, }}); } # Record $variable = process_variable_name($anvil, $variable); # 'Auto-Learn' and 'Auto_Learn' are both used because screw consistency, right? $variable =~ s/auto-learn/auto_learn/; $anvil->data->{cachevault}{serial_number}{$cachevault_serial_number}{$type}{$variable} = $value; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "cachevault:serial_number::${cachevault_serial_number}::${type}::$variable" => $anvil->data->{cachevault}{serial_number}{$cachevault_serial_number}{$type}{$variable}, }}); } } return(0); } # This gets the basic information about the controller. sub get_controller_info { my ($anvil, $adapter) = @_; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { adapter => $adapter }}); my $in_raid_level_supported = 0; my $multiline_value = ""; my $in_header = 1; my $in_basics = 0; my $in_supported_ops = 0; my $in_supported_pd_ops = 0; my $in_supported_vd_ops = 0; my $in_hardware_config = 0; my $in_capabilities = 0; my $in_policies = 0; my $in_defaults = 0; my $serial_number = ""; my $controller_data = []; my ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{storcli64}." /c".$adapter." ".$anvil->data->{'scan-storcli'}{arguments}{controller_info}}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code, }}); foreach my $line (split/\n/, $output) { $line = $anvil->Words->clean_spaces({string => $line}); $line =~ s/\s+:/:/; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); next if not $line; last if $line =~ /^TOPOLOGY:$/i; # The time changes constantly, so ignore it. if ($line =~ /Date\/Time/i) { next; } # RAID Level Supported can be multi-line because $reasons. if ($line =~ /RAID Level Supported = (.*)$/) { $multiline_value = $1; $in_raid_level_supported = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { multiline_value => $multiline_value, in_raid_level_supported => $in_raid_level_supported, }}); } if ($in_raid_level_supported) { if ($line =~ / = /) { # Found the next line, close up RAID Level Supported. push @{$controller_data}, "RAID Level Supported = ".$multiline_value; $multiline_value = ""; $in_raid_level_supported = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { multiline_value => $multiline_value, in_raid_level_supported => $in_raid_level_supported, }}); } else { $multiline_value .= " $line"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { multiline_value => $multiline_value }}); next; } } if ($line =~ /^Serial Number = (.*)?/i) { $serial_number = $1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { serial_number => $serial_number }}); next; } ### LSI reuses the save variable names in different sections. This tries to catch and rename ### them # Ignore stuff in the header if ($line =~ /^Basics/) { $in_header = 0; $in_basics = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_header => $in_header, in_basics => $in_basics, }}); } if ($in_basics) { if ($line =~ /^Version/) { $in_basics = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_basics => $in_basics }}); } } elsif ($line =~ /^Controller\s+=/) { # This is the header data, which we don't care about. next; } if ($in_header) { next; } # Mangle supported adapter operation variables. if ($line =~ /^Supported Adapter Operations/) { $in_supported_ops = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_supported_ops => $in_supported_ops }}); } if ($in_supported_ops) { if ($line =~ /^Supported PD Operations/) { $in_supported_ops = 0; $in_supported_pd_ops = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_supported_ops => $in_supported_ops, in_supported_pd_ops => $in_supported_pd_ops, }}); } if ($line =~ /^BBU /) { $line =~ s/^BBU /BBU Supported /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); } if ($line =~ /^Rebuild Rate /) { $line =~ s/^Rebuild Rate /Configurable Rebuild Rate /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); } if ($line =~ /^CC Rate /) { $line =~ s/^CC Rate /Configurable Consistency Check Rate /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); } if ($line =~ /^BGI Rate /) { $line =~ s/^BGI Rate /Configurable background Initialization Rate /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); } if ($line =~ /^Reconstruct Rate /) { $line =~ s/^Reconstruct Rate /Configurable Reconstruct Rate /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); } if ($line =~ /^Patrol Read Rate /) { $line =~ s/^Patrol Read Rate /Configurable Patrol Read Rate /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); } if ($line =~ /^Alarm Control /) { $line =~ s/^Alarm Control /Configurable Alarm Control /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); } if ($line =~ /^Spanning /) { $line =~ s/^Spanning /Spanning Supported /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); } if ($line =~ /Hot Spare /) { $line =~ s/Hot Spare /Hot Spare Supported /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); } if ($line =~ /^Foreign Config Import /) { $line =~ s/^Foreign Config Import /Foreign Config Import Supported /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); } if ($line =~ /^Self Diagnostic /) { $line =~ s/^Self Diagnostic /Self Diagnostic Supported /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); } if ($line =~ /^Abort CC on Error /) { $line =~ s/^Abort CC on Error /Configurable Abort CC on Error /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); } if ($line =~ /^Block SSD Write Disk Cache Change /) { $line =~ s/^Block SSD Write Disk Cache Change /Configurable Block SSD Write Disk Cache Change /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); } } # Mangle supported adapter physical disk operation variables. if ($in_supported_pd_ops) { if ($line =~ /^Supported VD Operations/) { $in_supported_pd_ops = 0; $in_supported_vd_ops = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_supported_pd_ops => $in_supported_pd_ops, in_supported_vd_ops => $in_supported_vd_ops, }}); } if ($line =~ /^Deny Locate /) { $line =~ s/^Deny Locate /Deny Physical Disk Locate /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); } if ($line =~ /^Read Policy /) { $line =~ s/^Read Policy /Physical Disk Read Policy /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); } if ($line =~ /^Write Policy /) { $line =~ s/^Write Policy /Physical Disk Write Policy /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); } } # Mangle supported adapter virtual drive operation variables. if ($in_supported_vd_ops) { if ($line =~ /^Advanced Software Option/) { $in_supported_vd_ops = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_supported_vd_ops => $in_supported_vd_ops }}); } if ($line =~ /^Deny Locate /) { $line =~ s/^Deny Locate /Deny Virtual Disk Locate /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); } if ($line =~ /^Read Policy /) { $line =~ s/^Read Policy /Virtual Disk Read Policy /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); } if ($line =~ /^Write Policy /) { $line =~ s/^Write Policy /Virtual Disk Write Policy /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); } } # Mangle hardware config variables. if ($line =~ /^HwCfg/) { $in_hardware_config = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "in_hardware_config" => $in_hardware_config }}); } if ($in_hardware_config) { if ($line =~ /^Policies/) { $in_hardware_config = 0; $in_policies = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_hardware_config => $in_hardware_config, in_policies => $in_policies, }}); } if ($line =~ /^BBU /) { $line =~ s/^BBU /BBU Connected /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); } } # Mangle policy variables. if ($in_policies) { if ($line =~ /^Boot/) { $in_policies = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_policies => $in_policies }}); } if ($line =~ /^Disable Online Controller Reset /) { $line =~ s/^Disable Online Controller Reset /Disable Online Controller Reset Policy /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); } if ($line =~ /^Expose Enclosure Devices /) { $line =~ s/^Expose Enclosure Devices /Expose Enclosure Devices Policy /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); } if ($line =~ /^Maintain PD Fail History /) { $line =~ s/^Maintain PD Fail History /Maintain PD Fail History Policy /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); } } # Mangle defaults variables if ($line =~ /^Defaults/) { $in_defaults = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_defaults => $in_defaults }}); } if ($in_defaults) { if ($line =~ /^Capabilities/) { $in_defaults = 0; $in_capabilities = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_defaults => $in_defaults, in_capabilities => $in_capabilities, }}); } if ($line =~ /^Disable Online Controller Reset /) { $line =~ s/^Disable Online Controller Reset /Disable Online Controller Reset Default /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); } if ($line =~ /^Expose Enclosure Devices /) { $line =~ s/^Expose Enclosure Devices /Expose Enclosure Devices Default /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); } if ($line =~ /^Maintain PD Fail History /) { $line =~ s/^Maintain PD Fail History /Maintain PD Fail History Default /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); } # Many of the variables in the 'Defaults' section are also used for their current # values. So to help differentiate them, we're going to prefix the variables with # 'Default '. if (($in_defaults) && ($line =~ / = /)) { $line = "Default ".$line if $line !~ /^Default /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); } } # Mangle capability variables. if ($in_capabilities) { if ($line =~ /^Scheduled Tasks/) { $in_capabilities = 0; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { in_capabilities => $in_capabilities }}); } if ($line =~ /^Boot Volume Supported /) { $line =~ s/^Boot Volume Supported /Boot Volume Capable /; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); } } # T10-PI (T10 working group, Protection Information) has three levels (Taken from the Seagate # PDF listed below): # Type 0 - Describes a drive that is not formatted with PI information bytes. This allows # for legacy support in non-PI systems. # Type 1 - Provides support of PI protection using 10- and 16-byte commands. The RDPROTECT # and WRTPROTECT bits allow for checking control through the CDB. Eight bytes of # Protection Information are transmitted at sector boundaries across the interface # if RDPROTECT and WRTPROTECT bits are non-zero values. Type I does not allow the # use of 32-byte commands. # Type 2 - Provides checking control and additional expected fields within the 32-byte CDBs. # Eight bytes of Protection Information are transmitted at sector boundaries across # the interface if RDPROTECT and WRTPROTECT bits are non-zero values. Type II does # allow the use of 10- and 16-byte commands with zero values in the RDPROTECT and # WRTPROTECT fields. The drive will generate a dummy (for example, 0xFFFF) eight # bytes of Protection Information in the media, but these eight bytes will not be # transferred to the host during read. # Type 3 - ? (GUARD tag, reference tag and app tag are combined) # - http://www.snia.org/sites/default/education/tutorials/2008/fall/storage/RichardVanderbilt_Why_Data_Integrity_rev2.pdf # Protection is enabled and the 32-byte commands are not valid. The Reference Tag is # not defined and may be used as an extension of the Application Tag. The drive will # not check the Reference Tag. # - https://www.hgst.com/sites/default/files/resources/End-to-end_Data_Protection.pdf # Type 3 does not define the ref tag. # - http://lxr.free-electrons.com/source/block/t10-pi.c # # LSI loves to randomly sticking things together... $line =~ s/BatteryFRU/Battery FRU/; $line =~ s/ChipRevision/Chip Revision/; $line =~ s/DisableHII/Disable HII/; $line =~ s/EnableCrashDump/Enable Crash-Dump/; $line =~ s/EnableLDBBM/Enable LD_BBM/; $line =~ s/elementcount/element_count/i; $line =~ s/perio/per_io/i; ### NOTE: ROC is RAID on Chip, which is what they call their controller's ASIC # and they love their weird short forms $line =~ s/Ctrl/Controller/i; $line =~ s/Mfg/Manufacture/i; $line =~ s/Cfg/Config/i; $line =~ s/Perf /Performance /i; $line =~ s/ Ext / External /i; $line =~ s/ VD/ Virtual Disk/i; $line =~ s/ VDs/ Virtual Disks/i; $line =~ s/VD /Virtual Disk /i; $line =~ s/ PD/ Physical Disk/i; $line =~ s/ PI/ Protection Information/i; $line =~ s/ LDPI/ Logical Disk Protection Information/i; # https://www.seagate.com/files/staticfiles/docs/pdf/whitepaper/safeguarding-data-from-corruption-technology-paper-tp621us.pdf $line =~ s/ CC/ Consistency Check/i; $line =~ s/ BGI/ Background Initialization/i; $line =~ s/ BGI/ Background/i; $line =~ s/ LD/ Logical Device/i; $line =~ s/ FW/ Firmware/i; $line =~ s/ HII/ Human Interface Infrastructure/i; $line =~ s/ PFK/ Premium Feature Key/i; $line =~ s/ WB/ Write-Back/i; $line =~ s/SSC /Security Subsystem Class /i; # https://en.wikipedia.org/wiki/Opal_Storage_Specification $line =~ s/ SMP/ Serial Management Protocol/i; # https://en.wikipedia.org/wiki/Serial_Attached_SCSI#Characteristics $line =~ s/ STP/ Serial ATA Tunneling Protocol/i; # https://en.wikipedia.org/wiki/Serial_Attached_SCSI#Characteristics $line =~ s/Phys /Physical Layers /i; # 12Gps MegaRAID SAS Software User Guide, March 14, 2016, 4.14.8, https://en.wikipedia.org/wiki/PHY_(chip) $line =~ s/Phy /Physical Layer /i; # 12Gps MegaRAID SAS Software User Guide, March 14, 2016, 4.14.8, https://en.wikipedia.org/wiki/PHY_(chip) $line =~ s/ OCE/ Online Capacity Expansion/i; # 12Gps MegaRAID SAS Software User Guide, March 14, 2016, 5.5.14 -> 5 $line =~ s/ RLM/ RAID Level Migration/i; # 12Gps MegaRAID SAS Software User Guide, March 14, 2016, 5.5.14 -> 5 $line =~ s/ EKM/ External Key Management/i; $line =~ s/ BBM/ Bad Block Management/i; $line =~ s/ QD/ Queue Depth/i; $line =~ s/NCQ/Native Command Queuing/i; # https://en.wikipedia.org/wiki/Native_Command_Queuing $line =~ s/ LDBBM/ Logical Disk Bad Block Management/i; $line =~ s/TPM/Trusted Platform Module/i; # https://en.wikipedia.org/wiki/Trusted_Platform_Module $line =~ s/\(hrs\)/\(#!variable!scan_storcli_unit_0002!#\)/i; $line =~ s/ hrs/ #!variable!scan_storcli_unit_0002!#/i; $line =~ s/ZCR /Zero-Channel RAID /i; $line =~ s/R1E /RAID 1E /i; $line =~ s/ R10/ RAID 10/i; $line =~ s/RAID(\d+)/RAID $1/gi; $line =~ s/TTY /Terminal /i; $line =~ s/CME /Continuous Media Error/i; $line =~ s/SGE /Scatter-Gather Element/i; # https://en.wikipedia.org/wiki/Vectored_I/O # Standardize some random strings with the same meaning $line =~ s/Id /ID /; $line =~ s/ NA/ N\/A/; $line =~ s/Bios/BIOS/; $line =~ s/S\.M\.A\.R\.T\./SMART/i; $line =~ s/S\.M\.A\.R\.T/SMART/i; # And randomly using sentances... $line =~ s/A rollback operation is in progress/Roll-back operation in progress/i; $line =~ s/must be rebooted to complete security operation/Reboot Required for Security Operation/i; $line =~ s/Maximum number of direct attached drives to spin up in 1 min/direct attached drives spun-up per minute/i; # And things that break variables when they change $line =~ s/\(Default\)//; $line =~ s/Sesmonitoring/SCSI Enclosure Service Monitoring/i; # https://en.wikipedia.org/wiki/SES-2_Enclosure_Management $line =~ s/SecurityonJBOD/Security on JBOD/i; $line =~ s/ForceFlash/Force Flash/i; $line =~ s/DisableImmediateIO/Disable Immediate IO/i; $line =~ s/LargeIOSupport/Large IO Support/i; $line =~ s/DrvActivityLEDSetting/Drive Activity LED Setting/i; $line =~ s/FlushWriteVerify/Flush Write Verify/i; $line =~ s/CPLDUpdate/Complex Programmable Logic Device Update/i; # https://en.wikipedia.org/wiki/Complex_programmable_logic_device $line =~ s/ForceTo512e/Force to 512e/i; # 512-byte sector size emulation $line =~ s/discardCacheDuringLDDelete/Discard Cache During Logical Disk Delete/; $line =~ s/Breakmirror/Break Mirror/i; # 12Gps MegaRAID SAS Software User Guide, March 14, 2016, 4.14.16 $line =~ s/Cachebypass/Cache Bypass/i; $line =~ s/PolaritySplit/Polarity Split/i; $line =~ s/EnableCrashDump/Enable Crash Dump/i; $line =~ s/PowerSave/Power Save/i; push @{$controller_data}, $line; } # If I didn't find a serial number, something went wrong. if (not $serial_number) { # Error out. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1 , key => "scan_storcli_error_0006", variables => { adapter => $adapter }}); $anvil->nice_exit({exit_code => 6}); } # Get the current alarm state. undef $output; undef $return_code; ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{storcli64}." /c".$adapter." ".$anvil->data->{'scan-storcli'}{arguments}{alarm_state}}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code, }}); foreach my $line (split/\n/, $output) { $line = $anvil->Words->clean_spaces({string => $line}); $line =~ s/\s+:/:/; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); if ($line =~ /^Alarm\s+(.*)$/i) { my $state = $1; $line = "Alarm State = ".$state; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); push @{$controller_data}, $line; } } # Get the rebuild rate undef $output; undef $return_code; ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{storcli64}." /c".$adapter." ".$anvil->data->{'scan-storcli'}{arguments}{rebuild_rate}}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code, }}); foreach my $line (split/\n/, $output) { $line = $anvil->Words->clean_spaces({string => $line}); $line =~ s/\s+:/:/; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); if ($line =~ /^Rebuildrate\s+(\d+)%$/i) { my $rate = $1; $line = "Rebuild Rate % = ".$rate; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); push @{$controller_data}, $line; } } # Get the background initialization rate undef $output; undef $return_code; ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{storcli64}." /c".$adapter." ".$anvil->data->{'scan-storcli'}{arguments}{bgi_rate}}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code, }}); foreach my $line (split/\n/, $output) { $line = $anvil->Words->clean_spaces({string => $line}); $line =~ s/\s+:/:/; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); if ($line =~ /^BGI Rate\s+(\d+)%$/i) { my $rate = $1; $line = "Background Initialization Rate % = ".$rate; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); push @{$controller_data}, $line; } } # Get the consistency check rate undef $output; undef $return_code; ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{storcli64}." /c".$adapter." ".$anvil->data->{'scan-storcli'}{arguments}{cc_rate}}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code, }}); foreach my $line (split/\n/, $output) { $line = $anvil->Words->clean_spaces({string => $line}); $line =~ s/\s+:/:/; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); if ($line =~ /^CC Rate\s+(\d+)%$/i) { my $rate = $1; $line = "Consistency Check Rate % = ".$rate; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); push @{$controller_data}, $line; } } # Get the patrol read rate undef $output; undef $return_code; ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{storcli64}." /c".$adapter." ".$anvil->data->{'scan-storcli'}{arguments}{pr_rate}}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code, }}); foreach my $line (split/\n/, $output) { $line = $anvil->Words->clean_spaces({string => $line}); $line =~ s/\s+:/:/; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); if ($line =~ /^Patrol Read Rate\s+(\d+)%$/i) { my $rate = $1; $line = "Patrol Read Rate % = ".$rate; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); push @{$controller_data}, $line; } } # Get the performance mode undef $output; undef $return_code; ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{storcli64}." /c".$adapter." ".$anvil->data->{'scan-storcli'}{arguments}{performance_mode}}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code, }}); foreach my $line (split/\n/, $output) { $line = $anvil->Words->clean_spaces({string => $line}); $line =~ s/\s+:/:/; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); if ($line =~ /^Perf Mode\s+(.*)$/i) { my $mode = $1; $line = "Performance Mode = ".$mode; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< line" => $line }}); push @{$controller_data}, $line; } } # If we're alive, we're ready to proceed. foreach my $line (@{$controller_data}) { my $type = "variable"; if ($line =~ /^(.*?)\s+=\s+(.*)$/) { my $variable = $1; my $value = $2; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, }}); # If the variable has units, pull them out. if ($variable =~ /\((\w+B)\)$/i) { my $units = $1; $variable =~ s/\($units\)//i; $variable =~ s/^\s+//; $variable =~ s/\s+$//; my $size = $anvil->Convert->human_readable_to_bytes({size => $value, type => $units, base2 => 1})." #!string!scan_storcli_unit_0001!#"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, units => $units, size => $size, }}); } elsif ($variable =~ /\(Degree Celsius\)/i) { # $variable =~ s/\(Degree Celsius\)/ C/i; $variable =~ s/^\s+//; $variable =~ s/\s+$//; $variable =~ s/\s+/ /; $type = "temperature"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, type => $type, }}); } elsif ($variable =~ /\(Degree Fahrenheit\)/i) { # Covert to °C $variable =~ s/\(Degree Fahrenheit\)//i; $variable =~ s/^\s+//; $variable =~ s/\s+$//; $value = $anvil->Convert->fahrenheit_to_celsius({temperature => $value}); $type = "temperature"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, type => $type, }}); } elsif ($variable =~ /\(hours\)/i) { # THis will get translated when generating an alert $variable =~ s/\(hours\)//; $variable =~ s/^\s+//; $variable =~ s/^\s+//; $variable =~ s/\s+/ /g; $value .= " #!variable!scan_storcli_unit_0002!#"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, }}); } elsif ($variable =~ /\(hour\)/i) { # THis will get translated when generating an alert $variable =~ s/\(min\)//; $variable =~ s/^\s+//; $variable =~ s/^\s+//; $variable =~ s/\s+/ /g; $value .= " #!variable!scan_storcli_unit_0003!#"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, }}); } elsif ($variable =~ /\(mins\)/i) { # THis will get translated when generating an alert $variable =~ s/\(mins\)//; $variable =~ s/^\s+//; $variable =~ s/^\s+//; $variable =~ s/\s+/ /g; $value .= " #!variable!scan_storcli_unit_0004!#"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, }}); } elsif ($variable =~ /\(min\)/i) { # THis will get translated when generating an alert $variable =~ s/\(min\)//; $variable =~ s/^\s+//; $variable =~ s/^\s+//; $variable =~ s/\s+/ /g; $value .= " #!variable!scan_storcli_unit_0005!#"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, }}); } elsif ($variable =~ /\(secs\)/i) { # THis will get translated when generating an alert $variable =~ s/\(secs\)//; $variable =~ s/^\s+//; $variable =~ s/^\s+//; $variable =~ s/\s+/ /g; $value .= " #!variable!scan_storcli_unit_0006!#"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, }}); } elsif ($variable =~ /\(sec\)/i) { # THis will get translated when generating an alert $variable =~ s/\(sec\)//; $variable =~ s/^\s+//; $variable =~ s/^\s+//; $variable =~ s/\s+/ /g; $value .= " #!variable!scan_storcli_unit_0007!#"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { variable => $variable, value => $value, }}); } # Convert some values. if ($value =~ /(\d\d)\/(\d\d)\/(\d\d\d\d), (\d\d:\d\d:\d\d)/) { # mm/dd/yyyy -> yyyy/mm/dd $value = $3."/".$1."/".$2.", ".$4; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { value => $value }}); } if ($value =~ /(\d+) hrs/) { $value = $1." #!variable!scan_storcli_unit_0002!#"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { value => $value }}); } if ($value =~ /\(hrs\)/) { $value =~ s/\(hrs\)/\(#!variable!scan_storcli_unit_0002!#\)/; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { value => $value }}); } if ($value =~ /^(\d+)s/) { $value = $1." #!variable!scan_storcli_unit_0006!#"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { value => $value }}); } if ($value =~ /^0x\w+$/) { # Hex value, leave it alone $anvil->data->{controller}{serial_number}{$serial_number}{$type}{$variable} = $value; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "controller::serial_number::${serial_number}::${type}::${variable}" => $anvil->data->{controller}{serial_number}{$serial_number}{$type}{$variable}, }}); next; } if (($value =~ /^(\d+\s*[A-Za-z]+B)$/i) or ($value =~ /^(\d+\.\d+\s*[A-Za-z]+B)$/i)) { my $size = $1; $value = $anvil->Convert->human_readable_to_bytes({base2 => 1, size => $size}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { size => $size, value => $value, }}); } if ($value =~ /^00\/00\/00$/i) { # N/A $value = "#!variable!scan_storcli_unit_0008!#"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { value => $value }}); } if ($value =~ /^AWB$/i) { $value = "#!variable!scan_storcli_unit_0009!#"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { value => $value }}); } if ($value =~ /^WB$/i) { $value = "#!variable!scan_storcli_unit_0010!#"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { value => $value }}); } if ($value =~ /^WT$/i) { $value = "#!variable!scan_storcli_unit_0011!#"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { value => $value }}); } if ($value =~ /^(\d+) sectors/i) { $value = $1." #!variable!scan_storcli_unit_0012!#"; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { value => $value }}); } # Store it if (exists $anvil->data->{controller}{serial_number}{$serial_number}{$type}{$variable}) { # Conflict! This is a dirty way to keep them separate $variable .= " 2"; } $variable = process_variable_name($anvil, $variable); $anvil->data->{controller}{serial_number}{$serial_number}{$type}{$variable} = $value; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "controller::serial_number::${serial_number}::${type}::${variable}" => $anvil->data->{controller}{serial_number}{$serial_number}{$type}{$variable}, }}); } } $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { serial_number => $serial_number }}); return($serial_number); } # This processes variable names to flatten them and remove spaces and special characters. sub process_variable_name { my ($anvil, $variable) = @_; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { ">> variable" => $variable }}); $variable = lc($variable); $variable =~ s/ /_/g; $variable =~ s/-/_/g; $variable =~ s/&/and/g; $variable =~ s/\//_or_/g; $variable =~ s/_%/_percent/g; $variable =~ s/{_}+/_/g; $variable =~ s/^_+//g; $variable =~ s/_+$//g; $variable =~ s/(\w)\(/$1_\(/; $variable =~ s/\((.*?)\)/-_$1/g; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "<< variable" => $variable }}); return($variable); } # This does two things; It checks to see if storcli64 is installed (exits '1' if not, exits '2' if not # executable) and then checks to see if any controllers are found in the system (exits '3' if not). sub find_lsi_controllers { my ($anvil) = @_; # This will keep track of how many controllers we find. my $adapter_count = 0; # Vendor-specific copies of storcli replace it. So first, check to see if that is the case on this # node. $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "path::exe::perccli64" => $anvil->data->{path}{exe}{perccli64} }}); if (-e $anvil->data->{path}{exe}{perccli64}) { # It does, replace the 'storcli64' path. $anvil->data->{path}{exe}{storcli64} = $anvil->data->{path}{exe}{perccli64}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "path::exe::storcli64" => $anvil->data->{path}{exe}{storcli64} }}); } # Do we have storcli64 installed? if (not -e $anvil->data->{path}{exe}{storcli64}) { # Nope, Call lspci to see if there's a MegaRAID controller. If there is, the user may need to # install the RPM. my ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{lspci}}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code, }}); my $megaraid_installed = 0; foreach my $line (split/\n/, $output) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); if ($line =~ /MegaRAID/i) { # This host appears to have a RAID card, but it's not installed. Lets try to # install it for them. $megaraid_installed = install_storcli($anvil); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { megaraid_installed => $megaraid_installed }}); } } # exit. if (not $megaraid_installed) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 2, key => "scan_storcli_error_0001", variables => { path => $anvil->data->{path}{exe}{storcli64} }}); $anvil->nice_exit({exit_code => 1}); } } # Make sure it is executable if (not -x $anvil->data->{path}{exe}{storcli64}) { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "scan_storcli_error_0002", variables => { path => $anvil->data->{path}{exe}{storcli64} }}); $anvil->nice_exit({exit_code => 2}); } # Still alive? Good! Look for controllers now. my ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{storcli64}." ".$anvil->data->{'scan-storcli'}{arguments}{adapter_count}}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code, }}); foreach my $line (split/\n/, $output) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); if ($line =~ /Controller Count = (\d+)/i) { $adapter_count = $1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { adapter_count => $adapter_count }}); } } # Have we any adapters? if ($adapter_count > 0) { $anvil->data->{'scan-storcli'}{adapter_count} = $adapter_count; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 0, level => 2, key => "scan_storcli_log_0001", variables => { count => $anvil->data->{'scan-storcli'}{adapter_count}, }}); } else { $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 2, level => 0, key => "scan_storcli_error_0003", variables => { path => $anvil->data->{path}{exe}{storcli64}, }}); $anvil->nice_exit({exit_code => 3}); } return(0); } sub install_storcli { my ($anvil) = @_; # Tell the user what we're doing. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, key => "scan_storcli_note_0071"}); # Is this a Dell? my $is_dell = 0; my ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{dmidecode}." --string system-manufacturer"}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code, }}); foreach my $line (split/\n/, $output) { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }}); if ($line =~ /Dell/i) { $is_dell = 1; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { is_dell => $is_dell }}); } } my $rpm_name = $is_dell ? "perccli" : "storcli"; my $shell_call = $anvil->data->{path}{exe}{dnf}." -y install ".$rpm_name; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }}); ($output, $return_code) = $anvil->System->call({shell_call => $shell_call}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code, }}); # Check now to see if the program is installed. If it is, register an alert announcing we installed it. my $program = $is_dell ? $anvil->data->{path}{exe}{perccli64} : $anvil->data->{path}{exe}{storcli64}; if (-e $program) { # Installed successfully! my $variables = { rpm => $rpm_name, }; $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_storcli_note_0072", variables => $variables}); $anvil->Alert->register({ alert_level => "notice", message => "scan_storcli_note_0072", show_header => 1, variables => $variables, sort_position => 0, set_by => $THIS_FILE, }); # Before we return, if we installed for Dell, switch out the 'storcli' program path. if ($is_dell) { $anvil->data->{path}{exe}{storcli64} = $anvil->data->{path}{exe}{perccli64}; $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "path::exe::storcli64" => $anvil->data->{path}{exe}{storcli64} }}); } return(1); } else { # Didn't work. $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, key => "scan_storcli_note_0073"}); } return(0); }