1764 lines
40 KiB
Perl
Executable File
1764 lines
40 KiB
Perl
Executable File
#!/usr/bin/perl -w
|
|
|
|
=head1 NAME
|
|
|
|
xen-create-image - Create a new Xen instance
|
|
|
|
=cut
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
|
|
Help Options:
|
|
--debug Show useful debugging information.
|
|
--help Show this scripts help information.
|
|
--manual Read this scripts manual.
|
|
--version Show the version number and exit.
|
|
|
|
Size / General options:
|
|
--accounts Copy all non-system accounts to the guest image
|
|
--boot Boot the new instance after creating it.
|
|
--cache Cache .deb files on the host when using B<--debootstrap>
|
|
--force Force overwriting existing images.
|
|
--fs Specify the filesystem type to use.
|
|
--image Specify whether to create "sparse" or "full" disk images.
|
|
--initrd Specify the initial ramdisk
|
|
--kernel Set the path to the kernel to use for dom U.
|
|
--memory Setup the amount of memory allocated to the instance.
|
|
--passwd Ask for a root password during setup.
|
|
--role Run a specific role command post-install.
|
|
--size Set the size of the primary disk image.
|
|
--swap Set the size of the swap partition.
|
|
--ide Use IDE names for virtual devices (hda not sda)
|
|
|
|
Installation options:
|
|
--copy Install the new image by copying from the given directory.
|
|
--dist Specify the distribution you wish to install
|
|
--debootstrap Use debootstrap to install the distribution B<--dist>
|
|
--mirror Setup the mirror to use when installing with B<--debootstrap>.
|
|
--rpmstrap Use rpmstrap to install the distribution B<--dist>
|
|
--tar Install the new image by untarring the given file.
|
|
|
|
Networking options:
|
|
--dhcp Configure the new image to fetch its networking details via DHCP.
|
|
--gateway Setup the network gateway for the new instance.
|
|
--ip Setup the IP address of the machine, multiple IPs are allowed.
|
|
--netmask Setup the netmask for the new instance.
|
|
|
|
Mandatory options:
|
|
|
|
--dir Specify where the output images should go.
|
|
--lvm Specify the volume group to save images within.
|
|
--hostname Set the images hostname.
|
|
|
|
=cut
|
|
|
|
|
|
=head1 OPTIONS
|
|
|
|
=over 8
|
|
|
|
=item B<--boot>
|
|
Start the new virtual instance as soon as the installation has finished.
|
|
|
|
=item B<--cache>
|
|
Cache the .deb files in /var/cache/apt/archives upon the host system when installing a new image with B<--debootstrap> for a large speed improvement. [Defaults to yes.]
|
|
|
|
=item B<--copy>
|
|
Install a new system by copying files recursively from the given directory. This is significantly faster than using B<--rpmstrap> or B<--debootstrap>, but it does require that you have installed a distribution "slowly" at least once.
|
|
|
|
=item B<--debug>
|
|
Show the commands this script executes as an aid to debugging, along with their output once it has completed.
|
|
|
|
=item B<--debootstrap>
|
|
Use the B<--debootstrap> tool to install the distribution B<--dist>. When specifying B<--debootstrap> you should choose a local mirror with B<--mirror>
|
|
|
|
=item B<--dhcp>
|
|
Specify that the virtual image should use DHCP to obtain its networking information. This option conflicts with B<--ip>.
|
|
|
|
=item B<--dir>
|
|
Specify the root directory beneath which the image should be saved. Subdirectories will be created for each virtual image. If you do not wish to use loopback files instead specify an LVM volume group with B<--lvm>.
|
|
|
|
=item B<--dist>
|
|
Specify the distribution to install, defaults to 'sarge'.
|
|
|
|
=item B<--force>
|
|
Force the script to overwrite any existing swap or disk images. By default the present of an existing image for the same hostname will cause the script to abort.
|
|
|
|
=item B<--fs>
|
|
Specify the filesystem the image should be given. Valid options are 'ext3',
|
|
'xfs', or 'reiserfs'.
|
|
|
|
=item B<--gateway>
|
|
Specify the gateway address for the virtual image, only useful if DHCP is not used.
|
|
|
|
=item B<--help>
|
|
Show the brief help information.
|
|
|
|
=item B<--hostname>
|
|
Set the hostname of the new instance. B<Note> this should ideally be a fully qualified hostname, sine several of the supported distributions will expect a domain name to be present.
|
|
|
|
=item B<--ide>
|
|
Use IDE style device names for the virtual devices.
|
|
|
|
=item B<--image>
|
|
Specify whether to use B<sparse> or B<full> images for the disk and swap volumes.
|
|
|
|
=item B<--initrd>
|
|
Specify the initial ramdisk to be used by the booting Xen instance.
|
|
|
|
=item B<--ip>
|
|
Set the IP address for the virtual image. Conflicts with B<--dhcp>. This argument may be specified multiple times to give your new instance multiple IP addresses.
|
|
|
|
=item B<--kernel>
|
|
Specify the kernel which should be used for booting the new Xen instance.
|
|
|
|
=item B<--manual>
|
|
Read the manual, with examples.
|
|
|
|
=item B<--memory>
|
|
Specify the amount of memory the virtual image should be allocated. Defaults
|
|
to 128Mb.
|
|
|
|
=item B<--mirror>
|
|
Specify the mirror to use when installing distributions with the debootstrap tool. This defaults to http://ftp.us.debian.org/debian
|
|
|
|
=item B<--netmask>
|
|
Set the netmask the virtual image should use.
|
|
|
|
=item B<--passwd>
|
|
Setup a password for the root account of the virtual machine. Note that specifying this will avoid copying user accounts into the new guest image. (i.e. The guest image will only have a B<root> account setup.)
|
|
|
|
=item B<--role>
|
|
Run a single, specific, role-script once the image has been setup.
|
|
|
|
=item B<--size>
|
|
Specify the size of the primary drive to give the virtual image. The size may be suffixed with either Mb, or Gb.
|
|
|
|
=item B<--swap>
|
|
Specify the size of the virtual swap partition to create. The size may be
|
|
suffixed with either Mb, or Gb.
|
|
|
|
=item B<--tar>
|
|
Install a new system by untarring the given file. This is significantly faster than using B<--rpmstrap> or B<--debootstrap>, but it does require that you have installed a distribution "slowly" at least once.
|
|
|
|
=item B<--version>
|
|
Show the version number and exit.
|
|
|
|
=item B<--lvm>
|
|
Specify the LVM volume group to store images within. If you wish to use loopback files instead please specify an output directory with B<--dir>. LVM and loopback files conflict with each other; choose only one.
|
|
|
|
=back
|
|
|
|
=cut
|
|
|
|
=head1 NOTES
|
|
|
|
This script is a simple wrapper around three external tools
|
|
|
|
=over 8
|
|
|
|
=item B<xt-install-image>
|
|
Install a new distribution.
|
|
|
|
=item B<xt-customize-image>
|
|
Run a collection of hook scripts to customise the freshly installed system.
|
|
|
|
=item B<xt-create-xen-config>
|
|
Create a configuration file in /etc/xen so that xm can create the new image.
|
|
|
|
=back
|
|
|
|
The result of invoking these three scripts, and some minor glue between
|
|
them is a simple means of creating new Xen guest domains.
|
|
|
|
=cut
|
|
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
xen-create-image is a simple script which allows you to create new
|
|
Xen instances easily. The new image will be given two volumes. These
|
|
volumes will be stored upon the host as either loopback files, or
|
|
LVM logical volumes:
|
|
|
|
1. An image for the systems root disk.
|
|
2. An image for the systems swap device.
|
|
|
|
The new virtual installations will be configured with networking,
|
|
have OpenSSH installed upon it, and have most of its basic files
|
|
setup correctly.
|
|
|
|
=cut
|
|
|
|
=head1 CONFIGURATION
|
|
|
|
To reduce the length of the command line each of the supported options
|
|
may be specified inside a configuration file.
|
|
|
|
The global configuration file read for options is:
|
|
|
|
/etc/xen-tools/xen-tools.conf
|
|
|
|
The configuration file may contain comments which begin with the
|
|
hash '#' character. Otherwise the format is 'key = value'.
|
|
|
|
A sample configuration file would look like this:
|
|
|
|
=for example begin
|
|
|
|
#
|
|
# Output directory. Images are stored beneath this directory, one
|
|
# subdirectory per hostname.
|
|
#
|
|
dir = /home/xen
|
|
|
|
#
|
|
# LVM users should disable the 'dir' setting above, and instead
|
|
# specify the name of the volume group to use.
|
|
#
|
|
# lvm = myvolume
|
|
|
|
#
|
|
# Disk and Sizing options.
|
|
#
|
|
size = 2Gb # Disk image size.
|
|
image = full # Allocate the full disk size immediately.
|
|
memory = 128Mb # Memory size
|
|
swap = 128Mb # Swap size
|
|
fs = ext3 # use EXT3 filesystems
|
|
dist = sarge # Default distribution to install.
|
|
|
|
#
|
|
# Kernel options.
|
|
#
|
|
initrd = /boot/initrd.img-2.6.16-2-xen-686
|
|
kernel = /boot/vmlinuz-2.6.16-2-xen-686
|
|
|
|
#
|
|
# Networking options.
|
|
#
|
|
gateway = 192.168.1.1
|
|
netmask = 255.255.255.0
|
|
|
|
=for example end
|
|
|
|
Using this configuration file a new image may be created with the
|
|
following command:
|
|
|
|
xen-create-image --hostname=vm03.my.flat --ip=192.168.1.201
|
|
|
|
This makes use of loopback images stored beneath B</home/xen> and
|
|
will be installed via the B<--debootstrap> command.
|
|
|
|
=cut
|
|
|
|
|
|
=head1 XEN CONFIGURATION FILE
|
|
|
|
Once a new image has been created an appropriate configuration file
|
|
for Xen will be saved in the directory B</etc/xen>.
|
|
|
|
The configuration file is built up using the template file
|
|
B</etc/xen-tools/xm.tmpl> - which is a file processed via
|
|
the Text::Template perl module.
|
|
|
|
If you wish to modify the files which are generated please make your
|
|
changes to that input file.
|
|
|
|
=cut
|
|
|
|
|
|
=head1 LOOPBACK EXAMPLES
|
|
|
|
The following will create a 2Gb disk image, along with a 128Mb
|
|
swap file with Debian Sarge setup and running via DHCP.
|
|
|
|
xen-create-image --size=2Gb --swap=128Mb --dhcp \
|
|
--dir=/home/xen --hostname=vm01.my.flat
|
|
|
|
This next example sets up a host which has the name 'vm02.my.flat' and
|
|
IP address 192.168.1.200, with the gateway address of 192.168.1.1
|
|
|
|
xen-create-image --size=2Gb --swap=128Mb \
|
|
--ip=192.168.1.200 --netmask=255.255.255.0
|
|
--gateway=192.168.1.1 \
|
|
--dir=/home/xen --hostname=vm02.my.flat
|
|
|
|
The directory specified for the output will be used to store the files
|
|
which are produced. To avoid clutter each host will have its images
|
|
stored beneath the specified directory, named after the hostname.
|
|
|
|
For example the images created above will be stored as:
|
|
|
|
$dir/domains/vm01.my.flat/
|
|
$dir/domains/vm01.my.flat/disk.img
|
|
$dir/domains/vm01.my.flat/swap.img
|
|
|
|
$dir/domains/vm02.my.flat/
|
|
$dir/domains/vm02.my.flat/disk.img
|
|
$dir/domains/vm02.my.flat/swap.img
|
|
|
|
The '/domains/' subdirectory will be created if necessary.
|
|
|
|
=cut
|
|
|
|
|
|
=head1 LVM EXAMPLES
|
|
|
|
If you wish to use an LVM volume group instead of a pair of loopback
|
|
images as shown above you can instead use the B<--lvm> argument to
|
|
specify one.
|
|
|
|
xen-create-image --size=2Gb --swap=128Mb --dhcp \
|
|
--lvm=myvolumegroup --hostname=vm01.my.flat
|
|
|
|
The given volume group will have two new logical volumes created within it:
|
|
|
|
${hostname}-swap
|
|
${hostname}-disk
|
|
|
|
This images can be mounted, as you would expect, by a command like this:
|
|
|
|
mkdir /mnt/foo
|
|
mount /dev/myvolumegroup/vm01.my.flat-disk /mnt/foo
|
|
|
|
=cut
|
|
|
|
|
|
=head1 INSTALLATION METHODS
|
|
|
|
The new guest images may be installed in several different ways:
|
|
|
|
1. With the B<debootstrap> command.
|
|
2. With the B<rpmstrap> command.
|
|
3. By copying an existing installation.
|
|
4. By untarring a file containing a previous installation.
|
|
|
|
These different methods can be selected by either the command line
|
|
arguments, or settings in the configuration file.
|
|
|
|
The default option would be to install new systems with debootstrap.
|
|
This is mostly reliable, but can be quite slow even with caching.
|
|
|
|
However after your first installation you have the option of tarring
|
|
up the new system, and specifying that this tarfile should be used
|
|
for future installations of the same distribution.
|
|
|
|
Similarly if you were to mount an existing installation of a given
|
|
distribution you could simply copy that filesystem into the new image
|
|
with the B<--copy> option.
|
|
|
|
=cut
|
|
|
|
=head1 DEBOOTSTRAP CACHING
|
|
|
|
When installing new systems with the debootstrap tool there is
|
|
a fair amount of network overhead.
|
|
|
|
To minimize this the .deb files which are downloaded into the
|
|
new instance are cached by default upon the host, in the directory
|
|
/var/cache/apt/archives.
|
|
|
|
When a new image is created these packages are copied into the new
|
|
image - before the debootstrap process runs - this should help avoid
|
|
expensive network reading.
|
|
|
|
If you wish to clean the cache upon the host you may do so with
|
|
apt-get, as you'd expect:
|
|
|
|
apt-get clean
|
|
|
|
(This feature can be disabled with the command line flag B<--cache=no>,
|
|
or by the matching setting in the configuration file.)
|
|
|
|
=cut
|
|
|
|
|
|
=head1 ROLES
|
|
|
|
Each supported distribution has a hook directory which has been
|
|
described already. If that directory contains the subdirectory 'role.d'
|
|
then roles support is available.
|
|
|
|
Currently there are some roles scripts included for the Debian
|
|
distribution, these are intended primarily as examples:
|
|
|
|
=over 8
|
|
|
|
=item builder
|
|
Setup the new virtual images with commonly used packages for rebuilding Debian packages from their source.
|
|
|
|
=item gdm
|
|
Install an X11 server, using VNC and GDM
|
|
|
|
=item minimal
|
|
Customise the generated images to remove some packages.
|
|
|
|
=item xdm
|
|
Install an X11 server, using VNC and XDM
|
|
|
|
=back
|
|
|
|
If you'd like to include your own role scripts you'll need to
|
|
create $dist.d/role.d/foo - then specify "--role=foo" when you're
|
|
creating your new instance.
|
|
|
|
=cut
|
|
|
|
|
|
|
|
=head1 THE SKELETON DIRECTORY
|
|
|
|
Any files present in the directory /etc/xen-tools/skel will be copied
|
|
across to each new guest image. The role of this directory is analogous
|
|
to the /etc/skel directory.
|
|
|
|
A typical use for this would be to copy a public key across to each
|
|
new system. You could do this by running:
|
|
|
|
=for example start
|
|
|
|
mkdir -p /etc/xen-tools/skel/root/.ssh
|
|
chmod -R 700 /etc/xen-tools/skel/root
|
|
cp /root/.ssh/id_rsa.pub /etc/xen-tools/skel/root/.ssh/authorized_keys2
|
|
chmod 644 /etc/xen-tools/skel/root/.ssh/authorized_keys2
|
|
|
|
=for example cut
|
|
|
|
|
|
=head1 AUTHOR
|
|
|
|
Steve
|
|
--
|
|
http://www.steve.org.uk/
|
|
|
|
$Id: xen-create-image,v 1.49 2006-06-25 20:17:39 steve Exp $
|
|
|
|
=cut
|
|
|
|
=head1 LICENSE
|
|
|
|
Copyright (c) 2005-2006 by Steve Kemp. 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 File::Temp qw/ tempdir /;
|
|
use Getopt::Long;
|
|
use Pod::Usage;
|
|
|
|
|
|
#
|
|
# Configuration values read initially from the global configuration
|
|
# file, then optionally overridden by the command line.
|
|
#
|
|
my %CONFIG;
|
|
|
|
#
|
|
# Global variable containing the temporary file where our image
|
|
# is mounted for installation purposes.
|
|
#
|
|
# Why is this here?
|
|
#
|
|
# Well it makes sure that the magic "END" section can unmount it
|
|
# if there are errors.
|
|
#
|
|
#
|
|
my $MOUNT_POINT = undef;
|
|
|
|
|
|
#
|
|
# Release number.
|
|
#
|
|
my $RELEASE = '2.1';
|
|
|
|
|
|
|
|
|
|
|
|
#
|
|
# Check the environment
|
|
#
|
|
checkSystem();
|
|
|
|
|
|
#
|
|
# Setup default options.
|
|
#
|
|
setupDefaultOptions();
|
|
|
|
|
|
#
|
|
# Read the global configuration file if it exists.
|
|
#
|
|
if ( -e "/etc/xen-tools/xen-tools.conf" )
|
|
{
|
|
readConfigurationFile( "/etc/xen-tools/xen-tools.conf" );
|
|
}
|
|
|
|
|
|
#
|
|
# Parse the command line arguments.
|
|
#
|
|
parseCommandLineArguments();
|
|
|
|
|
|
#
|
|
# Ensure we're started by root at this point. This is required
|
|
# to make sure we can create new LVM volumes, or mount loopback images.
|
|
#
|
|
testRootUser();
|
|
|
|
|
|
#
|
|
# Check our arguments
|
|
#
|
|
checkArguments();
|
|
|
|
|
|
#
|
|
# Check we have installed binaries.
|
|
#
|
|
checkFilesPresent();
|
|
|
|
|
|
#
|
|
# Show a summery of what we're going to do.
|
|
#
|
|
showSummery();
|
|
|
|
|
|
|
|
#
|
|
# Create and format the images if we're using loopback filesystems.
|
|
#
|
|
if ( $CONFIG{'dir'} )
|
|
{
|
|
createLoopbackImages();
|
|
}
|
|
|
|
#
|
|
# Create and format the LVM partitions if we're using LVM.
|
|
#
|
|
if ( $CONFIG{'lvm'} )
|
|
{
|
|
createLVMBits();
|
|
}
|
|
|
|
|
|
#
|
|
# Mount the image.
|
|
#
|
|
mountImage();
|
|
|
|
|
|
#
|
|
# Call xt-install-image to do the install.
|
|
#
|
|
installSystem();
|
|
|
|
|
|
#
|
|
# If that worked call xt-customise-image to setup networking, and
|
|
# run distro-specific hooks.
|
|
#
|
|
runCustomisationHooks();
|
|
|
|
#
|
|
# If the user specified role script then run it
|
|
#
|
|
runRoleScript();
|
|
|
|
|
|
#
|
|
# Create the Xen configuration file.
|
|
#
|
|
runXenConfigCreation();
|
|
|
|
#
|
|
# Setup the password if the user wanted that.
|
|
#
|
|
setupRootPassword() if ( $CONFIG{'passwd'} );
|
|
|
|
|
|
#
|
|
# Report success.
|
|
#
|
|
print "All done\n";
|
|
|
|
|
|
#
|
|
# If we're supposed to start the new instance do so - note here we
|
|
# have to unmount the image first.
|
|
#
|
|
if ( $CONFIG{'boot'} )
|
|
{
|
|
#
|
|
# Unmount.
|
|
#
|
|
runCommand( "umount $MOUNT_POINT" );
|
|
$MOUNT_POINT = undef;
|
|
|
|
#
|
|
# Start the image
|
|
#
|
|
my $pid = fork();
|
|
if ( $pid )
|
|
{
|
|
# Parent.
|
|
exit;
|
|
}
|
|
else
|
|
{
|
|
# Child.
|
|
system( "$CONFIG{'xm'} create $CONFIG{'hostname'}.cfg >/dev/null 2>/dev/null" );
|
|
}
|
|
}
|
|
|
|
|
|
#
|
|
# Finished.
|
|
#
|
|
exit;
|
|
|
|
|
|
|
|
=head2 checkSystem
|
|
|
|
Test that this system is fully setup for the new xen-create-image
|
|
script.
|
|
|
|
This means that the two binaries xt-install-image and xt-customize-image
|
|
are present.
|
|
|
|
=cut
|
|
|
|
sub checkSystem
|
|
{
|
|
if ( ! -x "/usr/bin/xt-customize-image" )
|
|
{
|
|
print "The script 'xt-customize-image' was not found.";
|
|
print "\nAborting\n\n";
|
|
}
|
|
if ( ! -x "/usr/bin/xt-install-image" )
|
|
{
|
|
print "The script 'xt-install-image' was not found.";
|
|
print "\nAborting\n\n";
|
|
}
|
|
}
|
|
|
|
|
|
|
|
=head2 setupDefaultOptions
|
|
|
|
Setup the default options we'd expect into our global CONFIG hash.
|
|
|
|
=cut
|
|
|
|
sub setupDefaultOptions
|
|
{
|
|
|
|
#
|
|
# Paths and files.
|
|
#
|
|
$CONFIG{'dir'} = '';
|
|
$CONFIG{'xm'} = '/usr/sbin/xm';
|
|
$CONFIG{'kernel'} = '/boot/vmlinuz-2.6.16-1-xen-686';
|
|
$CONFIG{'initrd'} = '/boot/initrd.img-2.6.16-1-xen-686';
|
|
|
|
#
|
|
# Sizing options.
|
|
#
|
|
$CONFIG{'memory'} = '96Mb';
|
|
$CONFIG{'size'} = '2000Mb';
|
|
$CONFIG{'swap'} = '128M';
|
|
$CONFIG{'cache'} = 'yes';
|
|
$CONFIG{'image'} = 'sparse';
|
|
|
|
#
|
|
# Misc. options.
|
|
#
|
|
$CONFIG{'mirror'} = 'http://ftp.us.debian.org/debian';
|
|
$CONFIG{'dist'} = 'sarge';
|
|
$CONFIG{'fs'} = 'ext3';
|
|
$CONFIG{'force'} = 0;
|
|
|
|
#
|
|
# Installation methods
|
|
#
|
|
$CONFIG{'rpmstrap'} = 0;
|
|
$CONFIG{'debootstrap'} = 0;
|
|
$CONFIG{'copy'} = '';
|
|
$CONFIG{'tar'} = '';
|
|
|
|
|
|
#
|
|
# The program to run to create a filesystem.
|
|
#
|
|
# NOTE: These commands end in a trailing slash. The last parameter is
|
|
# added as the loopback file/LVM volume to create the fs on....
|
|
#
|
|
$CONFIG{'make_fs_ext3'} = '/sbin/mkfs.ext3 -F ';
|
|
$CONFIG{'make_fs_xfs'} = '/sbin/mkfs.xfs -d name=';
|
|
$CONFIG{'make_fs_reiserfs'} = '/sbin/mkfs.reiserfs -f -q ';
|
|
|
|
#
|
|
# Flags to pass to "mount" to mount our image.
|
|
#
|
|
# NOTE: Kinda redundent and may go away since '-t auto' should do
|
|
# the right thing.
|
|
#
|
|
$CONFIG{'mount_fs_ext3'} = '-t ext3';
|
|
$CONFIG{'mount_fs_xfs'} = '-t xfs';
|
|
$CONFIG{'mount_fs_reiserfs'} = '-t reiserfs';
|
|
}
|
|
|
|
|
|
|
|
|
|
=head2 readConfigurationFile
|
|
|
|
read the global configuration file /etc/xen-tools/xen-tools.conf
|
|
|
|
=cut
|
|
|
|
sub readConfigurationFile
|
|
{
|
|
my ($file) = ( @_ );
|
|
my $line = "";
|
|
|
|
|
|
open( FILE, "<", $file ) or die "Cannot read file '$file' - $!";
|
|
|
|
while (defined($line = <FILE>) )
|
|
{
|
|
chomp $line;
|
|
if ($line =~ s/\\$//)
|
|
{
|
|
$line .= <FILE>;
|
|
redo unless eof(FILE);
|
|
}
|
|
|
|
# Skip lines beginning with comments
|
|
next if ( $line =~ /^([ \t]*)\#/ );
|
|
|
|
# Skip blank lines
|
|
next if ( length( $line ) < 1 );
|
|
|
|
# Strip trailing comments.
|
|
if ( $line =~ /(.*)\#(.*)/ )
|
|
{
|
|
$line = $1;
|
|
}
|
|
|
|
# Find variable settings
|
|
if ( $line =~ /([^=]+)=([^\n]+)/ )
|
|
{
|
|
my $key = $1;
|
|
my $val = $2;
|
|
|
|
# Strip leading and trailing whitespace.
|
|
$key =~ s/^\s+//;
|
|
$key =~ s/\s+$//;
|
|
$val =~ s/^\s+//;
|
|
$val =~ s/\s+$//;
|
|
|
|
# Store value.
|
|
$CONFIG{ $key } = $val;
|
|
}
|
|
}
|
|
|
|
close( FILE );
|
|
}
|
|
|
|
|
|
|
|
=head2 parseCommandLineArguments
|
|
|
|
Parse the command line arguments this script was given.
|
|
|
|
=cut
|
|
|
|
sub parseCommandLineArguments
|
|
{
|
|
my $HELP = 0;
|
|
my $MANUAL = 0;
|
|
my $VERSION = 0;
|
|
|
|
#
|
|
# Parse options.
|
|
#
|
|
GetOptions(
|
|
# Mandatory
|
|
"dist=s", \$CONFIG{'dist'},
|
|
|
|
# Size options.
|
|
"size=s", \$CONFIG{'size'},
|
|
"swap=s", \$CONFIG{'swap'},
|
|
"image=s", \$CONFIG{'image'},
|
|
"memory=s", \$CONFIG{'memory'},
|
|
|
|
# Locations
|
|
"dir=s", \$CONFIG{'dir'},
|
|
"kernel=s", \$CONFIG{'kernel'},
|
|
"initrd=s", \$CONFIG{'initrd'},
|
|
"mirror=s", \$CONFIG{'mirror'},
|
|
"lvm=s", \$CONFIG{'lvm'},
|
|
|
|
# Networking options
|
|
"dhcp", \$CONFIG{'dhcp'},
|
|
"gateway=s", \$CONFIG{'gateway'},
|
|
"hostname=s", \$CONFIG{'hostname'},
|
|
"ip=s@", \$CONFIG{'ip'},
|
|
"netmask=s", \$CONFIG{'netmask'},
|
|
|
|
# Exclusive
|
|
"copy=s", \$CONFIG{'copy'},
|
|
"debootstrap", \$CONFIG{'debootstrap'},
|
|
"rpmstrap", \$CONFIG{'rpmstrap'},
|
|
"tar=s", \$CONFIG{'tar'},
|
|
|
|
# Misc. options
|
|
"accounts", \$CONFIG{'accounts'},
|
|
"fs", \$CONFIG{'fs'},
|
|
"boot", \$CONFIG{'boot'},
|
|
"cache=s", \$CONFIG{'cache'},
|
|
"ide", \$CONFIG{'ide'},
|
|
"passwd", \$CONFIG{'passwd'},
|
|
"role=s", \$CONFIG{'role'},
|
|
"force", \$CONFIG{'force'},
|
|
|
|
# Help options
|
|
"debug", \$CONFIG{'verbose'},
|
|
"help", \$HELP,
|
|
"manual", \$MANUAL,
|
|
"verbose", \$CONFIG{'verbose'},
|
|
"version", \$VERSION
|
|
);
|
|
|
|
pod2usage(1) if $HELP;
|
|
pod2usage(-verbose => 2 ) if $MANUAL;
|
|
|
|
|
|
if ( $VERSION )
|
|
{
|
|
my $REVISION = '$Revision: 1.49 $';
|
|
|
|
if ( $REVISION =~ /1.([0-9.]+) / )
|
|
{
|
|
$REVISION = $1;
|
|
}
|
|
|
|
print "xen-create-image release $RELEASE - CVS: $REVISION\n";
|
|
exit;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
=head2 testRootUser
|
|
|
|
Make sure this script is being run by a user with UID 0.
|
|
|
|
=cut
|
|
|
|
sub testRootUser
|
|
{
|
|
if ( $EFFECTIVE_USER_ID != 0 )
|
|
{
|
|
print <<E_O_ROOT;
|
|
|
|
In order to use this script you must be running with root privileges.
|
|
|
|
(This is necessary to mount the disk images which are created.)
|
|
|
|
E_O_ROOT
|
|
|
|
exit;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
=head2 checkArguments
|
|
|
|
Test that the command line arguments we were given make sense.
|
|
|
|
=cut
|
|
|
|
sub checkArguments
|
|
{
|
|
#
|
|
# We require a distribution name.
|
|
#
|
|
if ( ! defined( $CONFIG{'dist'} ) )
|
|
{
|
|
print "The '--dist' argument is mandatory\n";
|
|
exit 1;
|
|
}
|
|
|
|
#
|
|
# We require a hostname.
|
|
#
|
|
if ( ! defined( $CONFIG{'hostname'} ) )
|
|
{
|
|
print "The '--hostname' argument is mandatory.\n";
|
|
exit 1;
|
|
}
|
|
|
|
#
|
|
# FAKE:
|
|
#
|
|
if ( $CONFIG{'dist'} eq 'fedora-core4' )
|
|
{
|
|
$CONFIG{'dist'} = 'stentz';
|
|
}
|
|
|
|
#
|
|
#
|
|
# Test that the distribution name we've been given
|
|
# to configure has a collection of hook scripts.
|
|
#
|
|
# If there are no scripts then we clearly cannot
|
|
# customise it!
|
|
#
|
|
my $dir = "/usr/lib/xen-tools/" . $CONFIG{'dist'} . ".d";
|
|
|
|
if ( ! -d $dir )
|
|
{
|
|
print <<E_OR;
|
|
|
|
We're trying to configure an installation of $CONFIG{'dist'} in
|
|
$CONFIG{'dir'} - but there is no hook directory for us to use.
|
|
|
|
This means we don't know how to configure this installation.
|
|
|
|
We'd expect the hook directory to be : $dir
|
|
|
|
Aborting.
|
|
E_OR
|
|
exit 1;
|
|
}
|
|
|
|
|
|
#
|
|
# Image must be 'sparse' or 'full'.
|
|
#
|
|
if ( defined( $CONFIG{'image'} ) )
|
|
{
|
|
if ( ( $CONFIG{'image'} ne "sparse" ) &&
|
|
( $CONFIG{'image'} ne "full" ) )
|
|
{
|
|
print "Image type must be 'sparse' or 'full'\n";
|
|
exit;
|
|
}
|
|
}
|
|
|
|
#
|
|
# If using LVM images cannot be sparse
|
|
#
|
|
if ( $CONFIG{'lvm'} )
|
|
{
|
|
$CONFIG{'image'} = "full";
|
|
print "Switching to 'full' images rather than 'sparse', since you're using LVM\n";
|
|
}
|
|
|
|
|
|
#
|
|
# The kernel + initrd images should exist.
|
|
#
|
|
if ( defined( $CONFIG{'kernel'} ) &&
|
|
length( $CONFIG{'kernel'} ) &&
|
|
! -e $CONFIG{'kernel'} )
|
|
{
|
|
print "The kernel image we're trying to use does not exist.\n";
|
|
print "The image is - $CONFIG{'kernel'}\n";
|
|
print "Aborting\n";
|
|
exit;
|
|
}
|
|
|
|
if ( defined( $CONFIG{'initrd'} ) &&
|
|
length( $CONFIG{'initrd'} ) &&
|
|
! -e $CONFIG{'initrd'} )
|
|
{
|
|
print "The initial ramdisk we're trying to use does not exist.\n";
|
|
print "The image is - $CONFIG{'initrd'}\n";
|
|
print "Aborting\n";
|
|
exit;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
=head2 checkFilesPresent
|
|
|
|
Check that we have some required files present.
|
|
|
|
=cut
|
|
|
|
sub checkFilesPresent
|
|
{
|
|
#
|
|
# Files we demand are present in all cases.
|
|
#
|
|
my @required = qw ( /usr/sbin/debootstrap /bin/dd /bin/mount /sbin/mkswap );
|
|
|
|
foreach my $file ( @required )
|
|
{
|
|
if ( ! -x $file )
|
|
{
|
|
print "The following binary is required to run this tool\n";
|
|
print "\t$file\n";
|
|
exit;
|
|
}
|
|
}
|
|
|
|
#
|
|
# LVM specific binaries
|
|
#
|
|
if ( $CONFIG{'lvm'} )
|
|
{
|
|
my @lvm = qw ( /sbin/lvcreate );
|
|
|
|
foreach my $file ( @lvm )
|
|
{
|
|
if ( ! -x $file )
|
|
{
|
|
print "The following binary is required to run this tool\n";
|
|
print "\t$file\n";
|
|
print "(This is only required for LVM volumes, which you've selected)\n";
|
|
exit;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
=head2 showSummery
|
|
|
|
Show the user a summery of what is going to be created for them
|
|
|
|
=cut
|
|
|
|
sub showSummery
|
|
{
|
|
#
|
|
# Show the user what to expect.
|
|
#
|
|
print "\nGeneral Infomation\n";
|
|
print "--------------------\n";
|
|
print "Hostname : $CONFIG{'hostname'}\n";
|
|
print "Distribution : $CONFIG{'dist'}\n";
|
|
print "Fileystem Type : $CONFIG{'fs'}\n";
|
|
|
|
print "\nSize Information\n";
|
|
print "----------------\n";
|
|
print "Image size : $CONFIG{'size'}\n";
|
|
print "Swap size : $CONFIG{'swap'}\n" unless ( $CONFIG{'no-swap' } );
|
|
print "Image type : $CONFIG{'image'}\n";
|
|
print "Memory size : $CONFIG{'memory'}\n";
|
|
print "Kernel path : $CONFIG{'kernel'}\n";
|
|
print "initrd path : $CONFIG{'initrd'}\n";
|
|
|
|
print "\nNetworking Information\n";
|
|
print "----------------------\n";
|
|
|
|
#
|
|
# Show each IP address added.
|
|
#
|
|
my $ips = $CONFIG{'ip'};
|
|
my $count = 1;
|
|
|
|
foreach my $i ( @$ips )
|
|
{
|
|
print "IP Address $count : $i\n";
|
|
$count += 1;
|
|
}
|
|
|
|
$CONFIG{'dhcp'} && print "IP Address : DHCP\n";
|
|
$CONFIG{'netmask'} && print "Netmask : $CONFIG{'netmask'}\n";
|
|
$CONFIG{'gateway'} && print "Gateway : $CONFIG{'gateway'}\n";
|
|
print "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
=head2 createLoopbackImages
|
|
|
|
Create the two images "swap.img" and "disk.img" in the directory
|
|
we've been given.
|
|
|
|
We also will call the filesystem creation routine to make sure we
|
|
have a valid filesystem.
|
|
|
|
=cut
|
|
|
|
sub createLoopbackImages
|
|
{
|
|
#
|
|
# The two files we need to test.
|
|
#
|
|
my $disk = $CONFIG{'dir'} . '/domains/' . $CONFIG{'hostname'} . "/disk.img" ;
|
|
my $swap = $CONFIG{'dir'} . '/domains/' . $CONFIG{'hostname'} . "/swap.img" ;
|
|
|
|
#
|
|
# Make sure we have the relevant output directory.
|
|
#
|
|
if ( ! -d $CONFIG{'dir'} . "/domains/" )
|
|
{
|
|
mkdir $CONFIG{'dir'} . '/domains', 0777
|
|
|| die "Cannot create $CONFIG{'dir'}/domains - $!";
|
|
}
|
|
if ( ! -d $CONFIG{'dir'} . "/domains/" . $CONFIG{'hostname'} )
|
|
{
|
|
mkdir $CONFIG{'dir'}. '/domains/' . $CONFIG{'hostname'}, 0777
|
|
|| die "Cannot create $CONFIG{'dir'}/domains/$CONFIG{'hostname'} - $!" ;
|
|
}
|
|
|
|
|
|
#
|
|
# Only proceed overwritting if we have --force specified.
|
|
#
|
|
if ( ( -e $disk ) && ! $CONFIG{'force'} )
|
|
{
|
|
print "The disk image already exists. Aborting.\n";
|
|
print "Specify '--force' to overwrite, or remove the following file\n";
|
|
print $disk . "\n";
|
|
exit;
|
|
}
|
|
if ( ( -e $swap ) && ! $CONFIG{'force'} )
|
|
{
|
|
print "The swap image already exists. Aborting.\n";
|
|
print "Specify '--force' to overwrite, or remove the following file\n";
|
|
print $swap . "\n";
|
|
exit;
|
|
}
|
|
|
|
|
|
#
|
|
# Modify the sizes to something reasonable
|
|
#
|
|
my $disk_size = $CONFIG{'size'};
|
|
my $swap_size = $CONFIG{'swap'};
|
|
|
|
#
|
|
# Convert Gb -> Mb for the disk image size, and swap size.
|
|
#
|
|
if ( $disk_size =~ /^(\d+)Gb*$/i )
|
|
{
|
|
$disk_size = $1 * 1024 . "M";
|
|
}
|
|
if ( $swap_size =~ /^(\d+)Gb*$/i )
|
|
{
|
|
$swap_size = $1 * 1024 . "M";
|
|
}
|
|
|
|
#
|
|
# Final adjustments to sizing.
|
|
#
|
|
$disk_size =~ s/Mb*$/k/i;
|
|
if ( $swap_size =~ /^(\d+)Mb*$/i )
|
|
{
|
|
$swap_size = $1;
|
|
}
|
|
|
|
|
|
# Use dd to create the swap
|
|
#
|
|
print "\nCreating swap image: $swap\n";
|
|
my $swap_cmd = "/bin/dd if=/dev/zero of=$swap bs=1024k count=$swap_size";
|
|
runCommand( $swap_cmd );
|
|
print "Done\n";
|
|
|
|
|
|
#
|
|
# Use dd to create the disk image.
|
|
#
|
|
print "\nCreating disk image: $disk\n";
|
|
my $image_cmd;
|
|
if ( $CONFIG{'image'} eq "sparse" )
|
|
{
|
|
$CONFIG{'verbose'} && print "Creating sparse image\n";
|
|
$image_cmd = "/bin/dd if=/dev/zero of=$disk bs=$disk_size count=0 seek=1024";
|
|
}
|
|
else
|
|
{
|
|
$CONFIG{'verbose'} && print "Creating full-sized image\n";
|
|
$image_cmd = "/bin/dd if=/dev/zero of=$disk bs=$disk_size count=1024";
|
|
}
|
|
|
|
runCommand( $image_cmd );
|
|
print "Done\n";
|
|
|
|
|
|
#
|
|
# Finally create the filesystem + swap
|
|
#
|
|
createFilesystem( $disk );
|
|
createSwap( $swap );
|
|
}
|
|
|
|
|
|
=head2 createLVMBits
|
|
|
|
This function is responsible for creating two new logical volumes within
|
|
a given LVM volume group.
|
|
|
|
=cut
|
|
|
|
sub createLVMBits
|
|
{
|
|
# The two volumes we will need to use..
|
|
my $disk = $CONFIG{'hostname'} . "-disk" ;
|
|
my $swap = $CONFIG{'hostname'} . "-swap" ;
|
|
|
|
#
|
|
# Check whether the disk volume exists already, and if so abort
|
|
# unless '--force' is specified.
|
|
#
|
|
my $lvm_disk = "/dev/$CONFIG{'lvm'}/$CONFIG{'hostname'}-disk";
|
|
if ( -e $lvm_disk )
|
|
{
|
|
# Delete if forcing
|
|
if ( $CONFIG{'force'} )
|
|
{
|
|
runCommand( "lvremove --force $lvm_disk" );
|
|
}
|
|
else
|
|
{
|
|
print "The LVM disk image already exists. Aborting.\n";
|
|
print "Specify '--force' to delete and recreate\n";
|
|
exit;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#
|
|
# Check whether the swap volume exists already, and if so abort
|
|
# unless '--force' is specified.
|
|
#
|
|
my $lvm_swap = "/dev/$CONFIG{'lvm'}/$CONFIG{'hostname'}-swap";
|
|
if ( -e $lvm_swap )
|
|
{
|
|
# Delete if forcing
|
|
if ( $CONFIG{'force'} )
|
|
{
|
|
runCommand( "lvremove --force $lvm_swap" );
|
|
}
|
|
else
|
|
{
|
|
print "The LVM swap image already exists. Aborting.\n";
|
|
print "Specify '--force' to delete and recreate\n";
|
|
exit;
|
|
}
|
|
}
|
|
|
|
|
|
#
|
|
# Modify the sizes to something reasonable
|
|
#
|
|
my $disk_size = $CONFIG{'size'};
|
|
my $swap_size = $CONFIG{'swap'};
|
|
|
|
#
|
|
# Convert Gb -> Mb for the disk image size, and swap size.
|
|
#
|
|
if ( $disk_size =~ /^(\d+)Gb*$/i )
|
|
{
|
|
$disk_size = $1 * 1024 . "M";
|
|
}
|
|
if ( $swap_size =~ /^(\d+)Gb*$/i )
|
|
{
|
|
$swap_size = $1 * 1024 . "M";
|
|
}
|
|
|
|
#
|
|
# Final adjustments to sizing.
|
|
#
|
|
$disk_size =~ s/Mb*$/k/i;
|
|
if ( $swap_size =~ /^(\d+)Mb*$/i )
|
|
{
|
|
$swap_size = $1;
|
|
}
|
|
|
|
|
|
#
|
|
# The commands to create the volumes.
|
|
#
|
|
my $disk_cmd = "/sbin/lvcreate $CONFIG{'lvm'} -L $CONFIG{'size'}M -n $disk";
|
|
my $swap_cmd = "/sbin/lvcreate $CONFIG{'lvm'} -L $CONFIG{'swap'} -n $swap";
|
|
|
|
#
|
|
# Create the volumes
|
|
#
|
|
runCommand( $disk_cmd );
|
|
runCommand( $swap_cmd );
|
|
|
|
#
|
|
# Initialise the partitions with the relevant filesystem.
|
|
#
|
|
createFilesystem( "/dev/$CONFIG{'lvm'}/$CONFIG{'hostname'}-disk" );
|
|
createSwap( "/dev/$CONFIG{'lvm'}/$CONFIG{'hostname'}-swap" );
|
|
}
|
|
|
|
|
|
|
|
=head2 createFilesystem
|
|
|
|
Format the given image in the users choice of filesystem.
|
|
|
|
=cut
|
|
|
|
sub createFilesystem
|
|
{
|
|
my( $image ) = ( @_ );
|
|
|
|
#
|
|
# We have the filesystem the user wanted, make sure that the
|
|
# binary exists.
|
|
#
|
|
my $command = $CONFIG{ "make_fs_" . $CONFIG{'fs'} };
|
|
|
|
#
|
|
# Split the command into "binary" + "args". Make sure that
|
|
# the binary exists and is executable.
|
|
#
|
|
if ( $command =~ /([^ ]+) (.*)$/ )
|
|
{
|
|
my $binary = $1;
|
|
my $args = $2;
|
|
|
|
if ( ! -x $binary )
|
|
{
|
|
print "The binary '$binary' required to create the filesystem $CONFIG{'fs'} is missing\n";
|
|
exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
print "The filesystem creation hash is bogus for filesystem : $CONFIG{'fs'}\n";
|
|
exit;
|
|
}
|
|
|
|
#
|
|
# Special case.
|
|
#
|
|
# If the filesystem is xfs, and the image already existed before
|
|
# so the user is using '--force' we add the '-f' flag to force
|
|
# mkfs.xfs to recreate an image rather than complaining.
|
|
#
|
|
if ( ( $CONFIG{'fs'} eq 'xfs' ) && $CONFIG{'force'} )
|
|
{
|
|
$command .= " -f ";
|
|
}
|
|
|
|
#
|
|
# OK we have the command and the filesystem. Create it.
|
|
#
|
|
print "\nCreating $CONFIG{'fs'} filesystem on $image\n";
|
|
|
|
$command .= $image;
|
|
|
|
runCommand( $command );
|
|
print "Done\n";
|
|
}
|
|
|
|
|
|
|
|
=head2 createSwap
|
|
|
|
Create the swap filesystem on the given device.
|
|
|
|
=cut
|
|
|
|
sub createSwap
|
|
{
|
|
my ( $path ) = ( @_ );
|
|
|
|
runCommand( "/sbin/mkswap $path" );
|
|
}
|
|
|
|
|
|
=head2 mountImage
|
|
|
|
Mount the loopback disk image into a temporary directory.
|
|
|
|
Alternatively mount the relevant LVM volume instead.
|
|
|
|
=cut
|
|
|
|
sub mountImage
|
|
{
|
|
#
|
|
# Determine what we're to mount
|
|
#
|
|
my $image;
|
|
|
|
if ( $CONFIG{'lvm'} )
|
|
{
|
|
$image = "/dev/" . $CONFIG{'lvm'} . "/" . $CONFIG{'hostname'} . '-disk';
|
|
}
|
|
elsif ( $CONFIG{'dir'} )
|
|
{
|
|
$image = $CONFIG{'dir'} . '/domains/' . $CONFIG{'hostname'} . "/disk.img" ;
|
|
}
|
|
else
|
|
{
|
|
print "I don't know what to mount!\n";
|
|
print "Please specify '--dir' or '--lvm'\n";
|
|
exit;
|
|
}
|
|
|
|
|
|
#
|
|
# Create a temporary mount-point to use for the image/volume.
|
|
#
|
|
$MOUNT_POINT = tempdir( CLEANUP => 1 );
|
|
|
|
#
|
|
# Lookup the correct arguments to pass to mount.
|
|
#
|
|
my $mount_cmd;
|
|
my $mount_type = $CONFIG{'mount_fs_' . $CONFIG{'fs'} };
|
|
|
|
#
|
|
# LVM partition
|
|
#
|
|
if ( $CONFIG{'lvm'} )
|
|
{
|
|
$mount_cmd = "mount $mount_type $image $MOUNT_POINT";
|
|
}
|
|
else
|
|
{
|
|
$mount_cmd = "mount $mount_type -o loop $image $MOUNT_POINT";
|
|
}
|
|
runCommand( $mount_cmd );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
=head2 installSystem
|
|
|
|
Install the system, by invoking the xt-install-system script.
|
|
|
|
The script will be given the appropriate arguments from our environment.
|
|
|
|
=cut
|
|
|
|
sub installSystem
|
|
{
|
|
|
|
print "\nInstalling your system with ";
|
|
|
|
#
|
|
# Basic command
|
|
#
|
|
my $cmd = "/usr/bin/xt-install-image --location=$MOUNT_POINT --dist=$CONFIG{'dist'}";
|
|
|
|
#
|
|
# Add on the current cache setting
|
|
#
|
|
$cmd .= " --cache=$CONFIG{'cache'}" if length( $CONFIG{'cache'} );
|
|
|
|
#
|
|
# Installation method
|
|
#
|
|
if ( $CONFIG{'copy'} )
|
|
{
|
|
$cmd .= " --copy=$CONFIG{'copy'}";
|
|
print "copy from $CONFIG{'copy'}\n";
|
|
}
|
|
if ( $CONFIG{'debootstrap'} )
|
|
{
|
|
$cmd .= " --debootstrap";
|
|
$cmd .= " --mirror=$CONFIG{'mirror'}";
|
|
print "debootstrap mirror $CONFIG{'mirror'}\n";
|
|
}
|
|
if ( $CONFIG{'rpmstrap'} )
|
|
{
|
|
$cmd .= " --rpmstrap";
|
|
print "rpmstrap\n";
|
|
}
|
|
if ( $CONFIG{'tar'} )
|
|
{
|
|
$cmd .= " --tar=$CONFIG{'tar'}";
|
|
print "tarfile $CONFIG{'tar'}\n";
|
|
}
|
|
|
|
|
|
#
|
|
# Propogate --verbose
|
|
#
|
|
if ( $CONFIG{'verbose'} )
|
|
{
|
|
$cmd .= " --verbose";
|
|
}
|
|
|
|
runCommand( $cmd );
|
|
|
|
print "Done\n";
|
|
}
|
|
|
|
|
|
|
|
=head2 runCustomisationHooks
|
|
|
|
Run the xt-customise-system script to customize our fresh installation.
|
|
|
|
Before we do this we must pass all the relevent options into our
|
|
environment.
|
|
|
|
=cut
|
|
|
|
sub runCustomisationHooks
|
|
{
|
|
|
|
#
|
|
# Setup the environment for the child processes.
|
|
#
|
|
foreach my $key ( keys %CONFIG )
|
|
{
|
|
if ( defined( $CONFIG{$key} ) )
|
|
{
|
|
$ENV{$key} = $CONFIG{$key};
|
|
}
|
|
}
|
|
|
|
#
|
|
# Now update the environment for each defined IP address.
|
|
# these are handled specially since we use arrays.
|
|
#
|
|
# Remove the value we set above.
|
|
delete $ENV{'ip'};
|
|
|
|
#
|
|
# Setup a seperate ip$count value for each IP address.
|
|
#
|
|
my $ips = $CONFIG{'ip'};
|
|
my $count = 1;
|
|
|
|
foreach my $i ( @$ips )
|
|
{
|
|
$ENV{'ip' . $count } = $i;
|
|
$count += 1;
|
|
}
|
|
$ENV{'ip_count'} = ($count - 1);
|
|
|
|
|
|
#
|
|
# Now show the environment the children get
|
|
#
|
|
if ( $CONFIG{'verbose'} )
|
|
{
|
|
print "Customization Script Environment:\n";
|
|
print "---------------------------------\n";
|
|
foreach my $key ( sort keys %ENV )
|
|
{
|
|
print "\t'" . $key . "' = '" . $ENV{$key} . "'\n";
|
|
}
|
|
}
|
|
|
|
|
|
#
|
|
# Actually run the appropriate hooks
|
|
#
|
|
my $customize = "xt-customize-image --dist=$CONFIG{'dist'} --location=$MOUNT_POINT";
|
|
if ( $CONFIG{'verbose'} )
|
|
{
|
|
$customize .= " --verbose";
|
|
}
|
|
print "\nRunning hooks\n";
|
|
runCommand( $customize );
|
|
print "Done\n";
|
|
}
|
|
|
|
|
|
|
|
|
|
=head2 runRoleScript
|
|
|
|
If the user specified a role for the new instance then execute it.
|
|
|
|
=cut
|
|
|
|
sub runRoleScript
|
|
{
|
|
my $roleDir = '/usr/lib/xen-tools/' . $CONFIG{'dist'} . '.d/role.d/' ;
|
|
my $role = $CONFIG{'role'};
|
|
|
|
if ( !defined( $role ) )
|
|
{
|
|
print "\nNo role script specified. Skipping\n";
|
|
return;
|
|
}
|
|
|
|
my $file = $roleDir . $role;
|
|
|
|
if ( -x $file )
|
|
{
|
|
print "\nRunning role script $file for role '$role'\n";
|
|
}
|
|
else
|
|
{
|
|
print "\nRole script not executable : $file for role '$role'\n";
|
|
print "Ignoring\n";
|
|
return;
|
|
}
|
|
|
|
|
|
#
|
|
# Our environment is already setup because of the call to
|
|
# runCustomisationHooks. So we just need to run the script
|
|
# with the mountpoint.
|
|
#
|
|
runCommand( $file . " " . $MOUNT_POINT );
|
|
|
|
print "Done\n";
|
|
}
|
|
|
|
|
|
|
|
=head2 runXenConfigCreation
|
|
|
|
Create the Xen configuration file.
|
|
|
|
Note that we don't need to do any setup for the environment since
|
|
we did this already before running the hook scripts.
|
|
|
|
=cut
|
|
|
|
sub runXenConfigCreation
|
|
{
|
|
|
|
my $command = '/usr/bin/xt-create-xen-config --output=/etc/xen';
|
|
|
|
print "\nCreating Xen configuration file\n";
|
|
runCommand( $command );
|
|
print "Done\n";
|
|
}
|
|
|
|
|
|
|
|
=head2 setupRootPassword
|
|
|
|
chroot() into the new system and setup the password.
|
|
|
|
=cut
|
|
|
|
sub setupRootPassword
|
|
{
|
|
print "Setting up root password\n";
|
|
|
|
if ( -x $MOUNT_POINT . "/usr/bin/passwd" )
|
|
{
|
|
system( "chroot $MOUNT_POINT /usr/bin/passwd" );
|
|
}
|
|
else
|
|
{
|
|
print "/usr/bin/passwd on the new system doesn't exist...\n";
|
|
}
|
|
}
|
|
|
|
|
|
|
|
=head2 runCommand
|
|
|
|
A utility method to run a system command. We will capture the return
|
|
value and exit if the command files.
|
|
|
|
When running verbosely we will also display any command output once
|
|
it has finished.
|
|
|
|
=cut
|
|
|
|
sub runCommand
|
|
{
|
|
my ( $cmd ) = (@_ );
|
|
|
|
#
|
|
# Header.
|
|
#
|
|
$CONFIG{'verbose'} && print "Executing : $cmd\n";
|
|
|
|
#
|
|
# Hide output unless running with --verbose.
|
|
#
|
|
if ( $CONFIG{'verbose'} )
|
|
{
|
|
#
|
|
# Copy stderr to stdout, so we can see it.
|
|
#
|
|
$cmd .= " 2>&1";
|
|
}
|
|
else
|
|
{
|
|
$cmd .= " >/dev/null 2>/dev/null" ;
|
|
}
|
|
|
|
#
|
|
# Run it.
|
|
#
|
|
my $output = `$cmd`;
|
|
|
|
#
|
|
# All done.
|
|
#
|
|
$CONFIG{'verbose'} && print "Output\n";
|
|
$CONFIG{'verbose'} && print "======\n";
|
|
$CONFIG{'verbose'} && print $output . "\n";
|
|
$CONFIG{'verbose'} && print "Finished : $cmd\n";
|
|
|
|
|
|
if ( $? != 0 )
|
|
{
|
|
print "Running command '$cmd' failed.\n";
|
|
print "Aborting\n";
|
|
exit;
|
|
}
|
|
|
|
|
|
return( $output );
|
|
}
|
|
|
|
|
|
|
|
|
|
=head2 END
|
|
|
|
If we still have the temporary image mounted then make sure
|
|
it is unmounted before we terminate.
|
|
|
|
=cut
|
|
|
|
sub END
|
|
{
|
|
if ( defined( $MOUNT_POINT ) )
|
|
{
|
|
#
|
|
# Run mount to see if this is still mounted.
|
|
#
|
|
my $mount = `/bin/mount`;
|
|
if ( $mount =~ /$MOUNT_POINT/)
|
|
{
|
|
runCommand( "umount $MOUNT_POINT" );
|
|
}
|
|
}
|
|
}
|