Rebased with main and resolved conflicts.

This branch resolves issue #462; Auto growing PVs. Specifically, it looks at the LVM PVs on the host and checks to see if there is unused free space after the backing partition. If there is, it auto-grows the partition and then resizes the PV. This featu
re is designed to make life easier for users who deleted the auto-created '/home' partition during the anaconda disk partitioning tool.
* Created Storage->auto_grow_pv() that does the above.
* Added the missing hidden method name _create_rsync_wrapper in the Storage module POD.
* Added a call to Storage->auto_grow_pv() in anvil-configure-host and anvil-version-changes for nodes and DR.

Signed-off-by: digimer <mkelly@alteeve.ca>
main
digimer 1 year ago
parent b8980b0ead
commit edc544255e
  1. 3
      Anvil/Tools.pm
  2. 240
      Anvil/Tools/Storage.pm
  3. 22
      share/words.xml
  4. 4
      tools/anvil-configure-host
  5. 4
      tools/anvil-manage-server-storage
  6. 6
      tools/anvil-version-changes

@ -1256,6 +1256,7 @@ sub _set_paths
'osinfo-query' => "/usr/bin/osinfo-query", 'osinfo-query' => "/usr/bin/osinfo-query",
pamscale => "/usr/bin/pamscale", pamscale => "/usr/bin/pamscale",
pamtopng => "/usr/bin/pamtopng", pamtopng => "/usr/bin/pamtopng",
parted => "/usr/sbin/parted",
passwd => "/usr/bin/passwd", passwd => "/usr/bin/passwd",
pcs => "/usr/sbin/anvil-pcs-wrapper", pcs => "/usr/sbin/anvil-pcs-wrapper",
perccli64 => "/opt/MegaRAID/perccli/perccli64", perccli64 => "/opt/MegaRAID/perccli/perccli64",
@ -1271,6 +1272,8 @@ sub _set_paths
postmap => "/usr/sbin/postmap", postmap => "/usr/sbin/postmap",
postqueue => "/usr/sbin/postqueue", postqueue => "/usr/sbin/postqueue",
pwd => "/usr/bin/pwd", pwd => "/usr/bin/pwd",
pvdisplay => "/usr/sbin/pvdisplay",
pvresize => "/usr/sbin/pvresize",
pvs => "/usr/sbin/pvs", pvs => "/usr/sbin/pvs",
pvscan => "/usr/sbin/pvscan", pvscan => "/usr/sbin/pvscan",
rm => "/usr/bin/rm", rm => "/usr/bin/rm",

@ -16,6 +16,7 @@ our $VERSION = "3.0.0";
my $THIS_FILE = "Storage.pm"; my $THIS_FILE = "Storage.pm";
### Methods; ### Methods;
# auto_grow_pv
# backup # backup
# change_mode # change_mode
# change_owner # change_owner
@ -113,6 +114,243 @@ sub parent
############################################################################################################# #############################################################################################################
=head2 auto_grow_pv
This looks at LVM PVs on the local host. For each one that is found, C<< parted >> is called to check if there's more that 1 GiB of free space available after it. If so, it will extend the PV partition to use the free space.
This method takes no parameters.
=cut
sub auto_grow_pv
{
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->_auto_grow_pv()" }});
# Look for disks that has unpartitioned space and grow it if needed.
my $host_uuid = $anvil->Get->host_uuid();
my $short_host_name = $anvil->Get->short_host_name();
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:host_uuid' => $host_uuid,
's2:short_host_name' => $short_host_name,
}});
my $shell_call = $anvil->data->{path}{exe}{pvs}." --noheadings --units b -o pv_name,vg_name,pv_size,pv_free --separator ,";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
my ($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
output => $output,
return_code => $return_code,
}});
if ($return_code)
{
# Bad return code.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", key => "warning_0159", variables => {
shell_call => $shell_call,
return_code => $return_code,
output => $output,
}});
next;
}
my $pv_found = 0;
foreach my $line (split/\n/, $output)
{
$line = $anvil->Words->clean_spaces({string => $line});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
my ($pv_name, $used_by_vg, $pv_size, $pv_free) = (split/,/, $line);
$pv_size =~ s/B$//;
$pv_free =~ s/B$//;
my $pv_used = $pv_size - $pv_free;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
pv_name => $pv_name,
used_by_vg => $used_by_vg,
pv_size => $pv_size." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $pv_size}).")",
pv_free => $pv_free." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $pv_free}).")",
pv_used => $pv_used." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $pv_used}).")",
}});
# Get the raw backing disk.
my $device_path = "";
my $pv_partition = 0;
if ($pv_name =~ /(\/dev\/nvme\d+n\d+)p(\d+)$/)
{
$device_path = $1;
$pv_partition = $2;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
device_path => $device_path,
pv_partition => $pv_partition,
}});
}
elsif ($pv_name =~ /(\/dev\/\w+)(\d+)$/)
{
$device_path = $1;
$pv_partition = $2;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
device_path => $device_path,
pv_partition => $pv_partition,
}});
}
else
{
# No device found for the PV.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0821", variables => { pv_name => $pv_name }});
next;
}
# See how much free space there is on the backing disk.
my $shell_call = $anvil->data->{path}{exe}{parted}." --align optimal ".$device_path." unit B print free";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
my ($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
output => $output,
return_code => $return_code,
}});
if ($return_code)
{
# Bad return code.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", key => "warning_0159", variables => {
shell_call => $shell_call,
return_code => $return_code,
output => $output,
}});
next;
}
my $pv_found = 0;
foreach my $line (split/\n/, $output)
{
$line = $anvil->Words->clean_spaces({string => $line});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
if ($pv_found)
{
#print "Checking if: [".$line."] is free space.\n";
if ($line =~ /^(\d+)B\s+(\d+)B\s+(\d+)B\s+Free Space/i)
{
my $start_byte = $1;
my $end_byte = $2;
my $size = $3;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:start_byte' => $start_byte." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $start_byte}).")",
's2:end_byte' => $end_byte." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $end_byte}).")",
's3:size' => $pv_used." (".$anvil->Convert->bytes_to_human_readable({'bytes' => $size}).")",
}});
# There's free space! If it's greater than 1 GiB, grow it automatically.
if ($size < 1073741824)
{
# Not enough free space
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0823", variables => {
free_space => $anvil->Convert->bytes_to_human_readable({'bytes' => $size}),
device_path => $device_path,
pv_partition => $pv_partition,
}});
next;
}
else
{
# Enough free space, grow!
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0822", variables => {
free_space => $anvil->Convert->bytes_to_human_readable({'bytes' => $size}),
device_path => $device_path,
pv_partition => $pv_partition,
}});
### Grow the partition
# parted --align optimal /dev/sda ---pretend-input-tty resizepart 2 100% Yes; echo $?
my $shell_call = $anvil->data->{path}{exe}{parted}." --align optimal ".$device_path." ---pretend-input-tty resizepart ".$pv_partition." 100% Yes";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
my ($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
output => $output,
return_code => $return_code,
}});
if ($return_code)
{
# Bad return code.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", key => "warning_0159", variables => {
shell_call => $shell_call,
return_code => $return_code,
output => $output,
}});
next;
}
else
{
# Looks like it worked. Call print again to log the new value.
my $shell_call = $anvil->data->{path}{exe}{parted}." --align optimal ".$device_path." unit B print free";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
my ($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $shell_call});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0825", variables => {
pv_name => $pv_name,
output => $output,
}});
}
### Resize the PV.
$shell_call = $anvil->data->{path}{exe}{pvresize}." ".$pv_name;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $shell_call});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
output => $output,
return_code => $return_code,
}});
if ($return_code)
{
# Bad return code.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", key => "warning_0159", variables => {
shell_call => $shell_call,
return_code => $return_code,
output => $output,
}});
next;
}
else
{
# Looks like it worked. Call print again to log the new value.
my $shell_call = $anvil->data->{path}{exe}{pvdisplay}." ".$pv_name;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
my ($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $shell_call});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0826", variables => {
pv_name => $pv_name,
output => $output,
}});
}
# Done.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0827", variables => { pv_name => $pv_name }});
}
}
else
{
# There's another partition after this PV, do nothing.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0824", variables => {
device_path => $device_path,
pv_partition => $pv_partition,
}});
next;
}
}
elsif ($line =~ /^$pv_partition\s/)
{
$pv_found = 1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { pv_found => $pv_found }});
}
else
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
device_path => $device_path,
pv_partition => $pv_partition,
pv_found => $pv_found,
}});
}
}
}
return(0);
}
=head2 backup =head2 backup
This will create a copy of the file under the C<< path::directories::backups >> directory with the datestamp as a suffix. The path is preserved under the backup directory. The path and file name are returned. This will create a copy of the file under the C<< path::directories::backups >> directory with the datestamp as a suffix. The path is preserved under the backup directory. The path and file name are returned.
@ -5647,7 +5885,7 @@ fi";
############################################################################################################# #############################################################################################################
=head2 =head2 _create_rsync_wrapper
This does the actual work of creating the C<< expect >> wrapper script and returns the path to that wrapper for C<< rsync >> calls. This does the actual work of creating the C<< expect >> wrapper script and returns the path to that wrapper for C<< rsync >> calls.

@ -2633,6 +2633,21 @@ The file: [#!variable!file!#] needs to be updated. The difference is:
<key name="log_0818">The server: [#!variable!server_name!#] libvirt definition will now be updated.</key> <key name="log_0818">The server: [#!variable!server_name!#] libvirt definition will now be updated.</key>
<key name="log_0819">Check to verify that the connection to the server: [#!variable!server_name!#] is valid.</key> <key name="log_0819">Check to verify that the connection to the server: [#!variable!server_name!#] is valid.</key>
<key name="log_0820">The network mapping flag is NOT set.</key> <key name="log_0820">The network mapping flag is NOT set.</key>
<key name="log_0821">No device found for PV: [#!variable!pv_name!#], skipping it.</key>
<key name="log_0822">Found: [#!variable!free_space!#] free space after the PV partition: [#!variable!device_path!#:#!variable!pv_partition!#]! Will grow the partition to use the free space.</key>
<key name="log_0823">Found: [#!variable!free_space!#] free space after the PV partition: [#!variable!device_path!#:#!variable!pv_partition!#]. This is too small for auto-growing the partition.</key>
<key name="log_0824">Found the PV partition: [#!variable!device_path!#:#!variable!device_partition!#], but there's another partition after it. Not going to grow it, of course.</key>
<key name="log_0825">The partition: [#!variable!pv_name!#] appears to have been grown successfully. The new partition scheme is:
====
#!variable!output!#
====
</key>
<key name="log_0826">The resize appears to have been successful. The physical volume: [#!variable!pv_name!#] details are now:
====
#!variable!output!#
====
</key>
<key name="log_0827">The physical volume: [#!variable!pv_name!#] has been resized!</key>
<!-- Messages for users (less technical than log entries), though sometimes used for logs, too. --> <!-- Messages for users (less technical than log entries), though sometimes used for logs, too. -->
<key name="message_0001">The host name: [#!variable!target!#] does not resolve to an IP address.</key> <key name="message_0001">The host name: [#!variable!target!#] does not resolve to an IP address.</key>
@ -3933,7 +3948,11 @@ We will wait: [#!variable!waiting!#] seconds and then try again. We'll give up i
<key name="warning_0156">[ Warning ] - The file: [#!variable!file_path!#] needed to provision the server: [#!variable!server_name!#] was found, but it's not ready yet.</key> <key name="warning_0156">[ Warning ] - The file: [#!variable!file_path!#] needed to provision the server: [#!variable!server_name!#] was found, but it's not ready yet.</key>
<key name="warning_0157">[ Warning ] - Waiting for a bit, and then will check if files are ready.</key> <key name="warning_0157">[ Warning ] - Waiting for a bit, and then will check if files are ready.</key>
<key name="warning_0158">[ Warning ] - There is a duplicate storage group named: [#!variable!group_name!#]. Keeping the group with UUID: [#!variable!keep_uuid!#], and deleting the group with the UUID: [#!variable!delete_uuid!#]</key> <key name="warning_0158">[ Warning ] - There is a duplicate storage group named: [#!variable!group_name!#]. Keeping the group with UUID: [#!variable!keep_uuid!#], and deleting the group with the UUID: [#!variable!delete_uuid!#]</key>
<key name="warning_0159">Please specify a storage group to use to add the new drive to.</key> <key name="warning_0159">[ Warning ] - The system call: [#!variable!shell_call!#] returned the non-zero return code: [#!variable!return_code!#]. The command output, if anything, was:
====
#!variable!output!#
====
</key>
<key name="warning_0160"> Warning! <key name="warning_0160"> Warning!
[ Warning ] - When trying to create the local meta-data on: [#!variable!drbd_resource!#/#!variable!next_drbd_volume!#] [ Warning ] - When trying to create the local meta-data on: [#!variable!drbd_resource!#/#!variable!next_drbd_volume!#]
[ Warning ] - using the command: [#!variable!shell_call!#] [ Warning ] - using the command: [#!variable!shell_call!#]
@ -3970,6 +3989,7 @@ We will try to proceed anyway.</key>
#!variable!error!# #!variable!error!#
==== ====
</key> </key>
<key name="warning_0168">Please specify a storage group to use to add the new drive to.</key>
</language> </language>
<!-- 日本語 --> <!-- 日本語 -->

@ -66,6 +66,10 @@ overwrite_variables_with_switches($anvil);
# Set maintenance mode # Set maintenance mode
$anvil->System->maintenance_mode({set => 1, debug => 2}); $anvil->System->maintenance_mode({set => 1, debug => 2});
# Check to see if there's a PV to grow.
$anvil->Storage->auto_grow_pv({debug => 2});
# Reconfigure the network.
reconfigure_network($anvil); reconfigure_network($anvil);
# Record that we've configured this machine. # Record that we've configured this machine.

@ -417,11 +417,11 @@ sub manage_disk_add
}}); }});
if (not $anvil->data->{switches}{'storage-group'}) if (not $anvil->data->{switches}{'storage-group'})
{ {
print $anvil->Words->string({key => 'warning_0159'})."\n"; print $anvil->Words->string({key => 'warning_0168'})."\n";
show_storage_groups($anvil); show_storage_groups($anvil);
$anvil->Job->update_progress({ $anvil->Job->update_progress({
progress => 100, progress => 100,
message => "warning_0159", message => "warning_0168",
}) if $anvil->data->{switches}{'job-uuid'}; }) if $anvil->data->{switches}{'job-uuid'};
$anvil->nice_exit({exit_code => 1}); $anvil->nice_exit({exit_code => 1});
} }

@ -117,6 +117,9 @@ sub node_checks
# see - https://access.redhat.com/solutions/1578823 # see - https://access.redhat.com/solutions/1578823
$anvil->Cluster->configure_logind({debug => 2}); $anvil->Cluster->configure_logind({debug => 2});
# Look for unused free space
$anvil->Storage->auto_grow_pv({debug => 2});
return(0); return(0);
} }
@ -128,6 +131,9 @@ sub dr_checks
# RHBZ #1961562 - https://bugzilla.redhat.com/show_bug.cgi?id=1961562#c16 # RHBZ #1961562 - https://bugzilla.redhat.com/show_bug.cgi?id=1961562#c16
handle_bz1961562($anvil); handle_bz1961562($anvil);
# Look for unused free space
$anvil->Storage->auto_grow_pv({debug => 2});
# Make sure DRBD compiled after a kernel upgrade. # Make sure DRBD compiled after a kernel upgrade.
$anvil->DRBD->_initialize_kmod({debug => 2}); $anvil->DRBD->_initialize_kmod({debug => 2});

Loading…
Cancel
Save