1{ system
2, # Use a minimal kernel?
3 minimal ? false
4, # Ignored
5 config ? null
6, # Nixpkgs, for qemu, lib and more
7 pkgs
8, # !!! See comment about args in lib/modules.nix
9 specialArgs ? {}
10, # NixOS configuration to add to the VMs
11 extraConfigurations ? []
12}:
13
14with pkgs.lib;
15with import ../lib/qemu-flags.nix { inherit pkgs; };
16
17rec {
18
19 inherit pkgs;
20
21 # Build a virtual network from an attribute set `{ machine1 =
22 # config1; ... machineN = configN; }', where `machineX' is the
23 # hostname and `configX' is a NixOS system configuration. Each
24 # machine is given an arbitrary IP address in the virtual network.
25 buildVirtualNetwork =
26 nodes: let nodesOut = mapAttrs (n: buildVM nodesOut) (assignIPAddresses nodes); in nodesOut;
27
28
29 buildVM =
30 nodes: configurations:
31
32 import ./eval-config.nix {
33 inherit system specialArgs;
34 modules = configurations ++ extraConfigurations;
35 baseModules = (import ../modules/module-list.nix) ++
36 [ ../modules/virtualisation/qemu-vm.nix
37 ../modules/testing/test-instrumentation.nix # !!! should only get added for automated test runs
38 { key = "no-manual"; documentation.nixos.enable = false; }
39 { key = "no-revision";
40 # Make the revision metadata constant, in order to avoid needless retesting.
41 # The human version (e.g. 21.05-pre) is left as is, because it is useful
42 # for external modules that test with e.g. nixosTest and rely on that
43 # version number.
44 config.system.nixos.revision = "constant-nixos-revision";
45 }
46 { key = "nodes"; _module.args.nodes = nodes; }
47 ] ++ optional minimal ../modules/testing/minimal-kernel.nix;
48 };
49
50
51 # Given an attribute set { machine1 = config1; ... machineN =
52 # configN; }, sequentially assign IP addresses in the 192.168.1.0/24
53 # range to each machine, and set the hostname to the attribute name.
54 assignIPAddresses = nodes:
55
56 let
57
58 machines = attrNames nodes;
59
60 machinesNumbered = zipLists machines (range 1 254);
61
62 nodes_ = forEach machinesNumbered (m: nameValuePair m.fst
63 [ ( { config, nodes, ... }:
64 let
65 interfacesNumbered = zipLists config.virtualisation.vlans (range 1 255);
66 interfaces = forEach interfacesNumbered ({ fst, snd }:
67 nameValuePair "eth${toString snd}" { ipv4.addresses =
68 [ { address = "192.168.${toString fst}.${toString m.snd}";
69 prefixLength = 24;
70 } ];
71 });
72 in
73 { key = "ip-address";
74 config =
75 { networking.hostName = mkDefault m.fst;
76
77 networking.interfaces = listToAttrs interfaces;
78
79 networking.primaryIPAddress =
80 optionalString (interfaces != []) (head (head interfaces).value.ipv4.addresses).address;
81
82 # Put the IP addresses of all VMs in this machine's
83 # /etc/hosts file. If a machine has multiple
84 # interfaces, use the IP address corresponding to
85 # the first interface (i.e. the first network in its
86 # virtualisation.vlans option).
87 networking.extraHosts = flip concatMapStrings machines
88 (m': let config = (getAttr m' nodes).config; in
89 optionalString (config.networking.primaryIPAddress != "")
90 ("${config.networking.primaryIPAddress} " +
91 optionalString (config.networking.domain != null)
92 "${config.networking.hostName}.${config.networking.domain} " +
93 "${config.networking.hostName}\n"));
94
95 virtualisation.qemu.options =
96 forEach interfacesNumbered
97 ({ fst, snd }: qemuNICFlags snd fst m.snd);
98 };
99 }
100 )
101 (getAttr m.fst nodes)
102 ] );
103
104 in listToAttrs nodes_;
105
106}