nixos/preload: init
[NixPkgs.git] / nixos / lib / testing / network.nix
blob1edc9e276530ca1185026d560bd8b8b81b3d10f8
1 { lib, nodes, ... }:
3 let
4   inherit (lib)
5     attrNames concatMap concatMapStrings flip forEach head
6     listToAttrs mkDefault mkOption nameValuePair optionalString
7     range toLower types zipListsWith zipLists
8     mdDoc
9     ;
11   nodeNumbers =
12     listToAttrs
13       (zipListsWith
14         nameValuePair
15         (attrNames nodes)
16         (range 1 254)
17       );
19   networkModule = { config, nodes, pkgs, ... }:
20     let
21       qemu-common = import ../qemu-common.nix { inherit lib pkgs; };
23       # Convert legacy VLANs to named interfaces and merge with explicit interfaces.
24       vlansNumbered = forEach (zipLists config.virtualisation.vlans (range 1 255)) (v: {
25         name = "eth${toString v.snd}";
26         vlan = v.fst;
27         assignIP = true;
28       });
29       explicitInterfaces = lib.mapAttrsToList (n: v: v // { name = n; }) config.virtualisation.interfaces;
30       interfaces = vlansNumbered ++ explicitInterfaces;
31       interfacesNumbered = zipLists interfaces (range 1 255);
33       # Automatically assign IP addresses to requested interfaces.
34       assignIPs = lib.filter (i: i.assignIP) interfaces;
35       ipInterfaces = forEach assignIPs (i:
36         nameValuePair i.name { ipv4.addresses =
37           [ { address = "192.168.${toString i.vlan}.${toString config.virtualisation.test.nodeNumber}";
38               prefixLength = 24;
39             }];
40         });
42       qemuOptions = lib.flatten (forEach interfacesNumbered ({ fst, snd }:
43         qemu-common.qemuNICFlags snd fst.vlan config.virtualisation.test.nodeNumber));
44       udevRules = forEach interfacesNumbered ({ fst, snd }:
45         # MAC Addresses for QEMU network devices are lowercase, and udev string comparison is case-sensitive.
46         ''SUBSYSTEM=="net",ACTION=="add",ATTR{address}=="${toLower(qemu-common.qemuNicMac fst.vlan config.virtualisation.test.nodeNumber)}",NAME="${fst.name}"'');
48       networkConfig =
49         {
50           networking.hostName = mkDefault config.virtualisation.test.nodeName;
52           networking.interfaces = listToAttrs ipInterfaces;
54           networking.primaryIPAddress =
55             optionalString (ipInterfaces != [ ]) (head (head ipInterfaces).value.ipv4.addresses).address;
57           # Put the IP addresses of all VMs in this machine's
58           # /etc/hosts file.  If a machine has multiple
59           # interfaces, use the IP address corresponding to
60           # the first interface (i.e. the first network in its
61           # virtualisation.vlans option).
62           networking.extraHosts = flip concatMapStrings (attrNames nodes)
63             (m':
64               let config = nodes.${m'}; in
65               optionalString (config.networking.primaryIPAddress != "")
66                 ("${config.networking.primaryIPAddress} " +
67                   optionalString (config.networking.domain != null)
68                     "${config.networking.hostName}.${config.networking.domain} " +
69                   "${config.networking.hostName}\n"));
71           virtualisation.qemu.options = qemuOptions;
72           boot.initrd.services.udev.rules = concatMapStrings (x: x + "\n") udevRules;
73         };
75     in
76     {
77       key = "network-interfaces";
78       config = networkConfig // {
79         # Expose the networkConfig items for tests like nixops
80         # that need to recreate the network config.
81         system.build.networkConfig = networkConfig;
82       };
83     };
85   nodeNumberModule = (regular@{ config, name, ... }: {
86     options = {
87       virtualisation.test.nodeName = mkOption {
88         internal = true;
89         default = name;
90         # We need to force this in specilisations, otherwise it'd be
91         # readOnly = true;
92         description = mdDoc ''
93           The `name` in `nodes.<name>`; stable across `specialisations`.
94         '';
95       };
96       virtualisation.test.nodeNumber = mkOption {
97         internal = true;
98         type = types.int;
99         readOnly = true;
100         default = nodeNumbers.${config.virtualisation.test.nodeName};
101         description = mdDoc ''
102           A unique number assigned for each node in `nodes`.
103         '';
104       };
106       # specialisations override the `name` module argument,
107       # so we push the real `virtualisation.test.nodeName`.
108       specialisation = mkOption {
109         type = types.attrsOf (types.submodule {
110           options.configuration = mkOption {
111             type = types.submoduleWith {
112               modules = [
113                 {
114                   config.virtualisation.test.nodeName =
115                     # assert regular.config.virtualisation.test.nodeName != "configuration";
116                     regular.config.virtualisation.test.nodeName;
117                 }
118               ];
119             };
120           };
121         });
122       };
123     };
124   });
128   config = {
129     extraBaseModules = { imports = [ networkModule nodeNumberModule ]; };
130   };