* Stuff for automatic and manual testing of NixOS VMs.
lib/build-vms.nix contains a function `buildVirtualNetwork' that
takes a specification of a network of machines (as an attribute set
of NixOS machine configurations) and builds a script that starts
each configuration in a separate QEMU/KVM VM and connects them
together in a virtual network. This script can be run manually to
test the VMs interactively. There is also a function `runTests'
that starts and runs the virtual network in a derivation, and
then executes a test specification that tells the VMs to do certain
things (i.e., letting one VM send an HTTP request to a webserver on
another VM). The tests are written in Perl (for now).
tests/subversion.nix shows a simple example, namely a network of two
machines: a webserver that runs the Subversion subservice, and a
client. Apache, Subversion and a few other packages are built with
coverage analysis instrumentation. For instance,
$ nix-build tests/subversion.nix -A vms
$ ./result/bin/run-vms
starts two QEMU/KVM instances. When they have finished booting, the
webserver can be accessed from the host through
http://localhost:8081/.
It also has a small test suite:
$ nix-build tests/subversion.nix -A report
This runs the VMs in a derivation, runs the tests, and then produces
a distributed code coverage analysis report (i.e. it shows the
combined coverage on both machines).
The Perl test driver program is in lib/test-driver. It executes
commands on the guest machines by connecting to a root shell running
on port 514 (provided by modules/testing/test-instrumentation.nix).
The VMs are connected together in a virtual network using QEMU's
multicast feature. This isn't very secure. At the very least,
other processes on the same machine can listen to or send packets on
the virtual network. On the plus side, we don't need to be root to
set up a multicast virtual network, so we can do it from a
derivation. Maybe we can use VDE instead.
(Moved from the vario repository.)
svn path=/nixos/trunk/; revision=16899
2009-08-31 16:25:12 +02:00
|
|
|
package Machine;
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
use Socket;
|
|
|
|
use IO::Handle;
|
|
|
|
use POSIX qw(dup2);
|
|
|
|
|
|
|
|
|
|
|
|
# Stuff our PID in the multicast address/port to prevent collissions
|
|
|
|
# with other NixOS VM networks.
|
|
|
|
my $mcastAddr = "232.18.1." . ($$ >> 8) . ":" . (64000 + ($$ & 0xff));
|
|
|
|
print STDERR "using multicast address $mcastAddr\n";
|
|
|
|
|
|
|
|
|
|
|
|
sub new {
|
|
|
|
my ($class, $vmScript) = @_;
|
|
|
|
|
|
|
|
$vmScript =~ /run-(.*)-vm$/ or die;
|
|
|
|
my $name = $1;
|
|
|
|
|
|
|
|
my $tmpDir = $ENV{'TMPDIR'} || "/tmp";
|
|
|
|
|
|
|
|
my $self = {
|
|
|
|
script => $vmScript,
|
|
|
|
name => $name,
|
|
|
|
booted => 0,
|
|
|
|
pid => 0,
|
|
|
|
connected => 0,
|
|
|
|
socket => undef,
|
|
|
|
stateDir => "$tmpDir/$name",
|
|
|
|
};
|
|
|
|
|
|
|
|
mkdir $self->{stateDir}, 0700;
|
|
|
|
|
|
|
|
bless $self, $class;
|
|
|
|
return $self;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub log {
|
|
|
|
my ($self, $msg) = @_;
|
|
|
|
chomp $msg;
|
|
|
|
print STDERR $self->{name}, ": $msg\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub name {
|
|
|
|
my ($self) = @_;
|
|
|
|
return $self->{name};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-09-02 00:22:45 +02:00
|
|
|
sub stateDir {
|
|
|
|
my ($self) = @_;
|
|
|
|
return $self->{stateDir};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
* Stuff for automatic and manual testing of NixOS VMs.
lib/build-vms.nix contains a function `buildVirtualNetwork' that
takes a specification of a network of machines (as an attribute set
of NixOS machine configurations) and builds a script that starts
each configuration in a separate QEMU/KVM VM and connects them
together in a virtual network. This script can be run manually to
test the VMs interactively. There is also a function `runTests'
that starts and runs the virtual network in a derivation, and
then executes a test specification that tells the VMs to do certain
things (i.e., letting one VM send an HTTP request to a webserver on
another VM). The tests are written in Perl (for now).
tests/subversion.nix shows a simple example, namely a network of two
machines: a webserver that runs the Subversion subservice, and a
client. Apache, Subversion and a few other packages are built with
coverage analysis instrumentation. For instance,
$ nix-build tests/subversion.nix -A vms
$ ./result/bin/run-vms
starts two QEMU/KVM instances. When they have finished booting, the
webserver can be accessed from the host through
http://localhost:8081/.
It also has a small test suite:
$ nix-build tests/subversion.nix -A report
This runs the VMs in a derivation, runs the tests, and then produces
a distributed code coverage analysis report (i.e. it shows the
combined coverage on both machines).
The Perl test driver program is in lib/test-driver. It executes
commands on the guest machines by connecting to a root shell running
on port 514 (provided by modules/testing/test-instrumentation.nix).
The VMs are connected together in a virtual network using QEMU's
multicast feature. This isn't very secure. At the very least,
other processes on the same machine can listen to or send packets on
the virtual network. On the plus side, we don't need to be root to
set up a multicast virtual network, so we can do it from a
derivation. Maybe we can use VDE instead.
(Moved from the vario repository.)
svn path=/nixos/trunk/; revision=16899
2009-08-31 16:25:12 +02:00
|
|
|
sub start {
|
|
|
|
my ($self) = @_;
|
|
|
|
return if $self->{booted};
|
|
|
|
|
|
|
|
$self->log("starting vm");
|
|
|
|
|
|
|
|
my $pid = fork();
|
|
|
|
die if $pid == -1;
|
|
|
|
|
|
|
|
if ($pid == 0) {
|
|
|
|
my $name = $self->{name};
|
|
|
|
open LOG, "| sed --unbuffered 's|^|$name console: |'" or die;
|
|
|
|
dup2(fileno(LOG), fileno(STDOUT));
|
|
|
|
dup2(fileno(LOG), fileno(STDERR));
|
|
|
|
$ENV{TMPDIR} = $self->{stateDir};
|
2009-09-02 09:52:36 +02:00
|
|
|
$ENV{QEMU_OPTS} = "-nographic -no-reboot -redir tcp:65535::514 -net nic,vlan=1 -net socket,vlan=1,mcast=$mcastAddr";
|
* Stuff for automatic and manual testing of NixOS VMs.
lib/build-vms.nix contains a function `buildVirtualNetwork' that
takes a specification of a network of machines (as an attribute set
of NixOS machine configurations) and builds a script that starts
each configuration in a separate QEMU/KVM VM and connects them
together in a virtual network. This script can be run manually to
test the VMs interactively. There is also a function `runTests'
that starts and runs the virtual network in a derivation, and
then executes a test specification that tells the VMs to do certain
things (i.e., letting one VM send an HTTP request to a webserver on
another VM). The tests are written in Perl (for now).
tests/subversion.nix shows a simple example, namely a network of two
machines: a webserver that runs the Subversion subservice, and a
client. Apache, Subversion and a few other packages are built with
coverage analysis instrumentation. For instance,
$ nix-build tests/subversion.nix -A vms
$ ./result/bin/run-vms
starts two QEMU/KVM instances. When they have finished booting, the
webserver can be accessed from the host through
http://localhost:8081/.
It also has a small test suite:
$ nix-build tests/subversion.nix -A report
This runs the VMs in a derivation, runs the tests, and then produces
a distributed code coverage analysis report (i.e. it shows the
combined coverage on both machines).
The Perl test driver program is in lib/test-driver. It executes
commands on the guest machines by connecting to a root shell running
on port 514 (provided by modules/testing/test-instrumentation.nix).
The VMs are connected together in a virtual network using QEMU's
multicast feature. This isn't very secure. At the very least,
other processes on the same machine can listen to or send packets on
the virtual network. On the plus side, we don't need to be root to
set up a multicast virtual network, so we can do it from a
derivation. Maybe we can use VDE instead.
(Moved from the vario repository.)
svn path=/nixos/trunk/; revision=16899
2009-08-31 16:25:12 +02:00
|
|
|
$ENV{QEMU_KERNEL_PARAMS} = "console=ttyS0 panic=1 hostTmpDir=$ENV{TMPDIR}";
|
|
|
|
chdir $self->{stateDir} or die;
|
|
|
|
exec $self->{script};
|
|
|
|
die;
|
|
|
|
}
|
|
|
|
|
|
|
|
$self->log("vm running as pid $pid");
|
|
|
|
$self->{pid} = $pid;
|
|
|
|
$self->{booted} = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub connect {
|
|
|
|
my ($self) = @_;
|
|
|
|
return if $self->{connected};
|
|
|
|
|
|
|
|
$self->start;
|
|
|
|
|
2009-09-02 10:36:30 +02:00
|
|
|
my $try = 0;
|
* Stuff for automatic and manual testing of NixOS VMs.
lib/build-vms.nix contains a function `buildVirtualNetwork' that
takes a specification of a network of machines (as an attribute set
of NixOS machine configurations) and builds a script that starts
each configuration in a separate QEMU/KVM VM and connects them
together in a virtual network. This script can be run manually to
test the VMs interactively. There is also a function `runTests'
that starts and runs the virtual network in a derivation, and
then executes a test specification that tells the VMs to do certain
things (i.e., letting one VM send an HTTP request to a webserver on
another VM). The tests are written in Perl (for now).
tests/subversion.nix shows a simple example, namely a network of two
machines: a webserver that runs the Subversion subservice, and a
client. Apache, Subversion and a few other packages are built with
coverage analysis instrumentation. For instance,
$ nix-build tests/subversion.nix -A vms
$ ./result/bin/run-vms
starts two QEMU/KVM instances. When they have finished booting, the
webserver can be accessed from the host through
http://localhost:8081/.
It also has a small test suite:
$ nix-build tests/subversion.nix -A report
This runs the VMs in a derivation, runs the tests, and then produces
a distributed code coverage analysis report (i.e. it shows the
combined coverage on both machines).
The Perl test driver program is in lib/test-driver. It executes
commands on the guest machines by connecting to a root shell running
on port 514 (provided by modules/testing/test-instrumentation.nix).
The VMs are connected together in a virtual network using QEMU's
multicast feature. This isn't very secure. At the very least,
other processes on the same machine can listen to or send packets on
the virtual network. On the plus side, we don't need to be root to
set up a multicast virtual network, so we can do it from a
derivation. Maybe we can use VDE instead.
(Moved from the vario repository.)
svn path=/nixos/trunk/; revision=16899
2009-08-31 16:25:12 +02:00
|
|
|
while (1) {
|
|
|
|
last if -e ($self->{stateDir} . "/running");
|
|
|
|
sleep 1;
|
2009-09-03 02:05:38 +02:00
|
|
|
die ("VM " . $self->{name} . " timed out") if $try++ > 300;
|
* Stuff for automatic and manual testing of NixOS VMs.
lib/build-vms.nix contains a function `buildVirtualNetwork' that
takes a specification of a network of machines (as an attribute set
of NixOS machine configurations) and builds a script that starts
each configuration in a separate QEMU/KVM VM and connects them
together in a virtual network. This script can be run manually to
test the VMs interactively. There is also a function `runTests'
that starts and runs the virtual network in a derivation, and
then executes a test specification that tells the VMs to do certain
things (i.e., letting one VM send an HTTP request to a webserver on
another VM). The tests are written in Perl (for now).
tests/subversion.nix shows a simple example, namely a network of two
machines: a webserver that runs the Subversion subservice, and a
client. Apache, Subversion and a few other packages are built with
coverage analysis instrumentation. For instance,
$ nix-build tests/subversion.nix -A vms
$ ./result/bin/run-vms
starts two QEMU/KVM instances. When they have finished booting, the
webserver can be accessed from the host through
http://localhost:8081/.
It also has a small test suite:
$ nix-build tests/subversion.nix -A report
This runs the VMs in a derivation, runs the tests, and then produces
a distributed code coverage analysis report (i.e. it shows the
combined coverage on both machines).
The Perl test driver program is in lib/test-driver. It executes
commands on the guest machines by connecting to a root shell running
on port 514 (provided by modules/testing/test-instrumentation.nix).
The VMs are connected together in a virtual network using QEMU's
multicast feature. This isn't very secure. At the very least,
other processes on the same machine can listen to or send packets on
the virtual network. On the plus side, we don't need to be root to
set up a multicast virtual network, so we can do it from a
derivation. Maybe we can use VDE instead.
(Moved from the vario repository.)
svn path=/nixos/trunk/; revision=16899
2009-08-31 16:25:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
$self->log("trying to connect");
|
|
|
|
my $socket = new IO::Handle;
|
|
|
|
$self->{socket} = $socket;
|
|
|
|
socket($socket, PF_UNIX, SOCK_STREAM, 0) or die;
|
|
|
|
connect($socket, sockaddr_un($self->{stateDir} . "/65535.socket")) or die;
|
|
|
|
$socket->autoflush(1);
|
|
|
|
print $socket "echo hello\n" or next;
|
|
|
|
flush $socket;
|
|
|
|
my $line = readline($socket);
|
|
|
|
chomp $line;
|
|
|
|
last if $line eq "hello";
|
|
|
|
sleep 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
$self->log("connected");
|
|
|
|
$self->{connected} = 1;
|
|
|
|
|
|
|
|
print { $self->{socket} } "PATH=/var/run/current-system/sw/bin:/var/run/current-system/sw/sbin:\$PATH\n";
|
|
|
|
print { $self->{socket} } "export GCOV_PREFIX=/tmp/coverage-data\n";
|
|
|
|
print { $self->{socket} } "cd /tmp\n";
|
|
|
|
# !!! Should make sure the commands above don't produce output, otherwise we're out of sync.
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub waitForShutdown {
|
|
|
|
my ($self) = @_;
|
|
|
|
return unless $self->{booted};
|
|
|
|
|
|
|
|
waitpid $self->{pid}, 0;
|
|
|
|
$self->{pid} = 0;
|
|
|
|
$self->{booted} = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub execute {
|
|
|
|
my ($self, $command) = @_;
|
|
|
|
|
|
|
|
$self->connect;
|
|
|
|
|
|
|
|
$self->log("running command: $command");
|
|
|
|
|
2009-09-02 00:22:45 +02:00
|
|
|
print { $self->{socket} } ("( $command ); echo '|!=EOF' \$?\n");
|
* Stuff for automatic and manual testing of NixOS VMs.
lib/build-vms.nix contains a function `buildVirtualNetwork' that
takes a specification of a network of machines (as an attribute set
of NixOS machine configurations) and builds a script that starts
each configuration in a separate QEMU/KVM VM and connects them
together in a virtual network. This script can be run manually to
test the VMs interactively. There is also a function `runTests'
that starts and runs the virtual network in a derivation, and
then executes a test specification that tells the VMs to do certain
things (i.e., letting one VM send an HTTP request to a webserver on
another VM). The tests are written in Perl (for now).
tests/subversion.nix shows a simple example, namely a network of two
machines: a webserver that runs the Subversion subservice, and a
client. Apache, Subversion and a few other packages are built with
coverage analysis instrumentation. For instance,
$ nix-build tests/subversion.nix -A vms
$ ./result/bin/run-vms
starts two QEMU/KVM instances. When they have finished booting, the
webserver can be accessed from the host through
http://localhost:8081/.
It also has a small test suite:
$ nix-build tests/subversion.nix -A report
This runs the VMs in a derivation, runs the tests, and then produces
a distributed code coverage analysis report (i.e. it shows the
combined coverage on both machines).
The Perl test driver program is in lib/test-driver. It executes
commands on the guest machines by connecting to a root shell running
on port 514 (provided by modules/testing/test-instrumentation.nix).
The VMs are connected together in a virtual network using QEMU's
multicast feature. This isn't very secure. At the very least,
other processes on the same machine can listen to or send packets on
the virtual network. On the plus side, we don't need to be root to
set up a multicast virtual network, so we can do it from a
derivation. Maybe we can use VDE instead.
(Moved from the vario repository.)
svn path=/nixos/trunk/; revision=16899
2009-08-31 16:25:12 +02:00
|
|
|
|
|
|
|
my $out = "";
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
my $line = readline($self->{socket}) or die "connection to VM lost unexpectedly";
|
|
|
|
#$self->log("got line: $line");
|
|
|
|
if ($line =~ /^(.*)\|\!\=EOF\s+(\d+)$/) {
|
|
|
|
$out .= $1;
|
|
|
|
$self->log("exit status $2");
|
|
|
|
return ($2, $out);
|
|
|
|
}
|
|
|
|
$out .= $line;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub mustSucceed {
|
|
|
|
my ($self, $command) = @_;
|
|
|
|
my ($status, $out) = $self->execute($command);
|
|
|
|
if ($status != 0) {
|
|
|
|
$self->log("output: $out");
|
|
|
|
die "command `$command' did not succeed (exit code $status)";
|
|
|
|
}
|
|
|
|
return $out;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub mustFail {
|
|
|
|
my ($self, $command) = @_;
|
|
|
|
my ($status, $out) = $self->execute($command);
|
|
|
|
die "command `$command' unexpectedly succeeded"
|
|
|
|
if $status == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# Wait for an Upstart job to reach the "running" state.
|
|
|
|
sub waitForJob {
|
|
|
|
my ($self, $jobName) = @_;
|
|
|
|
while (1) {
|
|
|
|
my ($status, $out) = $self->execute("initctl status $jobName");
|
|
|
|
return if $out =~ /\(start\)\s+running/;
|
|
|
|
sleep 1;
|
|
|
|
# !!! need a timeout
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-09-02 16:48:28 +02:00
|
|
|
# Wait until the specified file exists.
|
|
|
|
sub waitForFile {
|
|
|
|
my ($self, $fileName) = @_;
|
|
|
|
while (1) {
|
|
|
|
my ($status, $out) = $self->execute("test -e $fileName");
|
|
|
|
return if $status == 0;
|
|
|
|
sleep 1;
|
|
|
|
# !!! need a timeout
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
* Stuff for automatic and manual testing of NixOS VMs.
lib/build-vms.nix contains a function `buildVirtualNetwork' that
takes a specification of a network of machines (as an attribute set
of NixOS machine configurations) and builds a script that starts
each configuration in a separate QEMU/KVM VM and connects them
together in a virtual network. This script can be run manually to
test the VMs interactively. There is also a function `runTests'
that starts and runs the virtual network in a derivation, and
then executes a test specification that tells the VMs to do certain
things (i.e., letting one VM send an HTTP request to a webserver on
another VM). The tests are written in Perl (for now).
tests/subversion.nix shows a simple example, namely a network of two
machines: a webserver that runs the Subversion subservice, and a
client. Apache, Subversion and a few other packages are built with
coverage analysis instrumentation. For instance,
$ nix-build tests/subversion.nix -A vms
$ ./result/bin/run-vms
starts two QEMU/KVM instances. When they have finished booting, the
webserver can be accessed from the host through
http://localhost:8081/.
It also has a small test suite:
$ nix-build tests/subversion.nix -A report
This runs the VMs in a derivation, runs the tests, and then produces
a distributed code coverage analysis report (i.e. it shows the
combined coverage on both machines).
The Perl test driver program is in lib/test-driver. It executes
commands on the guest machines by connecting to a root shell running
on port 514 (provided by modules/testing/test-instrumentation.nix).
The VMs are connected together in a virtual network using QEMU's
multicast feature. This isn't very secure. At the very least,
other processes on the same machine can listen to or send packets on
the virtual network. On the plus side, we don't need to be root to
set up a multicast virtual network, so we can do it from a
derivation. Maybe we can use VDE instead.
(Moved from the vario repository.)
svn path=/nixos/trunk/; revision=16899
2009-08-31 16:25:12 +02:00
|
|
|
sub stopJob {
|
|
|
|
my ($self, $jobName) = @_;
|
|
|
|
$self->execute("initctl stop $jobName");
|
|
|
|
while (1) {
|
|
|
|
my ($status, $out) = $self->execute("initctl status $jobName");
|
|
|
|
return if $out =~ /\(stop\)\s+waiting/;
|
|
|
|
sleep 1;
|
|
|
|
# !!! need a timeout
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# Wait until the machine is listening on the given TCP port.
|
|
|
|
sub waitForOpenPort {
|
|
|
|
my ($self, $port) = @_;
|
|
|
|
while (1) {
|
|
|
|
my ($status, $out) = $self->execute("nc -z localhost $port");
|
|
|
|
return if $status == 0;
|
|
|
|
sleep 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# Wait until the machine is not listening on the given TCP port.
|
|
|
|
sub waitForClosedPort {
|
|
|
|
my ($self, $port) = @_;
|
|
|
|
while (1) {
|
|
|
|
my ($status, $out) = $self->execute("nc -z localhost $port");
|
|
|
|
return if $status != 0;
|
|
|
|
sleep 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub shutdown {
|
|
|
|
my ($self) = @_;
|
|
|
|
return unless $self->{booted};
|
|
|
|
|
|
|
|
$self->execute("poweroff -f &");
|
|
|
|
|
|
|
|
$self->waitForShutdown;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# Make the machine unreachable by shutting down eth1 (the multicast
|
|
|
|
# interface used to talk to the other VMs). We keep eth0 up so that
|
|
|
|
# the test driver can continue to talk to the machine.
|
|
|
|
sub block {
|
|
|
|
my ($self) = @_;
|
|
|
|
$self->mustSucceed("ifconfig eth1 down");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# Make the machine reachable.
|
|
|
|
sub unblock {
|
|
|
|
my ($self) = @_;
|
|
|
|
$self->mustSucceed("ifconfig eth1 up");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
1;
|