Skip to content

Commit

Permalink
This branch resolves issue #462; Auto growing PVs. Specifically, it l…
Browse files Browse the repository at this point in the history
…ooks 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>
  • Loading branch information
digimer committed Aug 31, 2023
1 parent 610bad6 commit 98f80ca
Show file tree
Hide file tree
Showing 5 changed files with 272 additions and 1 deletion.
3 changes: 3 additions & 0 deletions Anvil/Tools.pm
Original file line number Diff line number Diff line change
Expand Up @@ -1253,6 +1253,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",
Expand All @@ -1266,6 +1267,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",
Expand Down
240 changes: 239 additions & 1 deletion Anvil/Tools/Storage.pm
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ our $VERSION = "3.0.0";
my $THIS_FILE = "Storage.pm";

### Methods;
# auto_grow_pv
# backup
# change_mode
# change_owner
Expand Down Expand Up @@ -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_0748", 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_0750", 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_0749", 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_0752", 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_0753", variables => {
pv_name => $pv_name,
output => $output,
}});
}

# Done.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0754", 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_0751", 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.
Expand Down Expand Up @@ -5536,7 +5774,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.
Expand Down
20 changes: 20 additions & 0 deletions share/words.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2414,6 +2414,21 @@ The file: [#!variable!file!#] needs to be updated. The difference is:
<key name="log_0745">Adjusting the resource: [#!variable!resource!#] to ensure it's compatible with the peer's config prior to connection.</key>
<key name="log_0746">The local resource: [#!variable!resource!#] is StandAlone, attempting to connect.</key>
<key name="log_0747">Failed to connect to the host: [#!variable!host!#]! Unable to up the resource on it. The volume(s) backing this server are UpToDate locally, so booting should be fine.</key>
<key name="log_0748">No device found for PV: [#!variable!pv_name!#], skipping it.</key>
<key name="log_0749">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_0750">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_0751">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_0752">The partition: [#!variable!pv_name!#] appears to have been grown successfully. The new partition scheme is:
====
#!variable!output!#
====
</key>
<key name="log_0753">The resize appears to have been successful. The physical volume: [#!variable!pv_name!#] details are now:
====
#!variable!output!#
====
</key>
<key name="log_0754">The physical volume: [#!variable!pv_name!#] has been resized!</key>

<!-- 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>
Expand Down Expand Up @@ -3671,6 +3686,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_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_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>

</language>
<!-- 日本語 -->
Expand Down
4 changes: 4 additions & 0 deletions tools/anvil-configure-host
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ overwrite_variables_with_switches($anvil);
# Set maintenance mode
$anvil->System->maintenance_mode({set => 1});

# 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.
Expand Down
6 changes: 6 additions & 0 deletions tools/anvil-version-changes
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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});

Expand Down

0 comments on commit 98f80ca

Please sign in to comment.