* Fixed a bug in Convert->bytes_to_human_readable() to handle being passed in bytes (with the size units of 'b' ot 'bytes').
* Fixed a bug in Database->_find_behind_databases() _find_behind_databases() where the logic to figure out which column was the host_uuid reference was too liberal, causing the wrong column to be selected in some cases. Also added a check to not look for host_uuid columns on specific tables where a match would be made, but the column is allowed to be null (like server_host_uuid that indicates the host of the server). * Started work on the scan-filesystems scan agent, which is needed to record the data that the file system UI endpoints will need. * Removed the 'not null' constraint from 'servers' -> 'server_host_uuid'. * Fixed a bug in 'anvil-provision-server' where the driver ISO being 'none' caused the provision script to use the driver ISO switch. Signed-off-by: Digimer <digimer@alteeve.ca>main
parent
5536e8ff47
commit
296556328b
8 changed files with 731 additions and 22 deletions
@ -0,0 +1,536 @@ |
|||||||
|
#!/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->{switches}{force} = 0; |
||||||
|
|
||||||
|
$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; |
||||||
|
|
||||||
|
# If we're disabled and '--force' wasn't used, exit. |
||||||
|
if (($anvil->data->{scancore}{'scan-filesystems'}{disable}) && (not $anvil->data->{switches}{force})) |
||||||
|
{ |
||||||
|
# Exit. |
||||||
|
$anvil->nice_exit({exit_code => 0}); |
||||||
|
} |
||||||
|
|
||||||
|
# Handle start-up tasks |
||||||
|
my $problem = $anvil->ScanCore->agent_startup({agent => $THIS_FILE}); |
||||||
|
if ($problem) |
||||||
|
{ |
||||||
|
$anvil->nice_exit({exit_code => 1}); |
||||||
|
} |
||||||
|
|
||||||
|
if ($anvil->data->{switches}{purge}) |
||||||
|
{ |
||||||
|
# This can be called when doing bulk-database purges. |
||||||
|
my $schema_file = $anvil->data->{path}{directories}{scan_agents}."/".$THIS_FILE."/".$THIS_FILE.".sql"; |
||||||
|
$anvil->Database->purge_data({ |
||||||
|
debug => 2, |
||||||
|
tables => $anvil->Database->get_tables_from_schema({schema_file => $schema_file}), |
||||||
|
}); |
||||||
|
$anvil->nice_exit({exit_code => 0}); |
||||||
|
} |
||||||
|
|
||||||
|
# Find the block devices on this host. |
||||||
|
collect_data($anvil); |
||||||
|
|
||||||
|
# Load stored data. |
||||||
|
read_last_scan($anvil); |
||||||
|
|
||||||
|
# Loog for changes |
||||||
|
find_changes($anvil); |
||||||
|
|
||||||
|
# Mark that we ran. |
||||||
|
$anvil->Database->insert_or_update_updated({updated_by => $THIS_FILE}); |
||||||
|
|
||||||
|
$anvil->nice_exit({exit_code => 0}); |
||||||
|
|
||||||
|
############################################################################################################# |
||||||
|
# Functions # |
||||||
|
############################################################################################################# |
||||||
|
|
||||||
|
sub find_changes |
||||||
|
{ |
||||||
|
my ($anvil) = @_; |
||||||
|
|
||||||
|
=cut |
||||||
|
foreach my $scan_filesystem_type (keys %{$anvil->data->{filesystem}{scan_filesystem_type}}) |
||||||
|
{ |
||||||
|
my $scan_filesystem_device_path = $anvil->data->{filesystem}{scan_filesystem_type}{$scan_filesystem_type}{name}; |
||||||
|
my $scan_filesystem_mount_path = $anvil->data->{filesystem}{scan_filesystem_type}{$scan_filesystem_type}{used_by_vg}; |
||||||
|
my $scan_filesystem_media = $anvil->data->{filesystem}{scan_filesystem_type}{$scan_filesystem_type}{attributes}; |
||||||
|
my $scan_filesystem_size = $anvil->data->{filesystem}{scan_filesystem_type}{$scan_filesystem_type}{size}; |
||||||
|
my $scan_filesystem_free = $anvil->data->{filesystem}{scan_filesystem_type}{$scan_filesystem_type}{free_space}; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
scan_filesystem_type => $scan_filesystem_type, |
||||||
|
scan_filesystem_device_path => $scan_filesystem_device_path, |
||||||
|
scan_filesystem_mount_path => $scan_filesystem_mount_path, |
||||||
|
scan_filesystem_media => $scan_filesystem_media, |
||||||
|
scan_filesystem_size => $anvil->Convert->add_commas({number => $scan_filesystem_size})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $scan_filesystem_size}), |
||||||
|
scan_filesystem_free => $anvil->Convert->add_commas({number => $scan_filesystem_free})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $scan_filesystem_free}), |
||||||
|
}}); |
||||||
|
|
||||||
|
# Have we seen this before? |
||||||
|
if (exists $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}) |
||||||
|
{ |
||||||
|
# Yup, anything changed? |
||||||
|
my $scan_filesystem_uuid = $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_uuid}; |
||||||
|
my $old_scan_filesystem_device_path = $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_device_path}; |
||||||
|
my $old_scan_filesystem_mount_path = $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_mount_path}; |
||||||
|
my $old_scan_filesystem_media = $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_media}; |
||||||
|
my $old_scan_filesystem_size = $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_size}; |
||||||
|
my $old_scan_filesystem_free = $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_free}; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
old_scan_filesystem_device_path => $old_scan_filesystem_device_path, |
||||||
|
old_scan_filesystem_mount_path => $old_scan_filesystem_mount_path, |
||||||
|
old_scan_filesystem_media => $old_scan_filesystem_media, |
||||||
|
old_scan_filesystem_size => $anvil->Convert->add_commas({number => $old_scan_filesystem_size})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_filesystem_size}), |
||||||
|
old_scan_filesystem_free => $anvil->Convert->add_commas({number => $old_scan_filesystem_free})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_filesystem_free}), |
||||||
|
}}); |
||||||
|
|
||||||
|
my $update = 0; |
||||||
|
if ($scan_filesystem_device_path ne $old_scan_filesystem_device_path) |
||||||
|
{ |
||||||
|
$update = 1; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update => $update }}); |
||||||
|
if ($old_scan_filesystem_device_path eq "DELETED") |
||||||
|
{ |
||||||
|
# A lost PV is back |
||||||
|
my $variables = { |
||||||
|
pv_uuid => $scan_filesystem_type, |
||||||
|
pv_name => $scan_filesystem_device_path, |
||||||
|
}; |
||||||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_filesystem_alert_0001", variables => $variables}); |
||||||
|
$anvil->Alert->register({clear_alert => 1, alert_level => "warning", message => "scan_filesystem_alert_0001", variables => $variables, set_by => $THIS_FILE}); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
# Device (name) changed... This shouldn't normally happen. |
||||||
|
my $variables = { |
||||||
|
pv_uuid => $scan_filesystem_type, |
||||||
|
new_name => $scan_filesystem_device_path, |
||||||
|
old_name => $old_scan_filesystem_device_path, |
||||||
|
}; |
||||||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_filesystem_alert_0002", variables => $variables}); |
||||||
|
$anvil->Alert->register({alert_level => "warning", message => "scan_filesystem_alert_0002", variables => $variables, set_by => $THIS_FILE}); |
||||||
|
} |
||||||
|
} |
||||||
|
if ($scan_filesystem_mount_path ne $old_scan_filesystem_mount_path) |
||||||
|
{ |
||||||
|
# If the old value was blank, then this PV was added to a VG and that's fine. |
||||||
|
$update = 1; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update => $update }}); |
||||||
|
if (not $old_scan_filesystem_mount_path) |
||||||
|
{ |
||||||
|
# Added to a VG |
||||||
|
my $variables = { |
||||||
|
pv_uuid => $scan_filesystem_type, |
||||||
|
pv_name => $scan_filesystem_device_path, |
||||||
|
vg_name => $scan_filesystem_mount_path, |
||||||
|
}; |
||||||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_filesystem_alert_0004", variables => $variables}); |
||||||
|
$anvil->Alert->register({alert_level => "notice", message => "scan_filesystem_alert_0004", variables => $variables, set_by => $THIS_FILE}); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
# The VG was probably renamed. |
||||||
|
my $variables = { |
||||||
|
pv_uuid => $scan_filesystem_type, |
||||||
|
pv_name => $scan_filesystem_device_path, |
||||||
|
new_vg_name => $scan_filesystem_mount_path, |
||||||
|
old_vg_name => $old_scan_filesystem_mount_path, |
||||||
|
}; |
||||||
|
$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", variables => $variables, set_by => $THIS_FILE}); |
||||||
|
} |
||||||
|
} |
||||||
|
if ($scan_filesystem_media ne $old_scan_filesystem_media) |
||||||
|
{ |
||||||
|
# Attribute bits changed. |
||||||
|
$update = 1; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update => $update }}); |
||||||
|
my $variables = { |
||||||
|
pv_uuid => $scan_filesystem_type, |
||||||
|
pv_name => $scan_filesystem_device_path, |
||||||
|
new_attributes => $scan_filesystem_media, |
||||||
|
old_attributes => $old_scan_filesystem_media, |
||||||
|
}; |
||||||
|
$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", variables => $variables, set_by => $THIS_FILE}); |
||||||
|
} |
||||||
|
if ($scan_filesystem_size ne $old_scan_filesystem_size) |
||||||
|
{ |
||||||
|
# PE size changed, likely grew. |
||||||
|
$update = 1; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update => $update }}); |
||||||
|
my $variables = { |
||||||
|
pv_uuid => $scan_filesystem_type, |
||||||
|
pv_name => $scan_filesystem_device_path, |
||||||
|
new_size => $anvil->Convert->bytes_to_human_readable({'bytes' => $scan_filesystem_size}), |
||||||
|
new_size_bytes => $anvil->Convert->add_commas({number => $scan_filesystem_size}), |
||||||
|
old_size => $anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_filesystem_size}), |
||||||
|
old_size_bytes => $anvil->Convert->add_commas({number => $old_scan_filesystem_size}), |
||||||
|
}; |
||||||
|
if ($scan_filesystem_size > $old_scan_filesystem_size) |
||||||
|
{ |
||||||
|
# Yup |
||||||
|
$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", variables => $variables, set_by => $THIS_FILE}); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
# Uhhhh... We'll make this a warning as it shouldn't happen. |
||||||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_filesystem_alert_0008", variables => $variables}); |
||||||
|
$anvil->Alert->register({alert_level => "warning", message => "scan_filesystem_alert_0008", variables => $variables, set_by => $THIS_FILE}); |
||||||
|
} |
||||||
|
} |
||||||
|
if ($scan_filesystem_free ne $old_scan_filesystem_free) |
||||||
|
{ |
||||||
|
# PE size changed, likely shrunk. |
||||||
|
$update = 1; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update => $update }}); |
||||||
|
my $variables = { |
||||||
|
pv_uuid => $scan_filesystem_type, |
||||||
|
pv_name => $scan_filesystem_device_path, |
||||||
|
new_free => $anvil->Convert->bytes_to_human_readable({'bytes' => $scan_filesystem_free}), |
||||||
|
new_free_bytes => $anvil->Convert->add_commas({number => $scan_filesystem_free}), |
||||||
|
old_free => $anvil->Convert->bytes_to_human_readable({'bytes' => $old_scan_filesystem_free}), |
||||||
|
old_free_bytes => $anvil->Convert->add_commas({number => $old_scan_filesystem_free}), |
||||||
|
}; |
||||||
|
if ($scan_filesystem_free < $old_scan_filesystem_free) |
||||||
|
{ |
||||||
|
# Yup |
||||||
|
$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", variables => $variables, set_by => $THIS_FILE}); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
# Uhhhh... We'll make this a warning as it shouldn't happen. |
||||||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_filesystem_alert_0010", variables => $variables}); |
||||||
|
$anvil->Alert->register({alert_level => "warning", message => "scan_filesystem_alert_0010", variables => $variables, set_by => $THIS_FILE}); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update => $update }}); |
||||||
|
if ($update) |
||||||
|
{ |
||||||
|
my $query = " |
||||||
|
UPDATE |
||||||
|
scan_filesystems |
||||||
|
SET |
||||||
|
scan_filesystem_device_path = ".$anvil->Database->quote($scan_filesystem_device_path).", |
||||||
|
scan_filesystem_mount_path = ".$anvil->Database->quote($scan_filesystem_mount_path).", |
||||||
|
scan_filesystem_media = ".$anvil->Database->quote($scan_filesystem_media).", |
||||||
|
scan_filesystem_size = ".$anvil->Database->quote($scan_filesystem_size).", |
||||||
|
scan_filesystem_free = ".$anvil->Database->quote($scan_filesystem_free).", |
||||||
|
modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." |
||||||
|
WHERE |
||||||
|
scan_filesystem_uuid = ".$anvil->Database->quote($scan_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 loaded entry so we can check for missing PVs later. |
||||||
|
delete $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
# New PV |
||||||
|
my $variables = { |
||||||
|
pv_uuid => $scan_filesystem_type, |
||||||
|
pv_name => $scan_filesystem_device_path, |
||||||
|
vg_name => $scan_filesystem_mount_path ? $scan_filesystem_mount_path : "--", |
||||||
|
attributes => $scan_filesystem_media, |
||||||
|
pv_size => $anvil->Convert->bytes_to_human_readable({'bytes' => $scan_filesystem_size}), |
||||||
|
pv_size_bytes => $anvil->Convert->add_commas({number => $scan_filesystem_size}), |
||||||
|
pv_free => $anvil->Convert->bytes_to_human_readable({'bytes' => $scan_filesystem_free}), |
||||||
|
pv_free_bytes => $anvil->Convert->add_commas({number => $scan_filesystem_free}), |
||||||
|
}; |
||||||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_filesystem_alert_0019", variables => $variables}); |
||||||
|
$anvil->Alert->register({alert_level => "notice", message => "scan_filesystem_alert_0019", variables => $variables, set_by => $THIS_FILE}); |
||||||
|
|
||||||
|
my $scan_filesystem_uuid = $anvil->Get->uuid(); |
||||||
|
my $query = " |
||||||
|
INSERT INTO |
||||||
|
scan_filesystems |
||||||
|
( |
||||||
|
scan_filesystem_uuid, |
||||||
|
scan_filesystem_type, |
||||||
|
scan_filesystem_host_uuid, |
||||||
|
scan_filesystem_device_path, |
||||||
|
scan_filesystem_mount_path, |
||||||
|
scan_filesystem_media, |
||||||
|
scan_filesystem_size, |
||||||
|
scan_filesystem_free, |
||||||
|
modified_date |
||||||
|
) VALUES ( |
||||||
|
".$anvil->Database->quote($scan_filesystem_uuid).", |
||||||
|
".$anvil->Database->quote($scan_filesystem_type).", |
||||||
|
".$anvil->Database->quote($anvil->Get->host_uuid).", |
||||||
|
".$anvil->Database->quote($scan_filesystem_device_path).", |
||||||
|
".$anvil->Database->quote($scan_filesystem_mount_path).", |
||||||
|
".$anvil->Database->quote($scan_filesystem_media).", |
||||||
|
".$anvil->Database->quote($scan_filesystem_size).", |
||||||
|
".$anvil->Database->quote($scan_filesystem_free).", |
||||||
|
".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." |
||||||
|
);"; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); |
||||||
|
$anvil->Database->write({query => $query, source => $THIS_FILE, line => __LINE__}); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
# Any missing PVs? |
||||||
|
foreach my $scan_filesystem_type (keys %{$anvil->data->{sql}{scan_filesystems}{scan_filesystem_type}}) |
||||||
|
{ |
||||||
|
# This one is missing. |
||||||
|
my $scan_filesystem_uuid = $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_uuid}; |
||||||
|
my $old_scan_filesystem_device_path = $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_device_path}; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
's1:scan_filesystem_type' => $scan_filesystem_type, |
||||||
|
's2:scan_filesystem_uuid' => $scan_filesystem_uuid, |
||||||
|
's3:old_scan_filesystem_device_path' => $old_scan_filesystem_device_path, |
||||||
|
}}); |
||||||
|
next if $old_scan_filesystem_device_path eq "DELETED"; |
||||||
|
|
||||||
|
# Register an alert |
||||||
|
my $variables = { |
||||||
|
pv_uuid => $scan_filesystem_type, |
||||||
|
pv_name => $old_scan_filesystem_device_path, |
||||||
|
}; |
||||||
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "scan_filesystem_alert_0003", variables => $variables}); |
||||||
|
$anvil->Alert->register({alert_level => "warning", message => "scan_filesystem_alert_0003", variables => $variables, set_by => $THIS_FILE}); |
||||||
|
|
||||||
|
# Update it PV name to be 'DELTED' |
||||||
|
my $query = " |
||||||
|
UPDATE |
||||||
|
scan_filesystems |
||||||
|
SET |
||||||
|
scan_filesystem_device_path = 'DELETED', |
||||||
|
modified_date = ".$anvil->Database->quote($anvil->data->{sys}{database}{timestamp})." |
||||||
|
WHERE |
||||||
|
scan_filesystem_uuid = ".$anvil->Database->quote($scan_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__}); |
||||||
|
} |
||||||
|
=cut |
||||||
|
|
||||||
|
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_type, |
||||||
|
scan_filesystem_device_path, |
||||||
|
scan_filesystem_mount_path, |
||||||
|
scan_filesystem_media, |
||||||
|
scan_filesystem_description, |
||||||
|
scan_filesystem_size, |
||||||
|
scan_filesystem_free, |
||||||
|
scan_filesystem_host_uuid |
||||||
|
FROM |
||||||
|
scan_filesystems |
||||||
|
;"; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, 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]; |
||||||
|
$anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_type} = $row->[1]; |
||||||
|
$anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_device_path} = $row->[2]; |
||||||
|
$anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_mount_path} = $row->[3]; |
||||||
|
$anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_media} = $row->[4]; |
||||||
|
$anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_description} = $row->[4]; |
||||||
|
$anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_size} = $row->[5]; |
||||||
|
$anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_free} = $row->[6]; |
||||||
|
$anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_host_uuid} = $row->[7]; |
||||||
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
||||||
|
"sql::scan_filesystems::scan_filesystem_uuid::${scan_filesystem_uuid}::scan_filesystem_uuid" => $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_uuid}, |
||||||
|
"sql::scan_filesystems::scan_filesystem_uuid::${scan_filesystem_uuid}::scan_filesystem_device_path" => $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_device_path}, |
||||||
|
"sql::scan_filesystems::scan_filesystem_uuid::${scan_filesystem_uuid}::scan_filesystem_mount_path" => $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_mount_path}, |
||||||
|
"sql::scan_filesystems::scan_filesystem_uuid::${scan_filesystem_uuid}::scan_filesystem_media" => $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_media}, |
||||||
|
"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_free" => $anvil->Convert->add_commas({number => $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_free}})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_free}}), |
||||||
|
"sql::scan_filesystems::scan_filesystem_uuid::${scan_filesystem_uuid}::scan_filesystem_host_uuid" => $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_host_uuid}, |
||||||
|
}}); |
||||||
|
} |
||||||
|
|
||||||
|
return(0); |
||||||
|
} |
||||||
|
|
||||||
|
sub collect_data |
||||||
|
{ |
||||||
|
my ($anvil) = @_; |
||||||
|
|
||||||
|
### TODO: Swap oput '--separator \#!\#' for '--reportformat json' |
||||||
|
collect_bus_data($anvil); |
||||||
|
collect_fs_data($anvil); |
||||||
|
|
||||||
|
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 => $debug, 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); |
||||||
|
} |
||||||
|
|
@ -0,0 +1,76 @@ |
|||||||
|
-- This is the database schema for the 'scan-lvm Scan Agent'. |
||||||
|
|
||||||
|
|
||||||
|
-- This table stores physical volume information |
||||||
|
CREATE TABLE scan_filesystems ( |
||||||
|
scan_filesystem_uuid uuid primary key, -- This comes from the file system's UUID |
||||||
|
scan_filesystem_host_uuid uuid not null, -- The host that the file system is mounted on. Note that some FSes, like those from USB, can move between hosts. |
||||||
|
scan_filesystem_type text not null, -- This is the name of the file system type. |
||||||
|
scan_filesystem_device_path text not null, -- This is the backing device of the file system. |
||||||
|
scan_filesystem_mount_path text not null, -- This is the name of the mount point. |
||||||
|
scan_filesystem_media_type text not null, -- Optional description of the drive's media tpe (usb, nvme, sata_ssd, sata_platter, md, raid, optical, sdcard, etc - 'unknown') |
||||||
|
scan_filesystem_vendor text not null, -- Optional vendor of the drive the partition is on |
||||||
|
scan_filesystem_model text not null, -- Optional model of the drive the partiton is on |
||||||
|
scan_filesystem_serial_number text not null, -- Optional serial number of the drive the partition is on |
||||||
|
scan_filesystem_removable text not null, -- Optional string indicating if the file system is on removable media ('yes', 'no', 'unknown') |
||||||
|
scan_filesystem_description text not null, -- Free form description of the device. Usually make / model / serial number |
||||||
|
scan_filesystem_size numeric not null, -- The size of the PV in bytes |
||||||
|
scan_filesystem_free numeric not null, -- The free space, in bytes. |
||||||
|
modified_date timestamp with time zone not null, |
||||||
|
|
||||||
|
FOREIGN KEY(scan_filesystem_host_uuid) REFERENCES hosts(host_uuid) |
||||||
|
); |
||||||
|
ALTER TABLE scan_filesystems OWNER TO admin; |
||||||
|
|
||||||
|
CREATE TABLE history.scan_filesystems ( |
||||||
|
history_id bigserial, |
||||||
|
scan_filesystem_uuid uuid, |
||||||
|
scan_filesystem_host_uuid uuid, |
||||||
|
scan_filesystem_type text, |
||||||
|
scan_filesystem_device_path text, |
||||||
|
scan_filesystem_mount_path text, |
||||||
|
scan_filesystem_media text, |
||||||
|
scan_filesystem_description text, |
||||||
|
scan_filesystem_size numeric, |
||||||
|
scan_filesystem_free numeric, |
||||||
|
modified_date timestamp with time zone not null |
||||||
|
); |
||||||
|
ALTER TABLE history.scan_filesystems OWNER TO admin; |
||||||
|
|
||||||
|
CREATE FUNCTION history_scan_filesystems() RETURNS trigger |
||||||
|
AS $$ |
||||||
|
DECLARE |
||||||
|
history_scan_filesystems RECORD; |
||||||
|
BEGIN |
||||||
|
SELECT INTO history_scan_filesystems * FROM scan_filesystems WHERE scan_filesystem_uuid=new.scan_filesystem_uuid; |
||||||
|
INSERT INTO history.scan_filesystems |
||||||
|
(scan_filesystem_uuid, |
||||||
|
scan_filesystem_host_uuid, |
||||||
|
scan_filesystem_type, |
||||||
|
scan_filesystem_device_path, |
||||||
|
scan_filesystem_mount_path, |
||||||
|
scan_filesystem_media, |
||||||
|
scan_filesystem_description, |
||||||
|
scan_filesystem_size, |
||||||
|
scan_filesystem_free, |
||||||
|
modified_date) |
||||||
|
VALUES |
||||||
|
(history_scan_filesystems.scan_filesystem_uuid, |
||||||
|
history_scan_filesystems.scan_filesystem_host_uuid, |
||||||
|
history_scan_filesystems.scan_filesystem_type, |
||||||
|
history_scan_filesystems.scan_filesystem_device_path, |
||||||
|
history_scan_filesystems.scan_filesystem_mount_path, |
||||||
|
history_scan_filesystems.scan_filesystem_media, |
||||||
|
history_scan_filesystems.scan_filesystem_description, |
||||||
|
history_scan_filesystems.scan_filesystem_size, |
||||||
|
history_scan_filesystems.scan_filesystem_free, |
||||||
|
history_scan_filesystems.modified_date); |
||||||
|
RETURN NULL; |
||||||
|
END; |
||||||
|
$$ |
||||||
|
LANGUAGE plpgsql; |
||||||
|
ALTER FUNCTION history_scan_filesystems() OWNER TO admin; |
||||||
|
|
||||||
|
CREATE TRIGGER trigger_scan_filesystems |
||||||
|
AFTER INSERT OR UPDATE ON scan_filesystems |
||||||
|
FOR EACH ROW EXECUTE PROCEDURE history_scan_filesystems(); |
@ -0,0 +1,29 @@ |
|||||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||||
|
|
||||||
|
<!-- |
||||||
|
Company: Alteeve's Niche, Inc. |
||||||
|
License: GPL v2+ |
||||||
|
Author: Madison Kelly <mkelly@alteeve.ca> |
||||||
|
|
||||||
|
NOTE: All string keys MUST be prefixed with the agent name! ie: 'scan_filesystem_log_0001'. |
||||||
|
--> |
||||||
|
|
||||||
|
<words> |
||||||
|
<meta version="3.0.0" languages="en_CA,jp"/> |
||||||
|
<!-- Canadian English --> |
||||||
|
<language name="en_CA" long_name="Canadian English" description="ScanCore scan agent that monitors file systems and the devices backing them."> |
||||||
|
|
||||||
|
<!-- Alert entries --> |
||||||
|
<key name="scan_filesystem_alert_0001"></key> |
||||||
|
|
||||||
|
<!-- Log entries --> |
||||||
|
<key name="scan_filesystem_log_0001">Starting: [#!variable!program!#].</key> |
||||||
|
|
||||||
|
<!-- Message entries (usually meant to be alerts) --> |
||||||
|
<key name="scan_filesystem_message_0001"></key> |
||||||
|
|
||||||
|
<!-- Units --> |
||||||
|
<key name="scan_filesystem_unit_0001"></key> |
||||||
|
|
||||||
|
</language> |
||||||
|
</words> |
Loading…
Reference in new issue