From edc544255ed57c01e19551a3b1cbe28fbdf7c830 Mon Sep 17 00:00:00 2001 From: digimer Date: Wed, 8 Nov 2023 12:00:48 -0500 Subject: [PATCH 1/4] 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 --- Anvil/Tools.pm | 3 + Anvil/Tools/Storage.pm | 240 +++++++++++++++++++++++++++++- share/words.xml | 22 ++- tools/anvil-configure-host | 4 + tools/anvil-manage-server-storage | 4 +- tools/anvil-version-changes | 6 + 6 files changed, 275 insertions(+), 4 deletions(-) diff --git a/Anvil/Tools.pm b/Anvil/Tools.pm index 0a37e938..32d83268 100644 --- a/Anvil/Tools.pm +++ b/Anvil/Tools.pm @@ -1256,6 +1256,7 @@ sub _set_paths 'osinfo-query' => "/usr/bin/osinfo-query", pamscale => "/usr/bin/pamscale", pamtopng => "/usr/bin/pamtopng", + parted => "/usr/sbin/parted", passwd => "/usr/bin/passwd", pcs => "/usr/sbin/anvil-pcs-wrapper", perccli64 => "/opt/MegaRAID/perccli/perccli64", @@ -1271,6 +1272,8 @@ sub _set_paths postmap => "/usr/sbin/postmap", postqueue => "/usr/sbin/postqueue", pwd => "/usr/bin/pwd", + pvdisplay => "/usr/sbin/pvdisplay", + pvresize => "/usr/sbin/pvresize", pvs => "/usr/sbin/pvs", pvscan => "/usr/sbin/pvscan", rm => "/usr/bin/rm", diff --git a/Anvil/Tools/Storage.pm b/Anvil/Tools/Storage.pm index 06d381f5..d90685bf 100644 --- a/Anvil/Tools/Storage.pm +++ b/Anvil/Tools/Storage.pm @@ -16,6 +16,7 @@ our $VERSION = "3.0.0"; my $THIS_FILE = "Storage.pm"; ### Methods; +# auto_grow_pv # backup # change_mode # 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 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. diff --git a/share/words.xml b/share/words.xml index e795a34d..8dbf914b 100644 --- a/share/words.xml +++ b/share/words.xml @@ -2633,6 +2633,21 @@ The file: [#!variable!file!#] needs to be updated. The difference is: The server: [#!variable!server_name!#] libvirt definition will now be updated. Check to verify that the connection to the server: [#!variable!server_name!#] is valid. The network mapping flag is NOT set. + No device found for PV: [#!variable!pv_name!#], skipping it. + 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. + 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. + Found the PV partition: [#!variable!device_path!#:#!variable!device_partition!#], but there's another partition after it. Not going to grow it, of course. + The partition: [#!variable!pv_name!#] appears to have been grown successfully. The new partition scheme is: +==== +#!variable!output!# +==== + + The resize appears to have been successful. The physical volume: [#!variable!pv_name!#] details are now: +==== +#!variable!output!# +==== + + The physical volume: [#!variable!pv_name!#] has been resized! The host name: [#!variable!target!#] does not resolve to an IP address. @@ -3933,7 +3948,11 @@ We will wait: [#!variable!waiting!#] seconds and then try again. We'll give up i [ Warning ] - The file: [#!variable!file_path!#] needed to provision the server: [#!variable!server_name!#] was found, but it's not ready yet. [ Warning ] - Waiting for a bit, and then will check if files are ready. [ 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!#] - Please specify a storage group to use to add the new drive to. + [ Warning ] - The system call: [#!variable!shell_call!#] returned the non-zero return code: [#!variable!return_code!#]. The command output, if anything, was: +==== +#!variable!output!# +==== + Warning! [ Warning ] - When trying to create the local meta-data on: [#!variable!drbd_resource!#/#!variable!next_drbd_volume!#] [ Warning ] - using the command: [#!variable!shell_call!#] @@ -3970,6 +3989,7 @@ We will try to proceed anyway. #!variable!error!# ==== + Please specify a storage group to use to add the new drive to. diff --git a/tools/anvil-configure-host b/tools/anvil-configure-host index 4f02b3c7..48f2add2 100755 --- a/tools/anvil-configure-host +++ b/tools/anvil-configure-host @@ -66,6 +66,10 @@ overwrite_variables_with_switches($anvil); # Set maintenance mode $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); # Record that we've configured this machine. diff --git a/tools/anvil-manage-server-storage b/tools/anvil-manage-server-storage index f88bf088..421ca2fd 100755 --- a/tools/anvil-manage-server-storage +++ b/tools/anvil-manage-server-storage @@ -417,11 +417,11 @@ sub manage_disk_add }}); 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); $anvil->Job->update_progress({ progress => 100, - message => "warning_0159", + message => "warning_0168", }) if $anvil->data->{switches}{'job-uuid'}; $anvil->nice_exit({exit_code => 1}); } diff --git a/tools/anvil-version-changes b/tools/anvil-version-changes index c49176e9..6dec57fb 100755 --- a/tools/anvil-version-changes +++ b/tools/anvil-version-changes @@ -116,6 +116,9 @@ sub node_checks # Make sure logind is update to handle fencing properly # see - https://access.redhat.com/solutions/1578823 $anvil->Cluster->configure_logind({debug => 2}); + + # Look for unused free space + $anvil->Storage->auto_grow_pv({debug => 2}); return(0); } @@ -128,6 +131,9 @@ sub dr_checks # RHBZ #1961562 - https://bugzilla.redhat.com/show_bug.cgi?id=1961562#c16 handle_bz1961562($anvil); + # Look for unused free space + $anvil->Storage->auto_grow_pv({debug => 2}); + # Make sure DRBD compiled after a kernel upgrade. $anvil->DRBD->_initialize_kmod({debug => 2}); From ac84d5ba0ab96a1d61562475d0f4591cffb3c46e Mon Sep 17 00:00:00 2001 From: digimer Date: Thu, 31 Aug 2023 11:52:24 -0400 Subject: [PATCH 2/4] Removed Storage->auto_grow_pv from anvil-version-changes. Signed-off-by: digimer --- tools/anvil-configure-host | 5 +---- tools/anvil-version-changes | 6 ------ 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/tools/anvil-configure-host b/tools/anvil-configure-host index 48f2add2..231341ac 100755 --- a/tools/anvil-configure-host +++ b/tools/anvil-configure-host @@ -64,10 +64,7 @@ pickup_job_details($anvil); overwrite_variables_with_switches($anvil); # Set maintenance mode -$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}); +$anvil->System->maintenance_mode({set => 1}); # Reconfigure the network. reconfigure_network($anvil); diff --git a/tools/anvil-version-changes b/tools/anvil-version-changes index 6dec57fb..c49176e9 100755 --- a/tools/anvil-version-changes +++ b/tools/anvil-version-changes @@ -116,9 +116,6 @@ sub node_checks # Make sure logind is update to handle fencing properly # see - https://access.redhat.com/solutions/1578823 $anvil->Cluster->configure_logind({debug => 2}); - - # Look for unused free space - $anvil->Storage->auto_grow_pv({debug => 2}); return(0); } @@ -131,9 +128,6 @@ sub dr_checks # RHBZ #1961562 - https://bugzilla.redhat.com/show_bug.cgi?id=1961562#c16 handle_bz1961562($anvil); - # Look for unused free space - $anvil->Storage->auto_grow_pv({debug => 2}); - # Make sure DRBD compiled after a kernel upgrade. $anvil->DRBD->_initialize_kmod({debug => 2}); From 9bd98951b56bce456231b5fc94fff054847d7c19 Mon Sep 17 00:00:00 2001 From: digimer Date: Wed, 8 Nov 2023 20:59:40 -0500 Subject: [PATCH 3/4] Added backup/restore of partition table in Storage->auto_grow_pv * Auto-growing PVs (and the backing partition) is now supported by anvil-manage-host '--auto-grow-pv'. Signed-off-by: digimer --- Anvil/Tools.pm | 1 + Anvil/Tools/Storage.pm | 76 +++++++++++++++++++++++++++++++++-------- man/anvil-manage-host.8 | 8 +++++ share/words.xml | 22 ++++++++++++ tools/anvil-manage-host | 49 ++++++++++++++++++++++++++ 5 files changed, 142 insertions(+), 14 deletions(-) diff --git a/Anvil/Tools.pm b/Anvil/Tools.pm index 32d83268..5862c090 100644 --- a/Anvil/Tools.pm +++ b/Anvil/Tools.pm @@ -1281,6 +1281,7 @@ sub _set_paths rsync => "/usr/bin/rsync", sed => "/usr/bin/sed", setsid => "/usr/bin/setsid", # See: https://serverfault.com/questions/1105733/virsh-command-hangs-when-script-runs-in-the-background + sfdisk => "/usr/sbin/sfdisk", 'shutdown' => "/usr/sbin/shutdown", snmpget => "/usr/bin/snmpget", snmpset => "/usr/bin/snmpset", diff --git a/Anvil/Tools/Storage.pm b/Anvil/Tools/Storage.pm index d90685bf..2a158d39 100644 --- a/Anvil/Tools/Storage.pm +++ b/Anvil/Tools/Storage.pm @@ -147,7 +147,7 @@ sub auto_grow_pv if ($return_code) { # Bad return code. - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", key => "warning_0159", variables => { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, priority => "alert", key => "warning_0159", variables => { shell_call => $shell_call, return_code => $return_code, output => $output, @@ -195,7 +195,7 @@ sub auto_grow_pv 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 }}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0821", variables => { pv_name => $pv_name }}); next; } @@ -210,7 +210,7 @@ sub auto_grow_pv if ($return_code) { # Bad return code. - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", key => "warning_0159", variables => { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, priority => "alert", key => "warning_0159", variables => { shell_call => $shell_call, return_code => $return_code, output => $output, @@ -240,7 +240,7 @@ sub auto_grow_pv if ($size < 1073741824) { # Not enough free space - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0823", variables => { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0823", variables => { free_space => $anvil->Convert->bytes_to_human_readable({'bytes' => $size}), device_path => $device_path, pv_partition => $pv_partition, @@ -250,17 +250,53 @@ sub auto_grow_pv else { # Enough free space, grow! - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => $debug, key => "log_0822", variables => { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0822", variables => { free_space => $anvil->Convert->bytes_to_human_readable({'bytes' => $size}), device_path => $device_path, pv_partition => $pv_partition, }}); + ### Backup the partition table. + #sfdisk --dump /dev/sda > partition_table_backup_sda + my $device_name = ($device_path =~ /^\/dev\/(.*)$/)[0]; + my $partition_backup = "/tmp/".$device_name.".partition_table_backup"; + my $shell_call = $anvil->data->{path}{exe}{sfdisk}." --dump ".$device_path." > ".$partition_backup; + my $restore_shell_call = $anvil->data->{path}{exe}{sfdisk}." ".$device_path." < ".$partition_backup." --force"; + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + device_name => $device_name, + partition_backup => $partition_backup, + 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__, 'print' => 1, level => 1, priority => "alert", key => "warning_0159", variables => { + shell_call => $shell_call, + return_code => $return_code, + output => $output, + }}); + next; + } + else + { + # Tell the user about the backup. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0361", variables => { + device_path => $device_path, + partition_backup => $partition_backup, + restore_command => $restore_shell_call, + }}); + } + ### 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"; + $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}); + ($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, @@ -268,12 +304,24 @@ sub auto_grow_pv if ($return_code) { # Bad return code. - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", key => "warning_0159", variables => { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, priority => "alert", key => "warning_0159", variables => { shell_call => $shell_call, return_code => $return_code, output => $output, }}); - next; + + ### Restore the partition table + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, priority => "err", key => "error_0467"}); + + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { restore_shell_call => $restore_shell_call }}); + my ($output, $return_code) = $anvil->System->call({debug => 3, shell_call => $restore_shell_call}); + $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { + output => $output, + return_code => $return_code, + }}); + + # Error out. + $anvil->nice_exit({exit_code => 1}); } else { @@ -281,7 +329,7 @@ sub auto_grow_pv 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 => { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0825", variables => { pv_name => $pv_name, output => $output, }}); @@ -298,7 +346,7 @@ sub auto_grow_pv if ($return_code) { # Bad return code. - $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, priority => "alert", key => "warning_0159", variables => { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, priority => "alert", key => "warning_0159", variables => { shell_call => $shell_call, return_code => $return_code, output => $output, @@ -311,20 +359,20 @@ sub auto_grow_pv 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 => { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, 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 }}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, 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 => { + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0824", variables => { device_path => $device_path, pv_partition => $pv_partition, }}); diff --git a/man/anvil-manage-host.8 b/man/anvil-manage-host.8 index f6dbf9d8..9a587139 100644 --- a/man/anvil-manage-host.8 +++ b/man/anvil-manage-host.8 @@ -24,6 +24,11 @@ Set the log level to 1, 2 or 3 respectively. Be aware that level 3 generates a s \fB\-\-age\-out\-database\fR This requests the database check for records that are too old and purge them. .TP +\fB\-\-auto\-grow\-pv\fR +This looks at LVM physical volumes on the local host. For each one that is found, '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. +.TP +If you deleted the default '/home' partition during the install of a subnode or DR host, this should give you that space back. +.TP \fB\-\-check\-configured\fR Check to see if the host is marked as configured or yet. .TP @@ -33,6 +38,9 @@ This checks to see if the database is enabled or not. \fB\-\-check\-network\-mapping\fR This reports if the host is currently in network mapping (this disables several features and watches the network states much more frequently) .TP +\fB\-\-confirm\fR +This confirms actions that would normally prompt the user to confirm before proceeding. +.TP \fB\-\-database\-active\fR This enables the database on the local Striker dashboard. .TP diff --git a/share/words.xml b/share/words.xml index 8dbf914b..fe2b776e 100644 --- a/share/words.xml +++ b/share/words.xml @@ -735,6 +735,7 @@ The XML that failed sanity check was: ]]> + @@ -2648,6 +2649,7 @@ The file: [#!variable!file!#] needs to be updated. The difference is: ==== The physical volume: [#!variable!pv_name!#] has been resized! + The user answered: [#!variable!answer!#] The host name: [#!variable!target!#] does not resolve to an IP address. @@ -3201,6 +3203,26 @@ Proceed? [y/N] This host is already NOT configured to map the network. This host is no longer configured to map the network. No hosts with IPMI found, done. + Searching for free space to grow PVs into. + - The '--confirm' switch was used, proceeding. + 1GiB of free space available. +- If so, and if no other partitions are after it, it will be grown to use the free space. +- The PV itself will then be resized to use the new space + +This is generally used just after initializing a new subnode or DR host. If this host has real data +on it, please proceed with caution. + +The partition table will be backed up, and if the partition resize fails, the partition table will be +reloaded automatically. If this host has real data, ensure a complete backup is available before +proceeding. +]]> + - [ Note ] - The original partition table for: [#!variable!device_path!#] has been saved to: [#!variable!partition_backup!#] + If anything goes wrong, we will attempt to recover automatically. If needed, you can try + recovering with: [#!variable!restore_command!#] Normal Password diff --git a/tools/anvil-manage-host b/tools/anvil-manage-host index b793e332..ec32ebd6 100755 --- a/tools/anvil-manage-host +++ b/tools/anvil-manage-host @@ -26,9 +26,11 @@ my $anvil = Anvil::Tools->new(); # Read switches $anvil->Get->switches({list => [ "age-out-database", + "auto-grow-pv", "check-configured", "check-database", "check-network-mapping", + "confirm", "database-active", "database-inactive", "disable-network-mapping", @@ -68,6 +70,10 @@ elsif ($anvil->data->{switches}{'resync-database'}) { resync_database($anvil); } +elsif ($anvil->data->{switches}{'auto-grow-pv'}) +{ + auto_grow_pv($anvil); +} else { # Show the options. @@ -81,6 +87,49 @@ $anvil->nice_exit({exit_code => 0}); # Functions # ############################################################################################################# +sub auto_grow_pv +{ + my ($anvil) = @_; + + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0358"}); + if ($anvil->data->{switches}{confirm}) + { + # Already confirmed. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "message_0359"}); + } + else + { + # Ask to confirm, with a wee bit of fear; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0360"}); + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0021"}); + my $answer = ; + chomp $answer; + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "log_0828", variables => { answer => $answer }}); + + if ((lc($answer) eq "y") or (lc($answer) eq "yes")) + { + # Proceed. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, key => "message_0175"}); + } + else + { + # Abort. + $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "message_0022"}); + $anvil->nice_exit({exit_code => 0}); + } + } + + print "Enabling maintenance mode.\n"; + $anvil->System->maintenance_mode({set => 1}); + + $anvil->Storage->auto_grow_pv({debug => 2}); + + print "Disabling maintenance mode.\n"; + $anvil->System->maintenance_mode({set => 0}); + + return(0); +} + sub age_out_data { my ($anvil) = @_; From 3b6da647806ea5b810e04d1b30854b121083af07 Mon Sep 17 00:00:00 2001 From: digimer Date: Wed, 8 Nov 2023 21:38:54 -0500 Subject: [PATCH 4/4] Added parted and util-linux to core dependencies. Signed-off-by: digimer --- anvil.spec.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/anvil.spec.in b/anvil.spec.in index 26a15350..7bb3500a 100644 --- a/anvil.spec.in +++ b/anvil.spec.in @@ -67,6 +67,7 @@ Requires: mlocate Requires: net-snmp-utils Requires: NetworkManager-initscripts-updown Requires: nvme-cli +Requires: parted Requires: pciutils Requires: perl-Capture-Tiny Requires: perl-Data-Dumper @@ -110,6 +111,7 @@ Requires: tcpdump Requires: tmux Requires: unzip Requires: usbutils +Requires: util-linux Requires: vim Requires: wget # iptables-services conflicts with firewalld