Merge pull request #54 from ClusterLabs/webui_anvil_page

Webui anvil page
main
digimer-bot 4 years ago committed by GitHub
commit bdaa4a2ceb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      Anvil/Tools.pm
  2. 16
      Anvil/Tools/Cluster.pm
  3. 39
      Anvil/Tools/Convert.pm
  4. 67
      Anvil/Tools/Database.pm
  5. 20
      Anvil/Tools/Get.pm
  6. 268
      Anvil/Tools/Storage.pm
  7. 146
      Anvil/Tools/System.pm
  8. 22
      cgi-bin/Makefile.am
  9. 0
      cgi-bin/cold_stop
  10. 0
      cgi-bin/get_cpu
  11. 0
      cgi-bin/get_memory
  12. 0
      cgi-bin/get_replicated_storage
  13. 71
      cgi-bin/get_shared_storage
  14. 0
      cgi-bin/get_status
  15. 0
      cgi-bin/set_membership
  16. 0
      cgi-bin/set_power
  17. 6
      notes
  18. 607
      scancore-agents/scan-filesystems/scan-filesystems
  19. 88
      scancore-agents/scan-filesystems/scan-filesystems.sql
  20. 29
      scancore-agents/scan-filesystems/scan-filesystems.xml
  21. 2
      scancore-agents/scan-hardware/scan-hardware
  22. 6
      share/anvil.sql
  23. 2
      share/words.xml
  24. 29
      tools/anvil-configure-host
  25. 4
      tools/anvil-provision-server
  26. 4
      tools/striker-auto-initialize-all
  27. 10
      tools/striker-initialize-host

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

@ -273,8 +273,16 @@ sub assemble_storage_groups
if (not $anvil_uuid)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Cluster->assemble_storage_groups()", parameter => "anvil_uuid" }});
return("!!error!!");
# Can we deduce the anvil_uuid?
$anvil_uuid = $anvil->Cluster->get_anvil_uuid({debug => $debug});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { anvil_uuid=> $anvil_uuid }});
if (not $anvil_uuid)
{
# Still no anvil_uuid
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Cluster->assemble_storage_groups()", parameter => "anvil_uuid" }});
return("!!error!!");
}
}
# Get the node UUIDs for this anvil.
@ -372,7 +380,8 @@ ORDER BY
# only.
my $storage_group_uuid = $anvil->data->{storage_groups}{vg_uuid}{$scan_lvm_vg_internal_uuid}{storage_group_uuid};
my $group_name = $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}{$storage_group_uuid}{group_name};
my $storage_group_member_uuid = $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}{$storage_group_uuid}{host_uuid}{$host_uuid}{vg_uuid}{$scan_lvm_vg_internal_uuid}{storage_group_member_uuid};
#my $storage_group_member_uuid = $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}{$storage_group_uuid}{host_uuid}{$host_uuid}{vg_uuid}{$scan_lvm_vg_internal_uuid}{storage_group_member_uuid};
my $storage_group_member_uuid = $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_uuid}{$storage_group_uuid}{host_uuid}{$host_uuid}{storage_group_member_uuid};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
anvil_uuid => $anvil_uuid,
host_uuid => $host_uuid,
@ -434,6 +443,7 @@ ORDER BY
storage_group_member_host_uuid => $host_uuid,
storage_group_member_vg_uuid => $storage_group_member_vg_uuid,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { storage_group_member_uuid => $storage_group_member_uuid }});
$anvil->data->{ungrouped_vg_count}{$this_is}--;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {

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

@ -2015,7 +2015,7 @@ WHERE
$anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_node2_host_uuid} = $anvil_node2_host_uuid;
$anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_dr1_host_uuid} = $anvil_dr1_host_uuid;
$anvil->data->{anvils}{anvil_name}{$anvil_name}{modified_date} = $modified_date;
$anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{query_time} = time;
$anvil->data->{anvils}{anvil_name}{$anvil_uuid}{query_time} = time;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"anvils::anvil_name::${anvil_name}::anvil_uuid" => $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_uuid},
"anvils::anvil_name::${anvil_name}::anvil_description" => $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_description},
@ -4219,7 +4219,7 @@ ORDER BY
my $storage_group_name = $row->[2];
my $storage_group_member_uuid = $row->[3];
my $storage_group_member_host_uuid = $row->[4];
my $storage_group_member_vg_uuid = $row->[5];
my $storage_group_member_vg_uuid = $row->[5]; # This is the VG's internal UUID
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
storage_group_uuid => $storage_group_uuid,
storage_group_anvil_uuid => $storage_group_anvil_uuid,
@ -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 }});
@ -11644,7 +11652,9 @@ SELECT
FROM
storage_group_members
WHERE
storage_group_member_vg_uuid = ".$anvil->Database->quote($storage_group_member_vg_uuid)."
storage_group_member_vg_uuid = ".$anvil->Database->quote($storage_group_member_vg_uuid)."
AND
storage_group_member_host_uuid = ".$anvil->Database->quote($storage_group_member_host_uuid)."
;";
my $results = $anvil->Database->query({uuid => $uuid, query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__});
my $count = @{$results};
@ -14472,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});
@ -15496,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.
@ -15542,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).";";

@ -132,8 +132,14 @@ sub anvil_name_from_uuid
if (not $anvil_uuid)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Get->anvil_name_from_uuid()", parameter => "server_name" }});
return("!!error!!");
$anvil_uuid = $anvil->Cluster->get_anvil_uuid({debug => $debug});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { anvil_uuid => $anvil_uuid }});
if (not $anvil_uuid)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Get->anvil_name_from_uuid()", parameter => "server_name" }});
return("!!error!!");
}
}
my $query = "
@ -423,8 +429,14 @@ sub available_resources
if (not $anvil_uuid)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Get->available_resources()", parameter => "anvil_uuid" }});
return("!!error!!");
$anvil_uuid = $anvil->Cluster->get_anvil_uuid({debug => $debug});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { anvil_uuid => $anvil_uuid }});
if (not $anvil_uuid)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Get->available_resources()", parameter => "anvil_uuid" }});
return("!!error!!");
}
}
if (exists $anvil->data->{anvil_resources}{$anvil_uuid})

@ -6,9 +6,10 @@ package Anvil::Tools::Storage;
use strict;
use warnings;
use Data::Dumper;
use File::MimeInfo;
use JSON;
use Scalar::Util qw(weaken isweak);
use Text::Diff;
use File::MimeInfo;
use utf8;
our $VERSION = "3.0.0";
@ -25,6 +26,7 @@ my $THIS_FILE = "Storage.pm";
# get_storage_group_details
# make_directory
# move_file
# parse_df
# parse_lsblk
# read_config
# read_file
@ -2031,32 +2033,98 @@ fi";
}
=head2 parse_lsblk
This calls C<< lsblk >> (in json format) and parses the output. Data is stored as:
* lsblk::<target>::...
Parameters;
=head2 parse_df
=head3 password (optional)
This calls C<< df >> and parses the output. Data is stored as:
If C<< target >> is set, this is the password used to log into the remote system as the C<< remote_user >>. If it is not set, an attempt to connect without a password will be made..
* storage::df::<kernel_device_name>::...
=head3 port (optional, default 22)
This method takes no parameters.
If C<< target >> is set, this is the TCP port number used to connect to the remote machine.
=head3 remote_user (optional)
=cut
sub parse_df
{
my $self = shift;
my $parameter = shift;
my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Storage->parse_df()" }});
my $shell_call = $anvil->data->{path}{exe}{df}." --exclude-type=tmpfs --exclude-type=devtmpfs --no-sync --block-size=1 --output=source,fstype,size,used,avail,target";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }});
my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call});
if ($return_code)
{
# Failed.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "alert", key => "warning_0079", variables => {
return_code => $return_code,
output => $output,
}});
return(1);
}
foreach my $line (split/\n/, $output)
{
if ($line =~ /^\/dev\/(.*?)\s+(.*?)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\/.*)$/)
{
my $kernel_device_name = $1;
my $filesystem_type = $2;
my $size = $3;
my $used = $4;
my $free = $5;
my $mount_point = $6;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:kernel_device_name' => $kernel_device_name,
's2:mount_point' => $mount_point,
's3:filesystem_type' => $filesystem_type,
's4:size' => $anvil->Convert->add_commas({number => $size})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $size}).")",
's5:used' => $anvil->Convert->add_commas({number => $used})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $used}).")",
's6:free' => $anvil->Convert->add_commas({number => $free})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $free}).")",
}});
# If the line starts with 'mapper', we need to figure out what dm-X device it is.
if ($kernel_device_name =~ /^mapper\//)
{
# Use lstat
my $device_path = "/dev/".$kernel_device_name;
my $device_mapper = readlink($device_path);
if ($device_mapper =~ /^\.\.\/(.*)$/)
{
$kernel_device_name = $1;
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
device_path => $device_path,
kernel_device_name => $kernel_device_name,
}});
}
$anvil->{storage}{df}{$kernel_device_name}{filesystem_type} = $filesystem_type;
$anvil->{storage}{df}{$kernel_device_name}{mount_point} = $mount_point;
$anvil->{storage}{df}{$kernel_device_name}{size} = $size;
$anvil->{storage}{df}{$kernel_device_name}{used} = $used;
$anvil->{storage}{df}{$kernel_device_name}{free} = $free;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"storage::df::${kernel_device_name}::filesystem_type" => $anvil->{storage}{df}{$kernel_device_name}{filesystem_type},
"storage::df::${kernel_device_name}::mount_point" => $anvil->{storage}{df}{$kernel_device_name}{mount_point},
"storage::df::${kernel_device_name}::size" => $anvil->Convert->add_commas({number => $anvil->{storage}{df}{$kernel_device_name}{size}})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->{storage}{df}{$kernel_device_name}{size}}).")",
"storage::df::${kernel_device_name}::used" => $anvil->Convert->add_commas({number => $anvil->{storage}{df}{$kernel_device_name}{used}})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->{storage}{df}{$kernel_device_name}{used}}).")",
"storage::df::${kernel_device_name}::free" => $anvil->Convert->add_commas({number => $anvil->{storage}{df}{$kernel_device_name}{free}})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->{storage}{df}{$kernel_device_name}{free}}).")",
}});
}
}
return(0);
}
If C<< target >> is set, this is the user account that will be used when connecting to the remote system.
=head3 target (optional)
=head2 parse_lsblk
If set, C<< lsblk >> read from the target machine. This must be either an IP address or a resolvable host name.
This calls C<< lsblk >> (in json format) and parses the output. Data is stored as:
B<< Note >>: If not set, the short host name of this system is used in C<< lsblk::<short_host_name>::: >>.
* storage::lsblk::<kernel_device_name>::...
This method takes no parameters.
=cut
sub parse_lsblk
{
@ -2066,21 +2134,159 @@ sub parse_lsblk
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "Storage->parse_lsblk()" }});
# Setup default values
my $password = defined $parameter->{password} ? $parameter->{password} : "";
my $port = defined $parameter->{port} ? $parameter->{port} : 22;
my $remote_user = defined $parameter->{remote_user} ? $parameter->{remote_user} : "root";
my $target = defined $parameter->{target} ? $parameter->{target} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
password => $anvil->Log->is_secure($password),
port => $port,
remote_user => $remote_user,
target => $target,
}});
my $shell_call = $anvil->data->{path}{exe}{lsblk}." --output KNAME,FSTYPE,MOUNTPOINT,UUID,PARTLABEL,PARTUUID,RO,RM,HOTPLUG,MODEL,SERIAL,SIZE,STATE,ALIGNMENT,PHY-SEC,LOG-SEC,ROTA,SCHED,TYPE,TRAN,VENDOR --bytes --json";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }});
my $shell_call = $anvil->data->{path}{exe}{lsblk}." --all --bytes --json";
my ($output, $return_code) = $anvil->System->call({shell_call => $shell_call});
if ($return_code)
{
# Failed.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "alert", key => "warning_0079", variables => {
return_code => $return_code,
output => $output,
}});
return(1);
}
my $json = JSON->new->allow_nonref;
my $data = $json->decode($output);
foreach my $hash_ref (@{$data->{blockdevices}})
{
my $kernel_device_name = $hash_ref->{kname};
#next if $kernel_device_name =~ /^dm-/;
#next if $kernel_device_name =~ /^mmcblk/; # Add support for this later when 'System->parse_lshw' is done
$anvil->{storage}{lsblk}{$kernel_device_name}{alignment_offset} = defined $hash_ref->{alignment} ? $hash_ref->{alignment} : "";
$anvil->{storage}{lsblk}{$kernel_device_name}{device_type} = defined $hash_ref->{type} ? $hash_ref->{type} : "";
$anvil->{storage}{lsblk}{$kernel_device_name}{filesystem_type} = defined $hash_ref->{fstype} ? $hash_ref->{fstype} : "";
# This is the LVM formatted UUID, when it's an 'LVM2_member', so it should be easy to cross
# reference with: scan_lvm_lvs -> scan_lvm_lv_internal_uuid to map the LVs to a PV
$anvil->{storage}{lsblk}{$kernel_device_name}{filesystem_uuid} = defined $hash_ref->{uuid} ? $hash_ref->{uuid} : "";
$anvil->{storage}{lsblk}{$kernel_device_name}{hot_plug} = defined $hash_ref->{hotplug} ? $hash_ref->{hotplug} : "";
$anvil->{storage}{lsblk}{$kernel_device_name}{logical_sector_size} = defined $hash_ref->{'log-sec'} ? $hash_ref->{'log-sec'} : 0;
$anvil->{storage}{lsblk}{$kernel_device_name}{model} = defined $hash_ref->{model} ? $hash_ref->{model} : "";
$anvil->{storage}{lsblk}{$kernel_device_name}{model} = $anvil->Words->clean_spaces({string => $anvil->{storage}{lsblk}{$kernel_device_name}{model}});
$anvil->{storage}{lsblk}{$kernel_device_name}{mount_point} = defined $hash_ref->{mountpoint} ? $hash_ref->{mountpoint} : "";
$anvil->{storage}{lsblk}{$kernel_device_name}{partition_label} = defined $hash_ref->{partlabel} ? $hash_ref->{partlabel} : "";
$anvil->{storage}{lsblk}{$kernel_device_name}{partition_uuid} = defined $hash_ref->{partuuid} ? $hash_ref->{partuuid} : "";
$anvil->{storage}{lsblk}{$kernel_device_name}{physical_sector_size} = defined $hash_ref->{'phy-sec'} ? $hash_ref->{'phy-sec'} : 0;
$anvil->{storage}{lsblk}{$kernel_device_name}{read_only} = defined $hash_ref->{ro} ? $hash_ref->{ro} : "";
$anvil->{storage}{lsblk}{$kernel_device_name}{removable} = defined $hash_ref->{rm} ? $hash_ref->{rm} : "";
$anvil->{storage}{lsblk}{$kernel_device_name}{rotating_drive} = defined $hash_ref->{rota} ? $hash_ref->{rota} : "";
$anvil->{storage}{lsblk}{$kernel_device_name}{serial_number} = defined $hash_ref->{serial} ? $hash_ref->{serial} : "";
$anvil->{storage}{lsblk}{$kernel_device_name}{serial_number} = $anvil->Words->clean_spaces({string => $anvil->{storage}{lsblk}{$kernel_device_name}{serial_number}});
$anvil->{storage}{lsblk}{$kernel_device_name}{scheduler} = defined $hash_ref->{sched} ? $hash_ref->{sched} : "";
$anvil->{storage}{lsblk}{$kernel_device_name}{size} = defined $hash_ref->{size} ? $hash_ref->{size} : 0;
$anvil->{storage}{lsblk}{$kernel_device_name}{'state'} = defined $hash_ref->{'state'} ? $hash_ref->{'state'} : "";
$anvil->{storage}{lsblk}{$kernel_device_name}{transport} = defined $hash_ref->{tran} ? $hash_ref->{tran} : "";
$anvil->{storage}{lsblk}{$kernel_device_name}{type} = $anvil->{storage}{lsblk}{$kernel_device_name}{filesystem_uuid} ? "partition" : "drive";
$anvil->{storage}{lsblk}{$kernel_device_name}{vendor} = defined $hash_ref->{vendor} ? $hash_ref->{vendor} : "";
$anvil->{storage}{lsblk}{$kernel_device_name}{vendor} = $anvil->Words->clean_spaces({string => $anvil->{storage}{lsblk}{$kernel_device_name}{vendor}});
# There's precious little data that comes from SD cards.
if ($kernel_device_name =~ /^mmcblk/)
{
if ($kernel_device_name =~ /^mmcblk\d+p\d+/)
{
# This is a partition
$anvil->{storage}{lsblk}{$kernel_device_name}{filesystem_type} = "partition";
$anvil->{storage}{lsblk}{$kernel_device_name}{model} = "SD Card" if not $anvil->{storage}{lsblk}{$kernel_device_name}{model};
$anvil->{storage}{lsblk}{$kernel_device_name}{transport} = "pci" if not $anvil->{storage}{lsblk}{$kernel_device_name}{transport};
$anvil->{storage}{lsblk}{$kernel_device_name}{type} = "ssd" if not $anvil->{storage}{lsblk}{$kernel_device_name}{type};
$anvil->{storage}{lsblk}{$kernel_device_name}{vendor} = "unknown" if not $anvil->{storage}{lsblk}{$kernel_device_name}{vendor};
}
else
{
# It's the drive
$anvil->{storage}{lsblk}{$kernel_device_name}{filesystem_type} = "drive";
}
}
# Later, we'll want to trace device mapper devices back to the real device behind them (being
# LVM, crypt, etc). For now, this works.
if ($kernel_device_name =~ /^dm-/)
{
$anvil->{storage}{lsblk}{$kernel_device_name}{filesystem_type} = "partition";
$anvil->{storage}{lsblk}{$kernel_device_name}{model} = "Device Mapper" if not $anvil->{storage}{lsblk}{$kernel_device_name}{model};
$anvil->{storage}{lsblk}{$kernel_device_name}{transport} = "virtual" if not $anvil->{storage}{lsblk}{$kernel_device_name}{transport};
$anvil->{storage}{lsblk}{$kernel_device_name}{type} = "virtual" if not $anvil->{storage}{lsblk}{$kernel_device_name}{type};
$anvil->{storage}{lsblk}{$kernel_device_name}{vendor} = "Linux" if not $anvil->{storage}{lsblk}{$kernel_device_name}{vendor};
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"storage::lsblk::${kernel_device_name}::alignment_offset" => $anvil->{storage}{lsblk}{$kernel_device_name}{alignment_offset},
"storage::lsblk::${kernel_device_name}::device_type" => $anvil->{storage}{lsblk}{$kernel_device_name}{device_type},
"storage::lsblk::${kernel_device_name}::filesystem_type" => $anvil->{storage}{lsblk}{$kernel_device_name}{filesystem_type},
"storage::lsblk::${kernel_device_name}::filesystem_uuid" => $anvil->{storage}{lsblk}{$kernel_device_name}{filesystem_uuid},
"storage::lsblk::${kernel_device_name}::hot_plug" => $anvil->{storage}{lsblk}{$kernel_device_name}{hot_plug},
"storage::lsblk::${kernel_device_name}::logical_sector_size" => $anvil->{storage}{lsblk}{$kernel_device_name}{logical_sector_size},
"storage::lsblk::${kernel_device_name}::model" => $anvil->{storage}{lsblk}{$kernel_device_name}{model},
"storage::lsblk::${kernel_device_name}::mount_point" => $anvil->{storage}{lsblk}{$kernel_device_name}{mount_point},
"storage::lsblk::${kernel_device_name}::partition_label" => $anvil->{storage}{lsblk}{$kernel_device_name}{partition_label},
"storage::lsblk::${kernel_device_name}::partition_uuid" => $anvil->{storage}{lsblk}{$kernel_device_name}{partition_uuid},
"storage::lsblk::${kernel_device_name}::physical_sector_size" => $anvil->{storage}{lsblk}{$kernel_device_name}{physical_sector_size},
"storage::lsblk::${kernel_device_name}::read_only" => $anvil->{storage}{lsblk}{$kernel_device_name}{read_only},
"storage::lsblk::${kernel_device_name}::removable" => $anvil->{storage}{lsblk}{$kernel_device_name}{removable},
"storage::lsblk::${kernel_device_name}::rotating_drive" => $anvil->{storage}{lsblk}{$kernel_device_name}{rotating_drive},
"storage::lsblk::${kernel_device_name}::serial_number" => $anvil->{storage}{lsblk}{$kernel_device_name}{serial_number},
"storage::lsblk::${kernel_device_name}::scheduler" => $anvil->{storage}{lsblk}{$kernel_device_name}{scheduler},
"storage::lsblk::${kernel_device_name}::size" => $anvil->Convert->add_commas({number => $anvil->{storage}{lsblk}{$kernel_device_name}{size}})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->{storage}{lsblk}{$kernel_device_name}{size}}).")",
"storage::lsblk::${kernel_device_name}::state" => $anvil->{storage}{lsblk}{$kernel_device_name}{'state'},
"storage::lsblk::${kernel_device_name}::type" => $anvil->{storage}{lsblk}{$kernel_device_name}{type},
"storage::lsblk::${kernel_device_name}::transport" => $anvil->{storage}{lsblk}{$kernel_device_name}{transport},
"storage::lsblk::${kernel_device_name}::vendor" => $anvil->{storage}{lsblk}{$kernel_device_name}{vendor},
}});
}
# Now loop through devices and pass parent information (like transport, model, etc) from devices down to partitions.
my $parent_device = "";
foreach my $kernel_device_name (sort {$a cmp $b} keys %{$anvil->{storage}{lsblk}})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { kernel_device_name => $kernel_device_name }});
if ($anvil->{storage}{lsblk}{$kernel_device_name}{type} eq "drive")
{
$parent_device = $kernel_device_name;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { parent_device => $parent_device }});
next;
}
if (($parent_device) && (not $anvil->{storage}{lsblk}{$kernel_device_name}{model}))
{
$anvil->{storage}{lsblk}{$kernel_device_name}{model} = $anvil->{storage}{lsblk}{$parent_device}{model};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"storage::lsblk::${kernel_device_name}::model" => $anvil->{storage}{lsblk}{$kernel_device_name}{model},
}});
}
if (($parent_device) && (not $anvil->{storage}{lsblk}{$kernel_device_name}{serial_number}))
{
$anvil->{storage}{lsblk}{$kernel_device_name}{serial_number} = $anvil->{storage}{lsblk}{$parent_device}{serial_number};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"storage::lsblk::${kernel_device_name}::serial_number" => $anvil->{storage}{lsblk}{$kernel_device_name}{serial_number},
}});
}
if (($parent_device) && (not $anvil->{storage}{lsblk}{$kernel_device_name}{vendor}))
{
$anvil->{storage}{lsblk}{$kernel_device_name}{vendor} = $anvil->{storage}{lsblk}{$parent_device}{vendor};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"storage::lsblk::${kernel_device_name}::vendor" => $anvil->{storage}{lsblk}{$kernel_device_name}{vendor},
}});
}
if (($parent_device) && (not $anvil->{storage}{lsblk}{$kernel_device_name}{transport}))
{
$anvil->{storage}{lsblk}{$kernel_device_name}{transport} = $anvil->{storage}{lsblk}{$parent_device}{transport};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"storage::lsblk::${kernel_device_name}::transport" => $anvil->{storage}{lsblk}{$kernel_device_name}{transport},
}});
}
if (($parent_device) && (not $anvil->{storage}{lsblk}{$kernel_device_name}{'state'}))
{
$anvil->{storage}{lsblk}{$kernel_device_name}{'state'} = $anvil->{storage}{lsblk}{$parent_device}{'state'};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
"storage::lsblk::${kernel_device_name}::state" => $anvil->{storage}{lsblk}{$kernel_device_name}{'state'},
}});
}
}
return(0);
}

@ -36,6 +36,7 @@ my $THIS_FILE = "System.pm";
# maintenance_mode
# manage_authorized_keys
# manage_firewall
# parse_lshw
# read_ssh_config
# reload_daemon
# reboot_needed
@ -3806,6 +3807,151 @@ sub parse_arguments
return($hash);
}
=head2 parse_lshw
B<< NOTE >>: This method is not complete, do not use it yet!
This calls C<< lshw >> (in XML format) and parses the output. Data is stored as:
* lshw::...
Parameters;
=head3 xml (optional)
If set, the passed-in XML is parsed and C<< lshw -xml >> is not called. This should only be used for testing / debugging.
=cut
sub parse_lshw
{
my $self = shift;
my $parameter = shift;
my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0125", variables => { method => "System->parse_lsblk()" }});
my $xml = defined $parameter->{xml} ? $parameter->{xml} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { xml => $xml }});
if (not $xml)
{
my $shell_call = $anvil->data->{path}{exe}{lshw}." -xml";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { shell_call => $shell_call }});
($xml, my $return_code) = $anvil->System->call({shell_call => $shell_call});
if ($return_code)
{
# Failed.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "alert", key => "warning_0080", variables => {
return_code => $return_code,
output => $xml,
}});
return(1);
}
}
local $@;
my $dom = eval { XML::LibXML->load_xml(string => $xml); };
if ($@)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, key => "warning_0053", variables => {
cib => $xml,
error => $@,
}});
}
foreach my $node ($dom->findnodes('/list/node'))
{
my $id = $node->{id};
my $class = $node->{class};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
id => $id,
class => $class,
}});
}
=cut
foreach my $node ($dom->findnodes('/list/node'))
{
my $id = $node->{id};
my $class = $node->{class};
my $handle = $node->{handle};
my $parent_description = defined $node->findvalue('./description') ? $node->findvalue('./description') : "";
my $logical_name = defined $node->findvalue('./logicalname') ? $node->findvalue('./logicalname') : "";
my $parent_vendor = defined $node->findvalue('./vendor') ? $node->findvalue('./vendor') : "";
my $parent_model = defined $node->findvalue('./product') ? $node->findvalue('./product') : "";
my $parent_serial_number = defined $node->findvalue('./serial') ? $node->findvalue('./serial') : "";
my $parent_media = $id;
if ($id eq "device")
{
if (($parent_description =~ /sd card/i) or ($logical_name =~ /\/dev\/mmcblk/))
{
$parent_media = "sdcard";
}
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
id => $id,
class => $class,
logical_name => $logical_name,
parent_vendor => $parent_vendor,
parent_model => $parent_model,
}});
# Sub devices may not appear, so we'll later match logical names (when they're a path) to devices in 'df' later.
foreach my $device ($node->findnodes('./node'))
{
my $dev_id = $device->{id};
my $dev_class = $device->{class};
my $bus_info = defined $device->findvalue('./businfo') ? $device->findvalue('./businfo') : "";
my $path = defined $device->findvalue('./logicalname') ? $device->findvalue('./logicalname') : "";
my $description = defined $device->findvalue('./description') ? $device->findvalue('./description') : "";
my $vendor = defined $device->findvalue('./vendor') ? $device->findvalue('./vendor') : "";
$vendor = $parent_vendor if not $vendor;
my $model = defined $device->findvalue('./product') ? $device->findvalue('./product') : "";
$model = $parent_model if not $model;
my $serial_number = defined $device->findvalue('./serial') ? $device->findvalue('./serial') : "";
$serial_number = $parent_serial_number if not $serial_number;
my $size_number = defined $device->findvalue('./size') ? $device->findvalue('./size') : "";
$size_number = 0 if not $size_number;
my ($size_dom) = $device->findnodes('./size');
my $size_units = $size_dom->{units};
$size_units = "bytes" if not $size_units;
my $size_in_bytes = $anvil->Convert->human_readable_to_bytes({size => $size_number, type => $size_units});
my $media = $dev_id;
if (($bus_info =~ /nvme/i) or ($path =~ /\/dev\/nvm/))
{
$bus_info = "nvme";
$media = "ssd";
}
if ($dev_id eq "cdrom")
{
$media = "optical";
}
if (($bus_info =~ /^scsi/) or ($description =~ /ATA Disk/i))
{
# Call
}
$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,
description => $description,
vendor => $vendor,
model => $model,
serial_number => $serial_number,
size_number => $size_number,
size_units => $size_units,
size_in_bytes => $anvil->Convert->add_commas({number => $size_in_bytes})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $size_in_bytes}),
media => $media,
}});
}
}
=cut
return(0);
}
=head2 read_ssh_config
This reads /etc/ssh/ssh_config and notes hosts with defined ports. When found, the associated port will be automatically used for a given host name or IP address.

@ -1,9 +1,17 @@
MAINTAINERCLEANFILES = Makefile.in
MAINTAINERCLEANFILES = Makefile.in
cgibindir = $(localstatedir)/www/cgi-bin
dist_cgibin_SCRIPTS = \
striker \
upload.pl \
get_anvils \
get_anvil_status
cgibindir = $(localstatedir)/www/cgi-bin
dist_cgibin_SCRIPTS = \
cold_stop \
get_anvil_status \
get_anvils \
get_cpu \
get_memory \
get_replicated_storage \
get_shared_storage \
get_status \
set_membership \
set_power \
striker \
upload.pl

@ -0,0 +1,71 @@
#!/usr/bin/perl
#
# This prints JSON formated data reporting the status of an Anvil! system and it's member hosts.
#
use strict;
use warnings;
use Anvil::Tools;
use Data::Dumper;
use JSON;
$| = 1;
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();
$anvil->Get->switches;
$anvil->Database->connect;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => "log_0132"});
if (not $anvil->data->{sys}{database}{connections})
{
# No databases, exit.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, priority => "err", key => "error_0003"});
$anvil->nice_exit({exit_code => 1});
}
# Read in any CGI variables, if needed.
$anvil->Get->cgi();
$anvil->Database->get_hosts();
$anvil->Database->get_anvils();
print $anvil->Template->get({file => "shared.html", name => "json_headers", show_name => 0})."\n";
my $hash = {};
my $anvil_uuid = $anvil->data->{cgi}{anvil_uuid}{value};
if ((not $anvil_uuid) or (not exists $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}))
{
$anvil->data->{anvil_status}{anvil_name} = "!!invalid!anvil_uuid!!";
}
else
{
$anvil->data->{anvil_status}{anvil_name} = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_name};
$anvil->data->{anvil_status}{anvil_description} = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_description};
$anvil->data->{anvil_status}{timestamp} = time;
my $node1_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node1_host_uuid};
my $node2_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node2_host_uuid};
my $node2_status = $anvil->data->{hosts}{host_uuid}{$node2_uuid}{host_status} eq "online" ? 1 : 0;
$anvil->data->{anvil_status}{nodes}{node1}{host_name} = $anvil->data->{hosts}{host_uuid}{$node1_uuid}{host_name};
$anvil->data->{anvil_status}{nodes}{node1}{host_uuid} = $node1_uuid;
$anvil->data->{anvil_status}{nodes}{node1}{host_status} = $anvil->data->{hosts}{host_uuid}{$node1_uuid}{host_status} eq "online" ? 1 : 0;
$anvil->data->{anvil_status}{nodes}{node2}{host_name} = $anvil->data->{hosts}{host_uuid}{$node2_uuid}{host_name};
$anvil->data->{anvil_status}{nodes}{node2}{host_uuid} = $node2_uuid;
$anvil->data->{anvil_status}{nodes}{node2}{host_status} = $anvil->data->{hosts}{host_uuid}{$node2_uuid}{host_status} eq "online" ? 1 : 0;
$hash->{timestamp} = time;
$hash->{nodes} = [];
push @{$hash->{nodes}}, { on => $anvil->data->{anvil_status}{nodes}{node1}{host_status} };
push @{$hash->{nodes}}, { on => $anvil->data->{anvil_status}{nodes}{node2}{host_status} };
}
print JSON->new->utf8->encode($hash)."\n";

@ -7,6 +7,12 @@ TODO:
============
Jenkins;
-
============
DISABLE KSM!
- https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/virtualization_tuning_and_optimization_guide/chap-ksm

@ -0,0 +1,607 @@
#!/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_kernel_name = $anvil->data->{filesystem}{scan_filesystem_type}{$scan_filesystem_type}{name};
my $scan_filesystem_mount_point = $anvil->data->{filesystem}{scan_filesystem_type}{$scan_filesystem_type}{used_by_vg};
my $scan_filesystem_media_type = $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_kernel_name => $scan_filesystem_kernel_name,
scan_filesystem_mount_point => $scan_filesystem_mount_point,
scan_filesystem_media_type => $scan_filesystem_media_type,
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_kernel_name = $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_kernel_name};
my $old_scan_filesystem_mount_point = $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_mount_point};
my $old_scan_filesystem_media_type = $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_media_type};
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_kernel_name => $old_scan_filesystem_kernel_name,
old_scan_filesystem_mount_point => $old_scan_filesystem_mount_point,
old_scan_filesystem_media_type => $old_scan_filesystem_media_type,
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_kernel_name ne $old_scan_filesystem_kernel_name)
{
$update = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { update => $update }});
if ($old_scan_filesystem_kernel_name eq "DELETED")
{
# A lost PV is back
my $variables = {
pv_uuid => $scan_filesystem_type,
pv_name => $scan_filesystem_kernel_name,
};
$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_kernel_name,
old_name => $old_scan_filesystem_kernel_name,
};
$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_point ne $old_scan_filesystem_mount_point)
{
# 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_point)
{
# Added to a VG
my $variables = {
pv_uuid => $scan_filesystem_type,
pv_name => $scan_filesystem_kernel_name,
vg_name => $scan_filesystem_mount_point,
};
$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_kernel_name,
new_vg_name => $scan_filesystem_mount_point,
old_vg_name => $old_scan_filesystem_mount_point,
};
$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_type ne $old_scan_filesystem_media_type)
{
# 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_kernel_name,
new_attributes => $scan_filesystem_media_type,
old_attributes => $old_scan_filesystem_media_type,
};
$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_kernel_name,
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_kernel_name,
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_kernel_name = ".$anvil->Database->quote($scan_filesystem_kernel_name).",
scan_filesystem_mount_point = ".$anvil->Database->quote($scan_filesystem_mount_point).",
scan_filesystem_media_type = ".$anvil->Database->quote($scan_filesystem_media_type).",
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_kernel_name,
vg_name => $scan_filesystem_mount_point ? $scan_filesystem_mount_point : "--",
attributes => $scan_filesystem_media_type,
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_kernel_name,
scan_filesystem_mount_point,
scan_filesystem_media_type,
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_kernel_name).",
".$anvil->Database->quote($scan_filesystem_mount_point).",
".$anvil->Database->quote($scan_filesystem_media_type).",
".$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_kernel_name = $anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_kernel_name};
$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_kernel_name' => $old_scan_filesystem_kernel_name,
}});
next if $old_scan_filesystem_kernel_name eq "DELETED";
# Register an alert
my $variables = {
pv_uuid => $scan_filesystem_type,
pv_name => $old_scan_filesystem_kernel_name,
};
$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_kernel_name = '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_host_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
;";
$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];
$anvil->data->{sql}{scan_filesystems}{scan_filesystem_uuid}{$scan_filesystem_uuid}{scan_filesystem_host_uuid} = $row->[1];
$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->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_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_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) = @_;
### NOTE: If a drive is unmounted, we can't trust the sizes.
$anvil->Storage->parse_lsblk({debug => 2});
$anvil->Storage->parse_df({debug => 2});
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")
{
$anvil->data->{new}{partition}{$kernel_device_name}{type} = $anvil->{storage}{lsblk}{$kernel_device_name}{filesystem_type};
$anvil->data->{new}{partition}{$kernel_device_name}{mount_point} = $anvil->{storage}{lsblk}{$kernel_device_name}{mount_point};
$anvil->data->{new}{partition}{$kernel_device_name}{transport} = $anvil->{storage}{lsblk}{$kernel_device_name}{transport};
$anvil->data->{new}{partition}{$kernel_device_name}{media_type} = $anvil->{storage}{lsblk}{$kernel_device_name}{rotating_drive} ? "platter" : "solid_state"; # This could be network as well someday
$anvil->data->{new}{partition}{$kernel_device_name}{vendor} = $anvil->{storage}{lsblk}{$kernel_device_name}{vendor};
$anvil->data->{new}{partition}{$kernel_device_name}{model} = $anvil->{storage}{lsblk}{$kernel_device_name}{model};
$anvil->data->{new}{partition}{$kernel_device_name}{serial_number} = $anvil->{storage}{lsblk}{$kernel_device_name}{serial_number};
$anvil->data->{new}{partition}{$kernel_device_name}{description} = $anvil->{storage}{lsblk}{$kernel_device_name}{partition_label};
$anvil->data->{new}{partition}{$kernel_device_name}{size} = 0;
$anvil->data->{new}{partition}{$kernel_device_name}{used} = 0;
if (($anvil->data->{new}{partition}{$kernel_device_name}{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}{$kernel_device_name}{size} = $df_size;
$anvil->data->{new}{partition}{$kernel_device_name}{used} = $df_used;
my $calculated_free = $anvil->data->{new}{partition}{$kernel_device_name}{size} - $anvil->data->{new}{partition}{$kernel_device_name}{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}).")",
}});
}
# Record the partition
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"s1:new::partition::${kernel_device_name}::type" => $anvil->data->{new}{partition}{$kernel_device_name}{type},
"s2:new::partition::${kernel_device_name}::mount_point" => $anvil->data->{new}{partition}{$kernel_device_name}{mount_point},
"s3:new::partition::${kernel_device_name}::transport" => $anvil->data->{new}{partition}{$kernel_device_name}{transport},
"s4:new::partition::${kernel_device_name}::media_type" => $anvil->data->{new}{partition}{$kernel_device_name}{media_type},
"s5:new::partition::${kernel_device_name}::vendor" => $anvil->data->{new}{partition}{$kernel_device_name}{vendor},
"s6:new::partition::${kernel_device_name}::model" => $anvil->data->{new}{partition}{$kernel_device_name}{model},
"s7:new::partition::${kernel_device_name}::serial_number" => $anvil->data->{new}{partition}{$kernel_device_name}{serial_number},
"s8:new::partition::${kernel_device_name}::description" => $anvil->data->{new}{partition}{$kernel_device_name}{description},
"s9:new::partition::${kernel_device_name}::size" => $anvil->Convert->add_commas({number => $anvil->data->{new}{partition}{$kernel_device_name}{size}})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{new}{partition}{$kernel_device_name}{size}}).")",
"s10:new::partition::${kernel_device_name}::used" => $anvil->Convert->add_commas({number => $anvil->data->{new}{partition}{$kernel_device_name}{used}})." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $anvil->data->{new}{partition}{$kernel_device_name}{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);
}

@ -0,0 +1,88 @@
-- 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_kernel_name text not null, -- This is the backing device of the file system.
scan_filesystem_mount_point text not null, -- This is the name of the mount point.
scan_filesystem_transport text not null, -- Optional description of the drive's transport (usb, nvme, sata, sata, md, raid, optical, sdcard, etc - 'unknown')
scan_filesystem_media_type text not null, -- This is set to 'ssd' for solid state, 'platter' for spinning rust, 'network' for network mounts, etc.
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_description text not null, -- Free form description of the device.
scan_filesystem_size numeric not null, -- The size of the partition, in bytes
scan_filesystem_used numeric not null, -- The used 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_kernel_name text,
scan_filesystem_mount_point text,
scan_filesystem_transport text,
scan_filesystem_media_type text,
scan_filesystem_vendor text,
scan_filesystem_model text,
scan_filesystem_serial_number text,
scan_filesystem_description text,
scan_filesystem_size numeric,
scan_filesystem_used 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_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
(history_scan_filesystems.scan_filesystem_uuid,
history_scan_filesystems.scan_filesystem_host_uuid,
history_scan_filesystems.scan_filesystem_type,
history_scan_filesystems.scan_filesystem_kernel_name,
history_scan_filesystems.scan_filesystem_mount_point,
history_scan_filesystems.scan_filesystem_transport,
history_scan_filesystems.scan_filesystem_media_type,
history_scan_filesystems.scan_filesystem_vendor,
history_scan_filesystems.scan_filesystem_model,
history_scan_filesystems.scan_filesystem_serial_number,
history_scan_filesystems.scan_filesystem_description,
history_scan_filesystems.scan_filesystem_size,
history_scan_filesystems.scan_filesystem_used,
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>

@ -9,7 +9,7 @@
# 1 = Startup failure (not running as root, no DB, bad file read, etc)
#
# TODO:
# -
# - Finish System->parse_lwhw(), lots of good stuff in there.
#
use strict;

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

@ -2319,6 +2319,8 @@ Read UUID: .... [#!variable!read_uuid!#]
<key name="warning_0076">[ Warning ] - It looks like the postfix daemon is not running. Enabling and starting it now.</key>
<key name="warning_0077">[ Warning ] - Checking the mail queue after attempting to start postgres appears to have still failed. Output received was: [#!variable!output!#].</key>
<key name="warning_0078">[ Warning ] - Not installing the Alteeve repo! The package: [#!variable!anvil_role_rpm!#] is already installed. This is OK, but be aware that updates from Alteeve will not be available. To change this, please install: [#!variable!alteeve_repo!#].</key>
<key name="warning_0079">[ Warning ] - Failed to read the JSON formatted output of 'lsblk'. Expected the return code '0' but received: [#!variable!return_code!#]. The output, if any, was: [#!variable!output!#].</key>
<key name="warning_0080">[ Warning ] - Failed to read the XML formatted output of 'lshw'. Expected the return code '0' but received: [#!variable!return_code!#]. The output, if any, was: [#!variable!output!#].</key>
<!-- The entries below here are not sequential, but use a key to find the entry. -->
<!-- Run 'striker-parse-os-list to find new entries. -->

@ -36,6 +36,10 @@ my $anvil = Anvil::Tools->new();
# Read switches
$anvil->Get->switches;
### TODO: Remove this before final release
$anvil->Log->level({set => 2});
$anvil->Log->secure({set => 1});
##########################################
# Make sure we're running as 'root'
# $< == real UID, $> == effective UID
@ -1203,18 +1207,21 @@ AND
});
# If we're in a cluster, abort.
my ($problem) = $anvil->Cluster->parse_cib();
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }});
if ((not $problem) && ($anvil->data->{cib}{parsed}{'local'}{ready}))
if (-e $anvil->data->{path}{exe}{pcs})
{
# We're in a cluster, abort.
$anvil->Job->update_progress({
progress => 100,
message => "error_0250",
job_uuid => $anvil->data->{job}{uuid},
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, key => "error_0250"});
$anvil->nice_exit({exit_code => 7});
my ($problem) = $anvil->Cluster->parse_cib();
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { problem => $problem }});
if ((not $problem) && ($anvil->data->{cib}{parsed}{'local'}{ready}))
{
# We're in a cluster, abort.
$anvil->Job->update_progress({
progress => 100,
message => "error_0250",
job_uuid => $anvil->data->{job}{uuid},
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, 'print' => 1, key => "error_0250"});
$anvil->nice_exit({exit_code => 7});
}
}
return(0);

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

@ -35,6 +35,10 @@ if (not $anvil->data->{sys}{database}{connections})
$anvil->data->{switches}{config} = "";
$anvil->data->{switches}{'job-uuid'} = "";
$anvil->Get->switches;
### TODO: Remove this before final release
$anvil->Log->level({set => 2});
$anvil->Log->secure({set => 1});
##########################################
# Read in the config file
if ((not $anvil->data->{switches}{config}) or (not -f $anvil->data->{switches}{config}))

@ -33,6 +33,10 @@ my $anvil = Anvil::Tools->new();
# Read switches (target ([user@]host[:port]) and the file with the target's password.
$anvil->data->{switches}{'job-uuid'} = "";
$anvil->Get->switches;
### TODO: Remove this before final release
$anvil->Log->level({set => 2});
$anvil->Log->secure({set => 1});
##########################################
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0115", variables => { program => $THIS_FILE }});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
'switches::job-uuid' => $anvil->data->{switches}{'job-uuid'},
@ -184,7 +188,7 @@ sub add_databases
if ($db_host)
{
my $failed = $anvil->Database->manage_anvil_conf({
debug => 3,
debug => 2,
target => $target,
remote_user => "root",
password => $anvil->data->{data}{password},
@ -282,7 +286,7 @@ sub add_databases
if ($peer_host)
{
my $failed = $anvil->Database->manage_anvil_conf({
debug => 3,
debug => 2,
target => $target,
remote_user => "root",
password => $anvil->data->{data}{password},
@ -773,7 +777,7 @@ EOF
$shell_call = $anvil->data->{path}{exe}{'dnf'}." -y install ".$package;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
($output, $error, $return_code) = $anvil->Remote->call({
debug => 3,
debug => 2,
shell_call => $shell_call,
password => $anvil->data->{data}{password},
port => $anvil->data->{data}{ssh_port},

Loading…
Cancel
Save