1
0
mirror of synced 2026-02-08 00:31:28 +00:00
Files
xen-tools.xen-tools/xen-create-image
steve db45dfebfd 2005-12-18 16:08:13 by steve
Added ReiserFS support from Radu Spineanu
2005-12-18 16:08:13 +00:00

687 lines
13 KiB
Perl
Executable File

#!/usr/bin/perl -w
=head1 NAME
xen-create-image - Create a new Xen instance of Debian Sarge.
=head1 SYNOPSIS
xen-create-image [options]
Help Options:
--help Show this scripts help information.
--manual Read this scripts manual.
Size / General options:
--size Set the size of the primary disk image.
--swap Set the size of the swap partition.
--fs Specify the filesystem type to use.
--memory Setup the amount of memory allocated to the instance.
Networking options:
--dhcp Setup the image to get an IP address via DHCP
--network Setup the network the host is upon.
--gateway Setup the iamge's network gateway.
--broadcast Setup the image's network broadcast address.
--mirror Setup the mirror to use when installing Sarge.
Mandatory options:
--dir Specify where the output images should go.
--hostname Set the images hostname.
=cut
=head1 OPTIONS
=over 8
=item B<--size>
Specify the size of the virtual images primary drive. The size may be
suffixed with either Mb, or Gb.
=item B<--swap>
Specify the size of the virtual images swap partition. The size may be
suffixed with either Mb, or Gb.
=item B<--help>
Show the brief help information.
=item B<--manual>
Read the manual, with examples.
=back
=cut
=head1 EXAMPLE
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
=cut
=head1 DESCRIPTION
This is a reasonably simple script which is more of a cheap hack
than a production tool, however it does appear to work nicely for
my own uses.
The output of this script will be two files for each new instance:
1. One 2Gb file for the installations primary disk image.
2. One 128Mb file for the images swap partition.
Usage:
The initial two arguments '--hostname' and '--dir' are mandatory,
and control the hostname and output directory respectively. Other
parameters are all network based, either give the list as shown above
or use the '--dhcp' option to setup the host with a DHCP allocated
IP address.
'dir' is used to create the output disk images which are referenced
in the Xen configuration file. They will be created as:
$dir/domains/
$dir/domains/$hostname/disk.img
$dir/domains/$hostname/swap.img
The '/domains/' subdirectory, and sub-subdirectory named after the
hostname will be created if necessary.
Basic setup with your hosts file, resolv.conf, etc works just fine.
The image will also have an OpenSSH server installed upon it, and
an appropriate /etc/inittab file created.
You are *strongly* advised to chroot() into the new instance once
it has been setup to prune the users in the /etc/passwd file which
will be copied from your hosts system.
=head1 AUTHOR
Steve
--
http://www.steve.org.uk/
$Id: xen-create-image,v 1.10 2005-12-18 16:08:13 steve Exp $
=cut
=head1 LICENSE
Copyright (c) 2005 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 File::Copy;
use File::Temp qw/ tempdir /;
use Getopt::Long;
use Pod::Usage;
#
# Options set on the command line.
#
my $HOSTNAME; # Mandatory.
my $DIR; # Mandatory.
#
# Either *all* the relevent networking options must be setup, or
# DHCP must be selected.
#
#
my $IP; # set with '--ip=dd.dd.dd.dd'
my $GATEWAY; # set with '--gateway=dd.dd.dd.dd'
my $NETMASK="255.255.255.0"; # set with '--mask=dd.dd.dd.dd'
my $BROADCAST="192.168.1.255"; # set with '--broadcase=ddd.dd.dd.d'
my $NETWORK="192.168.1.0"; # set with '--network=dd.dd.dd.dd'
my $DHCP=0; # This setting overides the other network options
my $MIRROR="http://ftp.us.debian.org/debian"; # set with '--mirror=http://www.etc.com"'
my $SIZE="2000M"; # set with '--size=1000M[b] / 1G[b]"
my $SWAP_SIZE="128M"; # set with '--swapsize=... like --size"
my $MEMORY="96M"; # set with --memory=128Mb/128M
my $FS='ext3';
my $HELP;
my $MANUAL;
#
# Constants for filesystem usage.
#
my %FILESYSTEM_CREATE;
my %FILESYSTEM_MOUNT;
$FILESYSTEM_CREATE{'ext3'} = '/sbin/mkfs.ext3 -F ';
$FILESYSTEM_CREATE{'xfs'} = '/sbin/mkfs.xfs -d name=';
$FILESYSTEM_CREATE{'reiserfs'} = '/sbin/mkfs.reiserfs -f -q ';
$FILESYSTEM_MOUNT{'ext3'} = '-t ext3';
$FILESYSTEM_MOUNT{'xfs'} = '-t xfs';
$FILESYSTEM_MOUNT{'reiserfs'} = '-t reiserfs';
#
# Parse options.
#
GetOptions(
"hostname=s", \$HOSTNAME,
"ip=s", \$IP,
"gateway=s", \$GATEWAY,
"mask=s", \$NETMASK,
"broadcast=s", \$BROADCAST,
"network=s", \$NETWORK,
"dir=s", \$DIR,
"dhcp", \$DHCP,
"mirror=s", \$MIRROR,
"size=s", \$SIZE,
"swapsize=s", \$SWAP_SIZE,
"memory=s", \$MEMORY,
"fs=s", \$FS,
"help", \$HELP,
"manual", \$MANUAL
);
pod2usage(1) if $HELP;
pod2usage(-verbose => 2 ) if $MANUAL;
#
# Check that the arguments the user has supplied are both
# valid, and complete.
#
checkArguments();
#
# If the output directories don't exist then create them.
#
if ( ! -d $DIR . "/domains/" )
{
mkdir $DIR . '/domains', 0777
|| die "Cannot create $DIR/domains - $!";
}
if ( ! -d $DIR . "/domains/" . $HOSTNAME )
{
mkdir $DIR. '/domains/' . $HOSTNAME, 0777
|| die "Cannot create $DIR/domains/$HOSTNAME - $!" ;
}
#
# The two images we'll use, one for the disk image, one for swap.
#
my $image = $DIR . '/domains/' . $HOSTNAME . "/disk.img" ;
my $swap = $DIR . '/domains/' . $HOSTNAME . "/swap.img" ;
#
# Create swapfile and initialise it.
#
print "Creating swapfile : $swap\n";
$SWAP_SIZE =~ s/Mb*$//i;
`/bin/dd if=/dev/zero of=$swap bs=1024k count=$SWAP_SIZE >/dev/null 2>/dev/null`;
print "Initializing swap file\n";
`/sbin/mkswap $swap`;
print "Done\n";
#
# Create disk file and initialise it.
#
print "Creating disk image: $image\n";
$SIZE =~ s/Mb*$/k/i;
`/bin/dd if=/dev/zero of=$image bs=$SIZE count=1 seek=1024 >/dev/null 2>/dev/null`;
print "Creating EXT3 filesystem\n";
my $create = $FILESYSTEM_CREATE{lc( $FS ) } . $image;
`$create`;
print "Done\n";
#
# Now mount the image, in a secure temporary location.
#
my $dir = tempdir( CLEANUP => 1 );
my $mount_cmd = "mount " . $FILESYSTEM_MOUNT{lc($FS)} . " -o loop $image $dir";
`$mount_cmd`;
# Test that the mount worked
my $mount = `/bin/mount`;
if ( ! $mount =~ /$image/) {
print "Something went wrong trying to mount the new filesystem\n";
exit;
}
#
# Install the base system.
#
print "Running debootstrap to install the system. This will take a while!\n";
`debootstrap sarge $dir $MIRROR`;
print "Done\n";
#
# If the debootstrap failed then we'll setup the output directories
# for the configuration files here.
#
`mkdir -p $dir/etc/apt`;
`mkdir -p $dir/etc/network`;
#
# OK now we can do the basic setup.
#
print "Setting up APT sources .. ";
open( APT, ">", $dir . "/etc/apt/sources.list" );
print APT<<E_O_APT;
#
# /etc/apt/sources.list
#
# Stable
deb $MIRROR sarge main contrib non-free
#
# Security updates
#
deb http://security.debian.org/ stable/updates main contrib non-free
E_O_APT
close( APT );
print "Done\n";
#
# Copy some files from the host system, after setting up the hostname.
#
#
my @hostFiles = ( "/etc/resolv.conf",
"/etc/hosts",
"/etc/passwd",
"/etc/group",
"/etc/shadow",
"/etc/gshadow" );
foreach my $file ( @hostFiles )
{
File::Copy::cp( $file, $dir . "/etc" );
}
#
# Disable TLS
#
if ( -d $dir . "/lib/tls" )
{
`mv $dir/lib/tls $dir/lib/tls.disabled`;
}
#
# Now setup the fstab
#
print "Setting up /etc/fstab .. ";
open( TAB, ">", $dir . "/etc/fstab" );
print TAB<<E_O_TAB;
/dev/sda1 / ext3 errors=remount-ro 0 1
/dev/sda2 none swap sw 0 0
proc /proc proc defaults 0 0
E_O_TAB
close( TAB );
print "Done\n";
#
# Setup the /etc/network/interfaces file upon the guest image
#
setupNetworking( $dir );
#
# Install OpenSSH
#
installOpenSSH( $dir );
#
# Fixup Inittab file
#
fixupInittab( $dir );
#
# Now unmount the image.
#
`umount $dir`;
#
# Finally setup Xen to allow us to create the image.
#
print "Setting up Xen configuration file .. ";
open( XEN, ">", "/etc/xen/$HOSTNAME.cfg" );
print XEN<<E_O_XEN;
kernel = "/boot/vmlinuz-2.6.12-xenU"
memory = $MEMORY
name = "$HOSTNAME"
disk = [ 'file:$image,sda1,w','file:$swap,sda2,w' ]
root = "/dev/sda1 ro"
E_O_XEN
if ( $DHCP )
{
print XEN "dhcp=\"dhcp\"\n";
}
else
{
print XEN "#dhcp=\"dhcp\"\n";
}
close( XEN );
print "Done\n";
#
# Give status message
#
print <<EOEND;
To finish the setup of your new host $HOSTNAME please run:
mkdir /mnt/tmp
mount -t ext3 -o loop $image /mnt/tmp
chroot /mnt/tmp /bin/bash
# Get security upgrades.
apt-get upgrade
# setup passwords, etc.
passwd root
# Cleanup.
exit
umount /mnt/tmp
Once completed you may start your new instance of Xen with:
xm create $HOSTNAME.cfg -c
EOEND
=head2 checkArguments
Check that the arguments the user has specified are complete and
make sense.
=cut
sub checkArguments
{
if (!defined( $HOSTNAME ) )
{
print<<EOF
You should set a hostname with '--hostname=foo'.
This option is required.
EOF
;
exit;
}
if (!defined( $DIR ) )
{
print<<EOF
You should set an output directory with '--dir=/my/path'.
This option is required. Subdirectories will be created
beneath the directory you name.
EOF
;
exit;
}
#
# Make sure the directory exists.
#
if ( ! -d $DIR )
{
print "Output directory '$DIR' doesn't exist\n";
exit;
}
#
# Make sure we have a valid size
#
if ( !(($SIZE =~ /^\d+[GM]b*$/i) && ($SWAP_SIZE =~ /^\d+[GM]b*$/i)) )
{
print "Invalid size formats. Please use something like:\n";
print " --size=128Mb\n";
print " --size=2Gb\n";
exit;
}
# Convert Gb -> Mb
if ( $SIZE =~ /^(\d+)Gb*$/i )
{
$SIZE = $1 * 1024 . "M";
}
if ( $SWAP_SIZE =~ /^(\d+)Gb*$/i )
{
$SWAP_SIZE = $1 * 1024 . "M";
}
# Strip trailing Mb from the memory size.
if ( $MEMORY =~ /^(\d+)Mb*$/i )
{
$MEMORY = $1;
}
#
# Check mirror format
#
if (!($MIRROR =~ /^http/i))
{
print "Please enter a valid mirror.\n";
exit;
}
#
# Only one of DHCP / IP is required.
#
if ( $DHCP && $IP )
{
print "You've chosen both DHCP and an IP address.\n";
print "Only one is supported\n";
exit;
}
if ( $DHCP )
{
$GATEWAY="";
$NETMASK="";
$BROADCAST="";
$IP="";
}
#
# Ensure we know how to create *and* mount the given filesystem.
#
if ( !defined( $FILESYSTEM_CREATE{lc( $FS ) } ) ||
!defined( $FILESYSTEM_MOUNT{lc( $FS ) } ) )
{
print "Unknown filesystem. Valid choices are:\n";
foreach my $key (sort keys %FILESYSTEM_MOUNT )
{
print "\t" . $key . "\n";
}
exit;
}
}
=head2 setupNetworking
Setup the /etc/network/interfaces file, and the hostname
upon the virtual instance.
=cut
sub setupNetworking
{
my ( $prefix ) = ( @_ );
`echo '$HOSTNAME' > $prefix/etc/hostname`;
open( IP, ">", $prefix . "/etc/network/interfaces" );
if ( $DHCP )
{
print IP<<E_O_DHCP;
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).
# The loopback network interface
auto lo
iface lo inet loopback
# The primary network interface
auto eth0
iface eth0 inet dhcp
E_O_DHCP
}
else
{
print IP<<E_O_STATIC_IP;
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).
# The loopback network interface
auto lo
iface lo inet loopback
# The primary network interface
auto eth0
iface eth0 inet static
address $IP
gateway $GATEWAY
netmask $NETMASK
network $NETWORK
broadcast $BROADCAST
E_O_STATIC_IP
}
close( IP );
}
=head2 installOpenSSH
Install OpenSSH upon the virtual instance via apt-get.
=cut
sub installOpenSSH
{
my ( $prefix ) = ( @_ );
`chroot $prefix /usr/bin/apt-get update`;
`DEBIAN_FRONTEND=noninteractive chroot $prefix /usr/bin/apt-get --yes --force-yes install ssh`;
`chroot $prefix /etc/init.d/ssh stop`;
}
=head2 fixupInittab
Copy the host systems /etc/inittab to the virtual installation
making a couple of minor changes:
1. Setup the first console to be "Linux".
2. Disable all virtual consoles.
=cut
sub fixupInittab
{
my ( $prefix ) = ( @_ );
my @init;
open( INITTAB, "<", "/etc/inittab" );
foreach my $line ( <INITTAB> )
{
chomp $line;
if ( $line =~ /:respawn:/ )
{
if ( $line =~ /^1/ )
{
$line = "s0:12345:respawn:/sbin/getty 115200 ttyS0 linux"
}
else
{
$line = "#" . $line;
}
}
push @init, $line;
}
close( INITTAB );
open( OUTPUT, ">", "$prefix/etc/inittab" );
foreach my $line ( @init )
{
print OUTPUT $line . "\n";
}
close( OUTPUT )
}