495 lines
9.8 KiB
Perl
Executable File
495 lines
9.8 KiB
Perl
Executable File
#!/usr/bin/perl -w
|
|
|
|
=head1 NAME
|
|
|
|
xen-create-nfs - Create a Xen configuration file for an NFS-root guest.
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
xen-create-nfs [options]
|
|
|
|
Help Options:
|
|
--help Show help information.
|
|
--manual Read the manual for this script.
|
|
--version Show the version information and exit.
|
|
--verbose Show diagnostic output.
|
|
|
|
Networking Options:
|
|
--broadcast The broadcast address to use when configured with a static IP.
|
|
--dhcp Configure the guest to use DHCP for IP allocation.
|
|
--gateway The gateway address to use when configured with a static IP.
|
|
--hostname The hostname to configure for the guest.
|
|
--netmask The netmask to use when configured with a static IP.
|
|
--ip The IP address to use when configured with a static IP.
|
|
|
|
General options:
|
|
--admins Specify which users should be setup as xen-shell admins.
|
|
--force Force the overwriting of an existing configuration file.
|
|
--initrd Specify the initial ramdisk for the guest.
|
|
--kernel Specify the kernel to use for the guest.
|
|
--memory Specify the memory to allocate for this guest.
|
|
--mac Specify the MAC address to use for the guest.
|
|
--template Specify an alternative template file to use.
|
|
|
|
NFS options:
|
|
--nfs_server Specify the NFS server to mount the root partition from.
|
|
--nfs_root Specify the path, upon the NFS server, to mount.
|
|
|
|
=cut
|
|
|
|
|
|
=head1 OPTIONS
|
|
|
|
=over 8
|
|
|
|
=item B<--help>
|
|
Show help information.
|
|
|
|
=item B<--hostname>
|
|
Specify the hostname to delete.
|
|
|
|
=item B<--manual>
|
|
Read the manual for this script.
|
|
|
|
=item B<--version>
|
|
Show the version number and exit.
|
|
|
|
|
|
=back
|
|
|
|
=cut
|
|
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
xen-create-nfs is a simple script which allows you to easily create
|
|
a single configuration file for a Xen guest which will mount its remote
|
|
filesystem over an NFS root.
|
|
|
|
It doesn't create any images to use for local storage, and it doesn't
|
|
support more than the minimal number of options to completement the
|
|
existing xen-create-image script, however it is hopefully useful.
|
|
|
|
=cut
|
|
|
|
|
|
=head1 REFERENCE
|
|
|
|
For more details on what you'll need to support NFS-root Xen guests
|
|
the following article, written by the author, might be useful:
|
|
|
|
http://www.debian-administration.org/articles/505
|
|
|
|
=cut
|
|
|
|
|
|
=head1 AUTHOR
|
|
|
|
Steve
|
|
--
|
|
http://www.steve.org.uk/
|
|
|
|
=cut
|
|
|
|
|
|
=head1 LICENSE
|
|
|
|
Copyright (c) 2005-2009 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 Getopt::Long;
|
|
use Pod::Usage;
|
|
use Text::Template;
|
|
|
|
|
|
|
|
#
|
|
# Configuration values read from the command line.
|
|
#
|
|
# We do not need to read any configuration file.
|
|
#
|
|
my %CONFIG;
|
|
|
|
#
|
|
# Default options
|
|
#
|
|
$CONFIG{ 'template' } = '/etc/xen-tools/xm-nfs.tmpl';
|
|
|
|
|
|
#
|
|
# Release number.
|
|
#
|
|
my $RELEASE = '4.2rc1';
|
|
|
|
|
|
# store version number away.
|
|
$CONFIG{ 'xen_tools_version' } = $RELEASE;
|
|
|
|
|
|
#
|
|
# Read the global configuration file.
|
|
#
|
|
readConfigurationFile("/etc/xen-tools/xen-tools.conf");
|
|
|
|
|
|
#
|
|
# Parse the command line arguments.
|
|
#
|
|
parseCommandLineArguments();
|
|
|
|
|
|
#
|
|
# Validate our arguments.
|
|
#
|
|
testArguments();
|
|
|
|
|
|
#
|
|
# Create the image.
|
|
#
|
|
if ( -e "/etc/xen/$CONFIG{'hostname'}.cfg" )
|
|
{
|
|
die "Configuration file for $CONFIG{'hostname'} already exists"
|
|
unless ( $CONFIG{ 'force' } );
|
|
}
|
|
|
|
#
|
|
# If we've been given any administrators then set them up.
|
|
#
|
|
if ( $CONFIG{ 'admins' } )
|
|
{
|
|
setupAdminUsers();
|
|
}
|
|
|
|
#
|
|
# Now create the NFS configuration file.
|
|
#
|
|
createNewConfigurationFile();
|
|
|
|
|
|
|
|
#
|
|
# All done.
|
|
#
|
|
exit;
|
|
|
|
|
|
|
|
=begin doc
|
|
|
|
Read the specified configuration file, and update our global configuration
|
|
hash with the values found in it.
|
|
|
|
=end doc
|
|
|
|
=cut
|
|
|
|
sub readConfigurationFile
|
|
{
|
|
my ($file) = (@_);
|
|
|
|
# Don't read the file if it doesn't exist.
|
|
return if ( !-e $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+$//;
|
|
|
|
# command expansion?
|
|
if ( $val =~ /(.*)`([^`]+)`(.*)/ )
|
|
{
|
|
|
|
# store
|
|
my $pre = $1;
|
|
my $cmd = $2;
|
|
my $post = $3;
|
|
|
|
# get output
|
|
my $output = `$cmd`;
|
|
chomp($output);
|
|
|
|
# build up replacement.
|
|
$val = $pre . $output . $post;
|
|
}
|
|
|
|
# Store value.
|
|
$CONFIG{ $key } = $val;
|
|
}
|
|
}
|
|
|
|
close(FILE);
|
|
}
|
|
|
|
|
|
|
|
=begin doc
|
|
|
|
Parse the command line arguments this script was given.
|
|
|
|
=end doc
|
|
|
|
=cut
|
|
|
|
sub parseCommandLineArguments
|
|
{
|
|
my $HELP = 0;
|
|
my $MANUAL = 0;
|
|
my $VERSION = 0;
|
|
|
|
|
|
#
|
|
# Parse options.
|
|
#
|
|
GetOptions(
|
|
|
|
# Networking options
|
|
"dhcp", \$CONFIG{ 'dhcp' },
|
|
"gateway=s", \$CONFIG{ 'gateway' },
|
|
"broadcast=s", \$CONFIG{ 'broadcast' },
|
|
"ip=s", \$CONFIG{ 'ip' },
|
|
"netmask=s", \$CONFIG{ 'netmask' },
|
|
"hostname=s", \$CONFIG{ 'hostname' },
|
|
"memory=s", \$CONFIG{ 'memory' },
|
|
"mac=s", \$CONFIG{ 'mac' },
|
|
|
|
# NFS options.
|
|
"nfs_server=s", \$CONFIG{ 'nfs_server' },
|
|
"nfs_root=s", \$CONFIG{ 'nfs_root' },
|
|
|
|
# Misc. options
|
|
"admins=s", \$CONFIG{ 'admins' },
|
|
"kernel=s", \$CONFIG{ 'kernel' },
|
|
"initrd=s", \$CONFIG{ 'initrd' },
|
|
"force", \$CONFIG{ 'force' },
|
|
"template=s", \$CONFIG{ 'template' },
|
|
|
|
# Help options
|
|
"help", \$HELP,
|
|
"manual", \$MANUAL,
|
|
"verbose", \$CONFIG{ 'verbose' },
|
|
"version", \$VERSION
|
|
|
|
);
|
|
|
|
pod2usage(1) if $HELP;
|
|
pod2usage( -verbose => 2 ) if $MANUAL;
|
|
|
|
|
|
if ($VERSION)
|
|
{
|
|
my $REVISION = '$Revision: 1.13 $';
|
|
if ( $REVISION =~ /1.([0-9.]+) / )
|
|
{
|
|
$REVISION = $1;
|
|
}
|
|
|
|
logprint("xen-create-nfs release $RELEASE - CVS: $REVISION\n");
|
|
exit;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
=begin doc
|
|
|
|
Test that our arguments make sense.
|
|
|
|
=end doc
|
|
|
|
=cut
|
|
|
|
sub testArguments
|
|
{
|
|
|
|
#
|
|
# Hostname is mandatory
|
|
#
|
|
die "No hostname" unless ( $CONFIG{ 'hostname' } );
|
|
|
|
my @network = qw/ ip gateway netmask /;
|
|
|
|
#
|
|
# If DHCP then all the other options aren't needed
|
|
#
|
|
if ( $CONFIG{ 'dhcp' } )
|
|
{
|
|
foreach my $f (@network)
|
|
{
|
|
delete( $CONFIG{ $f } );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach my $f (@network)
|
|
{
|
|
die "Missing --$f" unless ( $CONFIG{ $f } );
|
|
}
|
|
}
|
|
|
|
#
|
|
# We need an NFS server + root
|
|
#
|
|
die "Missing NFS server." unless ( $CONFIG{ 'nfs_server' } );
|
|
die "Missing NFS root." unless ( $CONFIG{ 'nfs_root' } );
|
|
|
|
|
|
# All OK.
|
|
}
|
|
|
|
|
|
|
|
=begin doc
|
|
|
|
This routine is designed to ensure that any users specified with
|
|
the --admins flag are setup as administrators of the new instance.
|
|
|
|
=end doc
|
|
|
|
=cut
|
|
|
|
sub setupAdminUsers
|
|
{
|
|
|
|
#
|
|
# If we're not root we can't modify users.
|
|
#
|
|
return if ( $EFFECTIVE_USER_ID != 0 );
|
|
|
|
#
|
|
# If we don't have a sudoers file then we'll also ignore this.
|
|
#
|
|
return if ( !-e "/etc/sudoers" );
|
|
|
|
#
|
|
# Find the path to the xen-login-shell
|
|
#
|
|
my $shell = undef;
|
|
$shell = "/usr/bin/xen-login-shell" if ( -x "/usr/bin/xen-login-shell" );
|
|
$shell = "/usr/local/bin/xen-login-shell"
|
|
if ( -x "/usr/bin/local/xen-login-shell" );
|
|
|
|
return if ( !defined($shell) );
|
|
|
|
|
|
#
|
|
# For each user make sure they exist, and setup the
|
|
# login shell for them.
|
|
#
|
|
foreach my $user ( split( /,/, $ENV{ 'admins' } ) )
|
|
{
|
|
|
|
# Strip leading and trailing whitespace.
|
|
$user =~ s/^\s+//;
|
|
$user =~ s/\s+$//;
|
|
|
|
# Ignore root
|
|
next if ( $user =~ /^root$/i );
|
|
|
|
# Does the user exist?
|
|
if ( getpwnam($user) )
|
|
{
|
|
|
|
# Change shell.
|
|
$CONFIG{ 'verbose' } && print "Changing shell for $user: $shell\n";
|
|
system( "chsh", "-s", $shell, $user );
|
|
}
|
|
else
|
|
{
|
|
|
|
# Add a new user.
|
|
$CONFIG{ 'verbose' } && print "Adding new user: $user\n";
|
|
system( "useradd", "-s", $shell, $user );
|
|
}
|
|
|
|
#
|
|
# Add the entry to /etc/sudoers.
|
|
#
|
|
open( SUDOERS, ">>", "/etc/sudoers" ) or
|
|
warn "Failed to add user to sudoers file : $user - $!";
|
|
print SUDOERS
|
|
"$user ALL = NOPASSWD: /usr/sbin/xm, /usr/bin/xen-create-image\n";
|
|
close(SUDOERS);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
=begin doc
|
|
|
|
Create the Xen configuration file for our new Xen guest.
|
|
|
|
=end doc
|
|
|
|
=cut
|
|
|
|
sub createNewConfigurationFile
|
|
{
|
|
die "Template file missing: $CONFIG{'template'}"
|
|
unless ( -e $CONFIG{ 'template' } );
|
|
|
|
#
|
|
# Load the template.
|
|
#
|
|
my $template = new Text::Template( TYPE => 'FILE',
|
|
SOURCE => $CONFIG{ 'template' } );
|
|
|
|
my $result = $template->fill_in( HASH => \%CONFIG );
|
|
|
|
#
|
|
# The file we'll write to.
|
|
#
|
|
my $file = "/etc/xen/$CONFIG{'hostname'}.cfg";
|
|
|
|
#
|
|
# Output the configuration file.
|
|
#
|
|
open( FILE, ">", $file ) or die "Failed to write to $file - $!";
|
|
print FILE $result;
|
|
close(FILE);
|
|
}
|