mkdomu

Here's a quick script I hacked together in a couple hours to set up a domu for me. It doesn't do a lot of checking, and it's certainly not something I'd package and share1), but it is a way to quickly get a domU running on an Arch Linux dom0.

Requires arch-install-scripts, Xen, lvm2, xfs, and bunch of other stuff.

THIS WILL BREAK. THIS WILL DESTROY YOUR DOM0.

#!/usr/bin/perl -w
 
 
# THIS IS REALLY EXPERIMENTAL AND DOES NO CHECKING AT ALL
# IT WILL COMPLETELY BREAK YOUR SHIT
# 
#  I WROTE THIS BECAUSE *I* *AM* *LAZY*.  
#       TOO LAZY TO BE A GOOD CODER, EVEN.
#
#
# YEAH YEAH, SHUT UP.
 
use Getopt::Long;
use IPC::System::Simple qw(system);
 
$dom0_mount = "/mnt/new";
$xen_noautodir = "/etc/xen/noauto";
$ip_prefix = "192\.168\.(3|8)";
$mac_prefix = "01:02:03:";
 
my %CONFIG = ('hostname' => '',
			  'address'  => '',
			  'ram'      => '1G',
			  'disk'     => '10G',
			  'swap'     => '512M',
			  'cpus'     => 1,
			  'vnc'      => '');
my $crap;
my $result;
 
$result = GetOptions("h|hostname=s", \$CONFIG{'hostname'},
					 "a|address=s",  \$CONFIG{'address'},
					 "r|ram=s",      \$CONFIG{'ram'},
					 "d|disk=s",     \$CONFIG{'disk'},
					 "s|swap=s",     \$CONFIG{'swap'},
					 "c|cpus=i",     \$CONFIG{'cpus'},
					 "v|vnc=i",      \$CONFIG{'vnc'});
 
$CONFIG{'mac'} = $mac_prefix . macfix();
 
 
foreach (qw/hostname address ram disk swap vnc/) {
	die("$0: -h <hostname> -a <ip address> -r <ram> -d <disk> -s <swap> -v <vnc port> -c <cpus>\n") if ($CONFIG{"$_"} eq "");
}
 
die("address doesn't contain proper prefix ($ip_prefix)\n") if (!($CONFIG{"address"} =~ m/^$ip_prefix/));
 
print "Hostname: $CONFIG{'hostname'}\n";
print "Address:  $CONFIG{'address'}\n";
print "RAM:      $CONFIG{'ram'}\n";
print "Disk:     $CONFIG{'disk'}\n";
print "Swap:     $CONFIG{'swap'}\n";
print "CPUs:     $CONFIG{'cpus'}\n";
print "MAC Addr: $CONFIG{'mac'}\n";
print "VNC Port: $CONFIG{'vnc'}\n\n";
 
print "If this works, hit enter.  If it does not, hit ^C.\n";
$crap = <STDIN>;
 
 
## FILES GO HERE
 
@SERVICES = qw/ntpd.service cronie.service syslog-ng.service netcfg.service sshd.service/;
 
$FILE{'mkinitcpio_modules'} = 'xen-blkfront xen-fbfront xenfs xen-netfront xen-kbdfront';
$FILE{'root_hash'}          = 'ROOTHASH';
$FILE{'packages'}           = 'base openssh syslog-ng ntp screen vim sudo net-tools lsof htop strace';
$FILE{'zonefile'}           = '/usr/share/zoneinfo/America/Vancouver';
 
$FILE{'locale.gen'}         = "en_US ISO-8859-1\nen_US.UTF-8 UTF-8\n";
$FILE{'vconsole.conf'}      = "KEYMAP=us\n";
$FILE{'locale.conf'}        = "LANG=\"en_US.UTF-8\"\n";
 
$FILE{'menu.lst'} = <<'EndMENULIST';
timeout   5
 
title  Arch Linux [/boot/vmlinuz-linux]
root   (hd0)
kernel /boot/vmlinuz-linux root=/dev/xvda1 ro console=xvc console=tty
initrd /boot/initramfs-linux.img
 
title  Arch Linux Fallback [/boot/vmlinuz-linux]
root   (hd0)
kernel /boot/vmlinuz-linux root=/dev/xvda1 ro console=xvc console=tty
initrd /boot/initramfs-linux-fallback.img
EndMENULIST
 
$FILE{'fstab'}  = <<'EndFSTAB';
# 
# /etc/fstab: static file system information
#
# <file system>	<dir>	<type>	<options>	<dump>	<pass>
tmpfs		/tmp	tmpfs	nodev,nosuid	0	0
 
xenfs                  /proc/xen     xenfs     defaults            0      0
 
/dev/xvda1      /       xfs     defaults,nobarrier,noatime,errors=remount-ro 0 0
/dev/xvda2      swap    swap    defaults        0 0
EndFSTAB
 
$FILE{'xenconfig'} = <<'EndXENCFG';
kernel = "/usr/lib/xen/boot/pv-grub-x86_64.gz"
extra = "(hd0)/boot/grub/menu.lst"
 
memory = %RAM%
vcpus  = %CPUS%
name   = "%HOSTNAME%"
disk = [ "/dev/heG0/%HOSTNAME%.root,raw,xvda1,rw", 
         "/dev/heG0/%HOSTNAME%.swap,raw,xvda2,rw"]
 
vif    = [ "bridge=outer0,mac=%MACADDR%" ]
vfb    = [ "type=vnc,vnclisten=0.0.0.0,vncdisplay=%VNCPORT%,vncpasswd=smeghead" ]
EndXENCFG
 
 
$FILE{"netcfg"} = <<'EndNETCFG';
CONNECTION='ethernet'
INTERFACE='eth0'
IP='static'
IPCFG=('addr add dev eth0 %ADDRESS%/16 brd +' 'route add default via 10.128.0.1') 
DNS=('10.128.0.1')
EndNETCFG
 
 
 
# Users to create at boot time
# It will be assumed that these users will be on wheel
$USERS{'username'} = 'USERHASH';
 
# create root partition
 
system("lvcreate --name $CONFIG{'hostname'}.root --size $CONFIG{'disk'} /dev/heG0");
 
# create swap partition
 
system("lvcreate --name $CONFIG{'hostname'}.swap --size $CONFIG{'swap'} /dev/heG0");
# create filesystems
 
system("mkfs.xfs /dev/heG0/$CONFIG{'hostname'}.root");
system("mkswap /dev/heG0/$CONFIG{'hostname'}.swap");
 
# mount filesystem
 
system("mount /dev/heG0/$CONFIG{'hostname'}.root $dom0_mount");
 
# install packages
 
system("pacstrap $dom0_mount ". join(' ', $FILE{'packages'}));
 
# create locale.gen, vconsole.conf, locale.conf, menu.lst, fstab, hostname
 
open(LOCALE, ">$dom0_mount/etc/locale.gen") || die("couldn't locale.gen");
print LOCALE $FILE{"locale.gen"};
close(LOCALE);
 
open(VCONSOLE, ">$dom0_mount/etc/vconsole.conf") || die("couldn't vconsole.conf");
print VCONSOLE $FILE{'vconsole.conf'};
close(VCONSOLE);
 
open(LCONF, ">$dom0_mount/etc/locale.conf") || die("couldn't locale.conf");
print LCONF $FILE{'locale.conf'};
close(LCONF);
 
mkdir "$dom0_mount/boot/grub" || die("couldn't /boot/grub");
open(GRUB, ">$dom0_mount/boot/grub/menu.lst") || die("couldn't menu.lst");
print GRUB $FILE{"menu.lst"};
close(GRUB);
 
open(FSTAB, ">$dom0_mount/etc/fstab") || die("couldn't fstab");
print FSTAB $FILE{'fstab'};
close(FSTAB);
 
open(HOSTNAME, ">$dom0_mount/etc/hostname") || die("couldn't hostname");
print HOSTNAME "$CONFIG{'hostname'}\n";
close(HOSTNAME);
 
# zonefile go
 
system("arch-chroot /mnt/new ln -s $FILE{'zonefile'} /etc/localtime");
 
# edit sudo
 
open(INSUDO, "$dom0_mount/etc/sudoers") || die("couldn't get sudoers");
{ local $/; $sudoers = <INSUDO>; }
close(INSUDO);
 
$sudoers =~ s/^#\s+(\%wheel.*NOPASSWD.*)$/$1/m;
 
open(OUTSUDO, ">$dom0_mount/etc/sudoers") || die("couldn't put sudoers");
print OUTSUDO $sudoers;
close(OUTSUDO);
 
# edit mkinitcpio.conf
open(INCPIO, "$dom0_mount/etc/mkinitcpio.conf") || die("couldn't cpio");
{ local $/; $mkinitcpio = <INCPIO>; }
close(INCPIO);
 
$mkinitcpio =~ s/^(MODULES=\")(\")$/$1$FILE{"mkinitcpio_modules"}$2/m;
 
open(OUTCPIO, ">$dom0_mount/etc/mkinitcpio.conf") || die("couldn't out cpio");
print OUTCPIO $mkinitcpio;
close(OUTCPIO);
 
 
# create users, set password for root
foreach (keys(%USERS)) {
	system("arch-chroot $dom0_mount useradd -m -g users -G wheel -p '$USERS{$_}' $_");
}
system("arch-chroot $dom0_mount usermod -p '$FILE{'root_hash'}' root");
 
 
# run mkinitcpio, locale-gen
 
system("arch-chroot $dom0_mount mkinitcpio -p linux");
system("arch-chroot $dom0_mount locale-gen");
 
 
# configure network
$FILE{"netcfg"} =~ s/%ADDRESS%/$CONFIG{'address'}/m;
open(NETCFG, ">$dom0_mount/etc/network.d/main") || die("can't netcfg");
print NETCFG $FILE{'netcfg'};
 
 
open(INNCFD, "$dom0_mount/etc/conf.d/netcfg") || die("couldn't netcfg");
{ local $/; $confnetcfg = <INNCFD>; }
close(INNCFD);
 
$confnetcfg =~ s/^(NETWORKS=\().*(\))$/$1main$2/m;
 
 
open(OUTNCFD, ">$dom0_mount/etc/conf.d/netcfg") || die("couldn't out netcfg");
print OUTNCFD $confnetcfg;
close(OUTNCFD);
 
# Add local repository 
open(PACMAND, ">>$dom0_mount/etc/pacman.conf") || die("couldn't pacman.conf");
print PACMAND "[private]\nServer = http://your.private.repo.here\n";
close(PACMAND);
 
 
# enable services
 
foreach (@SERVICES) {
	system("arch-chroot /mnt/new systemctl enable $_");
}
 
# create xen config
 
$FILE{'xenconfig'} =~ s/%HOSTNAME%/$CONFIG{'hostname'}/mg;
$FILE{'xenconfig'} =~ s/%VNCPORT%/$CONFIG{'vnc'}/m;
$FILE{'xenconfig'} =~ s/%MACADDR%/$CONFIG{'mac'}/m;
$FILE{'xenconfig'} =~ s/%CPUS%/$CONFIG{'cpus'}/m;
$FILE{'xenconfig'} =~ s/%RAM%/$CONFIG{'ram'}/m;
 
open(XENCFG, ">$xen_noautodir/$CONFIG{'hostname'}.cfg") || die("Can't put xen configfile");
print XENCFG $FILE{'xenconfig'};
close(XENCFG);
 
# umount
 
print "The fs for the new vm is mounted at '$dom0_mount' for any pre-run stuff.\n".
	  "When done, umount $dom0_mount and then run:\n".
      "\txl create -c $xen_noautodir/$CONFIG{'hostname'}.cfg\n\n";
 
 
 
 
 
sub macfix {
    my @THING;
    for(0 .. 2) { push @THING, sprintf("%02x", int(rand(255))); }
    return join(":", @THING);
}
1)
if I weren't trying to document everything publicly