installer: simple PXE bootable NixOS installer

The Nix store squashfs is stored inside the initrd instead of separately

(cherry picked from commit 976fd407796877b538c470d3a5253ad3e1f7bc68)
Signed-off-by: Domen Kožar <domen@dev.si>

authored by

Nahum Shalman and committed by
Domen Kožar
83c0aca0 ecfc523d

+173 -2
+20
nixos/modules/installer/netboot/netboot-base.nix
··· 1 + # This module contains the basic configuration for building netboot 2 + # images 3 + 4 + { config, lib, pkgs, ... }: 5 + 6 + with lib; 7 + 8 + { 9 + imports = 10 + [ ./netboot.nix 11 + 12 + # Profiles of this basic netboot media 13 + ../../profiles/all-hardware.nix 14 + ../../profiles/base.nix 15 + ../../profiles/installation-device.nix 16 + ]; 17 + 18 + # Allow the user to log in as root without a password. 19 + users.extraUsers.root.initialHashedPassword = ""; 20 + }
+10
nixos/modules/installer/netboot/netboot-minimal.nix
··· 1 + # This module defines a small netboot environment. 2 + 3 + { config, lib, ... }: 4 + 5 + { 6 + imports = 7 + [ ./netboot-base.nix 8 + ../../profiles/minimal.nix 9 + ]; 10 + }
+91
nixos/modules/installer/netboot/netboot.nix
··· 1 + # This module creates netboot media containing the given NixOS 2 + # configuration. 3 + 4 + { config, lib, pkgs, ... }: 5 + 6 + with lib; 7 + 8 + { 9 + options = { 10 + 11 + netboot.storeContents = mkOption { 12 + example = literalExample "[ pkgs.stdenv ]"; 13 + description = '' 14 + This option lists additional derivations to be included in the 15 + Nix store in the generated netboot image. 16 + ''; 17 + }; 18 + 19 + }; 20 + 21 + config = { 22 + 23 + boot.loader.grub.version = 2; 24 + 25 + # Don't build the GRUB menu builder script, since we don't need it 26 + # here and it causes a cyclic dependency. 27 + boot.loader.grub.enable = false; 28 + 29 + boot.initrd.postMountCommands = '' 30 + mkdir -p /mnt-root/nix/store 31 + mount -t squashfs /nix-store.squashfs /mnt-root/nix/store 32 + ''; 33 + 34 + # !!! Hack - attributes expected by other modules. 35 + system.boot.loader.kernelFile = "bzImage"; 36 + environment.systemPackages = [ pkgs.grub2 pkgs.grub2_efi pkgs.syslinux ]; 37 + 38 + boot.consoleLogLevel = mkDefault 7; 39 + 40 + fileSystems."/" = 41 + { fsType = "tmpfs"; 42 + options = [ "mode=0755" ]; 43 + }; 44 + 45 + boot.initrd.availableKernelModules = [ "squashfs" ]; 46 + 47 + boot.initrd.kernelModules = [ "loop" ]; 48 + 49 + # Closures to be copied to the Nix store, namely the init 50 + # script and the top-level system configuration directory. 51 + netboot.storeContents = 52 + [ config.system.build.toplevel ]; 53 + 54 + # Create the squashfs image that contains the Nix store. 55 + system.build.squashfsStore = import ../../../lib/make-squashfs.nix { 56 + inherit (pkgs) stdenv squashfsTools perl pathsFromGraph; 57 + storeContents = config.netboot.storeContents; 58 + }; 59 + 60 + 61 + # Create the initrd 62 + system.build.netbootRamdisk = pkgs.makeInitrd { 63 + inherit (config.boot.initrd) compressor; 64 + prepend = [ "${config.system.build.initialRamdisk}/initrd" ]; 65 + 66 + contents = 67 + [ { object = config.system.build.squashfsStore; 68 + symlink = "/nix-store.squashfs"; 69 + } 70 + ]; 71 + }; 72 + 73 + system.build.netbootIpxeScript = pkgs.writeTextDir "netboot.ipxe" "#!ipxe\nkernel bzImage init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}\ninitrd initrd\nboot"; 74 + 75 + boot.loader.timeout = 10; 76 + 77 + boot.postBootCommands = 78 + '' 79 + # After booting, register the contents of the Nix store 80 + # in the Nix database in the tmpfs. 81 + ${config.nix.package}/bin/nix-store --load-db < /nix/store/nix-path-registration 82 + 83 + # nixos-rebuild also requires a "system" profile and an 84 + # /etc/NIXOS tag. 85 + touch /etc/NIXOS 86 + ${config.nix.package}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system 87 + ''; 88 + 89 + }; 90 + 91 + }
+14
nixos/release.nix
··· 104 104 initialRamdisk = buildFromConfig ({ pkgs, ... }: { }) (config: config.system.build.initialRamdisk); 105 105 106 106 107 + netboot = let build = (import lib/eval-config.nix { 108 + system = "x86_64-linux"; 109 + modules = [ 110 + ./modules/installer/netboot/netboot-minimal.nix 111 + versionModule 112 + ]; 113 + }).config.system.build; 114 + in 115 + pkgs.symlinkJoin "netboot" [ 116 + build.netbootRamdisk 117 + build.kernel 118 + build.netbootIpxeScript 119 + ]; 120 + 107 121 iso_minimal = forAllSystems (system: makeIso { 108 122 module = ./modules/installer/cd-dvd/installation-cd-minimal.nix; 109 123 type = "minimal";
+38 -2
nixos/tests/boot.nix
··· 44 44 usb => glob("${iso}/iso/*.iso"), 45 45 bios => '${pkgs.OVMF}/FV/OVMF.fd' 46 46 ''; 47 - } 48 - 47 + netboot = let 48 + config = (import ../lib/eval-config.nix { 49 + inherit system; 50 + modules = 51 + [ ../modules/installer/netboot/netboot.nix 52 + ../modules/testing/test-instrumentation.nix 53 + { key = "serial"; } 54 + ]; 55 + }).config; 56 + ipxeScriptDir = pkgs.writeTextFile { 57 + name = "ipxeScriptDir"; 58 + text = '' 59 + #!ipxe 60 + dhcp 61 + kernel bzImage init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} console=ttyS0 62 + initrd initrd 63 + boot 64 + ''; 65 + destination = "/boot.ipxe"; 66 + }; 67 + ipxeBootDir = pkgs.symlinkJoin "ipxeBootDir" [ 68 + config.system.build.netbootRamdisk 69 + config.system.build.kernel 70 + ipxeScriptDir 71 + ]; 72 + in 73 + makeTest { 74 + name = "boot-netboot"; 75 + nodes = { }; 76 + testScript = 77 + '' 78 + my $machine = createMachine({ qemuFlags => '-boot order=n -net nic,model=e1000 -net user,tftp=${ipxeBootDir}/,bootfile=boot.ipxe -m 2000M' }); 79 + $machine->start; 80 + $machine->waitForUnit("multi-user.target"); 81 + $machine->shutdown; 82 + ''; 83 + }; 84 + }