at 23.11-beta 4.8 kB view raw
1{ lib, nodes, ... }: 2 3let 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 ; 10 11 nodeNumbers = 12 listToAttrs 13 (zipListsWith 14 nameValuePair 15 (attrNames nodes) 16 (range 1 254) 17 ); 18 19 networkModule = { config, nodes, pkgs, ... }: 20 let 21 qemu-common = import ../qemu-common.nix { inherit lib pkgs; }; 22 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); 32 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 }); 41 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}"''); 47 48 networkConfig = 49 { 50 networking.hostName = mkDefault config.virtualisation.test.nodeName; 51 52 networking.interfaces = listToAttrs ipInterfaces; 53 54 networking.primaryIPAddress = 55 optionalString (ipInterfaces != [ ]) (head (head ipInterfaces).value.ipv4.addresses).address; 56 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")); 70 71 virtualisation.qemu.options = qemuOptions; 72 boot.initrd.services.udev.rules = concatMapStrings (x: x + "\n") udevRules; 73 }; 74 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 }; 84 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 }; 105 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 }); 125 126in 127{ 128 config = { 129 extraBaseModules = { imports = [ networkModule nodeNumberModule ]; }; 130 }; 131}