#!/usr/bin/perl
# 
# This scans the LVM (logical volume management) components (PV, VG and LV). 
# 
# Examples;
# 
# Exit codes;
# 0 = Normal exit.
# 1 = Startup failure (not running as root, no DB, bad file read, etc)
# 
# NOTE: 
# - 
# 
# TODO: 
# - 
#

use strict;
use warnings;
use Anvil::Tools;
use Data::Dumper;
use JSON;
use XML::LibXML;

# 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-filesystems'}{disable} = 0;
$anvil->data->{'scan-filesystems'}{alert_sort}        = 2;

$anvil->Storage->read_config();
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, 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});
}

# Find the block devices on this host.
collect_data($anvil);

# Load stored data.
read_last_scan($anvil);

# Loog for changes
find_changes($anvil);

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


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

sub find_changes
{
	my ($anvil) = @_;
	
	foreach my $filesystem_internal_uuid (sort {$a cmp $b} keys %{$anvil->data->{new}{partition}})
	{
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { filesystem_uuid => $filesystem_internal_uuid }});
		
		# Skip LVM devices as they're tracked by 'scan-lvm'.
		next if $anvil->data->{new}{partition}{$filesystem_internal_uuid}{type} eq "LVM2_member";
		
		my $new_kernel_name   = $anvil->data->{new}{partition}{$filesystem_internal_uuid}{kernel_name};
		my $new_type          = $anvil->data->{new}{partition}{$filesystem_internal_uuid}{type};
		my $new_mount_point   = $anvil->data->{new}{partition}{$filesystem_internal_uuid}{mount_point};
		my $new_transport     = $anvil->data->{new}{partition}{$filesystem_internal_uuid}{transport};
		my $new_media_type    = $anvil->data->{new}{partition}{$filesystem_internal_uuid}{media_type};
		my $new_vendor        = $anvil->data->{new}{partition}{$filesystem_internal_uuid}{vendor};
		my $new_model         = $anvil->data->{new}{partition}{$filesystem_internal_uuid}{model};
		my $new_serial_number = $anvil->data->{new}{partition}{$filesystem_internal_uuid}{serial_number};
		my $new_description   = $anvil->data->{new}{partition}{$filesystem_internal_uuid}{description};
		my $new_size          = $anvil->data->{new}{partition}{$filesystem_internal_uuid}{size};
		my $new_used          = $anvil->data->{new}{partition}{$filesystem_internal_uuid}{used};
		my $new_free          = $new_size - $new_used;
		my $new_free_percent  = $anvil->Convert->round({number => ((($new_size - $new_used)/$new_size) * 100), places => 1});
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			new_kernel_name   => $new_kernel_name, 
			new_type          => $new_type,
			new_mount_point   => $new_mount_point,
			new_transport     => $new_transport,
			new_media_type    => $new_media_type,
			new_vendor        => $new_vendor,
			new_model         => $new_model,
			new_serial_number => $new_serial_number,
			new_description   => $new_description,
			new_size          => $anvil->Convert->add_commas({number => $new_size})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $new_size}),
			new_used          => $anvil->Convert->add_commas({number => $new_used})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $new_used}),
			new_free          => $anvil->Convert->add_commas({number => $new_free})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $new_free}),
			new_free_percent  => $new_free_percent,
		}});
		
		# Have we seen this partition before?
		if (exists $anvil->data->{sql}{scan_filesystems}{scan_filesystem_internal_uuid}{$filesystem_internal_uuid})
		{
			my $filesystem_uuid = $anvil->data->{sql}{scan_filesystems}{scan_filesystem_internal_uuid}{$filesystem_internal_uuid}{filesystem_uuid};
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { filesystem_uuid => $filesystem_uuid }});
			
			# Note: We do not edit the description as the user may do so later in the WebUI
			my $old_kernel_name   = $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$filesystem_uuid}{scan_filesystem_kernel_name};
			my $old_type          = $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$filesystem_uuid}{scan_filesystem_type};
			my $old_mount_point   = $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$filesystem_uuid}{scan_filesystem_mount_point}; 
			my $old_transport     = $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$filesystem_uuid}{scan_filesystem_transport}; 
			my $old_media_type    = $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$filesystem_uuid}{scan_filesystem_media_type};
			my $old_vendor        = $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$filesystem_uuid}{scan_filesystem_vendor};
			my $old_model         = $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$filesystem_uuid}{scan_filesystem_model};
			my $old_serial_number = $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$filesystem_uuid}{scan_filesystem_serial_number};
			my $old_size          = $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$filesystem_uuid}{scan_filesystem_size};
			my $old_used          = $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$filesystem_uuid}{scan_filesystem_used};
			my $old_free          = $old_size - $old_used;
			my $old_free_percent  = $anvil->Convert->round({number => ((($old_size - $old_used)/$old_size) * 100), places => 1});
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				filesystem_uuid   => $filesystem_uuid, 
				old_kernel_name   => $old_kernel_name, 
				old_type          => $old_type,
				old_mount_point   => $old_mount_point,
				old_transport     => $old_transport,
				old_media_type    => $old_media_type,
				old_vendor        => $old_vendor,
				old_model         => $old_model,
				old_serial_number => $old_serial_number,
				old_size          => $anvil->Convert->add_commas({number => $old_size})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $old_size}),
				old_used          => $anvil->Convert->add_commas({number => $old_used})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $old_used}),
				old_free          => $anvil->Convert->add_commas({number => $old_free})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $old_free}),
				old_free_percent  => $old_free_percent,
			}});
			
			my $update    = 0;
			my $variables = {
				filesystem_internal_uuid => $filesystem_internal_uuid, 
				new_kernel_name          => $new_kernel_name, 
				old_kernel_name          => $old_kernel_name, 
				new_type                 => $new_type, 
				old_type                 => $old_type, 
				new_mount_point          => $new_mount_point, 
				old_mount_point          => $old_mount_point, 
				new_transport            => $new_transport, 
				old_transport            => $old_transport, 
				new_media_type           => $new_media_type, 
				old_media_type           => $old_media_type, 
				new_vendor               => $new_vendor, 
				old_vendor               => $old_vendor, 
				new_model                => $new_model, 
				old_model                => $old_model, 
				new_serial_number        => $new_serial_number, 
				old_serial_number        => $old_serial_number, 
				new_hr_size              => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_size}), 
				old_hr_size              => $anvil->Convert->bytes_to_human_readable({'bytes' => $old_size}), 
				new_byte_size            => $anvil->Convert->add_commas({number => $new_size}), 
				old_byte_size            => $anvil->Convert->add_commas({number => $old_size}), 
				new_hr_used              => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_used}), 
				old_hr_used              => $anvil->Convert->bytes_to_human_readable({'bytes' => $old_used}), 
				new_byte_used            => $anvil->Convert->add_commas({number => $new_used}), 
				old_byte_used            => $anvil->Convert->add_commas({number => $old_used}), 
				new_hr_free              => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_free}), 
				old_hr_free              => $anvil->Convert->bytes_to_human_readable({'bytes' => $old_free}), 
				new_byte_free            => $anvil->Convert->add_commas({number => $new_free}), 
				old_byte_free            => $anvil->Convert->add_commas({number => $old_free}), 
				new_free_percent         => $new_free_percent, 
				old_free_percent         => $old_free_percent, 
			};
			
			if ($new_kernel_name ne $old_kernel_name)
			{
				# Kernel device name changed.
				$update = 1;
				$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_filesystem_alert_0005", variables => $variables});
				$anvil->Alert->register({
					alert_level   => "notice", 
					message       => "scan_filesystem_alert_0005", 
					sort_position => $anvil->data->{'scan-filesystems'}{alert_sort}++,
					variables     => $variables, 
					set_by        => $THIS_FILE,
				});
			}
			if ($new_type ne $old_type)
			{
				# File system type has changed. WAT?
				$update = 1;
				$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_filesystem_alert_0006", variables => $variables});
				$anvil->Alert->register({
					alert_level   => "notice", 
					message       => "scan_filesystem_alert_0006", 
					sort_position => $anvil->data->{'scan-filesystems'}{alert_sort}++,
					variables     => $variables, 
					set_by        => $THIS_FILE,
				});
			}
			if ($new_mount_point ne $old_mount_point)
			{
				# Mount point has changed
				$update = 1;
				
				# Is this a returning filesystem?
				if ($old_mount_point eq "REMOVED")
				{
					# Yup, returned
					$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_filesystem_alert_0015", variables => $variables});
					$anvil->Alert->register({
						alert_level   => "notice", 
						message       => "scan_filesystem_alert_0015", 
						sort_position => $anvil->data->{'scan-filesystems'}{alert_sort}++,
						variables     => $variables, 
						set_by        => $THIS_FILE,
					});
				}
				else
				{
					# Nope, it's a remount
					$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_filesystem_alert_0007", variables => $variables});
					$anvil->Alert->register({
						alert_level   => "notice", 
						message       => "scan_filesystem_alert_0007", 
						sort_position => $anvil->data->{'scan-filesystems'}{alert_sort}++,
						variables     => $variables, 
						set_by        => $THIS_FILE,
					});
				}
			}
			if ($new_transport ne $old_transport)
			{
				# Transport changed?! Could it be a dd to a new device?
				$update = 1;
				$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_filesystem_alert_0008", variables => $variables});
				$anvil->Alert->register({
					alert_level   => "notice", 
					message       => "scan_filesystem_alert_0008", 
					sort_position => $anvil->data->{'scan-filesystems'}{alert_sort}++,
					variables     => $variables, 
					set_by        => $THIS_FILE,
				});
			}
			if ($new_media_type ne $old_media_type)
			{
				# Media changed?! Could it be a dd to a new device?
				$update = 1;
				$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_filesystem_alert_0009", variables => $variables});
				$anvil->Alert->register({
					alert_level   => "notice", 
					message       => "scan_filesystem_alert_0009", 
					sort_position => $anvil->data->{'scan-filesystems'}{alert_sort}++,
					variables     => $variables, 
					set_by        => $THIS_FILE,
				});
			}
			if ($new_vendor ne $old_vendor)
			{
				# Vendor changed?! Could it be a dd to a new device?
				$update = 1;
				$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_filesystem_alert_0010", variables => $variables});
				$anvil->Alert->register({
					alert_level   => "notice", 
					message       => "scan_filesystem_alert_0010", 
					sort_position => $anvil->data->{'scan-filesystems'}{alert_sort}++,
					variables     => $variables, 
					set_by        => $THIS_FILE,
				});
			}
			if ($new_model ne $old_model)
			{
				# Model changed?! Could it be a dd to a new device?
				$update = 1;
				$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_filesystem_alert_0011", variables => $variables});
				$anvil->Alert->register({
					alert_level   => "notice", 
					message       => "scan_filesystem_alert_0011", 
					sort_position => $anvil->data->{'scan-filesystems'}{alert_sort}++,
					variables     => $variables, 
					set_by        => $THIS_FILE,
				});
			}
			if ($new_serial_number ne $old_serial_number)
			{
				# Serial Number changed?! Could it be a dd to a new device?
				$update = 1;
				$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_filesystem_alert_0012", variables => $variables});
				$anvil->Alert->register({
					alert_level   => "notice", 
					message       => "scan_filesystem_alert_0012", 
					sort_position => $anvil->data->{'scan-filesystems'}{alert_sort}++,
					variables     => $variables, 
					set_by        => $THIS_FILE,
				});
			}
			if ($new_size ne $old_size)
			{
				# Size changed, could be an LV resize or dd.
				$update = 1;
				$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_filesystem_alert_0013", variables => $variables});
				$anvil->Alert->register({
					alert_level   => "notice", 
					message       => "scan_filesystem_alert_0013", 
					sort_position => $anvil->data->{'scan-filesystems'}{alert_sort}++,
					variables     => $variables, 
					set_by        => $THIS_FILE,
				});
			}
			if ($new_used ne $old_used)
			{
				# This is expected to nearly constantly change.
				$update = 1;
				$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "scan_filesystem_alert_0014", variables => $variables});
				$anvil->Alert->register({
					alert_level   => "info", 
					message       => "scan_filesystem_alert_0014", 
					sort_position => $anvil->data->{'scan-filesystems'}{alert_sort}++,
					variables     => $variables, 
					set_by        => $THIS_FILE,
				});
			}
			
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update => $update }});
			if ($update)
			{
				# Save the changed.
				my $query = "
UPDATE 
    scan_filesystems 
SET 
    scan_filesystem_type          = ".$anvil->Database->quote($new_type).", 
    scan_filesystem_kernel_name   = ".$anvil->Database->quote($new_kernel_name).", 
    scan_filesystem_mount_point   = ".$anvil->Database->quote($new_mount_point).", 
    scan_filesystem_transport     = ".$anvil->Database->quote($new_transport).", 
    scan_filesystem_media_type    = ".$anvil->Database->quote($new_media_type).", 
    scan_filesystem_vendor        = ".$anvil->Database->quote($new_vendor).", 
    scan_filesystem_model         = ".$anvil->Database->quote($new_model).", 
    scan_filesystem_serial_number = ".$anvil->Database->quote($new_serial_number).",
    scan_filesystem_description   = ".$anvil->Database->quote($new_description).", 
    scan_filesystem_size          = ".$anvil->Database->quote($new_size).", 
    scan_filesystem_used          = ".$anvil->Database->quote($new_used).", 
    modified_date                 = ".$anvil->Database->quote($anvil->Database->refresh_timestamp)." 
WHERE 
    scan_filesystem_uuid          = ".$anvil->Database->quote($filesystem_uuid)."
;";
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
				$anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__});
			}
			
			# Delete the SQL entry so we can tell which disappeared.
			delete $anvil->data->{sql}{scan_filesystems}{scan_filesystem_internal_uuid}{$filesystem_internal_uuid};
			delete $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$filesystem_uuid};
		}
		else
		{
			# It's new, add it.
			my $scan_filesystem_uuid = $anvil->Get->uuid();
			my $query                = "
INSERT INTO  
    scan_filesystems 
(
    scan_filesystem_uuid,
    scan_filesystem_host_uuid, 
    scan_filesystem_internal_uuid, 
    scan_filesystem_type, 
    scan_filesystem_kernel_name, 
    scan_filesystem_mount_point, 
    scan_filesystem_transport, 
    scan_filesystem_media_type, 
    scan_filesystem_vendor, 
    scan_filesystem_model, 
    scan_filesystem_serial_number, 
    scan_filesystem_description, 
    scan_filesystem_size, 
    scan_filesystem_used, 
    modified_date
) VALUES (
    ".$anvil->Database->quote($scan_filesystem_uuid).", 
    ".$anvil->Database->quote($anvil->Get->host_uuid).", 
    ".$anvil->Database->quote($filesystem_internal_uuid).", 
    ".$anvil->Database->quote($new_type).", 
    ".$anvil->Database->quote($new_kernel_name).", 
    ".$anvil->Database->quote($new_mount_point).", 
    ".$anvil->Database->quote($new_transport).", 
    ".$anvil->Database->quote($new_media_type).", 
    ".$anvil->Database->quote($new_vendor).", 
    ".$anvil->Database->quote($new_model).", 
    ".$anvil->Database->quote($new_serial_number).", 
    ".$anvil->Database->quote($new_description).", 
    ".$anvil->Database->quote($new_size).", 
    ".$anvil->Database->quote($new_used).", 
    ".$anvil->Database->quote($anvil->Database->refresh_timestamp)."
);";
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
			$anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__});
			
			# Register the notice level alert.
			my $variables = {
				filesystem_internal_uuid => $filesystem_internal_uuid, 
				new_kernel_name          => $new_kernel_name, 
				new_type                 => $new_type,
				new_mount_point          => $new_mount_point,
				new_transport            => $new_transport,
				new_media_type           => $new_media_type,
				new_vendor               => $new_vendor,
				new_model                => $new_model,
				new_serial_number        => $new_serial_number,
				new_description          => $new_description,
				new_hr_size              => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_size}),
				new_byte_size            => $anvil->Convert->add_commas({number => $new_size}),
				new_hr_used              => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_used}),
				new_byte_used            => $anvil->Convert->add_commas({number => $new_used}),
				new_hr_free              => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_free}),
				new_byte_free            => $anvil->Convert->add_commas({number => $new_free}),
				new_free_percent         => $new_free_percent,
			};
			$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_filesystem_alert_0001", variables => $variables});
			$anvil->Alert->register({
				alert_level   => "notice", 
				message       => "scan_filesystem_alert_0001", 
				sort_position => $anvil->data->{'scan-filesystems'}{alert_sort}++,
				variables     => $variables, 
				set_by        => $THIS_FILE,
			});
		}
		
		# Now that we know the partition is in the database; If the free percent is less than 5, set 
		# a warning alert. If it's less than 15%, set a notice alert. Otherwise, if the free space is
		# greater than 25%, clear any existing  warnings.
		my $percent_free_warn   = 5;
		my $percent_free_notice = 15; 
		my $percent_free_clear  = 25;
		my $variables           = {
			filesystem_uuid  => $filesystem_internal_uuid, 
			new_kernel_name  => $new_kernel_name, 
			new_type         => $new_type,
			new_mount_point  => $new_mount_point,
			new_hr_size      => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_size}),
			new_byte_size    => $anvil->Convert->add_commas({number => $new_size}),
			new_hr_free      => $anvil->Convert->bytes_to_human_readable({'bytes' => $new_free}),
			new_byte_free    => $anvil->Convert->add_commas({number => $new_free}),
			new_free_percent => $new_free_percent,
		};
		
		if ($new_free_percent < $percent_free_warn)
		{
			# See if we've warned the user.
			my $changed = $anvil->Alert->check_alert_sent({
				clear          => 0, 
				record_locator => $filesystem_internal_uuid.":very_low_space", 
				set_by         => $THIS_FILE,
			});
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }});
			if ($changed)
			{
				# First time we've fallen under 5%
				my $alert_level = "warning";
				if ($new_mount_point =~ /swap/i)
				{
					$alert_level = "notice";
				}
				$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_filesystem_alert_0002", variables => $variables});
				$anvil->Alert->register({
					alert_level   => $alert_level, 
					message       => "scan_filesystem_alert_0002", 
					sort_position => 1,
					variables     => $variables, 
					set_by        => $THIS_FILE,
				});
			}
		}
		elsif ($new_free_percent < $percent_free_notice)
		{
			# Send the low space notice, if needed.
			my $changed = $anvil->Alert->check_alert_sent({
				clear          => 0, 
				record_locator => $filesystem_internal_uuid.":low_space", 
				set_by         => $THIS_FILE,
			});
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { changed => $changed }});
			if ($changed)
			{
				# First time we've fallen under 5%
				$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_filesystem_alert_0003", variables => $variables});
				$anvil->Alert->register({
					alert_level   => "notice", 
					message       => "scan_filesystem_alert_0003", 
					sort_position => 1,
					variables     => $variables, 
					set_by        => $THIS_FILE,
				});
			}
		}
		elsif ($new_free_percent < $percent_free_clear)
		{
			# Send the low space cleared message, if needed.
			my $very_low_changed = $anvil->Alert->check_alert_sent({
				clear          => 1, 
				record_locator => $filesystem_internal_uuid.":very_low_space", 
				set_by         => $THIS_FILE,
			});
			my $low_changed = $anvil->Alert->check_alert_sent({
				clear          => 1, 
				record_locator => $filesystem_internal_uuid.":low_space", 
				set_by         => $THIS_FILE,
			});
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				very_low_changed => $very_low_changed,
				low_changed      => $low_changed, 
			}});
			if (($very_low_changed) or ($low_changed))
			{
				# Clear the alert. If this is swap, make it a notice level alert.
				my $alert_level = $very_low_changed ? "warning" : "notice";
				if ($new_mount_point eq "<swap>")
				{
					$alert_level = "notice";
				}
				$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_filesystem_alert_0004", variables => $variables});
				$anvil->Alert->register({
					alert_level   => $alert_level, 
					clear_alert   => 1,
					message       => "scan_filesystem_alert_0004", 
					sort_position => 1,
					variables     => $variables, 
					set_by        => $THIS_FILE,
				});
			}
		}
	}
	
	# Look now for lost file systems.
	foreach my $filesystem_uuid (keys %{$anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}})
	{
		# Is this one we already know has left?
		next if $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$filesystem_uuid}{scan_filesystem_mount_point} eq "REMOVED";
		
		my $old_kernel_name = $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$filesystem_uuid}{scan_filesystem_kernel_name};
		my $old_type        = $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$filesystem_uuid}{scan_filesystem_type};
		my $old_mount_point = $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$filesystem_uuid}{scan_filesystem_mount_point}; 
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			filesystem_uuid => $filesystem_uuid, 
			old_kernel_name => $old_kernel_name, 
			old_type        => $old_type,
			old_mount_point => $old_mount_point,
		}});
		
		my $query = "
UPDATE 
    scan_filesystems 
SET 
    scan_filesystem_mount_point = 'REMOVED', 
    modified_date               = ".$anvil->Database->quote($anvil->Database->refresh_timestamp)." 
WHERE 
    scan_filesystem_uuid        = ".$anvil->Database->quote($filesystem_uuid)."
;";
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
		$anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__});
		
		my $update    = 0;
		my $variables = {
			old_kernel_name => $old_kernel_name, 
			old_type        => $old_type,
			old_mount_point => $old_mount_point,
		};
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "scan_filesystem_alert_0016", variables => $variables});
		$anvil->Alert->register({
			alert_level   => "info", 
			message       => "scan_filesystem_alert_0016", 
			sort_position => $anvil->data->{'scan-filesystems'}{alert_sort}++,
			variables     => $variables, 
			set_by        => $THIS_FILE,
		});
	}

	return(0);
}

# This reads in the last scan's data.
sub read_last_scan
{
	my ($anvil) = @_;
	
	# Load disk data. We load all drives, so that we can track removable media. 
	my $query = "
SELECT 
    scan_filesystem_uuid, 
    scan_filesystem_internal_uuid, 
    scan_filesystem_type, 
    scan_filesystem_kernel_name, 
    scan_filesystem_mount_point, 
    scan_filesystem_transport, 
    scan_filesystem_media_type, 
    scan_filesystem_vendor, 
    scan_filesystem_model, 
    scan_filesystem_serial_number, 
    scan_filesystem_description, 
    scan_filesystem_size, 
    scan_filesystem_used 
FROM 
    scan_filesystems 
WHERE 
    scan_filesystem_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})
	{
		# We've got an entry in the 'scan_filesystems' table, so now we'll look for data in the node and 
		# services tables.
		my $scan_filesystem_uuid                                                                                                 = $row->[0]; 
		my $scan_filesystem_internal_uuid                                                                                        = $row->[1];
		   $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_internal_uuid}     = $scan_filesystem_internal_uuid;
		   $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_type}              = $row->[2];
		   $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_kernel_name}       = $row->[3]; 
		   $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_mount_point}       = $row->[4]; 
		   $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_transport}         = $row->[5]; 
		   $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_media_type}        = $row->[6];
		   $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_vendor}            = $row->[7];
		   $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_model}             = $row->[8];
		   $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_serial_number}     = $row->[9];
		   $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_description}       = $row->[10];
		   $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_size}              = $row->[11];
		   $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_used}              = $row->[12];
		   $anvil->data->{sql}{scan_filesystems}{scan_filesystem_internal_uuid}{$scan_filesystem_internal_uuid}{filesystem_uuid} = $scan_filesystem_uuid;
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			"sql::scan_filesystems::scan_filesystem_uuid::${scan_filesystem_uuid}::scan_filesystem_internal_uuid"     => $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_internal_uuid},
			"sql::scan_filesystems::scan_filesystem_uuid::${scan_filesystem_uuid}::scan_filesystem_kernel_name"       => $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_kernel_name},
			"sql::scan_filesystems::scan_filesystem_uuid::${scan_filesystem_uuid}::scan_filesystem_mount_point"       => $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_mount_point},
			"sql::scan_filesystems::scan_filesystem_uuid::${scan_filesystem_uuid}::scan_filesystem_transport"         => $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_transport},
			"sql::scan_filesystems::scan_filesystem_uuid::${scan_filesystem_uuid}::scan_filesystem_media_type"        => $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_media_type},
			"sql::scan_filesystems::scan_filesystem_uuid::${scan_filesystem_uuid}::scan_filesystem_vendor"            => $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_vendor},
			"sql::scan_filesystems::scan_filesystem_uuid::${scan_filesystem_uuid}::scan_filesystem_model"             => $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_model},
			"sql::scan_filesystems::scan_filesystem_uuid::${scan_filesystem_uuid}::scan_filesystem_serial_number"     => $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_serial_number},
			"sql::scan_filesystems::scan_filesystem_uuid::${scan_filesystem_uuid}::scan_filesystem_description"       => $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_description},
			"sql::scan_filesystems::scan_filesystem_uuid::${scan_filesystem_uuid}::scan_filesystem_size"              => $anvil->Convert->add_commas({number => $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_size}})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_size}}),
			"sql::scan_filesystems::scan_filesystem_uuid::${scan_filesystem_uuid}::scan_filesystem_used"              => $anvil->Convert->add_commas({number => $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_used}})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_used}}),
			"sql::scan_filesystems::scan_filesystem_internal_uuid::${scan_filesystem_internal_uuid}::filesystem_uuid" => $anvil->data->{sql}{scan_filesystems}{scan_filesystem_internal_uuid}{$scan_filesystem_internal_uuid}{filesystem_uuid}, 
		}});
	}
	
	return(0);
}

sub collect_data
{
	my ($anvil) = @_;
	
	### NOTE: If a drive is unmounted, we can't trust the sizes.
	$anvil->Storage->parse_lsblk({debug => 3});
	$anvil->Storage->parse_df({debug => 3});
	foreach my $kernel_device_name (sort {$a cmp $b} keys %{$anvil->{storage}{lsblk}})
	{
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { kernel_device_name => $kernel_device_name }});
		if ($anvil->{storage}{lsblk}{$kernel_device_name}{type} eq "partition")
		{
			my $filesystem_internal_uuid = $anvil->{storage}{lsblk}{$kernel_device_name}{filesystem_internal_uuid};
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { filesystem_internal_uuid => $filesystem_internal_uuid }});
			
			$anvil->data->{new}{partition}{$filesystem_internal_uuid}{kernel_name}     = $kernel_device_name;
			$anvil->data->{new}{partition}{$filesystem_internal_uuid}{type}            = $anvil->{storage}{lsblk}{$kernel_device_name}{filesystem_type};
			$anvil->data->{new}{partition}{$filesystem_internal_uuid}{mount_point}     = $anvil->{storage}{lsblk}{$kernel_device_name}{mount_point};
			$anvil->data->{new}{partition}{$filesystem_internal_uuid}{transport}       = $anvil->{storage}{lsblk}{$kernel_device_name}{transport};
			$anvil->data->{new}{partition}{$filesystem_internal_uuid}{media_type}      = $anvil->{storage}{lsblk}{$kernel_device_name}{rotating_drive} ? "platter" : "solid_state";	# This could be network as well someday
			$anvil->data->{new}{partition}{$filesystem_internal_uuid}{vendor}          = $anvil->{storage}{lsblk}{$kernel_device_name}{vendor};
			$anvil->data->{new}{partition}{$filesystem_internal_uuid}{model}           = $anvil->{storage}{lsblk}{$kernel_device_name}{model};
			$anvil->data->{new}{partition}{$filesystem_internal_uuid}{serial_number}   = $anvil->{storage}{lsblk}{$kernel_device_name}{serial_number};
			$anvil->data->{new}{partition}{$filesystem_internal_uuid}{description}     = $anvil->{storage}{lsblk}{$kernel_device_name}{partition_label};
			$anvil->data->{new}{partition}{$filesystem_internal_uuid}{size}            = $anvil->{storage}{lsblk}{$kernel_device_name}{size};
			$anvil->data->{new}{partition}{$filesystem_internal_uuid}{used}            = 0;
			if (($anvil->data->{new}{partition}{$filesystem_internal_uuid}{mount_point}) && (exists $anvil->{storage}{df}{$kernel_device_name}))
			{
				# Look for space usage from 'df'
				my $df_mount_point     = $anvil->{storage}{df}{$kernel_device_name}{mount_point};
				my $df_filesystem_type = $anvil->{storage}{df}{$kernel_device_name}{filesystem_type};
				my $df_size            = $anvil->{storage}{df}{$kernel_device_name}{size};
				my $df_used            = $anvil->{storage}{df}{$kernel_device_name}{used};
				my $df_free            = $anvil->{storage}{df}{$kernel_device_name}{free};
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					df_filesystem_type => $df_filesystem_type,
					df_mount_point     => $df_mount_point,
					size               => $anvil->Convert->add_commas({number => $df_size})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $df_size}).")",
					used               => $anvil->Convert->add_commas({number => $df_used})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $df_used}).")",
					free               => $anvil->Convert->add_commas({number => $df_free})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $df_free}).")",
				}});
				
				# This is a check to see if the calculated free space matches the reported free 
				# space.
				   $anvil->data->{new}{partition}{$filesystem_internal_uuid}{size} =  $df_size; 
				   $anvil->data->{new}{partition}{$filesystem_internal_uuid}{used} =  $df_used;
				my $calculated_free                                          =  $anvil->data->{new}{partition}{$filesystem_internal_uuid}{size} - $anvil->data->{new}{partition}{$filesystem_internal_uuid}{used};
				my $difference                                               =  $df_free - $calculated_free;
				   $difference                                               =~ s/^-//;
				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
					's1:calculated_free' => $anvil->Convert->add_commas({number => $calculated_free})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $calculated_free}).")",
					's2:difference'      => $anvil->Convert->add_commas({number => $difference})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $difference}).")",
				}});
			}
			
			# If this is swap, there won't be any information from 'df', so get the used space 
			# from swapon.
			if ($anvil->{storage}{lsblk}{$kernel_device_name}{mount_point} eq "<swap>")
			{
				my ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{swapon}."  --show=UUID,USED --bytes --noheadings"});
				$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 =~ /^$filesystem_internal_uuid (\d+)$/)
					{
						$anvil->data->{new}{partition}{$filesystem_internal_uuid}{used} = $1;
						$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
							"new::partition::${filesystem_internal_uuid}::used" => $anvil->Convert->add_commas({number => $anvil->data->{new}{partition}{$filesystem_internal_uuid}{used}})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{new}{partition}{$filesystem_internal_uuid}{used}}).")"
						}});
						last;
					}
				}
			}
			
			# Record the partition
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				"s1:new::partition::${filesystem_internal_uuid}::kernel_name"               => $anvil->data->{new}{partition}{$filesystem_internal_uuid}{kernel_name}, 
				"s2:new::partition::${filesystem_internal_uuid}::type"                      => $anvil->data->{new}{partition}{$filesystem_internal_uuid}{type}, 
				"s3:new::partition::${filesystem_internal_uuid}::mount_point"               => $anvil->data->{new}{partition}{$filesystem_internal_uuid}{mount_point}, 
				"s4:new::partition::${filesystem_internal_uuid}::transport"                 => $anvil->data->{new}{partition}{$filesystem_internal_uuid}{transport}, 
				"s5:new::partition::${filesystem_internal_uuid}::media_type"                => $anvil->data->{new}{partition}{$filesystem_internal_uuid}{media_type}, 
				"s6:new::partition::${filesystem_internal_uuid}::vendor"                    => $anvil->data->{new}{partition}{$filesystem_internal_uuid}{vendor}, 
				"s7:new::partition::${filesystem_internal_uuid}::model"                     => $anvil->data->{new}{partition}{$filesystem_internal_uuid}{model}, 
				"s8:new::partition::${filesystem_internal_uuid}::serial_number"             => $anvil->data->{new}{partition}{$filesystem_internal_uuid}{serial_number}, 
				"s9:new::partition::${filesystem_internal_uuid}::description"               => $anvil->data->{new}{partition}{$filesystem_internal_uuid}{description}, 
				"s10:new::partition::${filesystem_internal_uuid}::filesystem_internal_uuid" => $anvil->data->{new}{partition}{$filesystem_internal_uuid}{filesystem_internal_uuid}, 
				"s10:new::partition::${filesystem_internal_uuid}::size"                     => $anvil->Convert->add_commas({number => $anvil->data->{new}{partition}{$filesystem_internal_uuid}{size}})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{new}{partition}{$filesystem_internal_uuid}{size}}).")", 
				"s11:new::partition::${filesystem_internal_uuid}::used"                     => $anvil->Convert->add_commas({number => $anvil->data->{new}{partition}{$filesystem_internal_uuid}{used}})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{new}{partition}{$filesystem_internal_uuid}{used}}).")", 
			}});
		}
	}
	
	return(0);
}

# This collects data for buses and disk info.
sub collect_bus_data
{
	my ($anvil) = @_;
	
	my ($output, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{lshw}." -class disk -class storage -xml"});
	$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
		output      => $output, 
		return_code => $return_code,
	}});
	
	# We tried -json, but the output was not validly formatted.
	local $@;
	my $dom = eval { XML::LibXML->load_xml(string => $output); };
	if ($@)
	{
		$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, key => "warning_0053", variables => { 
			cib   => $output,
			error => $@,
		}});
	}
	
	foreach my $node ($dom->findnodes('/list/node'))
	{
		my $id          = $node->{id};
		my $class       = $node->{class};
		my $description = $node->findvalue('./description');
		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
			id          => $id, 
			class       => $class,
			description => $description, 
		}});
		foreach my $device ($node->findnodes('./node'))
		{
			my $dev_id    = $device->{id};
			my $dev_class = $device->{class};
			my $bus_info  = $device->findvalue('./businfo');
			my $path      = $device->findvalue('./logicalname');
			$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
				dev_id    => $dev_id, 
				dev_class => $dev_class,
				bus_info  => $bus_info,
				path      => $path,
			}});
		}

	
		### NOTE: Full CIB details; 
		###       - https://clusterlabs.org/pacemaker/doc/en-US/Pacemaker/2.0/html-single/Pacemaker_Explained/index.html
		# Successful parse!
# 		foreach my $nvpair ($dom->findnodes('/cib/configuration/crm_config/cluster_property_set/nvpair'))
# 		{
# 			my $nvpair_id = $nvpair->{id};
# 			foreach my $variable (sort {$a cmp $b} keys %{$nvpair})
# 			{
# 				next if $variable eq "id";
# 				$anvil->data->{cib}{parsed}{configuration}{crm_config}{cluster_property_set}{nvpair}{$nvpair_id}{$variable} = $nvpair->{$variable};
# 				$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
# 					"cib::parsed::configuration::crm_config::cluster_property_set::nvpair::${nvpair_id}::${variable}" => $anvil->data->{cib}{parsed}{configuration}{crm_config}{cluster_property_set}{nvpair}{$nvpair_id}{$variable}, 
# 				}});
# 			}
# 		}
# 		foreach my $node ($dom->findnodes('/cib/configuration/nodes/node'))
# 		{
# 		}
	}
	
# 	my $json     = JSON->new->allow_nonref;
# 	my $pvs_data = $json->decode($output);
# 	foreach my $hash_ref (@{$pvs_data->{report}->[0]->{pv}})
# 	{
# 		my $scan_filesystem_type                                                    = $hash_ref->{pv_uuid};
# 		   $anvil->data->{filesystem}{scan_filesystem_type}{$scan_filesystem_type}{name}       = $hash_ref->{pv_name};
# 		   $anvil->data->{filesystem}{scan_filesystem_type}{$scan_filesystem_type}{used_by_vg} = $hash_ref->{vg_name};
# 		   $anvil->data->{filesystem}{scan_filesystem_type}{$scan_filesystem_type}{attributes} = $hash_ref->{pv_attr};	# TODO: Parse this out
# 		   $anvil->data->{filesystem}{scan_filesystem_type}{$scan_filesystem_type}{size}       = ($hash_ref->{pv_size} =~ /^(\d+)B/)[0];
# 		   $anvil->data->{filesystem}{scan_filesystem_type}{$scan_filesystem_type}{free_space} = ($hash_ref->{pv_free} =~ /^(\d+)B/)[0];
# 		$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { 
# 			"filesystem::scan_filesystem_uuid::${scan_filesystem_uuid}::name"       => $anvil->data->{filesystem}{scan_filesystem_type}{$scan_filesystem_type}{name},
# 			"filesystem::scan_filesystem_uuid::${scan_filesystem_uuid}::used_by_vg" => $anvil->data->{filesystem}{scan_filesystem_type}{$scan_filesystem_type}{used_by_vg},
# 			"filesystem::scan_filesystem_uuid::${scan_filesystem_uuid}::attributes" => $anvil->data->{filesystem}{scan_filesystem_type}{$scan_filesystem_type}{attributes},
# 			"filesystem::scan_filesystem_uuid::${scan_filesystem_uuid}::size"       => $anvil->Convert->add_commas({number => $anvil->data->{filesystem}{scan_filesystem_type}{$scan_filesystem_type}{size}})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{filesystem}{scan_filesystem_type}{$scan_filesystem_type}{size}}).")",
# 			"filesystem::scan_filesystem_uuid::${scan_filesystem_uuid}::free_space" => $anvil->Convert->add_commas({number => $anvil->data->{filesystem}{scan_filesystem_type}{$scan_filesystem_type}{free_space}})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{filesystem}{scan_filesystem_type}{$scan_filesystem_type}{free_space}}).")",
# 		}});
# 	}
	
	return(0);
}
	
sub collect_fs_data
{
	my ($anvil) = @_;
	
	
	return(0);
}

