440 lines
9.3 KiB
Perl
Executable File
440 lines
9.3 KiB
Perl
Executable File
#!/usr/bin/perl -w
|
|
|
|
=encoding utf8
|
|
|
|
=head1 NAME
|
|
|
|
xen-update-image - Update the software installed upon offline Xen images.
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
xen-update-image [options] imageName1 imageName2 .. imageNameN
|
|
|
|
Help Options:
|
|
--help Show this scripts help information.
|
|
--manual Read this scripts manual.
|
|
--version Show the version number and exit.
|
|
|
|
General Options:
|
|
--dir Specify the directory which contains the image(s).
|
|
--lvm Specify the LVM volume group which contains the image(s).
|
|
--evms Specify the EVMS container which contains the image(s).
|
|
|
|
|
|
=head1 OPTIONS
|
|
|
|
=over 8
|
|
|
|
=item B<--dir>
|
|
Specify the directory which contains the image(s).
|
|
|
|
=item B<--evms>
|
|
Specify the EVMS container which contains the image(s).
|
|
|
|
=item B<--help>
|
|
Show the script help.
|
|
|
|
=item B<--lvm>
|
|
Specify the LVM volume group which contains the image(s).
|
|
|
|
=item B<--manual>
|
|
Read the manual.
|
|
|
|
=item B<--version>
|
|
Show the version number and exit.
|
|
|
|
=back
|
|
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
xen-update-image is a simple script which allows you to update
|
|
a Xen image of Debian which has been created with xen-create-image.
|
|
|
|
It does this by mounting the image inside a temporary directory
|
|
then running:
|
|
|
|
apt-get update
|
|
|
|
apt-get upgrade
|
|
|
|
NOTE If the image is already running within Xen this will cause
|
|
corruption otherwise it will allow you to update your image without
|
|
booting it.
|
|
|
|
|
|
=head1 EXAMPLES
|
|
|
|
The following assumes there are two images which are not currently
|
|
running. The images are called 'test.my.flat', and 'x11.my.flat'.
|
|
|
|
Updating both images can be accomplished by executing:
|
|
|
|
xen-update-images --dir=/home/xen test.my.flat x11.my.flat
|
|
|
|
|
|
=head1 AUTHORS
|
|
|
|
Steve Kemp, https://steve.fi/
|
|
Axel Beckert, https://axel.beckert.ch/
|
|
Stéphane Jourdois
|
|
|
|
|
|
=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 File::Temp qw/ tempdir /;
|
|
use File::Copy qw/ mv cp /;
|
|
use Getopt::Long;
|
|
use Pod::Usage;
|
|
use Xen::Tools::Common;
|
|
|
|
|
|
#
|
|
# Configuration options, initially read from the configuration file
|
|
# but may be overridden by the command line.
|
|
#
|
|
# Command line flags *always* take precedence over the configuration file.
|
|
#
|
|
my %CONFIG;
|
|
|
|
#
|
|
# Release number.
|
|
#
|
|
my $RELEASE = '4.9.2';
|
|
|
|
|
|
#
|
|
# Find xen toolstack command
|
|
#
|
|
$CONFIG{ 'xm' } = findXenToolstack();
|
|
|
|
|
|
#
|
|
# Read configuration file if it exists.
|
|
#
|
|
if ( -e "/etc/xen-tools/xen-tools.conf" )
|
|
{
|
|
readConfigurationFile("/etc/xen-tools/xen-tools.conf", \%CONFIG);
|
|
}
|
|
|
|
|
|
#
|
|
# Parse command line arguments, these override the values from the
|
|
# configuration file.
|
|
#
|
|
parseCommandLineArguments();
|
|
|
|
|
|
#
|
|
# Test that our arguments are sane.
|
|
#
|
|
checkArguments();
|
|
|
|
|
|
#
|
|
# Abort if non-root user.
|
|
#
|
|
if ( $EFFECTIVE_USER_ID != 0 )
|
|
{
|
|
print <<EOROOT;
|
|
|
|
This script is not running with root privileges.
|
|
|
|
root privileges are required to successfully mount the disk image(s).
|
|
|
|
EOROOT
|
|
|
|
exit;
|
|
}
|
|
|
|
|
|
|
|
#
|
|
# Loop over the supplied arguments, and attempt to update each image.
|
|
#
|
|
while ( my $name = shift )
|
|
{
|
|
if ( !xenRunning($name, \%CONFIG) )
|
|
{
|
|
updateXenImage($name);
|
|
}
|
|
else
|
|
{
|
|
print "Skipping xen guest '$name' - it appears to be running.\n";
|
|
}
|
|
}
|
|
|
|
|
|
#
|
|
# All done.
|
|
#
|
|
exit;
|
|
|
|
|
|
|
|
=begin doc
|
|
|
|
Mount the primary disk image, so that we're ready to update it.
|
|
|
|
=end doc
|
|
|
|
=cut
|
|
|
|
sub updateXenImage
|
|
{
|
|
my ($name) = (@_);
|
|
|
|
#
|
|
# Create a temporary directory, and prepare to mount the
|
|
# image there.
|
|
#
|
|
my $tmp = tempdir( CLEANUP => 1 );
|
|
my $img = '';
|
|
my $mount_cmd = '';
|
|
|
|
#
|
|
# If we're dealing with loopback images find the main one,
|
|
# and mount it.
|
|
#
|
|
if ( $CONFIG{ 'dir' } )
|
|
{
|
|
|
|
# The loopback image.
|
|
$img = $CONFIG{ 'dir' } . "/domains/" . $name . "/disk.img";
|
|
|
|
if ( !-e $img )
|
|
{
|
|
print "Disk image '$img' for host '$name' not found\n";
|
|
return;
|
|
}
|
|
|
|
$mount_cmd = "mount -t auto -o loop $img $tmp";
|
|
}
|
|
elsif ( $CONFIG{ 'lvm' } )
|
|
{
|
|
|
|
# The LVM volume
|
|
$img = "/dev/" . $CONFIG{ 'lvm' } . "/$name-disk";
|
|
|
|
# make sure it exists.
|
|
if ( !-e $img )
|
|
{
|
|
print "Logical volume '$img' for host '$name' not found\n";
|
|
return;
|
|
}
|
|
|
|
$mount_cmd = "mount -t auto $img $tmp";
|
|
}
|
|
elsif ( $CONFIG{ 'evms' } )
|
|
{
|
|
|
|
# The EVMS volume -- note, unlike LVM, you don't need the
|
|
# $CONFIG{'evms'} to see it and mount the
|
|
# volume. $CONFIG{'evms'} is only used for manipulating the
|
|
# underlying object. Still, I don't want to mess with the
|
|
# parse code and make it confusing - otherwise --evms takes an
|
|
# argument everywhere but here, which will confuse users. The
|
|
# better solution is to make it so that --evms can take a
|
|
# following container, but doesn't require it. For the
|
|
# moment, it is better to leave it as it is, take a container,
|
|
# and then ignore it.
|
|
|
|
# The best way to do it is to just read it out of the
|
|
# configuration file, tell the user what you got and where you
|
|
# got it from, and not bother the user with picking --dir or
|
|
# --lvm or --evms at all, but infer it from the config file's
|
|
# disk = parameter. xen-delete-image might work the same way,
|
|
# but it could be *slightly* more dangerous in the context of
|
|
# deleting.
|
|
$img = "/dev/evms/$name-disk";
|
|
|
|
# make sure it exists.
|
|
if ( !-e $img )
|
|
{
|
|
print "EVMS volume '$img' for host '$name' not found\n";
|
|
return;
|
|
}
|
|
|
|
$mount_cmd = "mount -t auto $img $tmp";
|
|
}
|
|
else
|
|
{
|
|
die "Can't happen?\n";
|
|
}
|
|
|
|
#
|
|
# Mount the image.
|
|
#
|
|
`$mount_cmd`;
|
|
|
|
#
|
|
# Make sure this is a Debian image.
|
|
#
|
|
if ( ( -e $tmp . "/usr/bin/apt-get" ) &&
|
|
( -x $tmp . "/usr/bin/apt-get" ) )
|
|
{
|
|
#
|
|
# Copy dom0's resolv.conf to domU
|
|
#
|
|
mv("$tmp/etc/resolv.conf", "$tmp/etc/resolv.conf.old") if -f "$tmp/etc/resolv.conf";
|
|
cp("/etc/resolv.conf", "$tmp/etc/resolv.conf");
|
|
|
|
#
|
|
# Now run the update command.
|
|
#
|
|
system("chroot $tmp /usr/bin/apt-get update");
|
|
|
|
|
|
#
|
|
# Now upgrade
|
|
#
|
|
system(
|
|
"DEBIAN_FRONTEND=noninteractive chroot $tmp /usr/bin/apt-get upgrade --yes"
|
|
);
|
|
|
|
#
|
|
# Restore domU's resolv.conf if needed
|
|
#
|
|
if (-f "$tmp/etc/resolv.conf") {
|
|
mv("$tmp/etc/resolv.conf.old", "$tmp/etc/resolv.conf");
|
|
} else {
|
|
unlink "$tmp/etc/resolv.conf";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
print "Xen image $name is not a Debian GNU/Linux image. Skipping\n";
|
|
}
|
|
|
|
|
|
#
|
|
# Unmount
|
|
#
|
|
`umount -l $tmp`;
|
|
`umount $tmp 2>/dev/null >/dev/null`;
|
|
|
|
}
|
|
|
|
|
|
|
|
=begin doc
|
|
|
|
Parse the arguments specified upon the command line.
|
|
|
|
=end doc
|
|
|
|
=cut
|
|
|
|
sub parseCommandLineArguments
|
|
{
|
|
my $HELP = 0;
|
|
my $MANUAL = 0;
|
|
my $VERSION = 0;
|
|
|
|
#
|
|
# We record the installation method here because we want
|
|
# to ensure that we allow the method supplied upon the command line
|
|
# to overwrite the one we might have ready read from the configuration
|
|
# file.
|
|
#
|
|
my %install;
|
|
$install{ 'evms' } = undef;
|
|
$install{ 'dir' } = undef;
|
|
$install{ 'lvm' } = undef;
|
|
|
|
# Parse options.
|
|
#
|
|
GetOptions( "dir=s", \$install{ 'dir' },
|
|
"lvm=s", \$install{ 'lvm' },
|
|
"evms=s", \$install{ 'evms' },
|
|
"help", \$HELP,
|
|
"manual", \$MANUAL,
|
|
"version", \$VERSION );
|
|
|
|
#
|
|
# Now make ensure that the command line setting of '--lvm', '--evms', '--zpool'
|
|
# and '--dir=x' override anything specified in the configuration file.
|
|
#
|
|
if ( $install{ 'dir' } )
|
|
{
|
|
$CONFIG{ 'dir' } = $install{ 'dir' };
|
|
$CONFIG{ 'evms' } = undef;
|
|
$CONFIG{ 'lvm' } = undef;
|
|
$CONFIG{ 'zpool' } = undef;
|
|
}
|
|
if ( $install{ 'evms' } )
|
|
{
|
|
$CONFIG{ 'dir' } = undef;
|
|
$CONFIG{ 'evms' } = $install{ 'evms' };
|
|
$CONFIG{ 'lvm' } = undef;
|
|
$CONFIG{ 'zpool' } = undef;
|
|
}
|
|
if ( $install{ 'lvm' } )
|
|
{
|
|
$CONFIG{ 'dir' } = undef;
|
|
$CONFIG{ 'evms' } = undef;
|
|
$CONFIG{ 'lvm' } = $install{ 'lvm' };
|
|
$CONFIG{ 'zpool' } = undef;
|
|
}
|
|
|
|
pod2usage(1) if $HELP;
|
|
pod2usage( -verbose => 2 ) if $MANUAL;
|
|
|
|
if ($VERSION)
|
|
{
|
|
print "xen-update-image release $RELEASE\n";
|
|
exit;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
=begin doc
|
|
|
|
Test that the options we received from the command line, or our
|
|
configuration file, make sense.
|
|
|
|
=end doc
|
|
|
|
=cut
|
|
|
|
sub checkArguments
|
|
{
|
|
|
|
#
|
|
# Make sure we got one and only one installation method.
|
|
#
|
|
my $count = 0;
|
|
foreach my $type (qw/dir lvm evms/)
|
|
{
|
|
$count += 1 if defined( $CONFIG{ $type } );
|
|
}
|
|
|
|
#
|
|
# Show a decent error for when either zero or more than one options
|
|
# were selected.
|
|
#
|
|
if ( $count != 1 )
|
|
{
|
|
print "Please select one and only one of the installation methods:\n";
|
|
print " --dir\n";
|
|
print " --evms\n";
|
|
print " --lvm\n";
|
|
exit;
|
|
}
|
|
}
|