* Allow more complex network topologies in distributed tests. Each
machine can now declare an option `virtualisation.vlans' that causes it to have network interfaces connected to each listed virtual network. For instance, virtualisation.vlans = [ 1 2 ]; causes the machine to have two interfaces (in addition to eth0, used by the test driver to control the machine): eth1 connected to network 1 with IP address 192.168.1.<i>, and eth2 connected to network 2 with address 192.168.2.<i> (where <i> is the index of the machine in the `nodes' attribute set). On the other hand, virtualisation.vlans = [ 2 ]; causes the machine to only have an eth1 connected to network 2 with address 192.168.2.<i>. So each virtual network <n> is assigned the IP range 192.168.<n>.0/24. Each virtual network is implemented using a separate multicast address on the host, so guests really cannot talk to networks to which they are not connected. * Added a simple NAT test to demonstrate this. * Added an option `virtualisation.qemu.options' to specify QEMU command-line options. Used to factor out some commonality between the test driver script and the interactive test script. svn path=/nixos/trunk/; revision=21928
This commit is contained in:
parent
85bd5bad32
commit
4dac9e5814
|
@ -40,7 +40,7 @@ rec {
|
||||||
for i in $out/vms/*; do
|
for i in $out/vms/*; do
|
||||||
port2=\$((port++))
|
port2=\$((port++))
|
||||||
echo "forwarding localhost:\$port2 to \$(basename \$i):80"
|
echo "forwarding localhost:\$port2 to \$(basename \$i):80"
|
||||||
QEMU_OPTS="-redir tcp:\$port2::80 -net nic,vlan=1,model=virtio -net socket,vlan=1,mcast=232.0.1.1:1234" \$i/bin/run-*-vm &
|
QEMU_OPTS="-redir tcp:\$port2::80" \$i/bin/run-*-vm &
|
||||||
done
|
done
|
||||||
EOF
|
EOF
|
||||||
chmod +x $out/bin/run-vms
|
chmod +x $out/bin/run-vms
|
||||||
|
@ -71,26 +71,57 @@ rec {
|
||||||
|
|
||||||
machines = lib.attrNames nodes;
|
machines = lib.attrNames nodes;
|
||||||
|
|
||||||
machinesWithIP = zip machines
|
machinesNumbered = zip machines (lib.range 1 254);
|
||||||
(map (n: "192.168.1.${toString n}") (lib.range 1 254));
|
|
||||||
|
|
||||||
# Generate a /etc/hosts file.
|
nodes_ = lib.flip map machinesNumbered (m: lib.nameValuePair m.first
|
||||||
hosts = lib.concatMapStrings (m: "${m.second} ${m.first}\n") machinesWithIP;
|
[ ( { config, pkgs, nodes, ... }:
|
||||||
|
let
|
||||||
|
interfacesNumbered = zip config.virtualisation.vlans (lib.range 1 255);
|
||||||
|
interfaces =
|
||||||
|
lib.flip map interfacesNumbered ({ first, second }:
|
||||||
|
{ name = "eth${toString second}";
|
||||||
|
ipAddress = "192.168.${toString first}.${toString m.second}";
|
||||||
|
}
|
||||||
|
);
|
||||||
|
in
|
||||||
|
{ key = "ip-address";
|
||||||
|
config =
|
||||||
|
{ networking.hostName = m.first;
|
||||||
|
|
||||||
nodes_ = map (m: lib.nameValuePair m.first [
|
networking.interfaces = interfaces;
|
||||||
{ key = "ip-address";
|
|
||||||
config =
|
networking.primaryIPAddress =
|
||||||
{ networking.hostName = m.first;
|
lib.optionalString (interfaces != []) (lib.head interfaces).ipAddress;
|
||||||
networking.interfaces =
|
|
||||||
[ { name = "eth1";
|
# Put the IP addresses of all VMs in this machine's
|
||||||
ipAddress = m.second;
|
# /etc/hosts file. If a machine has multiple
|
||||||
}
|
# interfaces, use the IP address corresponding to
|
||||||
];
|
# the first interface (i.e. the first network in its
|
||||||
networking.extraHosts = hosts;
|
# virtualisation.vlans option).
|
||||||
};
|
networking.extraHosts = lib.flip lib.concatMapStrings machines
|
||||||
}
|
(m: let config = (lib.getAttr m nodes).config; in
|
||||||
|
lib.optionalString (config.networking.primaryIPAddress != "")
|
||||||
|
("${config.networking.primaryIPAddress} " +
|
||||||
|
"${config.networking.hostName}\n"));
|
||||||
|
|
||||||
|
virtualisation.qemu.options =
|
||||||
|
lib.flip lib.concatMapStrings interfacesNumbered ({ first, second }:
|
||||||
|
"-net nic,vlan=${toString second},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 second},mcast=" +
|
||||||
|
"\${QEMU_MCAST_ADDR_${toString first}:-232.0.1.${toString first}:1234} "
|
||||||
|
);
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
(lib.getAttr m.first nodes)
|
(lib.getAttr m.first nodes)
|
||||||
]) machinesWithIP;
|
] );
|
||||||
|
|
||||||
in lib.listToAttrs nodes_;
|
in lib.listToAttrs nodes_;
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,14 @@ use Cwd;
|
||||||
|
|
||||||
|
|
||||||
# Stuff our PID in the multicast address/port to prevent collissions
|
# Stuff our PID in the multicast address/port to prevent collissions
|
||||||
# with other NixOS VM networks.
|
# with other NixOS VM networks. See
|
||||||
my $mcastAddr = "232.18.1." . ($$ >> 8) . ":" . (64000 + ($$ & 0xff));
|
# http://www.iana.org/assignments/multicast-addresses/.
|
||||||
print STDERR "using multicast address $mcastAddr\n";
|
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";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
sub new {
|
sub new {
|
||||||
|
@ -107,7 +112,7 @@ sub start {
|
||||||
dup2(fileno($serialC), fileno(STDOUT));
|
dup2(fileno($serialC), fileno(STDOUT));
|
||||||
dup2(fileno($serialC), fileno(STDERR));
|
dup2(fileno($serialC), fileno(STDERR));
|
||||||
$ENV{TMPDIR} = $self->{stateDir};
|
$ENV{TMPDIR} = $self->{stateDir};
|
||||||
$ENV{QEMU_OPTS} = "-nographic -no-reboot -redir tcp:65535::514 -net nic,vlan=1,model=virtio -net socket,vlan=1,mcast=$mcastAddr -monitor unix:./monitor";
|
$ENV{QEMU_OPTS} = "-nographic -no-reboot -redir tcp:65535::514 -monitor unix:./monitor";
|
||||||
$ENV{QEMU_KERNEL_PARAMS} = "hostTmpDir=$ENV{TMPDIR}";
|
$ENV{QEMU_KERNEL_PARAMS} = "hostTmpDir=$ENV{TMPDIR}";
|
||||||
chdir $self->{stateDir} or die;
|
chdir $self->{stateDir} or die;
|
||||||
exec $self->{startCommand};
|
exec $self->{startCommand};
|
||||||
|
|
|
@ -69,6 +69,37 @@ let
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
virtualisation.vlans =
|
||||||
|
mkOption {
|
||||||
|
default = [ 1 ];
|
||||||
|
example = [ 1 2 ];
|
||||||
|
description =
|
||||||
|
''
|
||||||
|
Virtual networks to which the VM is connected. Each
|
||||||
|
number <replaceable>N</replaceable> in this list causes
|
||||||
|
the VM to have a virtual Ethernet interface attached to a
|
||||||
|
separate virtual network on which it will be assigned IP
|
||||||
|
address
|
||||||
|
<literal>192.168.<replaceable>N</replaceable>.<replaceable>M</replaceable></literal>,
|
||||||
|
where <replaceable>M</replaceable> is the index of this VM
|
||||||
|
in the list of VMs.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.primaryIPAddress =
|
||||||
|
mkOption {
|
||||||
|
default = "";
|
||||||
|
internal = true;
|
||||||
|
description = "Primary IP address used in /etc/hosts.";
|
||||||
|
};
|
||||||
|
|
||||||
|
virtualisation.qemu.options =
|
||||||
|
mkOption {
|
||||||
|
default = "";
|
||||||
|
example = "-vga std";
|
||||||
|
description = "Options passed to QEMU.";
|
||||||
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
cfg = config.virtualisation;
|
cfg = config.virtualisation;
|
||||||
|
@ -94,13 +125,14 @@ let
|
||||||
# hanging the VM on x86_64.
|
# hanging the VM on x86_64.
|
||||||
exec ${pkgs.qemu_kvm}/bin/qemu-system-x86_64 -m ${toString config.virtualisation.memorySize} \
|
exec ${pkgs.qemu_kvm}/bin/qemu-system-x86_64 -m ${toString config.virtualisation.memorySize} \
|
||||||
-no-kvm-irqchip \
|
-no-kvm-irqchip \
|
||||||
-net nic,model=virtio -net user -smb / \
|
-net nic,vlan=0,model=virtio -net user,vlan=0 -smb / \
|
||||||
-drive file=$NIX_DISK_IMAGE,if=virtio,boot=on,werror=report \
|
-drive file=$NIX_DISK_IMAGE,if=virtio,boot=on,werror=report \
|
||||||
-kernel ${config.system.build.toplevel}/kernel \
|
-kernel ${config.system.build.toplevel}/kernel \
|
||||||
-initrd ${config.system.build.toplevel}/initrd \
|
-initrd ${config.system.build.toplevel}/initrd \
|
||||||
${qemuGraphics} \
|
${qemuGraphics} \
|
||||||
$QEMU_OPTS \
|
$QEMU_OPTS \
|
||||||
-append "$(cat ${config.system.build.toplevel}/kernel-params) init=${config.system.build.bootStage2} systemConfig=${config.system.build.toplevel} regInfo=${regInfo} ${kernelConsole} $QEMU_KERNEL_PARAMS"
|
-append "$(cat ${config.system.build.toplevel}/kernel-params) init=${config.system.build.bootStage2} systemConfig=${config.system.build.toplevel} regInfo=${regInfo} ${kernelConsole} $QEMU_KERNEL_PARAMS" \
|
||||||
|
${config.virtualisation.qemu.options}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
|
||||||
|
@ -186,7 +218,7 @@ in
|
||||||
# host filesystem and thus deadlocks the system.
|
# host filesystem and thus deadlocks the system.
|
||||||
networking.useDHCP = false;
|
networking.useDHCP = false;
|
||||||
|
|
||||||
networking.defaultGateway = "10.0.2.2";
|
networking.defaultGateway = mkOverride 200 {} "10.0.2.2";
|
||||||
|
|
||||||
networking.nameservers = [ "10.0.2.3" ];
|
networking.nameservers = [ "10.0.2.3" ];
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ with import ../lib/testing.nix { inherit nixpkgs services system; };
|
||||||
installer = pkgs.lib.mapAttrs (name: complete) (call (import ./installer.nix));
|
installer = pkgs.lib.mapAttrs (name: complete) (call (import ./installer.nix));
|
||||||
kde4 = apply (import ./kde4.nix);
|
kde4 = apply (import ./kde4.nix);
|
||||||
login = apply (import ./login.nix);
|
login = apply (import ./login.nix);
|
||||||
|
nat = apply (import ./nat.nix);
|
||||||
openssh = apply (import ./openssh.nix);
|
openssh = apply (import ./openssh.nix);
|
||||||
portmap = apply (import ./portmap.nix);
|
portmap = apply (import ./portmap.nix);
|
||||||
proxy = apply (import ./proxy.nix);
|
proxy = apply (import ./proxy.nix);
|
||||||
|
|
55
tests/nat.nix
Normal file
55
tests/nat.nix
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
# This is a simple distributed test involving a topology with two
|
||||||
|
# separate virtual networks - the "inside" and the "outside" - with a
|
||||||
|
# client on the inside network, a server on the outside network, and a
|
||||||
|
# router connected to both that performs Network Address Translation
|
||||||
|
# for the client.
|
||||||
|
|
||||||
|
{ pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
nodes =
|
||||||
|
{ client =
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
{ virtualisation.vlans = [ 1 ];
|
||||||
|
networking.defaultGateway = "192.168.1.2"; # !!! ugly
|
||||||
|
};
|
||||||
|
|
||||||
|
router =
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
{ virtualisation.vlans = [ 2 1 ];
|
||||||
|
environment.systemPackages = [ pkgs.iptables ];
|
||||||
|
};
|
||||||
|
|
||||||
|
server =
|
||||||
|
{ config, pkgs, ... }:
|
||||||
|
{ virtualisation.vlans = [ 2 ];
|
||||||
|
services.httpd.enable = true;
|
||||||
|
services.httpd.adminAddr = "foo@example.org";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript =
|
||||||
|
''
|
||||||
|
startAll;
|
||||||
|
|
||||||
|
# The router should have access to the server.
|
||||||
|
$server->waitForJob("httpd");
|
||||||
|
$router->mustSucceed("curl --fail http://server/ >&2");
|
||||||
|
|
||||||
|
# But the client shouldn't be able to reach the server.
|
||||||
|
$client->mustFail("curl --fail --connect-timeout 5 http://server/ >&2");
|
||||||
|
|
||||||
|
# Enable NAT on the router.
|
||||||
|
$router->mustSucceed(
|
||||||
|
"iptables -t nat -F",
|
||||||
|
"iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -d 192.168.1.0/24 -j ACCEPT",
|
||||||
|
"iptables -t nat -A POSTROUTING -s 192.168.1.0/24 -j SNAT --to-source 192.168.2.2", # !!! ugly
|
||||||
|
"echo 1 > /proc/sys/net/ipv4/ip_forward"
|
||||||
|
);
|
||||||
|
|
||||||
|
# Now the client should be able to connect.
|
||||||
|
$client->mustSucceed("curl --fail http://server/ >&2");
|
||||||
|
'';
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue