* Created Storage->get_size_of_block_device() that takes a block device path and returns the size of the path, if it's found in the database.

* More work on the storage management of anvil-manage-server.

Signed-off-by: Digimer <digimer@alteeve.ca>
main
Digimer 3 years ago
parent dc34e6e3b6
commit acaacd9a86
  1. 304
      Anvil/Tools/Storage.pm
  2. 100
      tools/anvil-manage-server

@ -24,6 +24,7 @@ my $THIS_FILE = "Storage.pm";
# delete_file # delete_file
# find # find
# get_file_stats # get_file_stats
# get_size_of_block_device
# get_storage_group_details # get_storage_group_details
# get_storage_group_from_path # get_storage_group_from_path
# make_directory # make_directory
@ -1626,6 +1627,305 @@ sub get_file_stats
} }
=head2 get_size_of_block_device
This takes a block device path (DRBD or LVM LV path) and tries to find the size of the device as it was recorded in the database. If found, the size in bytes is returned. If there is a problem, C<< !!error!! >> is returned. If the device in not found in the database, an empty string is returned.
B<< Note >>: If there are multiple results, the first found will be returned. If the results span multiple Anvil! systems, this could be a problem. If this is a concern, specifify either the C<< host_uuid >> or C<< anvil_uuid >> parameters.
Parameters;
=head3 anvil_uuid (optional)
In the case of an ambiguous path (a path found on multiple Anvil! systems), this can be set to specify which Anvil! we're searching for.
=head3 host_uuid (optional)
In the case of an ambiguous path (a path found on multiple hosts), this can be set to specify which host we're searching for.
=head3 path (required)
This is the full block device path.
=cut
sub get_size_of_block_device
{
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->get_size_of_block_device()" }});
my $anvil_uuid = defined $parameter->{anvil_uuid} ? $parameter->{anvil_uuid} : "";
my $host_uuid = defined $parameter->{host_uuid} ? $parameter->{host_uuid} : "";
my $path = defined $parameter->{path} ? $parameter->{path} : "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
anvil_uuid => $anvil_uuid,
host_uuid => $host_uuid,
path => $path,
}});
if (not $path)
{
# No source passed.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Storage->get_size_of_block_device()", parameter => "path" }});
return('!!error!!');
}
$anvil->Database->get_anvils({debug => $debug});
my $node1_host_uuid = "";
my $node2_host_uuid = "";
my $dr1_host_uuid = "";
if ($anvil_uuid)
{
$node1_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node1_host_uuid};
$node2_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node2_host_uuid};
$dr1_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_dr1_host_uuid};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
node1_host_uuid => $node1_host_uuid,
node1_host_uuid => $node2_host_uuid,
dr1_host_uuid => $dr1_host_uuid,
}});
}
# Is this a DRBD path?
if ($path !~ /drbd/)
{
# See if we can find this in LVs
my $query = "
SELECT
scan_lvm_lv_host_uuid,
scan_lvm_lv_size
FROM
scan_lvm_lvs
WHERE
scan_lvm_lv_name != 'DELETED'
AND
scan_lvm_lv_path = ".$anvil->Database->quote($path);
if ($host_uuid)
{
$query .= "
AND
scan_lvm_lv_host_uuid = ".$anvil->Database->quote($host_uuid);
}
elsif ($anvil_uuid)
{
$query .= "
AND
(
scan_lvm_lv_host_uuid = ".$anvil->Database->quote($node1_host_uuid)."
OR
scan_lvm_lv_host_uuid = ".$anvil->Database->quote($node2_host_uuid);
if ($dr1_host_uuid)
{
$query .= "
OR
scan_lvm_lv_host_uuid = ".$anvil->Database->quote($dr1_host_uuid);
}
$query .= "
)";
}
$query .= "
LIMIT 1
;";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, 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 => $debug, list => {
results => $results,
count => $count,
}});
if (not $count)
{
# Not found
return("");
}
my $scan_lvm_lv_host_uuid = $results->[0]->[0];
my $scan_lvm_lv_size = $results->[0]->[1];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
scan_lvm_lv_host_uuid => $scan_lvm_lv_host_uuid,
scan_lvm_lv_size => $scan_lvm_lv_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $scan_lvm_lv_size}).")",
}});
return($scan_lvm_lv_size);
}
else
{
# Looks like it. If the device path is '/dev/drbd/by-res/...' we'll need to pull out the
# resource name (server name) and volume number as the path only actually exists when DRBD is
# up and isn't referenced in the config file.
my $resource = "";
my $volume = "";
$anvil->DRBD->gather_data({debug => $debug});
if ($path =~ /\/dev\/drbd\/by-res\/(.*)\/(\d+)$/)
{
$resource = $1;
$volume = $2;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
resource => $resource,
volume => $volume,
}});
}
elsif ($path =~ /\/dev\/drbd_(.*)_(\d+)$/)
{
$resource = $1;
$volume = $2;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
resource => $resource,
volume => $volume,
}});
}
elsif ($path =~ /\/dev\/drbd(\d+)$/)
{
# This is a direct path to a minor device, we'll need to find it in the config.
my $minor = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { minor => $minor }});
# If we were passed an anvil_uuid but not a host_uuid, don't use this machine's host UUID
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host_uuid => $host_uuid }});
# These will be set if multiple options are found in the database.
foreach my $this_resource (sort {$a cmp $b} keys %{$anvil->data->{new}{resource}})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_resource => $this_resource }});
foreach my $this_host_name (sort {$a cmp $b} keys %{$anvil->data->{new}{resource}{$this_resource}{host}})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_host_name => $this_host_name }});
foreach my $this_volume (sort {$a cmp $b} keys %{$$anvil->data->{new}{resource}{$this_resource}{host}{$this_host_name}{volume}})
{
my $this_minor = $anvil->data->{new}{resource}{$this_resource}{host}{$this_host_name}{volume}{$this_volume}{device_minor};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
this_volume => $this_volume,
this_minor => $this_minor,
}});
next if $this_minor ne $minor;
my $this_host_uuid = $anvil->Get->host_uuid_from_name({host_name => $this_host_name});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { this_host_uuid => $this_host_uuid }});
next if not $this_host_uuid;
# Sorry, this is a bit of a mess. Logic is; If we're given a
# host_uuid, and it matches, use it. Otherwise, if an
# anvil_uuid is passed, and either node 1 or 2's UUID, or if
# there is a DR host, if it's host UUID matches, then we can
# use this.
if (
(
($host_uuid) && ($host_uuid eq $this_host_uuid)
)
or
(
($anvil_uuid) &&
(
($this_host_uuid eq $node1_host_uuid) or
($this_host_uuid eq $node2_host_uuid) or
(
($dr1_host_uuid) &&
($this_host_uuid eq $dr1_host_uuid)
)
)
)
)
{
# This is a node in the requested cluster.
$resource = $this_resource;
$volume = $this_volume;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
resource => $resource,
volume => $volume,
}});
last;
if (not $host_uuid)
{
$host_uuid = $this_host_uuid;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { host_uuid => $host_uuid }});
}
}
}
}
}
}
if (not $resource)
{
# Not found.
return("");
}
# The DRBD query is sorted by size because 'Secondary' resources can't have their size read
# and get set to '0'.
my $query = "
SELECT
a.scan_drbd_resource_host_uuid,
b.scan_drbd_volume_size
FROM
scan_drbd_resources a,
scan_drbd_volumes b
WHERE
a.scan_drbd_resource_uuid = b.scan_drbd_volume_scan_drbd_resource_uuid
AND
a.scan_drbd_resource_xml != 'DELETED'
AND
a.scan_drbd_resource_name = ".$anvil->Database->quote($resource)."
AND
b.scan_drbd_volume_number = ".$anvil->Database->quote($volume);
if ($host_uuid)
{
$query .= "
AND
a.scan_drbd_resource_host_uuid = ".$anvil->Database->quote($host_uuid);
}
elsif ($anvil_uuid)
{
$query .= "
AND
(
a.scan_drbd_resource_host_uuid = ".$anvil->Database->quote($node1_host_uuid)."
OR
a.scan_drbd_resource_host_uuid = ".$anvil->Database->quote($node2_host_uuid);
if ($dr1_host_uuid)
{
$query .= "
OR
a.scan_drbd_resource_host_uuid = ".$anvil->Database->quote($dr1_host_uuid);
}
$query .= "
)";
}
$query .= "
ORDER BY
scan_drbd_volume_size DESC
LIMIT 1;";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, 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 => $debug, list => {
results => $results,
count => $count,
}});
if (not $count)
{
# Not found
return("");
}
my $scan_drbd_resource_host_uuid = $results->[0]->[0];
my $scan_drbd_volume_size = $results->[0]->[1];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
scan_drbd_resource_host_uuid => $scan_drbd_resource_host_uuid,
scan_drbd_volume_size => $scan_drbd_volume_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $scan_drbd_volume_size}).")",
}});
return($scan_drbd_volume_size);
}
return("");
}
=head2 get_storage_group_details =head2 get_storage_group_details
This takes a C<< storage_group_uuid >> and loads information about members into the following hash; This takes a C<< storage_group_uuid >> and loads information about members into the following hash;
@ -1825,10 +2125,6 @@ sub get_storage_group_from_path
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { minor => $minor }}); $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { minor => $minor }});
$anvil->Database->get_anvils({debug => $debug}); $anvil->Database->get_anvils({debug => $debug});
my $local_host_uuid = $anvil->Get->host_uuid({debug => $debug});
my $local_anvil_uuid = $anvil->Cluster->get_anvil_uuid({debug => $debug});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { local_anvil_uuid => $local_anvil_uuid }});
my $node1_host_uuid = ""; my $node1_host_uuid = "";
my $node2_host_uuid = ""; my $node2_host_uuid = "";
my $dr1_host_uuid = ""; my $dr1_host_uuid = "";

@ -768,12 +768,24 @@ sub interactive_configure_storage
while(1) while(1)
{ {
# Here, we'll list each storage pool and for each indicate if it's used and, if so # Here, we'll list each storage pool and for each indicate if it's used and, if so
my $index = 1;
my $lines = 2;
my $changes = 0;
my $storage_group_selection = [];
push @{$storage_group_selection}, "NULL";
print $terminal->Tputs('cl');
print $anvil->Words->string({key => "job_0355", variables => {
anvil_name => $anvil->data->{target_server}{anvil_name},
server_name => $anvil->data->{target_server}{server_name},
}})."\n";
print "* Storage configuration.\n";
my $anvil_uuid = $anvil->data->{target_server}{anvil_uuid}; my $anvil_uuid = $anvil->data->{target_server}{anvil_uuid};
foreach my $storage_group_name (sort {$a cmp $b} keys %{$anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_name}}) foreach my $storage_group_name (sort {$a cmp $b} keys %{$anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_name}})
{ {
my $storage_group_uuid = $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_name}{$storage_group_name}{storage_group_uuid}; my $storage_group_uuid = $anvil->data->{storage_groups}{anvil_uuid}{$anvil_uuid}{storage_group_name}{$storage_group_name}{storage_group_uuid};
my $vg_size = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size}; my $vg_size = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size};
my $free_size = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{free_size my $free_size = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{free_size};
my $vg_size_on_dr = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size_on_dr}; my $vg_size_on_dr = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{vg_size_on_dr};
my $available_on_dr = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{available_on_dr}; my $available_on_dr = $anvil->data->{anvil_resources}{$anvil_uuid}{storage_group}{$storage_group_uuid}{available_on_dr};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
@ -785,20 +797,100 @@ sub interactive_configure_storage
's6:available_on_dr' => $free_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $available_on_dr}).")", 's6:available_on_dr' => $free_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $available_on_dr}).")",
}}); }});
my $paths = {};
foreach my $target (sort {$a cmp $b} keys %{$anvil->data->{target_server}{disk}}) foreach my $target (sort {$a cmp $b} keys %{$anvil->data->{target_server}{disk}})
{ {
my $device_bus = $anvil->data->{target_server}{disk}{$target}{device_bus} my $device_bus = $anvil->data->{target_server}{disk}{$target}{device_bus};
my $cache = $anvil->data->{target_server}{disk}{$target}{cache}; my $cache = $anvil->data->{target_server}{disk}{$target}{cache};
my $io = $anvil->data->{target_server}{disk}{$target}{io}; my $io = $anvil->data->{target_server}{disk}{$target}{io};
my $path = $anvil->data->{target_server}{disk}{$target}{path}; my $this_path = $anvil->data->{target_server}{disk}{$target}{path};
my $boot_order = $anvil->data->{target_server}{disk}{$target}{boot_order} ? $anvil->data->{target_server}{disk}{$target}{boot_order} : "--";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
target => $target,
device_bus => $device_bus, device_bus => $device_bus,
cache => $cache, cache => $cache,
io => $io, io => $io,
path => $path, this_path => $this_path,
boot_order => $boot_order,
}}); }});
my $this_storage_group_uuid = $anvil->Storage->get_storage_group_from_path({
debug => 3,
anvil_uuid => $anvil_uuid,
path => $this_path,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:storage_group_name' => $storage_group_name,
's2:storage_group_uuid' => $storage_group_uuid,
's3:this_storage_group_uuid' => $this_storage_group_uuid,
}});
if (($this_storage_group_uuid) && ($storage_group_uuid eq $this_storage_group_uuid))
{
$paths->{$target}{path} = $this_path;
$paths->{$target}{boot_order} = $boot_order;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
"s1:paths->{".$target."}{path}" => $paths->{$target}{path},
"s2:paths->{".$target."}{boot_order}" => $paths->{$target}{boot_order},
}});
}
} }
$lines += 2;
print "[ ".$index." ] - Name: \"".$storage_group_name."\"\n";
print " - Size: [".$anvil->Convert->bytes_to_human_readable({'bytes' => $vg_size})."], Free: [".$anvil->Convert->bytes_to_human_readable({'bytes' => $free_size})."]\n";
my $path_count = keys %{$paths};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { path_count => $path_count }});
if ($path_count)
{
foreach my $target (sort {$a cmp $b} keys %{$paths})
{
my $path = $paths->{$target}{path};
my $boot_order = $paths->{$target}{boot_order};
my $size = $anvil->Storage->get_size_of_block_device({
debug => 2,
path => $path,
});
my $say_size = "";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
size => $size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $size}).")",
}});
if ($size !~ /^\d+$/)
{
$say_size = "<unknwon>";
}
else
{
$say_size = $anvil->Convert->bytes_to_human_readable({'bytes' => $size})
}
print " - Target: [".$target."], boot order: [".$boot_order."], backed by: [".$path."], Size: [".$say_size."]\n";
$lines++;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { lines => $lines }});
}
}
else
{
print " - <Storage group not used by this server>\n";
}
$index++;
}
print "\n";
print "[ B ] - Back\n";
print "[ Q ] - Quit\n";
if ($changes)
{
print "- Changes can be committed on the main page.\n";
print $terminal->Tgoto('cm', 0, ($lines+5))."? ";
} }
else
{
print $terminal->Tgoto('cm', 0, ($lines+4))."? ";
}
my $answer = <STDIN>;
chomp $answer;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { answer => $answer }});
} }
return(0); return(0);

Loading…
Cancel
Save