#!/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->Get->switches({list => [
	"force", 
	"purge",
], man => $THIS_FILE});

# 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);

# Shut down.
$anvil->ScanCore->agent_shutdown({agent => $THIS_FILE});


#############################################################################################################
# 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};
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { value => $value }});
				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") && (lc($value) ne "absent"))
			{
				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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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 not $variable_uuid;
					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->Database->refresh_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 '<host_controller_sn>-vd<x>' 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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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
					next if not defined $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 $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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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->Database->refresh_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 ($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 => $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->Database->refresh_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->Database->refresh_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 not $variable_uuid;
					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->Database->refresh_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->Database->refresh_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." #!string!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." #!string!scan_storcli_unit_0001!#, ".$sectors." #!string!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." #!string!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, merge_spaces => 0});
		$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 .= " #!string!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 .= " #!string!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, merge_spaces => 0});
		$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 => { 
					's1:variable' => $variable, 
					's2: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 .= " #!string!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 .= " #!string!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\)/\(#!string!scan_storcli_unit_0002!#\)/i;
		$line =~ s/ hrs/ #!string!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    .= " #!string!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    .= " #!string!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    .= " #!string!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    .= " #!string!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    .= " #!string!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    .= " #!string!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." #!string!scan_storcli_unit_0002!#";
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { value => $value }});
			}
			if ($value =~ /\(hrs\)/)
			{
				$value =~ s/\(hrs\)/\(#!string!scan_storcli_unit_0002!#\)/;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { value => $value }});
			}
			if ($value =~ /^(\d+)s/)
			{
				$value = $1." #!string!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 = "#!string!scan_storcli_unit_0008!#";
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { value => $value }});
			}
			
			if ($value =~ /^AWB$/i)
			{
				$value = "#!string!scan_storcli_unit_0009!#";
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { value => $value }});
			}
			if ($value =~ /^WB$/i)
			{
				$value = "#!string!scan_storcli_unit_0010!#";
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { value => $value }});
			}
			if ($value =~ /^WT$/i)
			{
				$value = "#!string!scan_storcli_unit_0011!#";
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { value => $value }});
			}
			if ($value =~ /^(\d+) sectors/i)
			{
				$value = $1." #!string!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);
}
