nixos/tests/virtualbox: Add test for hostonlyif.

Essentially adds two more VirtualBox VMs to the test and also increases
the memory size of the qemu VM to 768 MB to make sure we don't run out
of memory too soon.

We're testing whether those two VMs can talk to either each other
(currently via ICMP only) or to/from the host via TCP/IP.

Also, this restructures the VM test a bit, so that we now pass in a
custom stage2Init script that has access to the store via a private
mount over the /nix/store that's already in the initrd. The reason why
this is a private mount is that we don't want to shadow the Nix store of
the initrd, essentially breaking cleanup functionality after the custom
stage 2 script (currently this is only "poweroff -f").

Note that setting the hostname inside the VirtualBox VM is *not* for
additional fanciness but to produce a different store path for the VM
image, so that VirtualBox doesn't bail out when trying to use an image
which is already attached to another VM.

Signed-off-by: aszlig <aszlig@redmoonstudios.org>
This commit is contained in:
aszlig 2014-12-15 08:58:12 +01:00
parent 245baeb2f6
commit 9bf16a9c33
No known key found for this signature in database
GPG key ID: D0EBD0EC8C2DC961

View file

@ -1,9 +1,27 @@
import ./make-test.nix ({ pkgs, ... }: with pkgs.lib; let
testVMConfig = { config, pkgs, ... }: {
boot.kernelParams = [
testVMConfig = vmName: attrs: { config, pkgs, ... }: {
boot.kernelParams = let
miniInit = ''
#!${pkgs.stdenv.shell} -xe
export PATH="${pkgs.coreutils}/bin:${pkgs.utillinux}/bin"
${pkgs.linuxPackages.virtualboxGuestAdditions}/sbin/VBoxService
${(attrs.vmScript or (const "")) pkgs}
i=0
while [ ! -e /mnt-root/shutdown ]; do
sleep 10
i=$(($i + 10))
[ $i -le 120 ] || fail
done
rm -f /mnt-root/boot-done /mnt-root/shutdown
'';
in [
"console=tty0" "console=ttyS0" "ignore_loglevel"
"boot.trace" "panic=1" "boot.panic_on_fail"
"init=${pkgs.writeScript "mini-init.sh" miniInit}"
];
fileSystems."/" = {
@ -13,24 +31,26 @@ import ./make-test.nix ({ pkgs, ... }: with pkgs.lib; let
services.virtualboxGuest.enable = true;
boot.initrd.kernelModules = [ "vboxsf" ];
boot.initrd.kernelModules = [
"af_packet" "vboxsf"
"virtio" "virtio_pci" "virtio_ring" "virtio_net" "vboxguest"
];
boot.initrd.extraUtilsCommands = ''
cp -av -t "$out/bin/" \
"${pkgs.linuxPackages.virtualboxGuestAdditions}/sbin/mount.vboxsf"
"${pkgs.linuxPackages.virtualboxGuestAdditions}/sbin/mount.vboxsf" \
"${pkgs.utillinux}/bin/unshare"
${(attrs.extraUtilsCommands or (const "")) pkgs}
'';
boot.initrd.postMountCommands = ''
touch /mnt-root/boot-done
i=0
while [ ! -e /mnt-root/shutdown ]; do
sleep 10
i=$(($i + 10))
[ $i -le 120 ] || fail
done
rm -f /mnt-root/boot-done /mnt-root/shutdown
hostname "${vmName}"
mkdir -p /nix/store
unshare -m "@shell@" -c '
mount -t vboxsf nixstore /nix/store
exec "$stage2Init"
'
poweroff -f
'';
@ -40,12 +60,12 @@ import ./make-test.nix ({ pkgs, ... }: with pkgs.lib; let
];
};
testVM = let
testVM = vmName: vmScript: let
cfg = (import ../lib/eval-config.nix {
system = "i686-linux";
modules = [
../modules/profiles/minimal.nix
testVMConfig
(testVMConfig vmName vmScript)
];
}).config;
in pkgs.vmTools.runInLinuxVM (pkgs.runCommand "virtualbox-image" {
@ -86,7 +106,7 @@ import ./make-test.nix ({ pkgs, ... }: with pkgs.lib; let
umount /mnt
'');
createVM = name: let
createVM = name: attrs: let
mkFlags = concatStringsSep " ";
sharePath = "/home/alice/vboxshare-${name}";
@ -96,10 +116,10 @@ import ./make-test.nix ({ pkgs, ... }: with pkgs.lib; let
"--register"
];
vmFlags = mkFlags [
vmFlags = mkFlags ([
"--uart1 0x3F8 4"
"--uartmode1 client /run/virtualbox-log-${name}.sock"
];
] ++ (attrs.vmFlags or []));
controllerFlags = mkFlags [
"--name SATA"
@ -114,13 +134,19 @@ import ./make-test.nix ({ pkgs, ... }: with pkgs.lib; let
"--device 0"
"--type hdd"
"--mtype immutable"
"--medium ${testVM}/disk.vdi"
"--medium ${testVM name attrs}/disk.vdi"
];
sharedFlags = mkFlags [
"--name vboxshare"
"--hostpath ${sharePath}"
];
nixstoreFlags = mkFlags [
"--name nixstore"
"--hostpath /nix/store"
"--readonly"
];
in {
machine = {
systemd.sockets = listToAttrs (singleton {
@ -147,13 +173,35 @@ import ./make-test.nix ({ pkgs, ... }: with pkgs.lib; let
};
testSubs = ''
sub checkRunning_${name} {
my $cmd = 'VBoxManage list runningvms | grep -q "^\"${name}\""';
my ($status, $out) = $machine->execute(ru $cmd);
return $status == 0;
}
sub cleanup_${name} {
$machine->execute(ru "VBoxManage controlvm ${name} poweroff")
if checkRunning_${name};
$machine->succeed("rm -rf ${sharePath}");
$machine->succeed("mkdir -p ${sharePath}");
$machine->succeed("chown alice.users ${sharePath}");
}
sub createVM_${name} {
vbm("createvm --name ${name} ${createFlags}");
vbm("modifyvm ${name} ${vmFlags}");
vbm("setextradata ${name} VBoxInternal/PDM/HaltOnReset 1");
vbm("storagectl ${name} ${controllerFlags}");
vbm("storageattach ${name} ${diskFlags}");
vbm("sharedfolder add ${name} ${sharedFlags}");
vbm("sharedfolder add ${name} ${nixstoreFlags}");
vbm("showvminfo ${name} >&2");
cleanup_${name};
}
sub destroyVM_${name} {
cleanup_${name};
vbm("unregistervm ${name} --delete");
}
sub waitForVMBoot_${name} {
@ -166,10 +214,22 @@ import ./make-test.nix ({ pkgs, ... }: with pkgs.lib; let
));
}
sub checkRunning_${name} {
my $cmd = 'VBoxManage list runningvms | grep -q "^\"${name}\""';
my ($status, $out) = $machine->execute(ru $cmd);
return $status == 0;
sub waitForIP_${name} ($) {
my $property = "/VirtualBox/GuestInfo/Net/$_[0]/V4/IP";
my $getip = "VBoxManage guestproperty get ${name} $property | ".
"sed -n -e 's/^Value: //p'";
my $ip = $machine->succeed(ru(
'for i in $(seq 1000); do '.
'if ipaddr="$('.$getip.')" && [ -n "$ipaddr" ]; then '.
'echo "$ipaddr"; exit 0; '.
'fi; '.
'sleep 1; '.
'done; '.
'echo "Could not get IPv4 address for ${name}!" >&2; '.
'exit 1'
));
chomp $ip;
return $ip;
}
sub waitForStartup_${name} {
@ -196,18 +256,36 @@ import ./make-test.nix ({ pkgs, ... }: with pkgs.lib; let
);
waitForShutdown_${name};
}
sub cleanup_${name} {
$machine->execute(ru "VBoxManage controlvm ${name} poweroff")
if checkRunning_${name};
$machine->succeed("rm -rf ${sharePath}");
$machine->succeed("mkdir -p ${sharePath}");
$machine->succeed("chown alice.users ${sharePath}");
}
'';
};
vboxVMs.test1 = createVM "test1";
hostonlyVMFlags = [
"--nictype1 virtio"
"--nictype2 virtio"
"--nic2 hostonly"
"--hostonlyadapter2 vboxnet0"
];
dhcpScript = pkgs: ''
${pkgs.dhcp}/bin/dhclient \
-lf /run/dhcp.leases \
-pf /run/dhclient.pid \
-v eth0 eth1
otherIP="$(${pkgs.netcat}/bin/netcat -clp 1234 || :)"
${pkgs.iputils}/bin/ping -I eth1 -c1 "$otherIP"
echo "$otherIP reachable" | ${pkgs.netcat}/bin/netcat -clp 5678 || :
'';
vboxVMs = mapAttrs createVM {
simple = {};
test1.vmFlags = hostonlyVMFlags;
test1.vmScript = dhcpScript;
test2.vmFlags = hostonlyVMFlags;
test2.vmScript = dhcpScript;
};
in {
name = "virtualbox";
@ -217,12 +295,15 @@ in {
mkVMConf = name: val: val.machine // { key = "${name}-config"; };
vmConfigs = mapAttrsToList mkVMConf vboxVMs;
in [ ./common/user-account.nix ./common/x11.nix ] ++ vmConfigs;
virtualisation.memorySize = 768;
services.virtualboxHost.enable = true;
users.extraUsers.alice.extraGroups = [ "vboxusers" ];
};
testScript = ''
sub ru {
return "su - alice -c '$_[0]'";
sub ru ($) {
my $esc = $_[0] =~ s/'/'\\${"'"}'/gr;
return "su - alice -c '$esc'";
}
sub vbm {
@ -233,9 +314,7 @@ in {
$machine->waitForX;
createVM_test1;
cleanup_test1;
createVM_simple;
subtest "simple-gui", sub {
$machine->succeed(ru "VirtualBox &");
@ -244,11 +323,11 @@ in {
$machine->screenshot("gui_manager_started");
$machine->sendKeys("ret");
$machine->screenshot("gui_manager_sent_startup");
waitForStartup_test1;
waitForStartup_simple;
$machine->screenshot("gui_started");
waitForVMBoot_test1;
waitForVMBoot_simple;
$machine->screenshot("gui_booted");
shutdownVM_test1;
shutdownVM_simple;
$machine->sleep(5);
$machine->screenshot("gui_stopped");
$machine->sendKeys("ctrl-q");
@ -256,20 +335,53 @@ in {
$machine->screenshot("gui_manager_stopped");
};
cleanup_test1;
cleanup_simple;
subtest "simple-cli", sub {
vbm("startvm test1");
waitForStartup_test1;
vbm("startvm simple");
waitForStartup_simple;
$machine->screenshot("cli_started");
waitForVMBoot_test1;
waitForVMBoot_simple;
$machine->screenshot("cli_booted");
shutdownVM_test1;
shutdownVM_simple;
};
cleanup_test1;
subtest "privilege-escalation", sub {
$machine->fail("test -e '/root/VirtualBox VMs'");
$machine->succeed("test -e '/home/alice/VirtualBox VMs'");
};
$machine->fail("test -e '/root/VirtualBox VMs'");
$machine->succeed("test -e '/home/alice/VirtualBox VMs'");
destroyVM_simple;
subtest "net-hostonlyif", sub {
createVM_test1;
createVM_test2;
vbm("startvm test1");
waitForStartup_test1;
vbm("startvm test2");
waitForStartup_test2;
waitForVMBoot_test1;
waitForVMBoot_test2;
$machine->screenshot("net_booted");
my $test1IP = waitForIP_test1 1;
my $test2IP = waitForIP_test2 1;
$machine->succeed("echo '$test2IP' | netcat -c '$test1IP' 1234");
$machine->succeed("echo '$test1IP' | netcat -c '$test2IP' 1234");
$machine->waitUntilSucceeds("netcat -c '$test1IP' 5678 >&2");
$machine->waitUntilSucceeds("netcat -c '$test2IP' 5678 >&2");
shutdownVM_test1;
shutdownVM_test2;
destroyVM_test1;
destroyVM_test2;
};
'';
})