* 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
Digimer 4 years ago
parent 5536e8ff47
commit 296556328b
  1. 4
      Anvil/Tools.pm
  2. 39
      Anvil/Tools/Convert.pm
  3. 59
      Anvil/Tools/Database.pm
  4. 536
      scancore-agents/scan-filesystems/scan-filesystems
  5. 76
      scancore-agents/scan-filesystems/scan-filesystems.sql
  6. 29
      scancore-agents/scan-filesystems/scan-filesystems.xml
  7. 6
      share/anvil.sql
  8. 4
      tools/anvil-provision-server

@ -1120,6 +1120,7 @@ sub _set_paths
crm_error => "/usr/sbin/crm_error",
crm_resource => "/usr/sbin/crm_resource",
crm_mon => "/usr/sbin/crm_mon",
df => "/usr/bin/df",
dmidecode => "/usr/sbin/dmidecode",
dnf => "/usr/bin/dnf",
drbdadm => "/usr/sbin/drbdadm",
@ -1147,8 +1148,9 @@ sub _set_paths
journalctl => "/usr/bin/journalctl",
logger => "/usr/bin/logger",
ls => "/usr/bin/ls",
lspci => "/usr/sbin/lspci",
lsblk => "/usr/bin/lsblk",
lshw => "/usr/sbin/lshw",
lspci => "/usr/sbin/lspci",
lvchange => "/usr/sbin/lvchange",
lvcreate => "/usr/sbin/lvcreate",
lvremove => "/usr/sbin/lvremove",

@ -204,9 +204,9 @@ sub bytes_to_human_readable
my $unit = defined $parameter->{unit} ? uc($parameter->{unit}) : "";
my $base2 = defined $parameter->{base2} ? $parameter->{base2} : $anvil->data->{sys}{use_base2};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
base2 => $base2,
'bytes' => $bytes,
unit => $unit,
base2 => $base2,
'bytes' => $bytes,
unit => $unit,
}});
# Expand exponential numbers.
@ -1044,14 +1044,24 @@ sub human_readable_to_bytes
# If the "type" is "Xib" or if '$base2' is set, make sure we're running in Base2 notation. Conversly,
# if the type is "Xb" or if '$base10' is set, make sure that we're running in Base10 notation. In
# either case, shorten the 'type' to just the first letter to make the next sanity check simpler.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
base2 => $base2,
base10 => $base10,
}});
if ((not $base2) && (not $base10))
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { type => $type }});
if ($type =~ /^(\w)ib$/)
{
# Make sure we're running in Base2.
$type = $1;
$base2 = 1;
$base10 = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
type => $type,
base2 => $base2,
base10 => $base10,
}});
}
elsif ($type =~ /^(\w)b$/)
{
@ -1059,6 +1069,22 @@ sub human_readable_to_bytes
$type = $1;
$base2 = 0;
$base10 = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
type => $type,
base2 => $base2,
base10 => $base10,
}});
}
elsif ($type =~ /^b/)
{
$type = "b";
$base2 = 1;
$base10 = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
type => $type,
base2 => $base2,
base10 => $base10,
}});
}
}
@ -1070,11 +1096,12 @@ sub human_readable_to_bytes
if (($type ne "p") &&
($type ne "e") &&
($type ne "z") &&
($type eq "y") &&
($type ne "y") &&
($type ne "t") &&
($type ne "g") &&
($type ne "m") &&
($type ne "k"))
($type ne "k") &&
($type ne "b"))
{
# Poop
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0119", variables => {
@ -1098,6 +1125,7 @@ sub human_readable_to_bytes
elsif ($type eq "g") { $bytes = ($size * (10 ** 9)) } # Gigabyte
elsif ($type eq "m") { $bytes = ($size * (10 ** 6)) } # Megabyte
elsif ($type eq "k") { $bytes = ($size * (10 ** 3)) } # Kilobyte
elsif ($type eq "b") { $bytes = $size; } # bytes
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'bytes' => $bytes }});
}
else
@ -1110,6 +1138,7 @@ sub human_readable_to_bytes
elsif ($type eq "g") { $bytes = ($size * (2 ** 30)) } # Gibibyte
elsif ($type eq "m") { $bytes = ($size * (2 ** 20)) } # Mebibyte
elsif ($type eq "k") { $bytes = ($size * (2 ** 10)) } # Kibibyte
elsif ($type eq "b") { $bytes = $size; } # bytes
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'bytes' => $bytes }});
}

@ -4423,6 +4423,14 @@ sub get_tables_from_schema
}
}
# Store the tables in 'sys::database::check_tables'
$anvil->data->{sys}{database}{check_tables} = $tables;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
tables => $tables,
'sys::database::check_tables' => $anvil->data->{sys}{database}{check_tables},
}});
my $table_count = @{$tables};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { table_count => $table_count }});
@ -14474,8 +14482,6 @@ sub resync_databases
return(0);
}
#$anvil->data->{sys}{database}{log_transactions} = 1;
# Archive old data before resync'ing
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0451"});
$anvil->Database->archive_database({debug => $debug});
@ -15498,7 +15504,7 @@ sub _find_behind_databases
### NOTE: Don't sort this! Tables need to be resynced in a specific order!
# Loop through and resync the tables.
foreach my $table (@{$tables})
foreach my $table (@{$tables})
{
# Record the table in 'sys::database::check_tables' array for later use in archive and
# resync methods.
@ -15544,16 +15550,43 @@ sub _find_behind_databases
if ($count == 1)
{
# Does this table have a '*_host_uuid' column?
my $query = "SELECT column_name FROM information_schema.columns WHERE table_schema = 'public' AND column_name LIKE '\%_host_uuid' AND table_name = ".$anvil->Database->quote($table).";";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }});
# See if there is a column that ends in '_host_uuid'. If there is, we'll use
# it later to restrict resync activity to these columns with the local
# 'sys::host_uuid'.
my $host_column = $anvil->Database->query({debug => $debug, uuid => $uuid, query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
$host_column = "" if not defined $host_column;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host_column => $host_column }});
# Some tables, like 'servers', has a host_uuid column, but it's not used to
# restrict data to a host, but instead show which host a movable resource is
# on. This prevents us from using the column by accident.
my $host_column = "";
if (($table ne "servers") &&
($table ne "jobs"))
{
# Does this table have a '*_host_uuid' column?
my $test_columns = [$table."_host_uuid"];
if ($table =~ /^(.*)s$/)
{
push @{$test_columns}, $1."_host_uuid";
}
if ($table =~ /^(.*)es$/)
{
push @{$test_columns}, $1."_host_uuid";
}
foreach my $test_column (@{$test_columns})
{
my $query = "SELECT COUNT(*) FROM information_schema.columns WHERE table_schema = 'public' AND column_name = ".$anvil->Database->quote($test_column)." AND table_name = ".$anvil->Database->quote($table).";";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { query => $query }});
# See if there is a column that ends in '_host_uuid'. If there is, we'll use
# it later to restrict resync activity to these columns with the local
# 'sys::host_uuid'.
my $count = $anvil->Database->query({debug => $debug, uuid => $uuid, query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { count => $count }});
if ($count)
{
$host_column = $test_column;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host_column => $host_column }});
last;
}
}
}
# Does this table have a history schema version?
$query = "SELECT COUNT(*) FROM information_schema.tables WHERE table_type = 'BASE TABLE' AND table_schema = 'history' AND table_name = ".$anvil->Database->quote($table).";";

@ -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>

@ -20,6 +20,10 @@
-- Tables can optionally have a '*_host_uuid uuid not null' colum. If this is found, when resync'ing the
-- table, the resync will be restricted to the host's 'sys::host_uuid'.
--
-- NOTE: If you have a column that is '<table>_host_uuid', and it's role is to show which host a moveable
-- thing is on (as opposed to a record bound to a host_uuid), be sure to add the table name to the
-- excluded list in Database->_find_behind_databases().
--
-- Most tables will want to have a matching table in the history schema with an additional
-- 'history_id bigserial' column. Match the function and trigger seen elsewhere to copy your data from the
-- public schema to the history schema on UPDATE or INSERT.
@ -1229,7 +1233,7 @@ CREATE TABLE servers (
server_user_stop boolean not null default FALSE, -- When set, the server was stopped by a user. The Anvil! will not start a server that has been cleanly stopped.
server_start_after_server_uuid uuid, -- This can be the server_uuid of another server. If set, this server will boot 'server_start_delay' seconds after the referenced server boots. A value of '00000000-0000-0000-0000-000000000000' will tell 'anvil-safe-start' to not boot the server at all. If a server is set not to start, any dependent servers will also stay off.
server_start_delay integer not null default 0, -- See above.
server_host_uuid uuid not null, -- This is the current hosts -> host_uuid for this server. If the server is off, this will be blank.
server_host_uuid uuid, -- This is the current hosts -> host_uuid for this server. If the server is off, this will be blank.
server_state text not null, -- This is the current state of this server, as reported by 'virsh list --all' (see: man virsh -> GENERIC COMMANDS -> --list)
server_live_migration boolean not null default TRUE, -- When false, servers will be frozen for a migration, instead of being migrated while the server is migrating. During a cold migration, the server will be unresponsive, so connections to it could time out. However, by being frozen the migration will complete faster.
server_pre_migration_file_uuid uuid, -- This is set to the files -> file_uuid of a script to run BEFORE migrating a server. If the file isn't found or can't run, the script is ignored.

@ -1012,7 +1012,7 @@ sub create_lv
my $vg_name = $anvil->data->{storage_groups}{storage_group_uuid}{$storage_group_uuid}{host_uuid}{$host_uuid}{vg_name};
my $extent_size = $anvil->data->{storage_groups}{storage_group_uuid}{$storage_group_uuid}{host_uuid}{$host_uuid}{vg_extent_size};
my $extent_count = int($anvil->data->{job}{storage_size} / $extent_size);
my $shell_call = $anvil->data->{path}{exe}{lvcreate}." -l ".$extent_count." -n ".$anvil->data->{job}{server_name}."_0 ".$vg_name;
my $shell_call = $anvil->data->{path}{exe}{lvcreate}." -l ".$extent_count." -n ".$anvil->data->{job}{server_name}."_0 ".$vg_name." -y";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:vg_name' => $vg_name,
's2:extent_size' => $extent_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $extent_size}).")",
@ -1361,7 +1361,7 @@ sub parse_job_data
}
# Driver disc is optional.
$anvil->data->{new_server}{driver_iso_path} = "";
if ($anvil->data->{job}{driver_iso_uuid})
if (($anvil->data->{job}{driver_iso_uuid}) && ($anvil->data->{job}{driver_iso_uuid} ne "none"))
{
my $driver_iso_uuid = $anvil->data->{job}{driver_iso_uuid};
my $driver_iso = $anvil->data->{files}{file_uuid}{$driver_iso_uuid}{file_directory}."/".$anvil->data->{files}{file_uuid}{$driver_iso_uuid}{file_name};

Loading…
Cancel
Save