#!/usr/bin/perl
# 
# This scans the scan_hardware, like RAM modules, CSS LED status, CPU information, etc.
# 
# Examples;
# 
# Exit codes;
# 0 = Normal exit.
# 1 = Startup failure (not running as root, no DB, bad file read, etc)
# 
# TODO: 
# - Finish System->parse_lwhw(), lots of good stuff in there. 
# - We can figure out what is using swap with:
#   - for file in /proc/*/status ; do awk '/VmSwap|Name/{printf $2 " " $3}END{ print ""}' $file; done | grep kB
#

use strict;
use warnings;
use Anvil::Tools;
use Data::Dumper;
use Text::Diff;

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

# These are the threasholds for when to alert when swap is running out.
$anvil->data->{scancore}{'scan-hardware'}{disable}               = 0;
$anvil->data->{scancore}{'scan-hardware'}{ram}{clear_threshold}  = 134217728;	# 128 MiB
$anvil->data->{scancore}{'scan-hardware'}{ram}{high_threshold}   = 1073741824;	# 1 GiB
$anvil->data->{scancore}{'scan-hardware'}{score}{less_ram}       = 5;
$anvil->data->{scancore}{'scan-hardware'}{swap}{clear_threshold} = 25;
$anvil->data->{scancore}{'scan-hardware'}{swap}{high_threshold}  = 75;

$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 => ["purge"], man => $THIS_FILE});

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

# Read the data.
collect_data($anvil);

# Load stored data.
read_last_scan($anvil);

# Look for changes.
find_changes($anvil);

# Finally, process health weights.
process_health($anvil);

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


#############################################################################################################
# Functions                                                                                                 #
#############################################################################################################

# This reads in data about the CPU
sub collect_cpu_data
{
	my ($anvil) = @_;

	my $total_cores   = 0;
	my $total_threads = 0;
	my $cores         = 0;
	my $threads       = 0;
	my $sockets       = 0;
	my $in_cpu        = "";
	
	my ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{dmidecode}." --type processor", source => $THIS_FILE, line => __LINE__});
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { output => $output, return_code => $return_code }});
	foreach my $line (split/\n/, $output)
	{
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { line => $line }});
		
		if ($line =~ /Socket Designation: (.*+)$/)
		{
			$in_cpu = $1;
			$sockets++;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				in_cpu  => $in_cpu,
				sockets => $sockets,
			}});
		}
		elsif (not $line)
		{
			$in_cpu  = "";
			$cores   = 0;
			$threads = 0;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				in_cpu  => $in_cpu,
				cores   => $cores,
				threads => $threads, 
			}});
		}
		next if $in_cpu eq "";
		
		if ($line =~ /Core Count: (\d+)$/)
		{
			$cores       =  $1;
			$total_cores += $cores; 
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				cores       => $cores,
				total_cores => $total_cores, 
			}});
		}
		if ($line =~ /Thread Count: (\d+)$/)
		{
			$threads       =  $1;
			$total_threads += $threads; 
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				threads       => $threads,
				total_threads => $total_threads, 
			}});
		}
	}
	
	# Read in /proc/cpuinfo.
	my $flags              = "";
	my $flags_mismatch     = 0;
	my $vmx_flags          = "";
	my $vmx_flags_mismatch = 0;
	my $bugs               = "";
	my $bugs_mismatch      = 0;
	my $model              = "";
	my $model_mismatch     = 0;
	my $cpu_info           = $anvil->Storage->read_file({debug => 3, file => $anvil->data->{path}{proc}{cpuinfo}});
	foreach my $line (split/\n/, $cpu_info)
	{
		$line = $anvil->Words->clean_spaces({string => $line});
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { line => $line }});
		
		if ($line =~ /^flags\s*: (.*?)$/)
		{
			# Full list of flags: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/x86/include/asm/cpufeatures.h
			my $these_flags = $1;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { these_flags => $these_flags }});
			if (not $flags)
			{
				$flags = $these_flags;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { flags => $flags }});
			}
			elsif ($flags ne $these_flags)
			{
				# This should never happen.
				   $vmx_flags_mismatch = 1;
				my $changed            = $anvil->Alert->check_alert_sent({
					debug          => 2,
					record_locator => "scan_hardware::cpu_flags_mismatch",
					set_by         => $THIS_FILE,
				});
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }});
				if ($changed)
				{
					# Register an alert.
					my $variables = {
						these_flags => $these_flags,
						flags       => $flags,
					};
					$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0001", variables => $variables});
					$anvil->Alert->register({
						alert_level => "notice", 
						message     => "scan_hardware_alert_0001",
						variables   => $variables,
						set_by      => $THIS_FILE,
					});
				}
			}
		}
		if ($line =~ /^vmx flags\s*: (.*?)$/)
		{
			# Full list of flags: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/arch/x86/include/asm/cpufeatures.h
			my $these_flags = $1;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { these_flags => $these_flags }});
			if (not $vmx_flags)
			{
				$vmx_flags = $these_flags;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { vmx_flags => $vmx_flags }});
			}
			elsif ($vmx_flags ne $these_flags)
			{
				# This should never happen.
				   $flags_mismatch = 1;
				my $changed        = $anvil->Alert->check_alert_sent({
					debug          => 2,
					record_locator => "scan_hardware::cpu_vmx_flags_mismatch",
					set_by         => $THIS_FILE,
				});
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }});
				if ($changed)
				{
					# Register an alert.
					my $variables = {
						these_flags => $these_flags,
						vmx_flags   => $vmx_flags,
					};
					$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0030", variables => $variables});
					$anvil->Alert->register({
						alert_level => "notice", 
						message     => "scan_hardware_alert_0030",
						variables   => $variables,
						set_by      => $THIS_FILE,
					});
				}
			}
		}
		if ($line =~ /^bugs\s*: (.*?)$/)
		{
			my $these_bugs = $1;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { these_bugs => $these_bugs }});
			if (not $bugs)
			{
				$bugs = $these_bugs;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { bugs => $bugs }});
			}
			elsif ($bugs ne $these_bugs)
			{
				# This should never happen...
			 	   $bugs_mismatch = 1;
				my $changed       = $anvil->Alert->check_alert_sent({
					debug          => 3,
					record_locator => "scan_hardware::cpu_bugs_mismatch",
					set_by         => $THIS_FILE,
				});
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }});
				if ($changed)
				{
					# Register an alert.
					$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0003", variables => {
						these_bugs => $these_bugs,
						bugs       => $bugs,
					}});
					$anvil->Alert->register({
						alert_level => "notice", 
						message     => "scan_hardware_alert_0003,!!these_bugs!".$these_bugs."!!,!!bugs!".$bugs."!!", 
						set_by      => $THIS_FILE,
					});
				}
			}
		}
		if ($line =~ /^model name\s*: (.*?)$/)
		{
			my $this_model = $1;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { this_model => $this_model }});
			if (not $model)
			{
				$model = $this_model;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { model => $model }});
			}
			elsif ($model ne $this_model)
			{
				# This should never happen...
				   $model_mismatch = 1;
				my $changed       = $anvil->Alert->check_alert_sent({
					debug          => 3,
					record_locator => "scan_hardware::cpu_model_mismatch",
					set_by         => $THIS_FILE,
				});
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }});
				if ($changed)
				{
					# Register an alert.
					my $variables = {
						this_model => $this_model,
						model       => $model,
					};
					$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0005", variables => $variables});
					$anvil->Alert->register({
						alert_level => "notice", 
						message     => "scan_hardware_alert_0005", 
						variables   => $variables,
						set_by      => $THIS_FILE,
					});
				}
			}
		}
	}
	
	# In the off chance that there was a flags, bugs or name mismatch before, clear it now if the issue is resolved.
	if (not $flags_mismatch)
	{
		my $changed = $anvil->Alert->check_alert_sent({
			debug          => 3,
			record_locator => "scan_hardware::cpu_flags_mismatch",
			set_by         => $THIS_FILE,
			clear          => 1,
		});
		if ($changed)
		{
			# Register an alert-cleared event.
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0002"});
			$anvil->Alert->register({
				alert_level => "notice", 
				clear       => 1,
				message     => "scan_hardware_alert_0002", 
				set_by      => $THIS_FILE,
			});
		}
	}
	if (not $vmx_flags_mismatch)
	{
		my $changed = $anvil->Alert->check_alert_sent({
			debug          => 3,
			record_locator => "scan_hardware::cpu_vmx_flags_mismatch",
			set_by         => $THIS_FILE,
			clear          => 1,
		});
		if ($changed)
		{
			# Register an alert-cleared event.
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0031"});
			$anvil->Alert->register({
				alert_level => "notice", 
				clear       => 1,
				message     => "scan_hardware_alert_0031", 
				set_by      => $THIS_FILE,
			});
		}
	}
	if (not $bugs_mismatch)
	{
		my $changed = $anvil->Alert->check_alert_sent({
			debug          => 3,
			record_locator => "scan_hardware::cpu_bugs_mismatch",
			set_by         => $THIS_FILE,
			clear          => 1,
		});
		if ($changed)
		{
			# Register an alert-cleared event.
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0004"});
			$anvil->Alert->register({
				alert_level => "notice", 
				clear       => 1,
				message     => "scan_hardware_alert_0004", 
				set_by      => $THIS_FILE,
			});
		}
	}
	if (not $model_mismatch)
	{
		my $changed = $anvil->Alert->check_alert_sent({
			debug          => 3,
			record_locator => "scan_hardware::cpu_model_mismatch",
			set_by         => $THIS_FILE,
			clear          => 1,
		});
		if ($changed)
		{
			# Register an alert-cleared event.
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0006"});
			$anvil->Alert->register({
				alert_level => "notice", 
				clear       => 1,
				message     => "scan_hardware_alert_0006", 
				set_by      => $THIS_FILE,
			});
		}
	}
	
	# If there are VMX flags, append them to the 'flags' variable.
	if ($vmx_flags)
	{
		$flags .= " ".$vmx_flags;
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { flags => $flags }});
	}
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { flags => $flags }});
	
	# Read in the features provided by virsh
	$anvil->Get->virsh_capabilities({debug => 2});
	my $host_uuid = $anvil->Get->host_uuid();
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_uuid => $host_uuid }});
	my $features = "";
	foreach my $feature (sort {$a cmp $b} keys %{$anvil->data->{capabilities}{host_uuid}{$host_uuid}{cpu}{feature}})
	{
		$features .= $feature." ";
	}
	$features =~ s/ $//;
	if ($features)
	{
		$flags .= " ".$features;
	}
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { flags => $flags }});
	
	# Dedupe flags
	my $new_flags = "";
	my $flag_hash = {};
	foreach my $flag (sort {$a cmp $b} split/ /, $flags)
	{
		if (not exists $flag_hash->{$flag})
		{
			$new_flags .= $flag." ";
			$flag_hash->{$flag} = 1;
		}
	}
	$new_flags =~ s/ $//;
	$flags     =  $new_flags;
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { flags => $flags }});
	
	# Record what we found.
	$anvil->data->{summary}{cpu}{model}   = $model;
	$anvil->data->{summary}{cpu}{sockets} = $sockets;
	$anvil->data->{summary}{cpu}{cores}   = $total_cores;
	$anvil->data->{summary}{cpu}{threads} = $total_threads;
	$anvil->data->{summary}{cpu}{bugs}    = $bugs;
	$anvil->data->{summary}{cpu}{flags}   = $flags;
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		"summary::cpu::model"   => $anvil->data->{summary}{cpu}{model}, 
		"summary::cpu::sockets" => $anvil->data->{summary}{cpu}{sockets}, 
		"summary::cpu::cores"   => $anvil->data->{summary}{cpu}{cores}, 
		"summary::cpu::threads" => $anvil->data->{summary}{cpu}{threads}, 
		"summary::cpu::bugs"    => $anvil->data->{summary}{cpu}{bugs}, 
		"summary::cpu::flags"   => $anvil->data->{summary}{cpu}{flags}, 
	}});
	
	return(0);
}

# This reads in all the data we can find on the local system
sub collect_data
{
	my ($anvil) = @_;
	
	# Collect CPU info.
	collect_cpu_data($anvil);
	
	# Collect RAM data.
	collect_ram_data($anvil);
	
	# If this is a machine with IPMI, see if we can gather more info.
	collect_led_states($anvil);
	
	return(0);
}

# If this is a machine with IPMI, see if we can gather more info.
sub collect_led_states
{
	my ($anvil) = @_;
	
	# I need to know what kind of machine I am. 
	my $manufacturer = "";
	my $id_led       = "unknown";
	my $css_led      = "unknown";
	my $error_led    = "unknown";
	
	my ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{dmidecode}." --string system-manufacturer", source => $THIS_FILE, line => __LINE__});
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { output => $output, return_code => $return_code }});
	foreach my $line (split/\n/, $output)
	{
		$manufacturer = lc($line);
		# Dell can report as 'Dell Inc.'.
		if ($manufacturer =~ /dell/)
		{
			$manufacturer = "dell";
		}
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { manufacturer => $manufacturer }});
	}
	
	if ($manufacturer eq "fujitsu")
	{
		my ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{'ipmi-oem'}." Fujitsu get-system-status", source => $THIS_FILE, line => __LINE__});
		$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});
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
			
			if ($line =~ /Identify LED: (.*)$/)
			{
				$id_led = lc($1);
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { id_led => $id_led }});
			}
			if ($line =~ /CSS LED: (.*)$/)
			{
				$css_led = lc($1);
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { css_led => $css_led }});
			}
			if ($line =~ /Global Error LED: (.*)$/)
			{
				$error_led = lc($1);
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { error_led => $error_led }});
			}
		}
	}
	elsif ($manufacturer eq "dell")
	{
		### TODO: When we get a dell, figure this out...
		### TODO: There are a lot of useful bits here, including power load/headroom. Excellent data for the UI later.
		#my ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{'ipmi-oem'}." Dell get-system-status", source => $THIS_FILE, line => __LINE__});
		#$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});
		#	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
		#}
	}
	
	# Record.
	$anvil->data->{summary}{led}{id_led}    = $id_led;
	$anvil->data->{summary}{led}{css_led}   = $css_led;
	$anvil->data->{summary}{led}{error_led} = $error_led;
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		"summary::led::id_led"    => $anvil->data->{summary}{led}{id_led},
		"summary::led::css_led"   => $anvil->data->{summary}{led}{css_led},
		"summary::led::error_led" => $anvil->data->{summary}{led}{error_led},
	}});
	
	return(0);
}

# This reads in data about the RAM
sub collect_ram_data
{
	my ($anvil) = @_;
	
	my $total_size    = 0;
	my $size          = "";
	my $locator       = "";
	my $manufacturer  = "";
	my $part_number   = "";
	my $serial_number = "";
	
	my ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{dmidecode}." --type memory", source => $THIS_FILE, line => __LINE__});
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { output => $output, return_code => $return_code }});
	foreach my $line (split/\n/, $output)
	{
		$line = $anvil->Words->clean_spaces({string => $line});
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { line => $line }});
	
		if ($line =~ /^Locator: (.*?)$/)
		{
			$locator = $1;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { locator => $locator }});
		}
		if ($line =~ /^Size: (.*?)$/)
		{
			$size = $1;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { size => $size }});
			
			# If the "size" is "no module installed", we're done here.
			if ($size !~ /^\d/)
			{
				$locator = "";
				$size    = "";
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					locator => $locator, 
					size    => $size,
				}});
				next;
			}
			
			# THis reports in 'MB' but it's really 'MiB'.
			$size = $anvil->Convert->human_readable_to_bytes({
				base2 => 1,
				size  => $size,
			});
			$total_size += $size;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				locator    => $locator, 
				total_size => $total_size,
			}});
		}
		if ($line =~ /^Manufacturer: (.*)$/)
		{
			$manufacturer = $1;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { manufacturer => $manufacturer }});
		}
		if ($line =~ /^Part Number: (.*)$/)
		{
			$part_number = $1;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { part_number => $part_number }});
		}
		if ($line =~ /^Serial Number: (.*)$/)
		{
			$serial_number = $1;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { serial_number => $serial_number }});
		}
		next if not $locator;
		if (not $line)
		{
			if ($size)
			{
				$anvil->data->{ram}{dmi}{locator}{$locator}{size}          = $size;
				$anvil->data->{ram}{dmi}{locator}{$locator}{manufacturer}  = $manufacturer;
				$anvil->data->{ram}{dmi}{locator}{$locator}{part_number}   = $part_number;
				$anvil->data->{ram}{dmi}{locator}{$locator}{serial_number} = $serial_number;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					"ram::dmi::locator::${locator}::size"          => $anvil->Convert->add_commas({number => $anvil->data->{ram}{dmi}{locator}{$locator}{size}})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{ram}{dmi}{locator}{$locator}{size}}).")", 
					"ram::dmi::locator::${locator}::manufacturer"  => $anvil->data->{ram}{dmi}{locator}{$locator}{manufacturer}, 
					"ram::dmi::locator::${locator}::part_number"   => $anvil->data->{ram}{dmi}{locator}{$locator}{part_number}, 
					"ram::dmi::locator::${locator}::serial_number" => $anvil->data->{ram}{dmi}{locator}{$locator}{serial_number}, 
				}});
			}
			
			$size          = "";
			$locator       = "";
			$manufacturer  = "";
			$part_number   = "";
			$serial_number = "";
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 
				"size"          => $size, 
				"locator"       => $locator, 
				"manufacturer"  => $manufacturer, 
				"part_number"   => $part_number, 
				"serial_number" => $serial_number, 
			}});
		}
	}
	
	my $cpu_info = $anvil->Storage->read_file({debug => 3, file => $anvil->data->{path}{proc}{meminfo}});
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { cpu_info => $cpu_info }});
	foreach my $line (split/\n/, $cpu_info)
	{
		$line = $anvil->Words->clean_spaces({string => $line});
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { line => $line }});
			
		if ($line =~ /^(.*?):\s+(\d+.*?)$/)
		{
			my $variable = $1;
			my $size     = $2;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 
				variable => $variable, 
				size     => $size, 
			}});
			
			# We care about a few variables only.
			my $say_variable = "";
			if ($variable eq "MemTotal")
			{
				$say_variable = "memory_total";
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { say_variable => $say_variable }});
			}
			if ($variable eq "MemFree")
			{
				$say_variable = "memory_free";
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { say_variable => $say_variable }});
			}
			if ($variable eq "SwapTotal")
			{
				$say_variable = "swap_total";
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { say_variable => $say_variable }});
			}
			if ($variable eq "SwapFree")
			{
				$say_variable = "swap_free";
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { say_variable => $say_variable }});
			}
			next if not $say_variable;
			
			# This reports sizes as 'kB', but it's really base2.
			$size = $anvil->Convert->human_readable_to_bytes({
				base2 => 1,
				size  => $size,
			});
			
			$anvil->data->{summary}{ram}{proc}{$say_variable} = $size;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				"summary::ram::proc::${say_variable}" => $anvil->Convert->add_commas({number => $anvil->data->{summary}{ram}{proc}{$say_variable}})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{summary}{ram}{proc}{$say_variable}}).")", 
			}});
		}
	}
	
	$anvil->data->{summary}{ram}{size} = $total_size;
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		"summary::ram::size" => $anvil->Convert->add_commas({number => $anvil->data->{summary}{ram}{size}})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{summary}{ram}{size}}).")", 
	}});
	
	return(0);
}

# This reads in the last scan data from one of the databases and compares it against the just-read data. If 
# anything changed, register an alert.
sub find_changes
{
	my ($anvil) = @_;
	
	# Walk through what I know. First the main info, then the RAM modules below.
	my $new_scan_hardware_cpu_model    = $anvil->data->{summary}{cpu}{model};
	my $new_scan_hardware_cpu_cores    = $anvil->data->{summary}{cpu}{cores};
	my $new_scan_hardware_cpu_threads  = $anvil->data->{summary}{cpu}{threads};
	my $new_scan_hardware_cpu_bugs     = $anvil->data->{summary}{cpu}{bugs};
	my $new_scan_hardware_cpu_flags    = $anvil->data->{summary}{cpu}{flags};
	my $new_scan_hardware_led_id       = $anvil->data->{summary}{led}{id_led};
	my $new_scan_hardware_led_css      = $anvil->data->{summary}{led}{css_led};
	my $new_scan_hardware_led_error    = $anvil->data->{summary}{led}{error_led};
	my $new_scan_hardware_ram_total    = $anvil->data->{summary}{ram}{size};		# This is from counting the actual module capacity
	my $new_scan_hardware_memory_total = $anvil->data->{summary}{ram}{proc}{memory_total};	# This is from /proc/meminfo and subtracts RAM used by shared video, etc.
	my $new_scan_hardware_memory_free  = $anvil->data->{summary}{ram}{proc}{memory_free};
	my $new_scan_hardware_swap_total   = $anvil->data->{summary}{ram}{proc}{swap_total};
	my $new_scan_hardware_swap_free    = $anvil->data->{summary}{ram}{proc}{swap_free};
	my $new_scan_hardware_swap_used    = $new_scan_hardware_swap_total - $new_scan_hardware_swap_free;
	my $new_swap_used_percentage       = 0;
	if ($new_scan_hardware_swap_total)
	{
		$new_swap_used_percentage = $anvil->Convert->round({
			number => (($new_scan_hardware_swap_used / $new_scan_hardware_swap_total) * 100),
			places => 0,
		});
	}
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		"new_scan_hardware_cpu_model"    => $new_scan_hardware_cpu_model, 
		"new_scan_hardware_cpu_cores"    => $new_scan_hardware_cpu_cores, 
		"new_scan_hardware_cpu_threads"  => $new_scan_hardware_cpu_threads, 
		"new_scan_hardware_cpu_bugs"     => $new_scan_hardware_cpu_bugs, 
		"new_scan_hardware_cpu_flags"    => $new_scan_hardware_cpu_flags, 
		"new_scan_hardware_led_id"       => $new_scan_hardware_led_id, 
		"new_scan_hardware_led_css"      => $new_scan_hardware_led_css, 
		"new_scan_hardware_ram_total"    => $new_scan_hardware_ram_total, 
		"new_scan_hardware_led_error"    => $new_scan_hardware_led_error, 
		"new_scan_hardware_memory_total" => $anvil->Convert->add_commas({number => $new_scan_hardware_memory_total})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_memory_total}).")", 
		"new_scan_hardware_memory_free"  => $anvil->Convert->add_commas({number => $new_scan_hardware_memory_free})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_memory_free}).")", 
		"new_scan_hardware_swap_total"   => $anvil->Convert->add_commas({number => $new_scan_hardware_swap_total})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_swap_total}).")", 
		"new_scan_hardware_swap_free"    => $anvil->Convert->add_commas({number => $new_scan_hardware_swap_free})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_swap_free}).")", 
		"new_scan_hardware_swap_used"    => $anvil->Convert->add_commas({number => $new_scan_hardware_swap_used})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_swap_used}).")", 
		"new_swap_used_percentage"       => $new_swap_used_percentage."%",
	}});
	
	# The LED status needs to be translated.
	my $say_new_scan_hardware_led_id = "#!string!scan_hardware_message_0001!#";
	if ($new_scan_hardware_led_id eq "on")
	{
		$say_new_scan_hardware_led_id = "#!string!scan_hardware_message_0002!#";
	}
	elsif ($new_scan_hardware_led_id eq "off")
	{
		$say_new_scan_hardware_led_id = "#!string!scan_hardware_message_0003!#";
	}
	my $say_new_scan_hardware_led_css = "#!string!scan_hardware_message_0001!#";
	if ($new_scan_hardware_led_css eq "on")
	{
		$say_new_scan_hardware_led_css = "#!string!scan_hardware_message_0002!#";
	}
	elsif ($new_scan_hardware_led_css eq "off")
	{
		$say_new_scan_hardware_led_css = "#!string!scan_hardware_message_0003!#";
	}
	my $say_new_scan_hardware_led_error = "#!string!scan_hardware_message_0001!#";
	if ($new_scan_hardware_led_error eq "on")
	{
		$say_new_scan_hardware_led_error = "#!string!scan_hardware_message_0002!#";
	}
	elsif ($new_scan_hardware_led_error eq "off")
	{
		$say_new_scan_hardware_led_error = "#!string!scan_hardware_message_0003!#";
	}
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		say_new_scan_hardware_led_id    => $say_new_scan_hardware_led_id, 
		say_new_scan_hardware_led_css   => $say_new_scan_hardware_led_css, 
		say_new_scan_hardware_led_error => $say_new_scan_hardware_led_error, 
	}});
	
	# INSERT or UPDATE?
	if ((exists $anvil->data->{sql}{scan_hardware_uuid}) && ($anvil->data->{sql}{scan_hardware_uuid}))
	{
		# Look for changed.
		my $scan_hardware_uuid = $anvil->data->{sql}{scan_hardware_uuid};
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { scan_hardware_uuid => $scan_hardware_uuid }});
		
		my $old_scan_hardware_cpu_model     = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_cpu_model};
		my $old_scan_hardware_cpu_cores     = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_cpu_cores};
		my $old_scan_hardware_cpu_threads   = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_cpu_threads};
		my $old_scan_hardware_cpu_bugs      = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_cpu_bugs};
		my $old_scan_hardware_cpu_flags     = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_cpu_flags};
		my $old_scan_hardware_ram_total     = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_ram_total};
		my $old_scan_hardware_memory_total  = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_memory_total};
		my $old_scan_hardware_memory_free   = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_memory_free};
		my $old_scan_hardware_swap_total    = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_swap_total};
		my $old_scan_hardware_swap_free     = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_swap_free};
		my $old_scan_hardware_led_id        = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_led_id};
		my $old_scan_hardware_led_css       = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_led_css};
		my $old_scan_hardware_led_error     = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_led_error};
		my $old_scan_hardware_modified_date = $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_modified_date};
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			old_scan_hardware_cpu_model     => $old_scan_hardware_cpu_model,
			old_scan_hardware_cpu_cores     => $old_scan_hardware_cpu_cores,
			old_scan_hardware_cpu_threads   => $old_scan_hardware_cpu_threads,
			old_scan_hardware_cpu_bugs      => $old_scan_hardware_cpu_bugs,
			old_scan_hardware_cpu_flags     => $old_scan_hardware_cpu_flags,
			old_scan_hardware_ram_total     => $old_scan_hardware_ram_total,
			old_scan_hardware_memory_total  => $old_scan_hardware_memory_total,
			old_scan_hardware_memory_free   => $old_scan_hardware_memory_free,
			old_scan_hardware_swap_total    => $old_scan_hardware_swap_total,
			old_scan_hardware_swap_free     => $old_scan_hardware_swap_free,
			old_scan_hardware_led_id        => $old_scan_hardware_led_id,
			old_scan_hardware_led_css       => $old_scan_hardware_led_css,
			old_scan_hardware_led_error     => $old_scan_hardware_led_error,
			old_scan_hardware_modified_date => $old_scan_hardware_modified_date,
		}});
		
		my $update = 0;
		if ($new_scan_hardware_cpu_model ne $old_scan_hardware_cpu_model)
		{
			# CPU model changed, probably from a CPU upgrade or firmware flash, so just a notice level alert.
			   $update    = 1;
			my $variables = {
				new => $new_scan_hardware_cpu_model, 
				old => $old_scan_hardware_cpu_model, 
			};
			$anvil->Alert->register({set_by => $THIS_FILE, alert_level => "notice", message => "scan_hardware_alert_0007", variables => $variables});
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0007", variables => $variables});
		}
		if ($new_scan_hardware_cpu_bugs ne $old_scan_hardware_cpu_bugs)
		{
			# This is probably from a CPU upgrade or firward flash
			   $update    = 1;
			my $variables = {
				new => $new_scan_hardware_cpu_bugs, 
				old => $old_scan_hardware_cpu_bugs, 
			};
			$anvil->Alert->register({set_by => $THIS_FILE, alert_level => "notice", message => "scan_hardware_alert_0008", variables => $variables});
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0008", variables => $variables});
		}
		if ($new_scan_hardware_cpu_flags ne $old_scan_hardware_cpu_flags)
		{
			# This is probably from a CPU upgrade or firward flash
			   $update    = 1;
			my $variables = {
				new => $new_scan_hardware_cpu_flags, 
				old => $old_scan_hardware_cpu_flags, 
			};
			$anvil->Alert->register({set_by => $THIS_FILE, alert_level => "notice", message => "scan_hardware_alert_0009", variables => $variables});
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0009", variables => $variables});
		}
		if ($new_scan_hardware_cpu_cores ne $old_scan_hardware_cpu_cores)
		{
			# This is probably from a CPU upgrade
			   $update    = 1;
			my $variables = {
				new => $new_scan_hardware_cpu_cores, 
				old => $old_scan_hardware_cpu_cores, 
			};
			$anvil->Alert->register({set_by => $THIS_FILE, alert_level => "notice", message => "scan_hardware_alert_0010", variables => $variables});
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0010", variables => $variables});
		}
		if ($new_scan_hardware_cpu_threads ne $old_scan_hardware_cpu_threads)
		{
			# This is probably from a CPU upgrade
			   $update    = 1;
			my $variables = {
				new => $new_scan_hardware_cpu_threads, 
				old => $old_scan_hardware_cpu_threads, 
			};
			$anvil->Alert->register({set_by => $THIS_FILE, alert_level => "notice", message => "scan_hardware_alert_0011", variables => $variables});
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0011", variables => $variables});
		}
		if ($new_scan_hardware_ram_total ne $old_scan_hardware_ram_total)
		{
			# If the RAM (from dmidecode) has increased, it's a safe upgrade. If it's a decrease,
			# it's likely a failure.
			if ($old_scan_hardware_ram_total > $new_scan_hardware_ram_total)
			{
				# It dropped, alarm
				   $update     = 1;
				my $difference = $old_scan_hardware_ram_total - $new_scan_hardware_ram_total;
				my $variables  = {
					new        => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_ram_total})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_ram_total})." #!string!scan_hardware_unit_0001!#)", 
					old        => $anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_hardware_ram_total})." (".$anvil->Convert->add_commas({number => $old_scan_hardware_ram_total})." #!string!scan_hardware_unit_0001!#)", 
					difference => $anvil->Convert->bytes_to_human_readable({'bytes' => $difference})." (".$anvil->Convert->add_commas({number => $difference})." #!string!scan_hardware_unit_0001!#)", 
				};
				$anvil->Alert->register({set_by => $THIS_FILE, alert_level => "warning", message => "scan_hardware_alert_0012", variables => $variables});
				$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0012", variables => $variables});
			}
			else
			{
				# Increased, probably an upgrade
				   $update     = 1;
				my $difference = $new_scan_hardware_ram_total - $old_scan_hardware_ram_total;
				my $variables  = {
					new        => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_ram_total})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_ram_total})." #!string!scan_hardware_unit_0001!#)", 
					old        => $anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_hardware_ram_total})." (".$anvil->Convert->add_commas({number => $old_scan_hardware_ram_total})." #!string!scan_hardware_unit_0001!#)", 
					difference => $anvil->Convert->bytes_to_human_readable({'bytes' => $difference})." (".$anvil->Convert->add_commas({number => $difference})." #!string!scan_hardware_unit_0001!#)", 
				};
				$anvil->Alert->register({set_by => $THIS_FILE, alert_level => "notice", message => "scan_hardware_alert_0029", variables => $variables});
				$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0029", variables => $variables});
			}
		}
		if ($new_scan_hardware_memory_total ne $old_scan_hardware_memory_total)
		{
			# If the difference is less than 1 GiB, or the amount has grown, ignore it.
			if ($old_scan_hardware_memory_total > $new_scan_hardware_memory_total)
			{
				my $difference = $old_scan_hardware_memory_total - $new_scan_hardware_memory_total;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { difference => $anvil->Convert->bytes_to_human_readable({'bytes' => $difference}) }});
				if ($difference > 1073741824)
				{
					# Memory (/proc/meminfo) changed could be a hardware upgrade, or it 
					# could be from a failed modules. As such, we set this to 'warning'.
					   $update    = 1;
					my $variables = {
						new        => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_memory_total})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_memory_total})." #!string!scan_hardware_unit_0001!#)", 
						old        => $anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_hardware_memory_total})." (".$anvil->Convert->add_commas({number => $old_scan_hardware_memory_total})." #!string!scan_hardware_unit_0001!#)", 
						difference => $anvil->Convert->bytes_to_human_readable({'bytes' => $difference})." (".$anvil->Convert->add_commas({number => $difference})." #!string!scan_hardware_unit_0001!#)", 
					};
					$anvil->Alert->register({set_by => $THIS_FILE, alert_level => "warning", message => "scan_hardware_alert_0013", variables => $variables});
					$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0013", variables => $variables});
				}
			}
		}
		if ($new_scan_hardware_swap_total ne $old_scan_hardware_swap_total)
		{
			# If the difference is less than 1 GiB, or the amount has grown, ignore it.
			if ($old_scan_hardware_swap_total > $new_scan_hardware_swap_total)
			{
				my $difference = $old_scan_hardware_swap_total - $new_scan_hardware_swap_total;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { difference => $anvil->Convert->bytes_to_human_readable({'bytes' => $difference}) }});
				if ($difference > 1073741824)
				{
					# Memory (/proc/meminfo) changed could be a hardware upgrade, or it
					# could be from a failed modules. As such, we set this to 'warning'.
					   $update    = 1;
					my $variables = {
						new        => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_swap_total})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_swap_total})." #!string!scan_hardware_unit_0001!#)", 
						old        => $anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_hardware_swap_total})." (".$anvil->Convert->add_commas({number => $old_scan_hardware_swap_total})." #!string!scan_hardware_unit_0001!#)", 
						difference => $anvil->Convert->bytes_to_human_readable({'bytes' => $difference})." (".$anvil->Convert->add_commas({number => $difference})." #!string!scan_hardware_unit_0001!#)", 
					};
					$anvil->Alert->register({set_by => $THIS_FILE, alert_level => "warning", message => "scan_hardware_alert_0014", variables => $variables});
					$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0014", variables => $variables});
				}
			}
		}
		if ($new_scan_hardware_led_id ne $old_scan_hardware_led_id)
		{
			   $update                  = 1;
			my $say_old_scan_hardware_led_id = "#!string!scan_hardware_message_0001!#";
			if ($old_scan_hardware_led_id eq "on")
			{
				$say_old_scan_hardware_led_id = "#!string!scan_hardware_message_0002!#";
			}
			elsif ($old_scan_hardware_led_id eq "off")
			{
				$say_old_scan_hardware_led_id = "#!string!scan_hardware_message_0003!#";
			}
			$anvil->Alert->register({set_by => $THIS_FILE, alert_level => "notice", message => "scan_hardware_alert_0015,!!new!".$say_new_scan_hardware_led_id."!!,!!old!".$say_old_scan_hardware_led_id."!!"});
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0015", variables => { new => $say_new_scan_hardware_led_id, old => $say_old_scan_hardware_led_id}});
		}
		if ($new_scan_hardware_led_css ne $old_scan_hardware_led_css)
		{
			# The CSS LED is an error LED, so it changing deserves to be a 'warning' level alert.
			   $update                   = 1;
			my $say_old_scan_hardware_led_css = "#!string!scan_hardware_message_0001!#";
			if ($old_scan_hardware_led_css eq "on")
			{
				$say_old_scan_hardware_led_css = "#!string!scan_hardware_message_0002!#";
			}
			elsif ($old_scan_hardware_led_css eq "off")
			{
				$say_old_scan_hardware_led_css = "#!string!scan_hardware_message_0003!#";
			}
			$anvil->Alert->register({set_by => $THIS_FILE, alert_level => "warning", message => "scan_hardware_alert_0016,!!new!".$say_new_scan_hardware_led_css."!!,!!old!".$say_old_scan_hardware_led_css."!!"});
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0016", variables => { new => $say_new_scan_hardware_led_css, old => $say_old_scan_hardware_led_css}});
		}
		if ($new_scan_hardware_led_error ne $old_scan_hardware_led_error)
		{
			# The Hardware Error LED deserves to be a 'warning' level alert.
			   $update                     = 1;
			my $say_old_scan_hardware_led_error = "#!string!scan_hardware_message_0001!#";
			if ($old_scan_hardware_led_error eq "on")
			{
				$say_old_scan_hardware_led_error = "#!string!scan_hardware_message_0002!#";
			}
			elsif ($old_scan_hardware_led_error eq "off")
			{
				$say_old_scan_hardware_led_error = "#!string!scan_hardware_message_0003!#";
			}
			$anvil->Alert->register({set_by => $THIS_FILE, alert_level => "warning", message => "scan_hardware_alert_0017,!!new!".$say_new_scan_hardware_led_error."!!,!!old!".$say_old_scan_hardware_led_error."!!"});
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0017", variables => { new => $say_new_scan_hardware_led_error, old => $say_old_scan_hardware_led_error}});
		}
		if ($new_scan_hardware_memory_free ne $old_scan_hardware_memory_free)
		{
			# This always changes, so it's an info-level alert
			   $update                              = 1;
			my $say_new_scan_hardware_memory_free = $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_memory_free})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_memory_free})." #!string!scan_hardware_unit_0001!#)";
			my $say_old_scan_hardware_memory_free = $anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_hardware_memory_free})." (".$anvil->Convert->add_commas({number => $old_scan_hardware_memory_free})." #!string!scan_hardware_unit_0001!#)";
			$anvil->Alert->register({set_by => $THIS_FILE, alert_level => "info", message => "scan_hardware_alert_0018,!!new!".$say_new_scan_hardware_memory_free."!!,!!old!".$say_old_scan_hardware_memory_free."!!"});
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "scan_hardware_alert_0018", variables => { new => $say_new_scan_hardware_memory_free, old => $say_old_scan_hardware_memory_free}});
		}
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			new_scan_hardware_swap_free => $new_scan_hardware_swap_free, 
			old_scan_hardware_swap_free => $old_scan_hardware_swap_free, 
		}});
		if ($new_scan_hardware_swap_free ne $old_scan_hardware_swap_free)
		{
			   $update                          = 1;
			my $say_new_scan_hardware_swap_free = $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_swap_free})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_swap_free})." #!string!scan_hardware_unit_0001!#)";
			my $say_old_scan_hardware_swap_free = $anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_hardware_swap_free})." (".$anvil->Convert->add_commas({number => $old_scan_hardware_swap_free})." #!string!scan_hardware_unit_0001!#)";
			$anvil->Alert->register({set_by => $THIS_FILE, alert_level => "info", message => "scan_hardware_alert_0019,!!new!".$say_new_scan_hardware_swap_free."!!,!!old!".$say_old_scan_hardware_swap_free."!!"});
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "scan_hardware_alert_0019", variables => { new => $say_new_scan_hardware_swap_free, old => $say_old_scan_hardware_swap_free}});

			my $new_swap_bytes_used   = $new_scan_hardware_swap_total - $new_scan_hardware_swap_free;
			my $old_swap_bytes_used   = $old_scan_hardware_swap_total - $old_scan_hardware_swap_free;
			my $new_swap_percent_used = $anvil->Convert->round({number => (($new_swap_bytes_used / $new_scan_hardware_swap_total) * 100)});
			my $old_swap_percent_used = $anvil->Convert->round({number => (($old_swap_bytes_used / $old_scan_hardware_swap_total) * 100)});
			my $swap_percent_high     = $anvil->data->{scancore}{'scan-hardware'}{swap}{high_threshold}  =~ /^\d+/ ? $anvil->data->{scancore}{'scan-hardware'}{swap}{high_threshold}  : 75;
			my $swap_percent_low      = $anvil->data->{scancore}{'scan-hardware'}{swap}{clear_threshold} =~ /^\d+/ ? $anvil->data->{scancore}{'scan-hardware'}{swap}{clear_threshold} : 25;
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				's1:new_swap_bytes_used'   => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_swap_bytes_used})." (".$anvil->Convert->add_commas({number => $new_swap_bytes_used})." #!string!scan_hardware_unit_0001!#)", 
				's2:old_swap_bytes_used'   => $anvil->Convert->bytes_to_human_readable({'bytes' => $old_swap_bytes_used})." (".$anvil->Convert->add_commas({number => $old_swap_bytes_used})." #!string!scan_hardware_unit_0001!#)",
				's3:new_swap_percent_used' => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_swap_percent_used})." (".$anvil->Convert->add_commas({number => $new_swap_percent_used})." #!string!scan_hardware_unit_0001!#)", 
				's4:old_swap_percent_used' => $anvil->Convert->bytes_to_human_readable({'bytes' => $old_swap_percent_used})." (".$anvil->Convert->add_commas({number => $old_swap_percent_used})." #!string!scan_hardware_unit_0001!#)", 
				's5:swap_percent_high'     => $anvil->Convert->bytes_to_human_readable({'bytes' => $swap_percent_high})." (".$anvil->Convert->add_commas({number => $swap_percent_high})." #!string!scan_hardware_unit_0001!#)", 
				's6:swap_percent_low'      => $anvil->Convert->bytes_to_human_readable({'bytes' => $swap_percent_low})." (".$anvil->Convert->add_commas({number => $swap_percent_low})." #!string!scan_hardware_unit_0001!#)", 
			}});
			
			# Check if swap has gone over the high threshold or dropped below the clear 
			# threashold.
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				new_scan_hardware_swap_free                     => $new_scan_hardware_swap_free, 
				"scancore::scan-hardware::swap::high_threshold" => $anvil->data->{scancore}{'scan-hardware'}{swap}{high_threshold}, 
			}});
			if ($new_swap_used_percentage > $anvil->data->{scancore}{'scan-hardware'}{swap}{high_threshold})
			{
				# It's high
				my $changed = $anvil->Alert->check_alert_sent({
					debug          => 3,
					record_locator => "scan_hardware::high_swap",
					set_by         => $THIS_FILE,
				});
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }});
				if ($changed)
				{
					# Log and register an alert.
					my $say_used  = $anvil->Convert->bytes_to_human_readable({'bytes' => $new_swap_bytes_used});
					my $say_swap  = $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_swap_total});
					my $variables = {
						say_used     => $say_used,
						say_swap     => $say_swap,
						swap_percent => $new_swap_percent_used,
					};
					$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 0, level => 1, key => "scan_hardware_alert_0020", variables => $variables});
					$anvil->Alert->register({alert_level => "notice", message => "scan_hardware_alert_0020", variables => $variables, set_by => $THIS_FILE });
				}
			}
			elsif ($new_swap_used_percentage < $anvil->data->{scancore}{'scan-hardware'}{swap}{clear_threshold})
			{
				# It's low
				my $changed = $anvil->Alert->check_alert_sent({
					debug          => 3,
					record_locator => "scan_hardware::high_swap",
					set_by         => $THIS_FILE,
					clear          => 1,
				});
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }});
				if ($changed)
				{
					# Log and register that the alert has cleared.
					my $say_used  = $anvil->Convert->bytes_to_human_readable({'bytes' => $new_swap_bytes_used});
					my $say_swap  = $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_swap_total});
					my $variables = {
						say_used     => $say_used,
						say_swap     => $say_swap,
						swap_percent => $new_swap_percent_used,
					};
					$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 0, level => 1, key => "scan_hardware_alert_0021", variables => $variables});
					$anvil->Alert->register({alert_level => "notice", message => "scan_hardware_alert_0021", variables => $variables, set_by => $THIS_FILE });
				}
			}
		}
		
		# Update?
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update => $update }});
		if ($update)
		{
			# Yup!
			my $query = "
UPDATE 
    scan_hardware 
SET
    scan_hardware_cpu_model    = ".$anvil->Database->quote($new_scan_hardware_cpu_model).", 
    scan_hardware_cpu_cores    = ".$anvil->Database->quote($new_scan_hardware_cpu_cores).", 
    scan_hardware_cpu_threads  = ".$anvil->Database->quote($new_scan_hardware_cpu_threads).", 
    scan_hardware_cpu_bugs     = ".$anvil->Database->quote($new_scan_hardware_cpu_bugs).", 
    scan_hardware_cpu_flags    = ".$anvil->Database->quote($new_scan_hardware_cpu_flags).", 
    scan_hardware_ram_total    = ".$anvil->Database->quote($new_scan_hardware_ram_total).", 
    scan_hardware_memory_total = ".$anvil->Database->quote($new_scan_hardware_memory_total).", 
    scan_hardware_memory_free  = ".$anvil->Database->quote($new_scan_hardware_memory_free).", 
    scan_hardware_swap_total   = ".$anvil->Database->quote($new_scan_hardware_swap_total).", 
    scan_hardware_swap_free    = ".$anvil->Database->quote($new_scan_hardware_swap_free).", 
    scan_hardware_led_id       = ".$anvil->Database->quote($new_scan_hardware_led_id).", 
    scan_hardware_led_css      = ".$anvil->Database->quote($new_scan_hardware_led_css).", 
    scan_hardware_led_error    = ".$anvil->Database->quote($new_scan_hardware_led_error).", 
    modified_date              = ".$anvil->Database->quote($anvil->Database->refresh_timestamp)."
WHERE 
    scan_hardware_uuid         = ".$anvil->Database->quote($scan_hardware_uuid)."
;";
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
			$anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__});
		}
	}
	else
	{
		# New record, send an alert telling that we've found data.
		my $variables = {
			total_cores      => $new_scan_hardware_cpu_cores, 
			total_threads    => $new_scan_hardware_cpu_threads, 
			cpu_bugs         => $new_scan_hardware_cpu_bugs, 
			cpu_flags        => $new_scan_hardware_cpu_flags, 
			id_led           => $say_new_scan_hardware_led_id, 
			css_led          => $say_new_scan_hardware_led_css, 
			error_led        => $say_new_scan_hardware_led_error, 
			ram_total_size   => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_ram_total})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_ram_total})." #!string!scan_hardware_unit_0001!#)", 
			ram_memory_total => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_memory_total})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_memory_total})." #!string!scan_hardware_unit_0001!#)", 
			ram_memory_free  => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_memory_free})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_memory_free})." #!string!scan_hardware_unit_0001!#)", 
			ram_swap_total   => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_swap_total})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_swap_total})." #!string!scan_hardware_unit_0001!#)", 
			ram_swap_free    => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_swap_free})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_swap_free})." #!string!scan_hardware_unit_0001!#)",
		};
		$anvil->Alert->register({alert_level => "notice", message => "scan_hardware_alert_0022", variables => $variables, set_by => $THIS_FILE });
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0022", variables => $variables});
		
		# INSERT
		my $scan_hardware_uuid = $anvil->Get->uuid();
		my $query = "
INSERT INTO 
    scan_hardware 
(
    scan_hardware_uuid, 
    scan_hardware_host_uuid, 
    scan_hardware_cpu_model, 
    scan_hardware_cpu_cores, 
    scan_hardware_cpu_threads, 
    scan_hardware_cpu_bugs, 
    scan_hardware_cpu_flags, 
    scan_hardware_ram_total, 
    scan_hardware_memory_total, 
    scan_hardware_memory_free, 
    scan_hardware_swap_total, 
    scan_hardware_swap_free, 
    scan_hardware_led_id, 
    scan_hardware_led_css, 
    scan_hardware_led_error, 
    modified_date 
) VALUES (
    ".$anvil->Database->quote($scan_hardware_uuid).", 
    ".$anvil->Database->quote($anvil->Get->host_uuid).", 
    ".$anvil->Database->quote($new_scan_hardware_cpu_model).", 
    ".$anvil->Database->quote($new_scan_hardware_cpu_cores).", 
    ".$anvil->Database->quote($new_scan_hardware_cpu_threads).", 
    ".$anvil->Database->quote($new_scan_hardware_cpu_bugs).", 
    ".$anvil->Database->quote($new_scan_hardware_cpu_flags).", 
    ".$anvil->Database->quote($new_scan_hardware_ram_total).", 
    ".$anvil->Database->quote($new_scan_hardware_memory_total).", 
    ".$anvil->Database->quote($new_scan_hardware_memory_free).", 
    ".$anvil->Database->quote($new_scan_hardware_swap_total).", 
    ".$anvil->Database->quote($new_scan_hardware_swap_free).", 
    ".$anvil->Database->quote($new_scan_hardware_led_id).", 
    ".$anvil->Database->quote($new_scan_hardware_led_css).", 
    ".$anvil->Database->quote($new_scan_hardware_led_css).", 
    ".$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 }});
		$anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__});
	}
	
	# Now the RAM modules.
	foreach my $hardware_ram_module_locator (sort {$a cmp $b} keys %{$anvil->data->{ram}{dmi}{locator}})
	{
		my $new_scan_hardware_ram_module_size          = $anvil->data->{ram}{dmi}{locator}{$hardware_ram_module_locator}{size};
		my $new_scan_hardware_ram_module_manufacturer  = $anvil->data->{ram}{dmi}{locator}{$hardware_ram_module_locator}{manufacturer};
		my $new_scan_hardware_ram_module_model         = $anvil->data->{ram}{dmi}{locator}{$hardware_ram_module_locator}{part_number};
		my $new_scan_hardware_ram_module_serial_number = $anvil->data->{ram}{dmi}{locator}{$hardware_ram_module_locator}{serial_number};
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			hardware_ram_module_locator                => $hardware_ram_module_locator, 
			new_scan_hardware_ram_module_size          => $anvil->Convert->add_commas({number => $new_scan_hardware_ram_module_size})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_ram_module_size}).")", 
			new_scan_hardware_ram_module_manufacturer  => $new_scan_hardware_ram_module_manufacturer, 
			new_scan_hardware_ram_module_model         => $new_scan_hardware_ram_module_model, 
			new_scan_hardware_ram_module_serial_number => $new_scan_hardware_ram_module_serial_number, 
		}});
		
		if ((exists $anvil->data->{sql}{ram_module_locator_to_uuid}{$hardware_ram_module_locator}) && ($anvil->data->{sql}{ram_module_locator_to_uuid}{$hardware_ram_module_locator}))
		{
			# We've seen this module before, look for changes.
			my $scan_hardware_ram_module_uuid = $anvil->data->{sql}{ram_module_locator_to_uuid}{$hardware_ram_module_locator};
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				scan_hardware_ram_module_locator => $hardware_ram_module_locator,
				scan_hardware_ram_module_uuid    => $scan_hardware_ram_module_uuid, 
			}});
			
			my $old_scan_hardware_ram_module_size          = $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_size};
			my $old_scan_hardware_ram_module_manufacturer  = $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_manufacturer};
			my $old_scan_hardware_ram_module_model         = $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_model};
			my $old_scan_hardware_ram_module_serial_number = $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_serial_number};
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				old_scan_hardware_ram_module_size          => $anvil->Convert->add_commas({number => $old_scan_hardware_ram_module_size})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_hardware_ram_module_size}).")", 
				old_scan_hardware_ram_module_manufacturer  => $old_scan_hardware_ram_module_manufacturer, 
				old_scan_hardware_ram_module_model         => $old_scan_hardware_ram_module_model, 
				old_scan_hardware_ram_module_serial_number => $old_scan_hardware_ram_module_serial_number, 
			}});
			
			# Delete the entry so we know we've processed this existing module.
			delete $anvil->data->{sql}{ram_module_locator_to_uuid}{$hardware_ram_module_locator};
			delete $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid};
			
			### We check all at once for a single alert as it's easy to see what has changed.
			# Looks for changes.
			if (($new_scan_hardware_ram_module_size          ne $old_scan_hardware_ram_module_size)         or 
			    ($new_scan_hardware_ram_module_manufacturer  ne $old_scan_hardware_ram_module_manufacturer) or 
			    ($old_scan_hardware_ram_module_model         ne $old_scan_hardware_ram_module_model)        or 
			    ($new_scan_hardware_ram_module_serial_number ne $old_scan_hardware_ram_module_serial_number))
			{
				# This shouldn't change, but maybe the RAM was replaced or upgraded? It could
				# be that a vanished module has returned?
				if (($old_scan_hardware_ram_module_manufacturer eq "VANISHED") && ($new_scan_hardware_ram_module_manufacturer ne "VANISHED"))
				{
					# A vanished module has returned.
					my $variables = {
						locator           => $hardware_ram_module_locator,
						old_size          => $anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_hardware_ram_module_size})." (".$anvil->Convert->add_commas({number => $old_scan_hardware_ram_module_size})." #!string!scan_hardware_unit_0001!#)",
						old_manufacturer  => $old_scan_hardware_ram_module_manufacturer, 
						old_model         => $old_scan_hardware_ram_module_model, 
						old_serial_number => $old_scan_hardware_ram_module_serial_number, 
					};
					$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 0, level => 1, key => "scan_hardware_alert_0023", variables => $variables});
					$anvil->Alert->register({alert_level => "warning", message => "scan_hardware_alert_0023", variables => $variables, set_by => $THIS_FILE});
				}
				else
				{
					# Something else changed.
					my $variables = {
						locator           => $hardware_ram_module_locator,
						new_size          => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_ram_module_size})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_ram_module_size})." #!string!scan_hardware_unit_0001!#)",
						new_manufacturer  => $new_scan_hardware_ram_module_manufacturer, 
						new_model         => $new_scan_hardware_ram_module_model, 
						new_serial_number => $new_scan_hardware_ram_module_serial_number, 
						old_size          => $anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_hardware_ram_module_size})." (".$anvil->Convert->add_commas({number => $old_scan_hardware_ram_module_size})." #!string!scan_hardware_unit_0001!#)",
						old_manufacturer  => $old_scan_hardware_ram_module_manufacturer, 
						old_model         => $old_scan_hardware_ram_module_model, 
						old_serial_number => $old_scan_hardware_ram_module_serial_number, 
					};
					$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 0, level => 1, key => "scan_hardware_alert_0024", variables => $variables});
					$anvil->Alert->register({alert_level => "warning", message => "scan_hardware_alert_0024", variables => $variables, set_by => $THIS_FILE});
				}
				
				my $query = "
UPDATE 
    scan_hardware_ram_modules 
SET
    scan_hardware_ram_module_locator       = ".$anvil->Database->quote($hardware_ram_module_locator).", 
    scan_hardware_ram_module_size          = ".$anvil->Database->quote($new_scan_hardware_ram_module_size).", 
    scan_hardware_ram_module_manufacturer  = ".$anvil->Database->quote($new_scan_hardware_ram_module_manufacturer).", 
    scan_hardware_ram_module_model         = ".$anvil->Database->quote($new_scan_hardware_ram_module_model).", 
    scan_hardware_ram_module_serial_number = ".$anvil->Database->quote($new_scan_hardware_ram_module_serial_number).", 
    modified_date                          = ".$anvil->Database->quote($anvil->Database->refresh_timestamp)."
WHERE 
    scan_hardware_ram_module_uuid          = ".$anvil->Database->quote($scan_hardware_ram_module_uuid)." 
;";
				# Now record the query in the array
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
				$anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__});
			}
		}
		else
		{
			# Send an alert telling the user that we've found a new module.
			my $variables = {
				locator       => $hardware_ram_module_locator,
				size          => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_scan_hardware_ram_module_size})." (".$anvil->Convert->add_commas({number => $new_scan_hardware_ram_module_size})." #!string!scan_hardware_unit_0001!#)",
				manufacturer  => $new_scan_hardware_ram_module_manufacturer, 
				model         => $new_scan_hardware_ram_module_model, 
				serial_number => $new_scan_hardware_ram_module_serial_number, 
			};
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 0, level => 2, key => "scan_hardware_alert_0025", variables => $variables});
			$anvil->Alert->register({alert_level => "notice", message => "scan_hardware_alert_0025", variables => $variables, set_by => $THIS_FILE});
			
			# INSERT
			my $scan_hardware_ram_module_uuid = $anvil->Get->uuid();
			my $query = "
INSERT INTO 
    scan_hardware_ram_modules 
(
    scan_hardware_ram_module_host_uuid, 
    scan_hardware_ram_module_uuid, 
    scan_hardware_ram_module_locator, 
    scan_hardware_ram_module_size, 
    scan_hardware_ram_module_manufacturer, 
    scan_hardware_ram_module_model, 
    scan_hardware_ram_module_serial_number, 
    modified_date 
) VALUES (
    ".$anvil->Database->quote($anvil->Get->host_uuid).", 
    ".$anvil->Database->quote($scan_hardware_ram_module_uuid).", 
    ".$anvil->Database->quote($hardware_ram_module_locator).", 
    ".$anvil->Database->quote($new_scan_hardware_ram_module_size).", 
    ".$anvil->Database->quote($new_scan_hardware_ram_module_manufacturer).", 
    ".$anvil->Database->quote($new_scan_hardware_ram_module_model).", 
    ".$anvil->Database->quote($new_scan_hardware_ram_module_serial_number).", 
    ".$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 }});
			$anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__});
		}
	}
	
	# Now look for left over modules we found in the database but not on the local system.
	foreach my $hardware_ram_module_locator (keys %{$anvil->data->{sql}{scan_hardware_ram_module_uuid}})
	{
		# Module vanished!
		my $scan_hardware_ram_module_uuid = $anvil->data->{sql}{ram_module_locator_to_uuid}{$hardware_ram_module_locator};
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			scan_hardware_ram_module_locator => $hardware_ram_module_locator,
			scan_hardware_ram_module_uuid    => $scan_hardware_ram_module_uuid, 
		}});
		
		my $old_scan_hardware_ram_module_size          = $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_size};
		my $old_scan_hardware_ram_module_manufacturer  = $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_manufacturer};
		my $old_scan_hardware_ram_module_model         = $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_model};
		my $old_scan_hardware_ram_module_serial_number = $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_serial_number};
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			old_scan_hardware_ram_module_size          => $old_scan_hardware_ram_module_size, 
			old_scan_hardware_ram_module_manufacturer  => $old_scan_hardware_ram_module_manufacturer, 
			old_scan_hardware_ram_module_model         => $old_scan_hardware_ram_module_model, 
			old_scan_hardware_ram_module_serial_number => $old_scan_hardware_ram_module_serial_number, 
		}});
		
		# Delete the entry so we know we've processed this existing module.
		delete $anvil->data->{sql}{ram_module_locator_to_uuid}{$hardware_ram_module_locator};
		delete $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid};
		
		my $variables = {
			locator           => $hardware_ram_module_locator,
			old_size          => $anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_hardware_ram_module_size})." (".$anvil->Convert->add_commas({number => $old_scan_hardware_ram_module_size})." #!string!scan_hardware_unit_0001!#)",
			old_manufacturer  => $old_scan_hardware_ram_module_manufacturer, 
			old_model         => $old_scan_hardware_ram_module_model, 
			old_serial_number => $old_scan_hardware_ram_module_serial_number, 
		};
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 0, level => 2, key => "scan_hardware_alert_0026", variables => $variables});
		$anvil->Alert->register({alert_level => "warning", message => "scan_hardware_alert_0026", variables => $variables, set_by => $THIS_FILE});
		
		my $query = "
UPDATE 
    scan_hardware_ram_modules 
SET
    scan_hardware_ram_module_manufacturer = 'VANISHED', 
    modified_date                         = ".$anvil->Database->quote($anvil->Database->refresh_timestamp)."
WHERE 
    scan_hardware_ram_module_uuid         = ".$anvil->Database->quote($scan_hardware_ram_module_uuid)." 
;";
		# Now record the query in the array
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
		$anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__});
	}

	return(0);
}

# Here we see if our peer has more or less RAM. If we have less, we'll mark our health as degraded.
sub process_health
{
	my ($anvil) = @_;
	
	# Only do this on nodes.
	my $host_type = $anvil->Get->host_type();
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_type => $host_type }});
	return(0) if $host_type ne "node";
	
	# Find the host_uuid for the peer's host name and UUID.
	$anvil->Cluster->get_peers({debug => 2});
	my $peer_is        = $anvil->data->{sys}{anvil}{peer_is};
	my $peer_host_name = $anvil->data->{sys}{anvil}{$peer_is}{host_name};
	my $peer_host_uuid = $anvil->data->{sys}{anvil}{$peer_is}{host_uuid};
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		peer_host_name => $peer_host_name,
		peer_host_uuid => $peer_host_uuid,
	}});
	
	# How much RAM is on the other node?
	my $query = "SELECT scan_hardware_ram_total FROM scan_hardware WHERE scan_hardware_host_uuid = ".$anvil->Database->quote($peer_host_uuid).";";
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
	
	my $peer_ram_total = $anvil->Database->query({level => 2, query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
	   $peer_ram_total = 0 if not defined $peer_ram_total;
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { peer_ram_total => $peer_ram_total }});
	
	# Do we know the peer's RAM?
	my $clear_alerts = 1;
	if ($peer_ram_total)
	{
		# We don't want to freak out unless the difference is at least 1GiB
		my $hardware_ram_total = $anvil->data->{summary}{ram}{size};
		my $difference         = $peer_ram_total - $hardware_ram_total;
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			hardware_ram_total => $anvil->Convert->add_commas({number => $hardware_ram_total})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $hardware_ram_total}).")", 
			difference         => $anvil->Convert->add_commas({number => $difference})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $difference}).")", 
		}});
		
		# greater than 1 GiB (default) or less than 128 MiB (default)
		if (($peer_ram_total > $hardware_ram_total) && ($difference > $anvil->data->{scancore}{'scan-hardware'}{ram}{high_threshold}))
		{
			# Mark us as having a fairly major health issue if this has been the case for more 
			# than five minutes.
			my $age = $anvil->Alert->check_condition_age({
				debug     => 2,
				name      => "scan-hardware::less_ram_than_peer",
				host_uuid => $anvil->Get->host_uuid, 
			});
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { age => $age }});
			
			if ($age > 300)
			{
				my $changed = $anvil->Alert->check_alert_sent({
					debug          => 3,
					record_locator => "scan_hardware::less_ram_than_peer",
					set_by         => $THIS_FILE,
				});
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }});
				if ($changed)
				{
					# Register an alert.
					my $variables = {
						local_ram  => $anvil->Convert->bytes_to_human_readable({'bytes' => $hardware_ram_total})." (".$anvil->Convert->add_commas({number => $hardware_ram_total})." #!string!suffix_0009!#)",
						peer_ram   => $anvil->Convert->bytes_to_human_readable({'bytes' => $peer_ram_total})." (".$anvil->Convert->add_commas({number => $peer_ram_total})." #!string!suffix_0009!#)",
						difference => $anvil->Convert->bytes_to_human_readable({'bytes' => $difference})." (".$anvil->Convert->add_commas({number => $difference})." #!string!suffix_0009!#)", 
					};
					$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0027", variables => $variables});
					$anvil->Alert->register({alert_level => "warning", message => "scan_hardware_alert_0027", variables => $variables, set_by => $THIS_FILE});
				}
			}
			my ($health_uuid) = $anvil->Database->insert_or_update_health({
				debug                => 2,
				health_agent_name    => $THIS_FILE,
				health_source_name   => "less_ram_than_peer",
				health_source_weight => $anvil->data->{scancore}{'scan-hardware'}{score}{less_ram},
			});
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { health_uuid => $health_uuid }});
		}
		elsif ((not $difference) or ($difference < $anvil->data->{scancore}{'scan-hardware'}{ram}{clear_threshold}))
		{
			my $age = $anvil->Alert->check_condition_age({
				debug     => 2,
				clear     => 1,
				name      => "scan-hardware::less_ram_than_peer",
				host_uuid => $anvil->Get->host_uuid, 
			});
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { age => $age }});
			my $changed = $anvil->Alert->check_alert_sent({
				debug          => 3,
				record_locator => "scan_hardware::less_ram_than_peer",
				set_by         => $THIS_FILE,
				clear          => 1,
			});
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }});
			if ($changed)
			{
				# Clear the alert.
				my $variables = {
					ram => $anvil->Convert->bytes_to_human_readable({'bytes' => $hardware_ram_total})." (".$anvil->Convert->add_commas({number => $hardware_ram_total})." #!string!suffix_0009!#)",
				};
				$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_hardware_alert_0028", variables => $variables});
				$anvil->Alert->register({alert_level => "warning", message => "scan_hardware_alert_0028", variables => $variables, set_by => $THIS_FILE});
			}
			my ($health_uuid) = $anvil->Database->insert_or_update_health({
				debug              => 2,
				health_agent_name  => $THIS_FILE,
				health_source_name => "less_ram_than_peer",
				'delete'           => 1,
			});
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { health_uuid => $health_uuid }});
		}
	}
	else
	{
		# We don't know how much RAM our peer has yet, so do nothing.
	}

	return(0);
}

# This reads in the last scan's data.
sub read_last_scan
{
	my ($anvil) = @_;
	
	# This calls up the entry for this host. There will only be one.
	my $query = "
SELECT 
    scan_hardware_uuid, 
    scan_hardware_cpu_model, 
    scan_hardware_cpu_cores, 
    scan_hardware_cpu_threads, 
    scan_hardware_cpu_bugs, 
    scan_hardware_cpu_flags, 
    scan_hardware_ram_total, 
    scan_hardware_memory_total, 
    scan_hardware_memory_free, 
    scan_hardware_swap_total, 
    scan_hardware_swap_free, 
    scan_hardware_led_id, 
    scan_hardware_led_css, 
    scan_hardware_led_error, 
    floor(extract(epoch from modified_date))
FROM 
    scan_hardware 
WHERE 
    scan_hardware_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 there are 2, then we've hit a bug where, somehow, we got listed twice. 
	if ($count > 1)
	{
		# Delete all and exit.
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, 'print' => 1, key => "scan_hardware_log_0002", variables => { count => $count }});
		my $queries = [];
		push @{$queries}, "DELETE FROM history.scan_hardware_ram_modules WHERE scan_hardware_ram_module_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid).";";
		push @{$queries}, "DELETE FROM scan_hardware_ram_modules WHERE scan_hardware_ram_module_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid).";";
		push @{$queries}, "DELETE FROM history.scan_hardware WHERE scan_hardware_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid).";";
		push @{$queries}, "DELETE FROM scan_hardware WHERE scan_hardware_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid).";";
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { query => $query }});
		$anvil->Database->write({query => $queries, source => $THIS_FILE, line => __LINE__});
	}
	
	foreach my $row (@{$results})
	{
		# We've got an entry in the 'scan_hardware' table, so now we'll look for data in the node and 
		# services tables.
		my $scan_hardware_uuid          = $row->[0]; 
		my $scan_hardware_cpu_model     = $row->[1]; 
		my $scan_hardware_cpu_cores     = $row->[2]; 
		my $scan_hardware_cpu_threads   = $row->[3]; 
		my $scan_hardware_cpu_bugs      = $row->[4]; 
		my $scan_hardware_cpu_flags     = $row->[5]; 
		my $scan_hardware_ram_total     = $row->[6]; 
		my $scan_hardware_memory_total  = $row->[7]; 
		my $scan_hardware_memory_free   = $row->[8]; 
		my $scan_hardware_swap_total    = $row->[9]; 
		my $scan_hardware_swap_free     = $row->[10]; 
		my $scan_hardware_led_id        = $row->[11]; 
		my $scan_hardware_led_css       = $row->[12]; 
		my $scan_hardware_led_error     = $row->[13];  
		my $scan_hardware_modified_date = $row->[14];  
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			"scan_hardware_uuid"          => $scan_hardware_uuid,
			"scan_hardware_cpu_model"     => $scan_hardware_cpu_model,
			"scan_hardware_cpu_cores"     => $scan_hardware_cpu_cores,
			"scan_hardware_cpu_threads"   => $scan_hardware_cpu_threads,
			"scan_hardware_cpu_bugs"      => $scan_hardware_cpu_bugs,
			"scan_hardware_cpu_flags"     => $scan_hardware_cpu_flags,
			"scan_hardware_ram_total"     => $scan_hardware_ram_total,
			"scan_hardware_memory_total"  => $scan_hardware_memory_total,
			"scan_hardware_memory_free"   => $scan_hardware_memory_free,
			"scan_hardware_swap_total"    => $scan_hardware_swap_total,
			"scan_hardware_swap_free"     => $scan_hardware_swap_free,
			"scan_hardware_led_id"        => $scan_hardware_led_id,
			"scan_hardware_led_css"       => $scan_hardware_led_css,
			"scan_hardware_led_error"     => $scan_hardware_led_error,
			"scan_hardware_modified_date" => $scan_hardware_modified_date,
		}});
		
		# Record the hardware_uuid in an easy to find place for later when looking for changes.
		$anvil->data->{sql}{scan_hardware_uuid} = $scan_hardware_uuid;
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "sql::scan_hardware_uuid" => $anvil->data->{sql}{scan_hardware_uuid} }});
		
		# Store the old data now.
		$anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid} = {
			scan_hardware_cpu_model     => $scan_hardware_cpu_model,
			scan_hardware_cpu_cores     => $scan_hardware_cpu_cores,
			scan_hardware_cpu_threads   => $scan_hardware_cpu_threads,
			scan_hardware_cpu_bugs      => $scan_hardware_cpu_bugs,
			scan_hardware_cpu_flags     => $scan_hardware_cpu_flags,
			scan_hardware_ram_total     => $scan_hardware_ram_total,
			scan_hardware_memory_total  => $scan_hardware_memory_total,
			scan_hardware_memory_free   => $scan_hardware_memory_free,
			scan_hardware_swap_total    => $scan_hardware_swap_total,
			scan_hardware_swap_free     => $scan_hardware_swap_free,
			scan_hardware_led_id        => $scan_hardware_led_id,
			scan_hardware_led_css       => $scan_hardware_led_css,
			scan_hardware_led_error     => $scan_hardware_led_error,
			scan_hardware_modified_date => $scan_hardware_modified_date,
		};
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			"sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_cpu_model"     => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_cpu_model},
			"sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_cpu_cores"     => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_cpu_cores},
			"sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_cpu_threads"   => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_cpu_threads},
			"sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_cpu_bugs"      => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_cpu_bugs},
			"sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_cpu_flags"     => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_cpu_flags},
			"sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_ram_total"     => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_ram_total},
			"sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_memory_total"  => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_memory_total},
			"sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_memory_free"   => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_memory_free},
			"sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_swap_total"    => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_swap_total},
			"sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_swap_free"     => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_swap_free},
			"sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_led_id"        => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_led_id},
			"sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_led_css"       => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_led_css},
			"sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_led_error"     => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_led_error},
			"sql::scan_hardware::scan_hardware_uuid::${scan_hardware_uuid}::scan_hardware_modified_date" => $anvil->data->{sql}{scan_hardware}{scan_hardware_uuid}{$scan_hardware_uuid}{scan_hardware_modified_date},
		}});
	}
	undef $count;
	undef $results;
	
	# Read in the RAM module data.
	$query = "
SELECT 
    scan_hardware_ram_module_uuid, 
    scan_hardware_ram_module_locator, 
    scan_hardware_ram_module_size, 
    scan_hardware_ram_module_manufacturer, 
    scan_hardware_ram_module_model, 
    scan_hardware_ram_module_serial_number 
FROM 
    scan_hardware_ram_modules 
WHERE 
    scan_hardware_ram_module_host_uuid = ".$anvil->Database->quote($anvil->Get->host_uuid)." 
;";
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, 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})
	{
		# We've got an entry in the 'scan_hardware_ram_modules' table, so now we'll look for data in the node and 
		# services tables.
		my $scan_hardware_ram_module_uuid          = $row->[0]; 
		my $scan_hardware_ram_module_locator       = $row->[1]; 
		my $scan_hardware_ram_module_size          = $row->[2]; 
		my $scan_hardware_ram_module_manufacturer  = $row->[3]; 
		my $scan_hardware_ram_module_model         = $row->[4]; 
		my $scan_hardware_ram_module_serial_number = $row->[5]; 
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { 
			"scan_hardware_ram_module_uuid"          => $scan_hardware_ram_module_uuid,
			"scan_hardware_ram_module_locator"       => $scan_hardware_ram_module_locator,
			"scan_hardware_ram_module_size"          => $scan_hardware_ram_module_size,
			"scan_hardware_ram_module_manufacturer"  => $scan_hardware_ram_module_manufacturer,
			"scan_hardware_ram_module_model"         => $scan_hardware_ram_module_model,
			"scan_hardware_ram_module_serial_number" => $scan_hardware_ram_module_serial_number,
		}});
		
		# Record the scan_hardware_ram_module_uuid in an easy to find place for later when looking for changes.
		$anvil->data->{sql}{ram_module_locator_to_uuid}{$scan_hardware_ram_module_locator} = $scan_hardware_ram_module_uuid;
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			"sql::ram_module_locator_to_uuid::${scan_hardware_ram_module_locator}" => $anvil->data->{sql}{ram_module_locator_to_uuid}{$scan_hardware_ram_module_locator}, 
		}});
		
		# Store the old data now.
		$anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid} = {
			scan_hardware_ram_module_locator       => $scan_hardware_ram_module_locator,
			scan_hardware_ram_module_size          => $scan_hardware_ram_module_size,
			scan_hardware_ram_module_manufacturer  => $scan_hardware_ram_module_manufacturer,
			scan_hardware_ram_module_model         => $scan_hardware_ram_module_model,
			scan_hardware_ram_module_serial_number => $scan_hardware_ram_module_serial_number,
		};
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			"sql::scan_hardware_ram_module::scan_hardware_ram_module_uuid::${scan_hardware_ram_module_uuid}::scan_hardware_ram_module_locator"       => $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_locator},
			"sql::scan_hardware_ram_module::scan_hardware_ram_module_uuid::${scan_hardware_ram_module_uuid}::scan_hardware_ram_module_size"          => $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_size},
			"sql::scan_hardware_ram_module::scan_hardware_ram_module_uuid::${scan_hardware_ram_module_uuid}::scan_hardware_ram_module_manufacturer"  => $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_manufacturer},
			"sql::scan_hardware_ram_module::scan_hardware_ram_module_uuid::${scan_hardware_ram_module_uuid}::scan_hardware_ram_module_model"         => $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_model},
			"sql::scan_hardware_ram_module::scan_hardware_ram_module_uuid::${scan_hardware_ram_module_uuid}::scan_hardware_ram_module_serial_number" => $anvil->data->{sql}{scan_hardware_ram_module}{scan_hardware_ram_module_uuid}{$scan_hardware_ram_module_uuid}{scan_hardware_ram_module_serial_number},
		}});
	}
	
	return(0);
}
