forked from mirrors/nixpkgs
* Use vde_switch instead of QEMU's multicast feature to tie QEMU VMs
together into virtual networks. This has several advantages: - It's more secure because the QEMU instances use Unix domain sockets to talk to the switch. - It doesn't depend on the host's network interfaces. (Local multicast fails if there is no default gateway, so for instance it fails if a laptop is not connected to any network.) - VDE devices can be connected together to form arbitrary network topologies. - VDE has a "wirefilter" tool to emulate delays and packet loss, which are useful for network testing. svn path=/nixos/trunk/; revision=25526
This commit is contained in:
parent
fd48855284
commit
b3dbcbe249
|
@ -4,12 +4,6 @@
|
|||
|
||||
qemuNICFlags = nic: net: machine:
|
||||
"-net nic,vlan=${toString nic},macaddr=52:54:00:12:${toString net}:${toString machine},model=virtio " +
|
||||
# Use 232.0.1.<vlan> as the multicast address to connect VMs on
|
||||
# the same vlan, but allow it to be overriden using the
|
||||
# $QEMU_MCAST_ADDR_<vlan> environment variable. The test driver
|
||||
# sets this variable to prevent collisions between parallel
|
||||
# builds.
|
||||
"-net socket,vlan=${toString nic},mcast=" +
|
||||
"\${QEMU_MCAST_ADDR_${toString net}:-232.0.1.${toString net}:1234} ";
|
||||
|
||||
"-net vde,vlan=${toString nic},sock=$QEMU_VDE_SOCKET_${toString net} ";
|
||||
|
||||
}
|
||||
|
|
|
@ -10,16 +10,6 @@ use Cwd;
|
|||
use File::Basename;
|
||||
|
||||
|
||||
# Stuff our PID in the multicast address/port to prevent collissions
|
||||
# with other NixOS VM networks. See
|
||||
# http://www.iana.org/assignments/multicast-addresses/.
|
||||
my $mcastPrefix = "232.18";
|
||||
my $mcastSuffix = ($$ >> 8) . ":" . (64000 + ($$ & 0xff));
|
||||
print STDERR "using multicast addresses $mcastPrefix.<vlan>.$mcastSuffix\n";
|
||||
for (my $n = 0; $n < 256; $n++) {
|
||||
$ENV{"QEMU_MCAST_ADDR_$n"} = "$mcastPrefix.$n.$mcastSuffix";
|
||||
}
|
||||
|
||||
my $showGraphics = defined $ENV{'DISPLAY'};
|
||||
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ use Machine;
|
|||
use Term::ReadLine;
|
||||
use IO::File;
|
||||
use Logger;
|
||||
use Cwd;
|
||||
|
||||
$SIG{PIPE} = 'IGNORE'; # because Unix domain sockets may die unexpectedly
|
||||
|
||||
|
@ -13,6 +14,19 @@ STDERR->autoflush(1);
|
|||
my $log = new Logger;
|
||||
|
||||
|
||||
# Start vde_switch for each network required by the test.
|
||||
my %vlans;
|
||||
foreach my $vlan (split / /, $ENV{VLANS} || "") {
|
||||
next if defined $vlans{$vlan};
|
||||
$log->log("starting VDE switch for network $vlan");
|
||||
my $socket = Cwd::abs_path "./vde$vlan.ctl";
|
||||
system("vde_switch -d -s $socket") == 0
|
||||
or die "cannot start vde_switch";
|
||||
$ENV{"QEMU_VDE_SOCKET_$vlan"} = $socket;
|
||||
$vlans{$vlan} = 1;
|
||||
}
|
||||
|
||||
|
||||
my %vms;
|
||||
my $context = "";
|
||||
|
||||
|
@ -83,23 +97,26 @@ sub runTests {
|
|||
|
||||
# Copy the kernel coverage data for each machine, if the kernel
|
||||
# has been compiled with coverage instrumentation.
|
||||
foreach my $vm (values %vms) {
|
||||
my $gcovDir = "/sys/kernel/debug/gcov";
|
||||
$log->nest("collecting coverage data", sub {
|
||||
foreach my $vm (values %vms) {
|
||||
my $gcovDir = "/sys/kernel/debug/gcov";
|
||||
|
||||
next unless $vm->isUp();
|
||||
next unless $vm->isUp();
|
||||
|
||||
my ($status, $out) = $vm->execute("test -e $gcovDir");
|
||||
next if $status != 0;
|
||||
my ($status, $out) = $vm->execute("test -e $gcovDir");
|
||||
next if $status != 0;
|
||||
|
||||
# Figure out where to put the *.gcda files so that the report
|
||||
# generator can find the corresponding kernel sources.
|
||||
my $kernelDir = $vm->mustSucceed("echo \$(dirname \$(readlink -f /var/run/current-system/kernel))/.build/linux-*");
|
||||
chomp $kernelDir;
|
||||
my $coverageDir = "/hostfs" . $vm->stateDir() . "/coverage-data/$kernelDir";
|
||||
# Figure out where to put the *.gcda files so that the
|
||||
# report generator can find the corresponding kernel
|
||||
# sources.
|
||||
my $kernelDir = $vm->mustSucceed("echo \$(dirname \$(readlink -f /var/run/current-system/kernel))/.build/linux-*");
|
||||
chomp $kernelDir;
|
||||
my $coverageDir = "/hostfs" . $vm->stateDir() . "/coverage-data/$kernelDir";
|
||||
|
||||
# Copy all the *.gcda files.
|
||||
$vm->execute("for d in $gcovDir/nix/store/*/.build/linux-*; do for i in \$(cd \$d && find -name '*.gcda'); do echo \$i; mkdir -p $coverageDir/\$(dirname \$i); cp -v \$d/\$i $coverageDir/\$i; done; done");
|
||||
}
|
||||
# Copy all the *.gcda files.
|
||||
$vm->execute("for d in $gcovDir/nix/store/*/.build/linux-*; do for i in \$(cd \$d && find -name '*.gcda'); do echo \$i; mkdir -p $coverageDir/\$(dirname \$i); cp -v \$d/\$i $coverageDir/\$i; done; done");
|
||||
}
|
||||
});
|
||||
|
||||
if ($nrTests != 0) {
|
||||
$log->log("$nrSucceeded out of $nrTests tests succeeded",
|
||||
|
@ -118,12 +135,14 @@ sub createDisk {
|
|||
|
||||
|
||||
END {
|
||||
foreach my $vm (values %vms) {
|
||||
if ($vm->{pid}) {
|
||||
$log->log("killing " . $vm->{name} . " (pid " . $vm->{pid} . ")");
|
||||
kill 9, $vm->{pid};
|
||||
$log->nest("cleaning up", sub {
|
||||
foreach my $vm (values %vms) {
|
||||
if ($vm->{pid}) {
|
||||
$log->log("killing " . $vm->{name} . " (pid " . $vm->{pid} . ")");
|
||||
kill 9, $vm->{pid};
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
$log->close();
|
||||
}
|
||||
|
||||
|
|
|
@ -27,31 +27,27 @@ rec {
|
|||
cp ${./test-driver/Logger.pm} $libDir/Logger.pm
|
||||
|
||||
wrapProgram $out/bin/nixos-test-driver \
|
||||
--prefix PATH : "${imagemagick}/bin" \
|
||||
--prefix PATH : "${pkgs.qemu_kvm}/bin:${pkgs.vde2}/bin:${imagemagick}/bin" \
|
||||
--prefix PERL5LIB : "${lib.makePerlPath [ perlPackages.TermReadLineGnu perlPackages.XMLWriter ]}:$out/lib/perl5/site_perl"
|
||||
'';
|
||||
};
|
||||
|
||||
|
||||
# Run an automated test suite in the given virtual network.
|
||||
# `network' must be the result of a call to the
|
||||
# `buildVirtualNetwork' function. `tests' is a Perl fragment
|
||||
# describing the tests.
|
||||
runTests = network: tests:
|
||||
# `driver' is the script that runs the network.
|
||||
runTests = driver:
|
||||
stdenv.mkDerivation {
|
||||
name = "vm-test-run";
|
||||
|
||||
requiredSystemFeatures = [ "kvm" ];
|
||||
|
||||
inherit tests;
|
||||
|
||||
buildInputs = [ pkgs.qemu_kvm pkgs.libxslt ];
|
||||
buildInputs = [ pkgs.libxslt ];
|
||||
|
||||
buildCommand =
|
||||
''
|
||||
mkdir -p $out/nix-support
|
||||
|
||||
LOGFILE=$out/log.xml ${testDriver}/bin/nixos-test-driver ${network}/vms/*/bin/run-*-vm || failed=1
|
||||
LOGFILE=$out/log.xml tests="testScript" ${driver}/bin/nixos-test-driver || failed=1
|
||||
|
||||
# Generate a pretty-printed log.
|
||||
xsltproc --output $out/log.html ${./test-driver/log2html.xsl} $out/log.xml
|
||||
|
@ -138,10 +134,6 @@ rec {
|
|||
then t.testScript { inherit (vms) nodes; }
|
||||
else t.testScript;
|
||||
|
||||
test = runTests vms testScript;
|
||||
|
||||
report = makeReport test;
|
||||
|
||||
# Generate a convenience wrapper for running the test driver
|
||||
# interactively with the specified network.
|
||||
driver = runCommand "nixos-test-driver"
|
||||
|
@ -150,14 +142,19 @@ rec {
|
|||
}
|
||||
''
|
||||
mkdir -p $out/bin
|
||||
echo "$testScript" > $out/test-script
|
||||
ln -s ${vms}/bin/* $out/bin/
|
||||
ln -s ${testDriver}/bin/* $out/bin/
|
||||
wrapProgram $out/bin/nixos-test-driver \
|
||||
--add-flags "${vms}/vms/*/bin/run-*-vm" \
|
||||
--run "testScript=\"\$(cat $out/test-script)\"" \
|
||||
--set testScript '"$testScript"'
|
||||
echo "$testScript" > $out/test-script
|
||||
--set testScript '"$testScript"' \
|
||||
--set VLANS '"${toString (map (m: m.config.virtualisation.vlans) (lib.attrValues vms.nodes))}"' \
|
||||
''; # "
|
||||
|
||||
test = runTests driver;
|
||||
|
||||
report = makeReport test;
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Reference in a new issue