448 lines
8.1 KiB
Perl
Executable File
448 lines
8.1 KiB
Perl
Executable File
#!/usr/bin/perl -w
|
|
|
|
=encoding utf8
|
|
|
|
=head1 NAME
|
|
|
|
xen-resize-guest - Resize a loopback or LVM based xen guest.
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
xen-resize-guest [options]
|
|
|
|
Help Options:
|
|
--help Show help information.
|
|
--manual Read the manual for this script.
|
|
--version Show the version information and exit.
|
|
--verbose Show diagnostic output.
|
|
|
|
General Options:
|
|
--add Specify the amount of space to add, e.g. --add=1gb
|
|
--dir Specify the path to the loopback image root.
|
|
--force Force the resize to happen without a last-chance delay.
|
|
--hostname Specify the hostname of the guest to resize.
|
|
|
|
|
|
=head1 OPTIONS
|
|
|
|
=over 8
|
|
|
|
=item B<--add>
|
|
Specify the amount of storage to add to the primary disk.
|
|
|
|
=item B<--dir>
|
|
Specify the directory where the loopback files are based.
|
|
|
|
=item B<--force>
|
|
Don't pause for 10 seconds prior to commencing.
|
|
|
|
=item B<--help>
|
|
Show help information.
|
|
|
|
=item B<--hostname>
|
|
Specify the hostname to delete.
|
|
|
|
=item B<--lvm>
|
|
Specify the volume group to use.
|
|
|
|
=item B<--manual>
|
|
Read the manual for this script.
|
|
|
|
=item B<--version>
|
|
Show the version number and exit.
|
|
|
|
=back
|
|
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
This tool will ease the resizing of Xen guests, whether they are based
|
|
upon loopback files or LVM partitions.
|
|
|
|
Whilst the process of resizing a guest is pretty simple it can be fiddly
|
|
to do the steps correctly in the right order:
|
|
|
|
1. Shutdown the guest.
|
|
2. Unmount the volume, if it is mounted.
|
|
3. Add to the space.
|
|
4. Check the filesystem.
|
|
5. Resize the filesystem.
|
|
6. Restart the guest.
|
|
|
|
More than once I've heard of users making mistakes and breaking their
|
|
filesystems; hence this tool.
|
|
|
|
|
|
=head1 AUTHORS
|
|
|
|
Steve Kemp, https://steve.fi/
|
|
Axel Beckert, https://axel.beckert.ch/
|
|
|
|
|
|
=head1 LICENSE
|
|
|
|
Copyright (c) 2005-2009 by Steve Kemp, (c) 2010 by The Xen-Tools
|
|
Development Team. All rights reserved.
|
|
|
|
This module is free software;
|
|
you can redistribute it and/or modify it under
|
|
the same terms as Perl itself.
|
|
The LICENSE file contains the full text of the license.
|
|
|
|
=cut
|
|
|
|
|
|
use strict;
|
|
use English;
|
|
use Env;
|
|
use Getopt::Long;
|
|
use Pod::Usage;
|
|
use Text::Template;
|
|
use Xen::Tools::Common;
|
|
|
|
|
|
|
|
#
|
|
# Configuration values read from the command line, or configuration file.
|
|
#
|
|
my %CONFIG;
|
|
|
|
|
|
#
|
|
# Release number.
|
|
#
|
|
my $RELEASE = '4.9.1';
|
|
|
|
|
|
#
|
|
# Find xen toolstack command
|
|
#
|
|
$CONFIG{ 'xm' } = findXenToolstack();
|
|
|
|
|
|
#
|
|
# Read the global configuration file.
|
|
#
|
|
readConfigurationFile("/etc/xen-tools/xen-tools.conf", \%CONFIG);
|
|
|
|
|
|
#
|
|
# Parse the command line arguments.
|
|
#
|
|
parseCommandLineArguments();
|
|
|
|
|
|
#
|
|
# Validate our arguments.
|
|
#
|
|
testArguments();
|
|
|
|
|
|
#
|
|
# The path to the file, or device, to resize.
|
|
#
|
|
my $path = undef;
|
|
|
|
if ( $CONFIG{ 'dir' } )
|
|
{
|
|
|
|
#
|
|
# Make sure we can find the disk
|
|
#
|
|
$path =
|
|
$CONFIG{ 'dir' } . "/domains/" . $CONFIG{ 'hostname' } . "/disk.img";
|
|
if ( !-e $path )
|
|
{
|
|
print <<EOF;
|
|
|
|
The disk image for the guest domain $CONFIG{'hostname'} was not found
|
|
where we expected it to be:
|
|
|
|
$path
|
|
|
|
Please check and try again, or report this as a bug.
|
|
EOF
|
|
exit 1;
|
|
}
|
|
|
|
#
|
|
# Ensure the file is an ext3 filesystem.
|
|
#
|
|
my $type = `file $path`;
|
|
if ( $type !~ /ext3/ )
|
|
{
|
|
print "Filesystem type is not understood.\n";
|
|
print "We only handle ext3 right now!\n";
|
|
print "Aborting\n";
|
|
exit 1;
|
|
}
|
|
|
|
|
|
#
|
|
# OK we have the size, we have the image.
|
|
#
|
|
# Resize with DD
|
|
#
|
|
print "Preparing to resize image: $path\n";
|
|
|
|
#
|
|
# Allow panic
|
|
#
|
|
if ( !$CONFIG{ 'force' } )
|
|
{
|
|
foreach my $i ( 1 .. 10 )
|
|
{
|
|
my $delay = 10 - $i;
|
|
print "Sleeping for $delay seconds to allow cancel\n";
|
|
sleep 1;
|
|
}
|
|
}
|
|
|
|
print "DO NOT INTERRUPT\n";
|
|
|
|
my $cmd = "dd if=/dev/zero bs=1M count=$CONFIG{'add'}k >> $path";
|
|
system($cmd );
|
|
}
|
|
elsif ( $CONFIG{ 'lvm' } )
|
|
{
|
|
|
|
#
|
|
# Make sure we can find the disk
|
|
#
|
|
$path = "/dev/" . $CONFIG{ 'lvm' } . "/" . $CONFIG{ 'hostname' } . "-disk";
|
|
if ( !-e $path )
|
|
{
|
|
print <<EOF;
|
|
|
|
The disk image for the guest domain $CONFIG{'hostname'} was not found
|
|
where we expected it to be:
|
|
|
|
$path
|
|
|
|
Please check and try again, or report this as a bug.
|
|
EOF
|
|
exit 1;
|
|
}
|
|
|
|
#
|
|
# Ensure the file is an ext3 filesystem.
|
|
#
|
|
my $type = `head $path | file -`;
|
|
if ( $type !~ /ext3/ )
|
|
{
|
|
print "Filesystem type is not understood.\n";
|
|
print "We only handle ext3 right now!\n";
|
|
print "Aborting\n";
|
|
exit 1;
|
|
}
|
|
|
|
|
|
#
|
|
# OK we have the size, we have the image.
|
|
#
|
|
# Resize with DD
|
|
#
|
|
print "Preparing to resize image: $path\n";
|
|
|
|
#
|
|
# Allow panic
|
|
#
|
|
if ( !$CONFIG{ 'force' } )
|
|
{
|
|
foreach my $i ( 1 .. 10 )
|
|
{
|
|
my $delay = 10 - $i;
|
|
print "Sleeping for $delay seconds to allow cancel\n";
|
|
sleep 1;
|
|
}
|
|
}
|
|
|
|
print "DO NOT INTERRUPT\n";
|
|
my $cmd = "lvextend -L+$CONFIG{'add'}M $path";
|
|
system($cmd );
|
|
}
|
|
else
|
|
{
|
|
print "Unknown storage type. (Neither LVM nor loopback.)\n";
|
|
exit 1;
|
|
}
|
|
|
|
|
|
#
|
|
# Check filesystem.
|
|
#
|
|
print "Checking filesystem\n";
|
|
system("fsck.ext3 -f $path");
|
|
|
|
#
|
|
# Run ext3resize.
|
|
#
|
|
print "Resizing in place\n";
|
|
system("resize2fs $path");
|
|
|
|
|
|
#
|
|
# Job done.
|
|
#
|
|
print "All done\n";
|
|
exit 0;
|
|
|
|
|
|
|
|
=begin doc
|
|
|
|
Parse the command line arguments this script was given.
|
|
|
|
=end doc
|
|
|
|
=cut
|
|
|
|
sub parseCommandLineArguments
|
|
{
|
|
my $HELP = 0;
|
|
my $MANUAL = 0;
|
|
my $VERSION = 0;
|
|
|
|
#
|
|
# Local variables.
|
|
#
|
|
my %install;
|
|
|
|
|
|
#
|
|
# Parse options.
|
|
#
|
|
GetOptions(
|
|
|
|
# Misc. options
|
|
"add=s", \$CONFIG{ 'add' },
|
|
"dir=s", \$install{ 'dir' },
|
|
"lvm=s", \$install{ 'lvm' },
|
|
"hostname=s", \$CONFIG{ 'hostname' },
|
|
"force", \$CONFIG{ 'force' },
|
|
|
|
# Help options
|
|
"help", \$HELP,
|
|
"manual", \$MANUAL,
|
|
"verbose", \$CONFIG{ 'verbose' },
|
|
"version", \$VERSION
|
|
);
|
|
|
|
pod2usage(1) if $HELP;
|
|
pod2usage( -verbose => 2 ) if $MANUAL;
|
|
|
|
|
|
if ($VERSION)
|
|
{
|
|
print("xen-resize-guest release $RELEASE\n");
|
|
exit 1;
|
|
}
|
|
|
|
#
|
|
# Setup mutually exclusive options in such a way that
|
|
# they will allow the configuration values to be overridden by
|
|
# the command line.
|
|
#
|
|
if ( $install{ 'lvm' } )
|
|
{
|
|
$CONFIG{ 'lvm' } = $install{ 'lvm' };
|
|
$CONFIG{ 'dir' } = undef;
|
|
delete $CONFIG{ 'dir' };
|
|
}
|
|
if ( $install{ 'dir' } )
|
|
{
|
|
$CONFIG{ 'dir' } = $install{ 'dir' };
|
|
$CONFIG{ 'lvm' } = undef;
|
|
delete $CONFIG{ 'lvm' };
|
|
}
|
|
}
|
|
|
|
|
|
|
|
=begin doc
|
|
|
|
Test our arguments are complete.
|
|
|
|
=end doc
|
|
|
|
=cut
|
|
|
|
sub testArguments
|
|
{
|
|
|
|
#
|
|
# Make sure we received a hostname.
|
|
#
|
|
if ( !$CONFIG{ 'hostname' } )
|
|
{
|
|
print <<EOF;
|
|
|
|
A hostname is mandatory!
|
|
|
|
Please specify one with --hostname=some.host.name
|
|
|
|
EOF
|
|
exit 1;
|
|
}
|
|
|
|
#
|
|
# Make sure we received a size.
|
|
#
|
|
if ( !$CONFIG{ 'add' } )
|
|
{
|
|
print <<EOF;
|
|
|
|
Please specify a size to increase the guest by:
|
|
|
|
--add=1Gb -> Increase the size of the disk image by 1Gb.
|
|
--add=1 -> Increase the size of the disk image by 1Mb.
|
|
|
|
EOF
|
|
exit 1;
|
|
}
|
|
|
|
#
|
|
# Make sure the guest isn't running
|
|
#
|
|
if ( xenRunning( $CONFIG{ 'hostname' }, \%CONFIG ) )
|
|
{
|
|
print "The guest $CONFIG{'hostname'} appears to be running!\n";
|
|
exit 1;
|
|
}
|
|
|
|
#
|
|
# We should either have LVM *or* directory - not neither or both.
|
|
#
|
|
my $options = 0;
|
|
$options += 1
|
|
if ( defined( $CONFIG{ 'lvm' } ) && length( $CONFIG{ 'lvm' } ) );
|
|
$options += 1
|
|
if ( defined( $CONFIG{ 'dir' } ) && length( $CONFIG{ 'dir' } ) );
|
|
|
|
#
|
|
# Report
|
|
#
|
|
if ( $options == 0 )
|
|
{
|
|
print "Please specify one of --lvm or --dir\n";
|
|
exit 1;
|
|
}
|
|
if ( $options > 1 )
|
|
{
|
|
print "Please specify only one of --lvm or --dir - not both!\n";
|
|
exit 1;
|
|
}
|
|
|
|
#
|
|
# Convert from Gb -> Mb;
|
|
#
|
|
if ( $CONFIG{ 'add' } =~ /^([0-9.]+)Gb*$/i )
|
|
{
|
|
$CONFIG{ 'add' } = $1 * 1024;
|
|
}
|
|
if ( $CONFIG{ 'add' } =~ /^([0-9.]+)Mb*$/i )
|
|
{
|
|
$CONFIG{ 'add' } = $1;
|
|
}
|
|
}
|