* 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:
|
qemuNICFlags = nic: net: machine:
|
||||||
"-net nic,vlan=${toString nic},macaddr=52:54:00:12:${toString net}:${toString machine},model=virtio " +
|
"-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
|
"-net vde,vlan=${toString nic},sock=$QEMU_VDE_SOCKET_${toString net} ";
|
||||||
# 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} ";
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,16 +10,6 @@ use Cwd;
|
|||||||
use File::Basename;
|
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'};
|
my $showGraphics = defined $ENV{'DISPLAY'};
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ use Machine;
|
|||||||
use Term::ReadLine;
|
use Term::ReadLine;
|
||||||
use IO::File;
|
use IO::File;
|
||||||
use Logger;
|
use Logger;
|
||||||
|
use Cwd;
|
||||||
|
|
||||||
$SIG{PIPE} = 'IGNORE'; # because Unix domain sockets may die unexpectedly
|
$SIG{PIPE} = 'IGNORE'; # because Unix domain sockets may die unexpectedly
|
||||||
|
|
||||||
@ -13,6 +14,19 @@ STDERR->autoflush(1);
|
|||||||
my $log = new Logger;
|
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 %vms;
|
||||||
my $context = "";
|
my $context = "";
|
||||||
|
|
||||||
@ -83,23 +97,26 @@ sub runTests {
|
|||||||
|
|
||||||
# Copy the kernel coverage data for each machine, if the kernel
|
# Copy the kernel coverage data for each machine, if the kernel
|
||||||
# has been compiled with coverage instrumentation.
|
# has been compiled with coverage instrumentation.
|
||||||
foreach my $vm (values %vms) {
|
$log->nest("collecting coverage data", sub {
|
||||||
my $gcovDir = "/sys/kernel/debug/gcov";
|
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");
|
my ($status, $out) = $vm->execute("test -e $gcovDir");
|
||||||
next if $status != 0;
|
next if $status != 0;
|
||||||
|
|
||||||
# Figure out where to put the *.gcda files so that the report
|
# Figure out where to put the *.gcda files so that the
|
||||||
# generator can find the corresponding kernel sources.
|
# report generator can find the corresponding kernel
|
||||||
my $kernelDir = $vm->mustSucceed("echo \$(dirname \$(readlink -f /var/run/current-system/kernel))/.build/linux-*");
|
# sources.
|
||||||
chomp $kernelDir;
|
my $kernelDir = $vm->mustSucceed("echo \$(dirname \$(readlink -f /var/run/current-system/kernel))/.build/linux-*");
|
||||||
my $coverageDir = "/hostfs" . $vm->stateDir() . "/coverage-data/$kernelDir";
|
chomp $kernelDir;
|
||||||
|
my $coverageDir = "/hostfs" . $vm->stateDir() . "/coverage-data/$kernelDir";
|
||||||
|
|
||||||
# Copy all the *.gcda files.
|
# 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");
|
$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) {
|
if ($nrTests != 0) {
|
||||||
$log->log("$nrSucceeded out of $nrTests tests succeeded",
|
$log->log("$nrSucceeded out of $nrTests tests succeeded",
|
||||||
@ -118,12 +135,14 @@ sub createDisk {
|
|||||||
|
|
||||||
|
|
||||||
END {
|
END {
|
||||||
foreach my $vm (values %vms) {
|
$log->nest("cleaning up", sub {
|
||||||
if ($vm->{pid}) {
|
foreach my $vm (values %vms) {
|
||||||
$log->log("killing " . $vm->{name} . " (pid " . $vm->{pid} . ")");
|
if ($vm->{pid}) {
|
||||||
kill 9, $vm->{pid};
|
$log->log("killing " . $vm->{name} . " (pid " . $vm->{pid} . ")");
|
||||||
|
kill 9, $vm->{pid};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
$log->close();
|
$log->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,31 +27,27 @@ rec {
|
|||||||
cp ${./test-driver/Logger.pm} $libDir/Logger.pm
|
cp ${./test-driver/Logger.pm} $libDir/Logger.pm
|
||||||
|
|
||||||
wrapProgram $out/bin/nixos-test-driver \
|
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"
|
--prefix PERL5LIB : "${lib.makePerlPath [ perlPackages.TermReadLineGnu perlPackages.XMLWriter ]}:$out/lib/perl5/site_perl"
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
# Run an automated test suite in the given virtual network.
|
# Run an automated test suite in the given virtual network.
|
||||||
# `network' must be the result of a call to the
|
# `driver' is the script that runs the network.
|
||||||
# `buildVirtualNetwork' function. `tests' is a Perl fragment
|
runTests = driver:
|
||||||
# describing the tests.
|
|
||||||
runTests = network: tests:
|
|
||||||
stdenv.mkDerivation {
|
stdenv.mkDerivation {
|
||||||
name = "vm-test-run";
|
name = "vm-test-run";
|
||||||
|
|
||||||
requiredSystemFeatures = [ "kvm" ];
|
requiredSystemFeatures = [ "kvm" ];
|
||||||
|
|
||||||
inherit tests;
|
buildInputs = [ pkgs.libxslt ];
|
||||||
|
|
||||||
buildInputs = [ pkgs.qemu_kvm pkgs.libxslt ];
|
|
||||||
|
|
||||||
buildCommand =
|
buildCommand =
|
||||||
''
|
''
|
||||||
mkdir -p $out/nix-support
|
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.
|
# Generate a pretty-printed log.
|
||||||
xsltproc --output $out/log.html ${./test-driver/log2html.xsl} $out/log.xml
|
xsltproc --output $out/log.html ${./test-driver/log2html.xsl} $out/log.xml
|
||||||
@ -138,10 +134,6 @@ rec {
|
|||||||
then t.testScript { inherit (vms) nodes; }
|
then t.testScript { inherit (vms) nodes; }
|
||||||
else t.testScript;
|
else t.testScript;
|
||||||
|
|
||||||
test = runTests vms testScript;
|
|
||||||
|
|
||||||
report = makeReport test;
|
|
||||||
|
|
||||||
# Generate a convenience wrapper for running the test driver
|
# Generate a convenience wrapper for running the test driver
|
||||||
# interactively with the specified network.
|
# interactively with the specified network.
|
||||||
driver = runCommand "nixos-test-driver"
|
driver = runCommand "nixos-test-driver"
|
||||||
@ -150,14 +142,19 @@ rec {
|
|||||||
}
|
}
|
||||||
''
|
''
|
||||||
mkdir -p $out/bin
|
mkdir -p $out/bin
|
||||||
|
echo "$testScript" > $out/test-script
|
||||||
ln -s ${vms}/bin/* $out/bin/
|
ln -s ${vms}/bin/* $out/bin/
|
||||||
ln -s ${testDriver}/bin/* $out/bin/
|
ln -s ${testDriver}/bin/* $out/bin/
|
||||||
wrapProgram $out/bin/nixos-test-driver \
|
wrapProgram $out/bin/nixos-test-driver \
|
||||||
--add-flags "${vms}/vms/*/bin/run-*-vm" \
|
--add-flags "${vms}/vms/*/bin/run-*-vm" \
|
||||||
--run "testScript=\"\$(cat $out/test-script)\"" \
|
--run "testScript=\"\$(cat $out/test-script)\"" \
|
||||||
--set testScript '"$testScript"'
|
--set testScript '"$testScript"' \
|
||||||
echo "$testScript" > $out/test-script
|
--set VLANS '"${toString (map (m: m.config.virtualisation.vlans) (lib.attrValues vms.nodes))}"' \
|
||||||
''; # "
|
''; # "
|
||||||
|
|
||||||
|
test = runTests driver;
|
||||||
|
|
||||||
|
report = makeReport test;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user