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 <mkelly@alteeve.ca>
main
digimer 1 year ago
parent ac84d5ba0a
commit 9bd98951b5
  1. 1
      Anvil/Tools.pm
  2. 76
      Anvil/Tools/Storage.pm
  3. 8
      man/anvil-manage-host.8
  4. 22
      share/words.xml
  5. 49
      tools/anvil-manage-host

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

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

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

@ -735,6 +735,7 @@ The XML that failed sanity check was:
]]></key>
<key name="error_0465"><![CDATA[The file: [#!variable!file!#] doesn't exist on the target: [#!variable!target!#].]]></key>
<key name="error_0466"><![CDATA[The Anvil! string (name or UUID): [#!variable!string!#] didn't match any known Anvil! in the database.]]></key>
<key name="error_0467"><![CDATA[[ Error ] - The repartition attemp failed! Reloading the partition table now!]]]></key>
<!-- Files templates -->
<!-- NOTE: Translating these files requires an understanding of which lines are translatable -->
@ -2648,6 +2649,7 @@ The file: [#!variable!file!#] needs to be updated. The difference is:
====
</key>
<key name="log_0827">The physical volume: [#!variable!pv_name!#] has been resized!</key>
<key name="log_0828">The user answered: [#!variable!answer!#]</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>
@ -3201,6 +3203,26 @@ Proceed? [y/N]</key>
<key name="message_0355">This host is already NOT configured to map the network.</key>
<key name="message_0356">This host is no longer configured to map the network.</key>
<key name="message_0357">No hosts with IPMI found, done.</key>
<key name="message_0358">Searching for free space to grow PVs into.</key>
<key name="message_0359">- The '--confirm' switch was used, proceeding.</key>
<key name="message_0360"><![CDATA[
[ Warning ] - Auto-growing the LVM physical volumes could, in some case, leave the system unbootable.
The steps that will taken are;
- LVM Pyhiscal volumes will be found.
- For each found, 'parted' is used to see if there is > 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.
]]></key>
<key name="message_0361">- [ 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!#]</key>
<!-- Translate names (protocols, etc) -->
<key name="name_0001">Normal Password</key> <!-- none in mail-server -->

@ -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 = <STDIN>;
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) = @_;

Loading…
Cancel
Save