diff --git a/nixos/modules/installer/netboot/netboot-base.nix b/nixos/modules/installer/netboot/netboot-base.nix new file mode 100644 index 00000000000..b12eaccf870 --- /dev/null +++ b/nixos/modules/installer/netboot/netboot-base.nix @@ -0,0 +1,20 @@ +# This module contains the basic configuration for building netboot +# images + +{ config, lib, pkgs, ... }: + +with lib; + +{ + imports = + [ ./netboot.nix + + # Profiles of this basic netboot media + ../../profiles/all-hardware.nix + ../../profiles/base.nix + ../../profiles/installation-device.nix + ]; + + # Allow the user to log in as root without a password. + users.extraUsers.root.initialHashedPassword = ""; +} diff --git a/nixos/modules/installer/netboot/netboot-minimal.nix b/nixos/modules/installer/netboot/netboot-minimal.nix new file mode 100644 index 00000000000..8ad6234edc7 --- /dev/null +++ b/nixos/modules/installer/netboot/netboot-minimal.nix @@ -0,0 +1,10 @@ +# This module defines a small netboot environment. + +{ config, lib, ... }: + +{ + imports = + [ ./netboot-base.nix + ../../profiles/minimal.nix + ]; +} diff --git a/nixos/modules/installer/netboot/netboot.nix b/nixos/modules/installer/netboot/netboot.nix new file mode 100644 index 00000000000..366591a8114 --- /dev/null +++ b/nixos/modules/installer/netboot/netboot.nix @@ -0,0 +1,91 @@ +# This module creates netboot media containing the given NixOS +# configuration. + +{ config, lib, pkgs, ... }: + +with lib; + +{ + options = { + + netboot.storeContents = mkOption { + example = literalExample "[ pkgs.stdenv ]"; + description = '' + This option lists additional derivations to be included in the + Nix store in the generated netboot image. + ''; + }; + + }; + + config = { + + boot.loader.grub.version = 2; + + # Don't build the GRUB menu builder script, since we don't need it + # here and it causes a cyclic dependency. + boot.loader.grub.enable = false; + + boot.initrd.postMountCommands = '' + mkdir -p /mnt-root/nix/store + mount -t squashfs /nix-store.squashfs /mnt-root/nix/store + ''; + + # !!! Hack - attributes expected by other modules. + system.boot.loader.kernelFile = "bzImage"; + environment.systemPackages = [ pkgs.grub2 pkgs.grub2_efi pkgs.syslinux ]; + + boot.consoleLogLevel = mkDefault 7; + + fileSystems."/" = + { fsType = "tmpfs"; + options = [ "mode=0755" ]; + }; + + boot.initrd.availableKernelModules = [ "squashfs" ]; + + boot.initrd.kernelModules = [ "loop" ]; + + # Closures to be copied to the Nix store, namely the init + # script and the top-level system configuration directory. + netboot.storeContents = + [ config.system.build.toplevel ]; + + # Create the squashfs image that contains the Nix store. + system.build.squashfsStore = import ../../../lib/make-squashfs.nix { + inherit (pkgs) stdenv squashfsTools perl pathsFromGraph; + storeContents = config.netboot.storeContents; + }; + + + # Create the initrd + system.build.netbootRamdisk = pkgs.makeInitrd { + inherit (config.boot.initrd) compressor; + prepend = [ "${config.system.build.initialRamdisk}/initrd" ]; + + contents = + [ { object = config.system.build.squashfsStore; + symlink = "/nix-store.squashfs"; + } + ]; + }; + + system.build.netbootIpxeScript = pkgs.writeTextDir "netboot.ipxe" "#!ipxe\nkernel bzImage init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams}\ninitrd initrd\nboot"; + + boot.loader.timeout = 10; + + boot.postBootCommands = + '' + # After booting, register the contents of the Nix store + # in the Nix database in the tmpfs. + ${config.nix.package}/bin/nix-store --load-db < /nix/store/nix-path-registration + + # nixos-rebuild also requires a "system" profile and an + # /etc/NIXOS tag. + touch /etc/NIXOS + ${config.nix.package}/bin/nix-env -p /nix/var/nix/profiles/system --set /run/current-system + ''; + + }; + +} diff --git a/nixos/release.nix b/nixos/release.nix index d78c1bb1c15..fa4a9860150 100644 --- a/nixos/release.nix +++ b/nixos/release.nix @@ -104,6 +104,20 @@ in rec { initialRamdisk = buildFromConfig ({ pkgs, ... }: { }) (config: config.system.build.initialRamdisk); + netboot = let build = (import lib/eval-config.nix { + system = "x86_64-linux"; + modules = [ + ./modules/installer/netboot/netboot-minimal.nix + versionModule + ]; + }).config.system.build; + in + pkgs.symlinkJoin "netboot" [ + build.netbootRamdisk + build.kernel + build.netbootIpxeScript + ]; + iso_minimal = forAllSystems (system: makeIso { module = ./modules/installer/cd-dvd/installation-cd-minimal.nix; type = "minimal"; diff --git a/nixos/tests/boot.nix b/nixos/tests/boot.nix index 905d1645882..a138ba4bcf0 100644 --- a/nixos/tests/boot.nix +++ b/nixos/tests/boot.nix @@ -44,5 +44,41 @@ in { usb => glob("${iso}/iso/*.iso"), bios => '${pkgs.OVMF}/FV/OVMF.fd' ''; - } - + netboot = let + config = (import ../lib/eval-config.nix { + inherit system; + modules = + [ ../modules/installer/netboot/netboot.nix + ../modules/testing/test-instrumentation.nix + { key = "serial"; } + ]; + }).config; + ipxeScriptDir = pkgs.writeTextFile { + name = "ipxeScriptDir"; + text = '' + #!ipxe + dhcp + kernel bzImage init=${config.system.build.toplevel}/init ${toString config.boot.kernelParams} console=ttyS0 + initrd initrd + boot + ''; + destination = "/boot.ipxe"; + }; + ipxeBootDir = pkgs.symlinkJoin "ipxeBootDir" [ + config.system.build.netbootRamdisk + config.system.build.kernel + ipxeScriptDir + ]; + in + makeTest { + name = "boot-netboot"; + nodes = { }; + testScript = + '' + my $machine = createMachine({ qemuFlags => '-boot order=n -net nic,model=e1000 -net user,tftp=${ipxeBootDir}/,bootfile=boot.ipxe -m 2000M' }); + $machine->start; + $machine->waitForUnit("multi-user.target"); + $machine->shutdown; + ''; + }; +} \ No newline at end of file