5 attrNames concatMap concatMapStrings flip forEach head
6 listToAttrs mkDefault mkOption nameValuePair optionalString
7 range toLower types zipListsWith zipLists
18 networkModule = { config, nodes, pkgs, ... }:
20 qemu-common = import ../qemu-common.nix { inherit lib pkgs; };
22 # Convert legacy VLANs to named interfaces and merge with explicit interfaces.
23 vlansNumbered = forEach (zipLists config.virtualisation.vlans (range 1 255)) (v: {
24 name = "eth${toString v.snd}";
28 explicitInterfaces = lib.mapAttrsToList (n: v: v // { name = n; }) config.virtualisation.interfaces;
29 interfaces = vlansNumbered ++ explicitInterfaces;
30 interfacesNumbered = zipLists interfaces (range 1 255);
32 # Automatically assign IP addresses to requested interfaces.
33 assignIPs = lib.filter (i: i.assignIP) interfaces;
34 ipInterfaces = forEach assignIPs (i:
35 nameValuePair i.name {
38 address = "192.168.${toString i.vlan}.${toString config.virtualisation.test.nodeNumber}";
44 address = "2001:db8:${toString i.vlan}::${toString config.virtualisation.test.nodeNumber}";
50 qemuOptions = lib.flatten (forEach interfacesNumbered ({ fst, snd }:
51 qemu-common.qemuNICFlags snd fst.vlan config.virtualisation.test.nodeNumber));
52 udevRules = forEach interfacesNumbered ({ fst, snd }:
53 # MAC Addresses for QEMU network devices are lowercase, and udev string comparison is case-sensitive.
54 ''SUBSYSTEM=="net",ACTION=="add",ATTR{address}=="${toLower(qemu-common.qemuNicMac fst.vlan config.virtualisation.test.nodeNumber)}",NAME="${fst.name}"'');
58 networking.hostName = mkDefault config.virtualisation.test.nodeName;
60 networking.interfaces = listToAttrs ipInterfaces;
62 networking.primaryIPAddress =
63 optionalString (ipInterfaces != [ ]) (head (head ipInterfaces).value.ipv4.addresses).address;
65 networking.primaryIPv6Address =
66 optionalString (ipInterfaces != [ ]) (head (head ipInterfaces).value.ipv6.addresses).address;
68 # Put the IP addresses of all VMs in this machine's
69 # /etc/hosts file. If a machine has multiple
70 # interfaces, use the IP address corresponding to
71 # the first interface (i.e. the first network in its
72 # virtualisation.vlans option).
73 networking.extraHosts = flip concatMapStrings (attrNames nodes)
78 optionalString (config.networking.domain != null) "${config.networking.hostName}.${config.networking.domain} " +
79 "${config.networking.hostName}\n";
81 optionalString (config.networking.primaryIPAddress != "")
82 "${config.networking.primaryIPAddress} ${hostnames}" +
83 optionalString (config.networking.primaryIPv6Address != "")
84 ("${config.networking.primaryIPv6Address} ${hostnames}"));
86 virtualisation.qemu.options = qemuOptions;
87 boot.initrd.services.udev.rules = concatMapStrings (x: x + "\n") udevRules;
92 key = "network-interfaces";
93 config = networkConfig // {
94 # Expose the networkConfig items for tests like nixops
95 # that need to recreate the network config.
96 system.build.networkConfig = networkConfig;
100 nodeNumberModule = (regular@{ config, name, ... }: {
102 virtualisation.test.nodeName = mkOption {
105 # We need to force this in specilisations, otherwise it'd be
108 The `name` in `nodes.<name>`; stable across `specialisations`.
111 virtualisation.test.nodeNumber = mkOption {
115 default = nodeNumbers.${config.virtualisation.test.nodeName};
117 A unique number assigned for each node in `nodes`.
121 # specialisations override the `name` module argument,
122 # so we push the real `virtualisation.test.nodeName`.
123 specialisation = mkOption {
124 type = types.attrsOf (types.submodule {
125 options.configuration = mkOption {
126 type = types.submoduleWith {
129 config.virtualisation.test.nodeName =
130 # assert regular.config.virtualisation.test.nodeName != "configuration";
131 regular.config.virtualisation.test.nodeName;
144 extraBaseModules = { imports = [ networkModule nodeNumberModule ]; };