Merge pull request #305886 from wegank/ocaml-jane-street-old-drop
[NixPkgs.git] / nixos / lib / testing / network.nix
blob0f1615a0ad3b9769324a5bc85f37b0d93414d104
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     ;
10   nodeNumbers =
11     listToAttrs
12       (zipListsWith
13         nameValuePair
14         (attrNames nodes)
15         (range 1 254)
16       );
18   networkModule = { config, nodes, pkgs, ... }:
19     let
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}";
25         vlan = v.fst;
26         assignIP = true;
27       });
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 { ipv4.addresses =
36           [ { address = "192.168.${toString i.vlan}.${toString config.virtualisation.test.nodeNumber}";
37               prefixLength = 24;
38             }];
39         });
41       qemuOptions = lib.flatten (forEach interfacesNumbered ({ fst, snd }:
42         qemu-common.qemuNICFlags snd fst.vlan config.virtualisation.test.nodeNumber));
43       udevRules = forEach interfacesNumbered ({ fst, snd }:
44         # MAC Addresses for QEMU network devices are lowercase, and udev string comparison is case-sensitive.
45         ''SUBSYSTEM=="net",ACTION=="add",ATTR{address}=="${toLower(qemu-common.qemuNicMac fst.vlan config.virtualisation.test.nodeNumber)}",NAME="${fst.name}"'');
47       networkConfig =
48         {
49           networking.hostName = mkDefault config.virtualisation.test.nodeName;
51           networking.interfaces = listToAttrs ipInterfaces;
53           networking.primaryIPAddress =
54             optionalString (ipInterfaces != [ ]) (head (head ipInterfaces).value.ipv4.addresses).address;
56           # Put the IP addresses of all VMs in this machine's
57           # /etc/hosts file.  If a machine has multiple
58           # interfaces, use the IP address corresponding to
59           # the first interface (i.e. the first network in its
60           # virtualisation.vlans option).
61           networking.extraHosts = flip concatMapStrings (attrNames nodes)
62             (m':
63               let config = nodes.${m'}; in
64               optionalString (config.networking.primaryIPAddress != "")
65                 ("${config.networking.primaryIPAddress} " +
66                   optionalString (config.networking.domain != null)
67                     "${config.networking.hostName}.${config.networking.domain} " +
68                   "${config.networking.hostName}\n"));
70           virtualisation.qemu.options = qemuOptions;
71           boot.initrd.services.udev.rules = concatMapStrings (x: x + "\n") udevRules;
72         };
74     in
75     {
76       key = "network-interfaces";
77       config = networkConfig // {
78         # Expose the networkConfig items for tests like nixops
79         # that need to recreate the network config.
80         system.build.networkConfig = networkConfig;
81       };
82     };
84   nodeNumberModule = (regular@{ config, name, ... }: {
85     options = {
86       virtualisation.test.nodeName = mkOption {
87         internal = true;
88         default = name;
89         # We need to force this in specilisations, otherwise it'd be
90         # readOnly = true;
91         description = ''
92           The `name` in `nodes.<name>`; stable across `specialisations`.
93         '';
94       };
95       virtualisation.test.nodeNumber = mkOption {
96         internal = true;
97         type = types.int;
98         readOnly = true;
99         default = nodeNumbers.${config.virtualisation.test.nodeName};
100         description = ''
101           A unique number assigned for each node in `nodes`.
102         '';
103       };
105       # specialisations override the `name` module argument,
106       # so we push the real `virtualisation.test.nodeName`.
107       specialisation = mkOption {
108         type = types.attrsOf (types.submodule {
109           options.configuration = mkOption {
110             type = types.submoduleWith {
111               modules = [
112                 {
113                   config.virtualisation.test.nodeName =
114                     # assert regular.config.virtualisation.test.nodeName != "configuration";
115                     regular.config.virtualisation.test.nodeName;
116                 }
117               ];
118             };
119           };
120         });
121       };
122     };
123   });
127   config = {
128     extraBaseModules = { imports = [ networkModule nodeNumberModule ]; };
129   };