#!/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: --dir Specify where the output images should go. --fs Specify the filesystem type to use. --memory Setup the amount of memory allocated to the instance. --size Set the size of the primary disk image. --swap Set the size of the swap partition. 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: --hostname Set the images hostname. =cut =head1 OPTIONS =over 8 =item B<--broadcast> Specify the broadcast address for the virtual image, only useful if DHCP is not used. =item B<--dhcp> Specify that the virtual image should use DHCP to obtain its networking information. =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<--manual> Read the manual, with examples. =item B<--memory> Specify the amount of memory the virtual image should be allocated. Defaults to 96Mb. =item B<--mirror> Specify the mirror to use to the installation of Sarge, defaults to http://ftp.us.debian.org/debian =item B<--network> Specify the network the virtual image is living upon. Only useful if DHCP is not used. =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. =back =cut =head1 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' 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 --network=192.168.1.0 \ --gateway=192.168.1.1 \ --dir=/home/xen --hostname=vm02 To save time these command line options may be specified in the configuration file discussed later. 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 DESCRIPTION xen-create-image is a simple script which allows you to create new Xen instances of Debian Sarge. The new images conmprise of two files: 1. One disk image which will be treated as the primary disk drive. 2. One swap image. The image will also have an OpenSSH server installed upon it, and an appropriate /etc/inittab file created, along with copies of the hosts password and shadow files. =head1 CONFIGURATION To reduce the length of the command line each of the options may be specified inside a configuration file. The script will check three files for options: 1. /etc/xen-tools/xen-tools.conf 2. ~/.xen-tools.conf 3. ./etc/xen-tools.conf The files may contain comments, which begin with the hash '#' character and are otherwise of the format 'key = value. A sample configuration file would look like this: =for example begin # # General options. # dir = /home/xen # Ouptut directory memory = 128Mb # 128Mb for each new image. # # Images # fs = ext3 # We like EXT3 swap = 128mb # 128Mb of swap. size = 2Gb # 2Gb images. # # Networking options. # network = 192.168.1.0 gateway = 192.168.1.1 broadcast = 255.255.255.0 =for example end This allows a new image to be created with only two command line flags: xen-create-image --hostname='vm03.my.flat' --ip=192.168.1.201 =head1 AUTHOR Steve -- http://www.steve.org.uk/ $Id: xen-create-image,v 1.21 2005-12-18 23:45:17 steve Exp $ =cut =head1 CONTRIBUTORS Radu Spineanu =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 English; use File::Copy; use File::Temp qw/ tempdir /; use Getopt::Long; use Pod::Usage; if ( $EFFECTIVE_USER_ID != 0 ) { print </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"; $CONFIG{'size'} =~ s/Mb*$/k/i; `/bin/dd if=/dev/zero of=$image bs=$CONFIG{'size'} count=1 seek=1024 >/dev/null 2>/dev/null`; print "Creating $CONFIG{'fs'} filesystem\n"; my $create = $FILESYSTEM_CREATE{lc( $CONFIG{'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($CONFIG{'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; } # # Copy any local .deb files into the debootstrap archive as a potential # speedup. # `mkdir -p $dir/var/cache/apt/archives`; foreach my $file ( glob( "/var/cache/apt/archives/*.deb" ) ) { File::Copy::cp( $file, "$dir/var/cache/apt/archives" ); } # # Install the base system. # print "Running debootstrap to install the system. This will take a while!\n"; `debootstrap sarge $dir $CONFIG{'mirror'}`; print "Done\n"; # # Copy these files as a speed boost for the next run. # foreach my $file ( glob( "$dir/var/cache/apt/archives/*.deb" ) ) { File::Copy::cp( $file, "/var/cache/apt/archives" ); } # # 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<", $dir . "/etc/fstab" ); print TAB<", "/etc/xen/$CONFIG{'hostname'}.cfg" ); print XEN<) ) { chomp $line; if ($line =~ s/\\$//) { $line .= ; 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 arguments specified upon the command line. =cut sub parseCommandLineArguments { my $HELP = 0; my $MANUAL = 0; # Parse options. # GetOptions( "hostname=s", \$CONFIG{'hostname'}, "ip=s", \$CONFIG{'ip'}, "gateway=s", \$CONFIG{'gateway'}, "mask=s", \$CONFIG{'netmask'}, "broadcast=s",\$CONFIG{'broadcast'}, "network=s", \$CONFIG{'network'}, "dir=s", \$CONFIG{'dir'}, "dhcp", \$CONFIG{'dhcp'}, "mirror=s", \$CONFIG{'mirror'}, "size=s", \$CONFIG{'size'}, "swap=s", \$CONFIG{'swap'}, "memory=s", \$CONFIG{'memory'}, "fs=s", \$CONFIG{'fs'}, "help", \$HELP, "manual", \$MANUAL ); pod2usage(1) if $HELP; pod2usage(-verbose => 2 ) if $MANUAL; } =head2 checkArguments Check that the arguments the user has specified are complete and make sense. =cut sub checkArguments { if (!defined( $CONFIG{'hostname'} ) ) { print< Mb if ( $CONFIG{'size'} =~ /^(\d+)Gb*$/i ) { $CONFIG{'size'} = $1 * 1024 . "M"; } if ( $CONFIG{'swap'} =~ /^(\d+)Gb*$/i ) { $CONFIG{'swap'} = $1 * 1024 . "M"; } # Strip trailing Mb from the memory size. if ( $CONFIG{'memory'} =~ /^(\d+)Mb*$/i ) { $CONFIG{'memory'} = $1; } # # Check mirror format # if (!($CONFIG{'mirror'} =~ /^http/i)) { print "Please enter a valid mirror.\n"; exit; } # # Only one of DHCP / IP is required. # if ( $CONFIG{'ip'} && $CONFIG{'dhcp'}) { print "You've chosen both DHCP and an IP address.\n"; print "Only one is supported\n"; exit; } if ( $CONFIG{'dhcp'} ) { $CONFIG{'gateway'} = ''; $CONFIG{'netmask'} = ''; $CONFIG{'broadcast'} = ''; $CONFIG{'ip'} = ''; } # # Ensure we know how to create *and* mount the given filesystem. # if ( !defined( $FILESYSTEM_CREATE{lc( $CONFIG{'fs'} ) } ) || !defined( $FILESYSTEM_MOUNT{lc( $CONFIG{'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 '$CONFIG{'hostname'}' > $prefix/etc/hostname`; open( IP, ">", $prefix . "/etc/network/interfaces" ); if ( $CONFIG{'dhcp'} ) { print IP< ) { chomp $line; if ( $line =~ /:respawn:/ ) { if ( $line =~ /^1/ ) { # NOP - leave line unchanged. } else { # Otherwise comment out the line, we don't need multiple # terminals since we can only access one. $line = "#" . $line; } } push @init, $line; } close( INITTAB ); open( OUTPUT, ">", "$prefix/etc/inittab" ); foreach my $line ( @init ) { print OUTPUT $line . "\n"; } close( OUTPUT ) }