522 lines
9.8 KiB
Perl
Executable File
522 lines
9.8 KiB
Perl
Executable File
#!/usr/bin/perl -w
|
|
#
|
|
# Create a new Xen instances containing an installation of Debian
|
|
#
|
|
# 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:
|
|
#
|
|
# ./xen-create-image --hostname='vm03' --dir=/home/zen \
|
|
# --ip=192.168.1.93 --gateway=192.168.1.1 \
|
|
# --network=192.168.1.0 --mask=255.255.255.0 \
|
|
#
|
|
# 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.
|
|
#
|
|
#
|
|
# Steve
|
|
# --
|
|
# http://www.steve.org.uk/
|
|
#
|
|
# $Id: xen-create-image,v 1.4 2005-12-18 03:33:54 steve Exp $
|
|
#
|
|
|
|
use strict;
|
|
use File::Copy;
|
|
use File::Temp qw/ tempdir /;
|
|
use Getopt::Long;
|
|
|
|
|
|
#
|
|
# 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"
|
|
|
|
#
|
|
# 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
|
|
);
|
|
|
|
|
|
#
|
|
# 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";
|
|
`/sbin/mkfs.ext3 -F $image`;
|
|
print "Done\n";
|
|
|
|
|
|
#
|
|
# Now mount the image, in a secure temporary location.
|
|
#
|
|
my $dir = tempdir( CLEANUP => 1 );
|
|
`mount -t xfs -o loop $image $dir`;
|
|
|
|
|
|
# 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.
|
|
#
|
|
#
|
|
`echo '$HOSTNAME' > $dir/etc/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 IP address
|
|
#
|
|
print "Setting up IP address .. ";
|
|
open( IP, ">", $dir . "/etc/network/interfaces" );
|
|
print IP<<E_O_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
|
|
E_O_IP
|
|
|
|
if ( $DHCP )
|
|
{
|
|
print IP "iface eth0 dhcp\n";
|
|
}
|
|
else
|
|
{
|
|
print IP <<E_O_STATIC;
|
|
iface eth0 inet static
|
|
address $IP
|
|
gateway $GATEWAY
|
|
netmask $NETMASK
|
|
network $NETWORK
|
|
broadcast $BROADCAST
|
|
E_O_STATIC
|
|
}
|
|
|
|
close( IP );
|
|
|
|
print "Done\n";
|
|
|
|
|
|
|
|
#
|
|
# 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";
|
|
|
|
|
|
|
|
|
|
#
|
|
# Install OpenSSH
|
|
#
|
|
installOpenSSH();
|
|
|
|
#
|
|
# Fixup Inittab file
|
|
#
|
|
fixupInittab();
|
|
|
|
|
|
|
|
#
|
|
# 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 = 96
|
|
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";
|
|
}
|
|
|
|
|
|
#
|
|
# 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="";
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
=head2 installOpenSSH
|
|
|
|
Install OpenSSH upon the virtual instance.
|
|
|
|
=cut
|
|
|
|
sub installOpenSSH
|
|
{
|
|
`chroot $dir /usr/bin/apt-get update`;
|
|
`DEBIAN_FRONTEND=noninteractive chroot $dir /usr/bin/apt-get --yes --force-yes install ssh`;
|
|
`chroot $dir /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 @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, ">", "$dir/etc/inittab" );
|
|
foreach my $line ( @init )
|
|
{
|
|
print OUTPUT $line . "\n";
|
|
}
|
|
close( OUTPUT )
|
|
}
|