mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-11-25 16:33:15 +00:00
Merge pull request #207039 from NixOS/qemu-boot-disk-using-make-disk-image
nixos/qemu-vm: refactor bootDisk using make-disk-image
This commit is contained in:
commit
4b4e4c3ef9
3
.github/CODEOWNERS
vendored
3
.github/CODEOWNERS
vendored
@ -90,6 +90,9 @@
|
||||
# NixOS integration test driver
|
||||
/nixos/lib/test-driver @tfc
|
||||
|
||||
# NixOS QEMU virtualisation
|
||||
/nixos/virtualisation/qemu-vm.nix @raitobezarius
|
||||
|
||||
# Systemd
|
||||
/nixos/modules/system/boot/systemd.nix @NixOS/systemd
|
||||
/nixos/modules/system/boot/systemd @NixOS/systemd
|
||||
|
@ -55,6 +55,11 @@ let
|
||||
|
||||
};
|
||||
|
||||
selectPartitionTableLayout = { useEFIBoot, useDefaultFilesystems }:
|
||||
if useDefaultFilesystems then
|
||||
if useEFIBoot then "efi" else "legacy"
|
||||
else "none";
|
||||
|
||||
driveCmdline = idx: { file, driveExtraOpts, deviceExtraOpts, ... }:
|
||||
let
|
||||
drvId = "drive${toString idx}";
|
||||
@ -98,7 +103,6 @@ let
|
||||
addDeviceNames =
|
||||
imap1 (idx: drive: drive // { device = driveDeviceName idx; });
|
||||
|
||||
|
||||
# Shell script to start the VM.
|
||||
startVM =
|
||||
''
|
||||
@ -111,8 +115,23 @@ let
|
||||
NIX_DISK_IMAGE=$(readlink -f "''${NIX_DISK_IMAGE:-${toString config.virtualisation.diskImage}}") || test -z "$NIX_DISK_IMAGE"
|
||||
|
||||
if test -n "$NIX_DISK_IMAGE" && ! test -e "$NIX_DISK_IMAGE"; then
|
||||
${qemu}/bin/qemu-img create -f qcow2 "$NIX_DISK_IMAGE" \
|
||||
${toString config.virtualisation.diskSize}M
|
||||
echo "Disk image do not exist, creating the virtualisation disk image..."
|
||||
# If we are using a bootloader and default filesystems layout.
|
||||
# We have to reuse the system image layout as a backing image format (CoW)
|
||||
# So we can write on the top of it.
|
||||
|
||||
# If we are not using the default FS layout, potentially, we are interested into
|
||||
# performing operations in postDeviceCommands or at early boot on the raw device.
|
||||
# We can still boot through QEMU direct kernel boot feature.
|
||||
|
||||
# CoW prevent size to be attributed to an image.
|
||||
# FIXME: raise this issue to upstream.
|
||||
${qemu}/bin/qemu-img create \
|
||||
${concatStringsSep " \\\n" ([ "-f qcow2" ]
|
||||
++ optional (cfg.useBootLoader && cfg.useDefaultFilesystems) "-F qcow2 -b ${systemImage}/nixos.qcow2"
|
||||
++ optional (!(cfg.useBootLoader && cfg.useDefaultFilesystems)) "-o size=${toString config.virtualisation.diskSize}M"
|
||||
++ [ "$NIX_DISK_IMAGE" ])}
|
||||
echo "Virtualisation disk image created."
|
||||
fi
|
||||
|
||||
# Create a directory for storing temporary data of the running VM.
|
||||
@ -152,19 +171,13 @@ let
|
||||
|
||||
${lib.optionalString cfg.useBootLoader
|
||||
''
|
||||
if ${if !cfg.persistBootDevice then "true" else "! test -e $TMPDIR/disk.img"}; then
|
||||
# Create a writable copy/snapshot of the boot disk.
|
||||
# A writable boot disk can be booted from automatically.
|
||||
${qemu}/bin/qemu-img create -f qcow2 -F qcow2 -b ${bootDisk}/disk.img "$TMPDIR/disk.img"
|
||||
fi
|
||||
|
||||
NIX_EFI_VARS=$(readlink -f "''${NIX_EFI_VARS:-${cfg.efiVars}}")
|
||||
NIX_EFI_VARS=$(readlink -f "''${NIX_EFI_VARS:-${config.system.name}-efi-vars.fd}")
|
||||
|
||||
${lib.optionalString cfg.useEFIBoot
|
||||
''
|
||||
# VM needs writable EFI vars
|
||||
if ! test -e "$NIX_EFI_VARS"; then
|
||||
cp ${bootDisk}/efi-vars.fd "$NIX_EFI_VARS"
|
||||
cp ${systemImage}/efi-vars.fd "$NIX_EFI_VARS"
|
||||
chmod 0644 "$NIX_EFI_VARS"
|
||||
fi
|
||||
''}
|
||||
@ -200,95 +213,29 @@ let
|
||||
|
||||
regInfo = pkgs.closureInfo { rootPaths = config.virtualisation.additionalPaths; };
|
||||
|
||||
|
||||
# Generate a hard disk image containing a /boot partition and GRUB
|
||||
# in the MBR. Used when the `useBootLoader' option is set.
|
||||
# Uses `runInLinuxVM` to create the image in a throwaway VM.
|
||||
# See note [Disk layout with `useBootLoader`].
|
||||
# FIXME: use nixos/lib/make-disk-image.nix.
|
||||
bootDisk =
|
||||
pkgs.vmTools.runInLinuxVM (
|
||||
pkgs.runCommand "nixos-boot-disk"
|
||||
{ preVM =
|
||||
''
|
||||
mkdir $out
|
||||
diskImage=$out/disk.img
|
||||
${qemu}/bin/qemu-img create -f qcow2 $diskImage "120M"
|
||||
${lib.optionalString cfg.useEFIBoot ''
|
||||
efiVars=$out/efi-vars.fd
|
||||
cp ${cfg.efi.variables} $efiVars
|
||||
chmod 0644 $efiVars
|
||||
''}
|
||||
'';
|
||||
buildInputs = [ pkgs.util-linux ];
|
||||
QEMU_OPTS = "-nographic -serial stdio -monitor none"
|
||||
+ lib.optionalString cfg.useEFIBoot (
|
||||
" -drive if=pflash,format=raw,unit=0,readonly=on,file=${cfg.efi.firmware}"
|
||||
+ " -drive if=pflash,format=raw,unit=1,file=$efiVars");
|
||||
}
|
||||
''
|
||||
# Create a /boot EFI partition with 120M and arbitrary but fixed GUIDs for reproducibility
|
||||
${pkgs.gptfdisk}/bin/sgdisk \
|
||||
--set-alignment=1 --new=1:34:2047 --change-name=1:BIOSBootPartition --typecode=1:ef02 \
|
||||
--set-alignment=512 --largest-new=2 --change-name=2:EFISystem --typecode=2:ef00 \
|
||||
--attributes=1:set:1 \
|
||||
--attributes=2:set:2 \
|
||||
--disk-guid=97FD5997-D90B-4AA3-8D16-C1723AEA73C1 \
|
||||
--partition-guid=1:1C06F03B-704E-4657-B9CD-681A087A2FDC \
|
||||
--partition-guid=2:970C694F-AFD0-4B99-B750-CDB7A329AB6F \
|
||||
--hybrid 2 \
|
||||
--recompute-chs /dev/vda
|
||||
|
||||
${optionalString (config.boot.loader.grub.device != "/dev/vda")
|
||||
# In this throwaway VM, we only have the /dev/vda disk, but the
|
||||
# actual VM described by `config` (used by `switch-to-configuration`
|
||||
# below) may set `boot.loader.grub.device` to a different device
|
||||
# that's nonexistent in the throwaway VM.
|
||||
# Create a symlink for that device, so that the `grub-install`
|
||||
# by `switch-to-configuration` will hit /dev/vda anyway.
|
||||
''
|
||||
ln -s /dev/vda ${config.boot.loader.grub.device}
|
||||
''
|
||||
}
|
||||
|
||||
${pkgs.dosfstools}/bin/mkfs.fat -F16 /dev/vda2
|
||||
export MTOOLS_SKIP_CHECK=1
|
||||
${pkgs.mtools}/bin/mlabel -i /dev/vda2 ::boot
|
||||
|
||||
# Mount /boot; load necessary modules first.
|
||||
${pkgs.kmod}/bin/insmod ${pkgs.linux}/lib/modules/*/kernel/fs/nls/nls_cp437.ko.xz || true
|
||||
${pkgs.kmod}/bin/insmod ${pkgs.linux}/lib/modules/*/kernel/fs/nls/nls_iso8859-1.ko.xz || true
|
||||
${pkgs.kmod}/bin/insmod ${pkgs.linux}/lib/modules/*/kernel/fs/fat/fat.ko.xz || true
|
||||
${pkgs.kmod}/bin/insmod ${pkgs.linux}/lib/modules/*/kernel/fs/fat/vfat.ko.xz || true
|
||||
${pkgs.kmod}/bin/insmod ${pkgs.linux}/lib/modules/*/kernel/fs/efivarfs/efivarfs.ko.xz || true
|
||||
mkdir /boot
|
||||
mount /dev/vda2 /boot
|
||||
|
||||
${optionalString config.boot.loader.efi.canTouchEfiVariables ''
|
||||
mount -t efivarfs efivarfs /sys/firmware/efi/efivars
|
||||
''}
|
||||
|
||||
# This is needed for GRUB 0.97, which doesn't know about virtio devices.
|
||||
mkdir /boot/grub
|
||||
echo '(hd0) /dev/vda' > /boot/grub/device.map
|
||||
|
||||
# This is needed for systemd-boot to find ESP, and udev is not available here to create this
|
||||
mkdir -p /dev/block
|
||||
ln -s /dev/vda2 /dev/block/254:2
|
||||
|
||||
# Set up system profile (normally done by nixos-rebuild / nix-env --set)
|
||||
mkdir -p /nix/var/nix/profiles
|
||||
ln -s ${config.system.build.toplevel} /nix/var/nix/profiles/system-1-link
|
||||
ln -s /nix/var/nix/profiles/system-1-link /nix/var/nix/profiles/system
|
||||
|
||||
# Install bootloader
|
||||
touch /etc/NIXOS
|
||||
export NIXOS_INSTALL_BOOTLOADER=1
|
||||
${config.system.build.toplevel}/bin/switch-to-configuration boot
|
||||
|
||||
umount /boot
|
||||
'' # */
|
||||
);
|
||||
# System image is akin to a complete NixOS install with
|
||||
# a boot partition and root partition.
|
||||
systemImage = import ../../lib/make-disk-image.nix {
|
||||
inherit pkgs config lib;
|
||||
additionalPaths = [ regInfo ];
|
||||
format = "qcow2";
|
||||
onlyNixStore = false;
|
||||
partitionTableType = selectPartitionTableLayout { inherit (cfg) useDefaultFilesystems useEFIBoot; };
|
||||
# Bootloader should be installed on the system image only if we are booting through bootloaders.
|
||||
# Though, if a user is not using our default filesystems, it is possible to not have any ESP
|
||||
# or a strange partition table that's incompatible with GRUB configuration.
|
||||
# As a consequence, this may lead to disk image creation failures.
|
||||
# To avoid this, we prefer to let the user find out about how to install the bootloader on its ESP/disk.
|
||||
# Usually, this can be through building your own disk image.
|
||||
# TODO: If a user is interested into a more fine grained heuristic for `installBootLoader`
|
||||
# by examining the actual contents of `cfg.fileSystems`, please send a PR.
|
||||
installBootLoader = cfg.useBootLoader && cfg.useDefaultFilesystems;
|
||||
touchEFIVars = cfg.useEFIBoot;
|
||||
diskSize = "auto";
|
||||
additionalSpace = "0M";
|
||||
copyChannel = false;
|
||||
OVMF = cfg.efi.OVMF;
|
||||
};
|
||||
|
||||
storeImage = import ../../lib/make-disk-image.nix {
|
||||
inherit pkgs config lib;
|
||||
@ -297,17 +244,42 @@ let
|
||||
onlyNixStore = true;
|
||||
partitionTableType = "none";
|
||||
installBootLoader = false;
|
||||
touchEFIVars = false;
|
||||
diskSize = "auto";
|
||||
additionalSpace = "0M";
|
||||
copyChannel = false;
|
||||
};
|
||||
|
||||
bootConfiguration =
|
||||
if cfg.useDefaultFilesystems
|
||||
then
|
||||
if cfg.useBootLoader
|
||||
then
|
||||
if cfg.useEFIBoot then "efi_bootloading_with_default_fs"
|
||||
else "legacy_bootloading_with_default_fs"
|
||||
else
|
||||
"direct_boot_with_default_fs"
|
||||
else
|
||||
"custom";
|
||||
suggestedRootDevice = {
|
||||
"efi_bootloading_with_default_fs" = "${cfg.bootLoaderDevice}2";
|
||||
"legacy_bootloading_with_default_fs" = "${cfg.bootLoaderDevice}1";
|
||||
"direct_boot_with_default_fs" = cfg.bootLoaderDevice;
|
||||
# This will enforce a NixOS module type checking error
|
||||
# to ask explicitly the user to set a rootDevice.
|
||||
# As it will look like `rootDevice = lib.mkDefault null;` after
|
||||
# all "computations".
|
||||
"custom" = null;
|
||||
}.${bootConfiguration};
|
||||
in
|
||||
|
||||
{
|
||||
imports = [
|
||||
../profiles/qemu-guest.nix
|
||||
(mkRenamedOptionModule [ "virtualisation" "pathsInNixDB" ] [ "virtualisation" "additionalPaths" ])
|
||||
(mkRemovedOptionModule [ "virtualisation" "bootDevice" ] "This option was renamed to `virtualisation.rootDevice`, as it was incorrectly named and misleading. Take the time to review what you want to do and look at the new options like `virtualisation.{bootLoaderDevice, bootPartition}`, open an issue in case of issues.")
|
||||
(mkRemovedOptionModule [ "virtualisation" "efiVars" ] "This option was removed, it is possible to provide a template UEFI variable with `virtualisation.efi.variables` ; if this option is important to you, open an issue")
|
||||
(mkRemovedOptionModule [ "virtualisation" "persistBootDevice" ] "Boot device is always persisted if you use a bootloader through the root disk image ; if this does not work for your usecase, please examine carefully what `virtualisation.{bootDevice, rootDevice, bootPartition}` options offer you and open an issue explaining your need.`")
|
||||
];
|
||||
|
||||
options = {
|
||||
@ -362,25 +334,48 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
virtualisation.bootDevice =
|
||||
virtualisation.bootLoaderDevice =
|
||||
mkOption {
|
||||
type = types.path;
|
||||
default = lookupDriveDeviceName "root" cfg.qemu.drives;
|
||||
defaultText = literalExpression ''lookupDriveDeviceName "root" cfg.qemu.drives'';
|
||||
example = "/dev/vda";
|
||||
description =
|
||||
lib.mdDoc ''
|
||||
The disk to be used for the root filesystem.
|
||||
The disk to be used for the boot filesystem.
|
||||
By default, it is the same disk as the root filesystem.
|
||||
'';
|
||||
};
|
||||
|
||||
virtualisation.bootPartition =
|
||||
mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = if cfg.useEFIBoot then "${cfg.bootLoaderDevice}1" else null;
|
||||
defaultText = literalExpression ''if cfg.useEFIBoot then "''${cfg.bootLoaderDevice}1" else null'';
|
||||
example = "/dev/vda1";
|
||||
description =
|
||||
lib.mdDoc ''
|
||||
The boot partition to be used to mount /boot filesystem.
|
||||
In legacy boots, this should be null.
|
||||
By default, in EFI boot, it is the first partition of the boot device.
|
||||
'';
|
||||
};
|
||||
|
||||
virtualisation.persistBootDevice =
|
||||
virtualisation.rootDevice =
|
||||
mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
type = types.nullOr types.path;
|
||||
example = "/dev/vda2";
|
||||
description =
|
||||
lib.mdDoc ''
|
||||
If useBootLoader is specified, whether to recreate the boot device
|
||||
on each instantiaton or allow it to persist.
|
||||
'';
|
||||
The disk or partition to be used for the root filesystem.
|
||||
By default (read the source code for more details):
|
||||
|
||||
- under EFI with a bootloader: 2nd partition of the boot disk
|
||||
- in legacy boot with a bootloader: 1st partition of the boot disk
|
||||
- in direct boot (i.e. without a bootloader): whole disk
|
||||
|
||||
In case you are not using a default boot device or a default filesystem, you have to set explicitly your root device.
|
||||
'';
|
||||
};
|
||||
|
||||
virtualisation.emptyDiskImages =
|
||||
@ -749,10 +744,22 @@ in
|
||||
};
|
||||
|
||||
virtualisation.efi = {
|
||||
OVMF = mkOption {
|
||||
type = types.package;
|
||||
default = (pkgs.OVMF.override {
|
||||
secureBoot = cfg.useSecureBoot;
|
||||
}).fd;
|
||||
defaultText = ''(pkgs.OVMF.override {
|
||||
secureBoot = cfg.useSecureBoot;
|
||||
}).fd'';
|
||||
description =
|
||||
lib.mdDoc "OVMF firmware package, defaults to OVMF configured with secure boot if needed.";
|
||||
};
|
||||
|
||||
firmware = mkOption {
|
||||
type = types.path;
|
||||
default = pkgs.OVMF.firmware;
|
||||
defaultText = literalExpression "pkgs.OVMF.firmware";
|
||||
default = cfg.efi.OVMF.firmware;
|
||||
defaultText = literalExpression "cfg.efi.OVMF.firmware";
|
||||
description =
|
||||
lib.mdDoc ''
|
||||
Firmware binary for EFI implementation, defaults to OVMF.
|
||||
@ -761,8 +768,8 @@ in
|
||||
|
||||
variables = mkOption {
|
||||
type = types.path;
|
||||
default = pkgs.OVMF.variables;
|
||||
defaultText = literalExpression "pkgs.OVMF.variables";
|
||||
default = cfg.efi.OVMF.variables;
|
||||
defaultText = literalExpression "cfg.efi.OVMF.variables";
|
||||
description =
|
||||
lib.mdDoc ''
|
||||
Platform-specific flash binary for EFI variables, implementation-dependent to the EFI firmware.
|
||||
@ -786,18 +793,17 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
virtualisation.efiVars =
|
||||
virtualisation.useSecureBoot =
|
||||
mkOption {
|
||||
type = types.str;
|
||||
default = "./${config.system.name}-efi-vars.fd";
|
||||
defaultText = literalExpression ''"./''${config.system.name}-efi-vars.fd"'';
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description =
|
||||
lib.mdDoc ''
|
||||
Path to nvram image containing UEFI variables. The will be created
|
||||
on startup if it does not exist.
|
||||
Enable Secure Boot support in the EFI firmware.
|
||||
'';
|
||||
};
|
||||
|
||||
|
||||
virtualisation.bios =
|
||||
mkOption {
|
||||
type = types.nullOr types.package;
|
||||
@ -853,30 +859,13 @@ in
|
||||
${opt.writableStore} = false;
|
||||
'';
|
||||
|
||||
# Note [Disk layout with `useBootLoader`]
|
||||
#
|
||||
# If `useBootLoader = true`, we configure 2 drives:
|
||||
# `/dev/?da` for the root disk, and `/dev/?db` for the boot disk
|
||||
# which has the `/boot` partition and the boot loader.
|
||||
# Concretely:
|
||||
#
|
||||
# * The second drive's image `disk.img` is created in `bootDisk = ...`
|
||||
# using a throwaway VM. Note that there the disk is always `/dev/vda`,
|
||||
# even though in the final VM it will be at `/dev/*b`.
|
||||
# * The disks are attached in `virtualisation.qemu.drives`.
|
||||
# Their order makes them appear as devices `a`, `b`, etc.
|
||||
# * `fileSystems."/boot"` is adjusted to be on device `b`.
|
||||
# * The disk.img is recreated each time the VM is booted unless
|
||||
# virtualisation.persistBootDevice is set.
|
||||
|
||||
# If `useBootLoader`, GRUB goes to the second disk, see
|
||||
# note [Disk layout with `useBootLoader`].
|
||||
boot.loader.grub.device = mkVMOverride (
|
||||
if cfg.useBootLoader
|
||||
then driveDeviceName 2 # second disk
|
||||
else cfg.bootDevice
|
||||
);
|
||||
# In UEFI boot, we use a EFI-only partition table layout, thus GRUB will fail when trying to install
|
||||
# legacy and UEFI. In order to avoid this, we have to put "nodev" to force UEFI-only installs.
|
||||
# Otherwise, we set the proper bootloader device for this.
|
||||
# FIXME: make a sense of this mess wrt to multiple ESP present in the system, probably use boot.efiSysMountpoint?
|
||||
boot.loader.grub.device = mkVMOverride (if cfg.useEFIBoot then "nodev" else cfg.bootLoaderDevice);
|
||||
boot.loader.grub.gfxmodeBios = with cfg.resolution; "${toString x}x${toString y}";
|
||||
virtualisation.rootDevice = mkDefault suggestedRootDevice;
|
||||
|
||||
boot.initrd.kernelModules = optionals (cfg.useNixStoreImage && !cfg.writableStore) [ "erofs" ];
|
||||
|
||||
@ -890,10 +879,10 @@ in
|
||||
''
|
||||
# If the disk image appears to be empty, run mke2fs to
|
||||
# initialise.
|
||||
FSTYPE=$(blkid -o value -s TYPE ${cfg.bootDevice} || true)
|
||||
PARTTYPE=$(blkid -o value -s PTTYPE ${cfg.bootDevice} || true)
|
||||
FSTYPE=$(blkid -o value -s TYPE ${cfg.rootDevice} || true)
|
||||
PARTTYPE=$(blkid -o value -s PTTYPE ${cfg.rootDevice} || true)
|
||||
if test -z "$FSTYPE" -a -z "$PARTTYPE"; then
|
||||
mke2fs -t ext4 ${cfg.bootDevice}
|
||||
mke2fs -t ext4 ${cfg.rootDevice}
|
||||
fi
|
||||
'';
|
||||
|
||||
@ -939,8 +928,6 @@ in
|
||||
optional cfg.writableStore "overlay"
|
||||
++ optional (cfg.qemu.diskInterface == "scsi") "sym53c8xx";
|
||||
|
||||
virtualisation.bootDevice = mkDefault (driveDeviceName 1);
|
||||
|
||||
virtualisation.additionalPaths = [ config.system.build.toplevel ];
|
||||
|
||||
virtualisation.sharedDirectories = {
|
||||
@ -997,7 +984,7 @@ in
|
||||
])
|
||||
(mkIf cfg.useEFIBoot [
|
||||
"-drive if=pflash,format=raw,unit=0,readonly=on,file=${cfg.efi.firmware}"
|
||||
"-drive if=pflash,format=raw,unit=1,file=$NIX_EFI_VARS"
|
||||
"-drive if=pflash,format=raw,unit=1,readonly=off,file=$NIX_EFI_VARS"
|
||||
])
|
||||
(mkIf (cfg.bios != null) [
|
||||
"-bios ${cfg.bios}/bios.bin"
|
||||
@ -1013,23 +1000,14 @@ in
|
||||
file = ''"$NIX_DISK_IMAGE"'';
|
||||
driveExtraOpts.cache = "writeback";
|
||||
driveExtraOpts.werror = "report";
|
||||
deviceExtraOpts.bootindex = "1";
|
||||
}])
|
||||
(mkIf cfg.useNixStoreImage [{
|
||||
name = "nix-store";
|
||||
file = ''"$TMPDIR"/store.img'';
|
||||
deviceExtraOpts.bootindex = if cfg.useBootLoader then "3" else "2";
|
||||
deviceExtraOpts.bootindex = "2";
|
||||
driveExtraOpts.format = if cfg.writableStore then "qcow2" else "raw";
|
||||
}])
|
||||
(mkIf cfg.useBootLoader [
|
||||
# The order of this list determines the device names, see
|
||||
# note [Disk layout with `useBootLoader`].
|
||||
{
|
||||
name = "boot";
|
||||
file = ''"$TMPDIR"/disk.img'';
|
||||
driveExtraOpts.media = "disk";
|
||||
deviceExtraOpts.bootindex = "1";
|
||||
}
|
||||
])
|
||||
(imap0 (idx: _: {
|
||||
file = "$(pwd)/empty${toString idx}.qcow2";
|
||||
driveExtraOpts.werror = "report";
|
||||
@ -1065,7 +1043,7 @@ in
|
||||
device = "tmpfs";
|
||||
fsType = "tmpfs";
|
||||
} else {
|
||||
device = cfg.bootDevice;
|
||||
device = cfg.rootDevice;
|
||||
fsType = "ext4";
|
||||
autoFormat = true;
|
||||
});
|
||||
@ -1086,9 +1064,8 @@ in
|
||||
options = [ "mode=0755" ];
|
||||
neededForBoot = true;
|
||||
};
|
||||
# see note [Disk layout with `useBootLoader`]
|
||||
"/boot" = lib.mkIf cfg.useBootLoader {
|
||||
device = "${lookupDriveDeviceName "boot" cfg.qemu.drives}2"; # 2 for e.g. `vdb2`, as created in `bootDisk`
|
||||
"/boot" = lib.mkIf (cfg.useBootLoader && cfg.bootPartition != null) {
|
||||
device = cfg.bootPartition; # 1 for e.g. `vda1`, as created in `systemImage`
|
||||
fsType = "vfat";
|
||||
noCheck = true; # fsck fails on a r/o filesystem
|
||||
};
|
||||
|
@ -108,9 +108,9 @@ in
|
||||
machine.start()
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
|
||||
machine.succeed("test -e /run/current-system/bootspec/boot.json")
|
||||
machine.succeed("test -e /run/current-system/boot.json")
|
||||
|
||||
bootspec = json.loads(machine.succeed("jq -r '.v1' /run/current-system/bootspec/boot.json"))
|
||||
bootspec = json.loads(machine.succeed("jq -r '.v1' /run/current-system/boot.json"))
|
||||
|
||||
assert all(key in bootspec for key in ('initrd', 'initrdSecrets')), "Bootspec should contain initrd or initrdSecrets field when initrd is enabled"
|
||||
'';
|
||||
|
@ -63,7 +63,7 @@ in makeTest {
|
||||
# Small root disk for installer
|
||||
512
|
||||
];
|
||||
virtualisation.bootDevice = "/dev/vdb";
|
||||
virtualisation.rootDevice = "/dev/vdb";
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -30,26 +30,26 @@ in {
|
||||
specialisation.boot-luks-wrong-keyfile.configuration = {
|
||||
boot.initrd.luks.devices = lib.mkVMOverride {
|
||||
cryptroot = {
|
||||
device = "/dev/vdc";
|
||||
device = "/dev/vdb";
|
||||
keyFile = "/etc/cryptroot.key";
|
||||
tryEmptyPassphrase = true;
|
||||
fallbackToPassword = !systemdStage1;
|
||||
};
|
||||
};
|
||||
virtualisation.bootDevice = "/dev/mapper/cryptroot";
|
||||
virtualisation.rootDevice = "/dev/mapper/cryptroot";
|
||||
boot.initrd.secrets."/etc/cryptroot.key" = keyfile;
|
||||
};
|
||||
|
||||
specialisation.boot-luks-missing-keyfile.configuration = {
|
||||
boot.initrd.luks.devices = lib.mkVMOverride {
|
||||
cryptroot = {
|
||||
device = "/dev/vdc";
|
||||
device = "/dev/vdb";
|
||||
keyFile = "/etc/cryptroot.key";
|
||||
tryEmptyPassphrase = true;
|
||||
fallbackToPassword = !systemdStage1;
|
||||
};
|
||||
};
|
||||
virtualisation.bootDevice = "/dev/mapper/cryptroot";
|
||||
virtualisation.rootDevice = "/dev/mapper/cryptroot";
|
||||
};
|
||||
};
|
||||
|
||||
@ -76,7 +76,7 @@ in {
|
||||
|
||||
# Create encrypted volume
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
machine.succeed("echo "" | cryptsetup luksFormat /dev/vdc --batch-mode")
|
||||
machine.succeed("echo "" | cryptsetup luksFormat /dev/vdb --batch-mode")
|
||||
machine.succeed("bootctl set-default nixos-generation-1-specialisation-boot-luks-wrong-keyfile.conf")
|
||||
machine.succeed("sync")
|
||||
machine.crash()
|
||||
|
@ -15,7 +15,6 @@ testing.makeTest {
|
||||
|
||||
nodes.machine = { ... }: {
|
||||
virtualisation.useBootLoader = true;
|
||||
virtualisation.persistBootDevice = true;
|
||||
|
||||
boot.loader.grub.device = "/dev/vda";
|
||||
|
||||
|
@ -316,8 +316,9 @@ let
|
||||
# installer. This ensures the target disk (/dev/vda) is
|
||||
# the same during and after installation.
|
||||
virtualisation.emptyDiskImages = [ 512 ];
|
||||
virtualisation.bootDevice =
|
||||
virtualisation.rootDevice =
|
||||
if grubVersion == 1 then "/dev/disk/by-id/scsi-0QEMU_QEMU_HARDDISK_drive2" else "/dev/vdb";
|
||||
virtualisation.bootLoaderDevice = "/dev/vda";
|
||||
virtualisation.qemu.diskInterface =
|
||||
if grubVersion == 1 then "scsi" else "virtio";
|
||||
|
||||
|
@ -18,10 +18,10 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
|
||||
boot-luks.configuration = {
|
||||
boot.initrd.luks.devices = lib.mkVMOverride {
|
||||
# We have two disks and only type one password - key reuse is in place
|
||||
cryptroot.device = "/dev/vdc";
|
||||
cryptroot2.device = "/dev/vdd";
|
||||
cryptroot.device = "/dev/vdb";
|
||||
cryptroot2.device = "/dev/vdc";
|
||||
};
|
||||
virtualisation.bootDevice = "/dev/mapper/cryptroot";
|
||||
virtualisation.rootDevice = "/dev/mapper/cryptroot";
|
||||
};
|
||||
boot-luks-custom-keymap.configuration = lib.mkMerge [
|
||||
boot-luks.configuration
|
||||
@ -37,8 +37,8 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
|
||||
testScript = ''
|
||||
# Create encrypted volume
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdb -")
|
||||
machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdc -")
|
||||
machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdd -")
|
||||
|
||||
# Boot from the encrypted disk
|
||||
machine.succeed("bootctl set-default nixos-generation-1-specialisation-boot-luks.conf")
|
||||
|
@ -1,18 +1,18 @@
|
||||
{ kernelPackages ? null, flavour }: let
|
||||
preparationCode = {
|
||||
raid = ''
|
||||
machine.succeed("vgcreate test_vg /dev/vdc /dev/vdd")
|
||||
machine.succeed("vgcreate test_vg /dev/vdb /dev/vdc")
|
||||
machine.succeed("lvcreate -L 512M --type raid0 test_vg -n test_lv")
|
||||
'';
|
||||
|
||||
thinpool = ''
|
||||
machine.succeed("vgcreate test_vg /dev/vdc")
|
||||
machine.succeed("vgcreate test_vg /dev/vdb")
|
||||
machine.succeed("lvcreate -L 512M -T test_vg/test_thin_pool")
|
||||
machine.succeed("lvcreate -n test_lv -V 16G --thinpool test_thin_pool test_vg")
|
||||
'';
|
||||
|
||||
vdo = ''
|
||||
machine.succeed("vgcreate test_vg /dev/vdc")
|
||||
machine.succeed("vgcreate test_vg /dev/vdb")
|
||||
machine.succeed("lvcreate --type vdo -n test_lv -L 6G -V 12G test_vg/vdo_pool_lv")
|
||||
'';
|
||||
}.${flavour};
|
||||
@ -79,7 +79,7 @@ in import ../make-test-python.nix ({ pkgs, ... }: {
|
||||
kernelPackages = lib.mkIf (kernelPackages != null) kernelPackages;
|
||||
};
|
||||
|
||||
specialisation.boot-lvm.configuration.virtualisation.bootDevice = "/dev/test_vg/test_lv";
|
||||
specialisation.boot-lvm.configuration.virtualisation.rootDevice = "/dev/test_vg/test_lv";
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
|
@ -5,9 +5,10 @@ import ./make-test-python.nix ({ lib, pkgs, ... }:
|
||||
nodes.machine =
|
||||
{ config, pkgs, lib, ... }:
|
||||
let
|
||||
disk = config.virtualisation.bootDevice;
|
||||
disk = config.virtualisation.rootDevice;
|
||||
in
|
||||
{
|
||||
virtualisation.rootDevice = "/dev/vda";
|
||||
virtualisation.useDefaultFilesystems = false;
|
||||
|
||||
boot.initrd.availableKernelModules = [ "btrfs" ];
|
||||
|
@ -9,7 +9,7 @@ import ./make-test-python.nix ({ lib, ... }:
|
||||
{
|
||||
virtualisation.useDefaultFilesystems = false;
|
||||
|
||||
virtualisation.bootDevice = "/dev/vda";
|
||||
virtualisation.rootDevice = "/dev/vda";
|
||||
|
||||
boot.initrd.postDeviceCommands = ''
|
||||
${pkgs.btrfs-progs}/bin/mkfs.btrfs --label root /dev/vda
|
||||
|
@ -7,7 +7,7 @@ import ./make-test-python.nix ({ lib, pkgs, ... }:
|
||||
{
|
||||
virtualisation.useDefaultFilesystems = false;
|
||||
|
||||
virtualisation.bootDevice = "/dev/vda1";
|
||||
virtualisation.rootDevice = "/dev/vda1";
|
||||
|
||||
boot.initrd.postDeviceCommands = ''
|
||||
if ! test -b /dev/vda1; then
|
||||
|
@ -21,14 +21,14 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
|
||||
fileSystems = lib.mkVMOverride {
|
||||
"/".fsType = lib.mkForce "btrfs";
|
||||
};
|
||||
virtualisation.bootDevice = "/dev/vdc";
|
||||
virtualisation.rootDevice = "/dev/vdb";
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
# Create RAID
|
||||
machine.succeed("mkfs.btrfs -d raid0 /dev/vdc /dev/vdd")
|
||||
machine.succeed("mkdir -p /mnt && mount /dev/vdc /mnt && echo hello > /mnt/test && umount /mnt")
|
||||
machine.succeed("mkfs.btrfs -d raid0 /dev/vdb /dev/vdc")
|
||||
machine.succeed("mkdir -p /mnt && mount /dev/vdb /mnt && echo hello > /mnt/test && umount /mnt")
|
||||
|
||||
# Boot from the RAID
|
||||
machine.succeed("bootctl set-default nixos-generation-1-specialisation-boot-btrfs-raid.conf")
|
||||
@ -38,7 +38,7 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
|
||||
|
||||
# Ensure we have successfully booted from the RAID
|
||||
assert "(initrd)" in machine.succeed("systemd-analyze") # booted with systemd in stage 1
|
||||
assert "/dev/vdc on / type btrfs" in machine.succeed("mount")
|
||||
assert "/dev/vdb on / type btrfs" in machine.succeed("mount")
|
||||
assert "hello" in machine.succeed("cat /test")
|
||||
assert "Total devices 2" in machine.succeed("btrfs filesystem show")
|
||||
'';
|
||||
|
@ -19,19 +19,19 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
|
||||
specialisation.boot-luks.configuration = {
|
||||
boot.initrd.luks.devices = lib.mkVMOverride {
|
||||
cryptroot = {
|
||||
device = "/dev/vdc";
|
||||
device = "/dev/vdb";
|
||||
crypttabExtraOpts = [ "fido2-device=auto" ];
|
||||
};
|
||||
};
|
||||
virtualisation.bootDevice = "/dev/mapper/cryptroot";
|
||||
virtualisation.rootDevice = "/dev/mapper/cryptroot";
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
# Create encrypted volume
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdc -")
|
||||
machine.succeed("PASSWORD=supersecret SYSTEMD_LOG_LEVEL=debug systemd-cryptenroll --fido2-device=auto /dev/vdc |& systemd-cat")
|
||||
machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdb -")
|
||||
machine.succeed("PASSWORD=supersecret SYSTEMD_LOG_LEVEL=debug systemd-cryptenroll --fido2-device=auto /dev/vdb |& systemd-cat")
|
||||
|
||||
# Boot from the encrypted disk
|
||||
machine.succeed("bootctl set-default nixos-generation-1-specialisation-boot-luks.conf")
|
||||
|
@ -27,11 +27,11 @@ in {
|
||||
specialisation.boot-luks.configuration = {
|
||||
boot.initrd.luks.devices = lib.mkVMOverride {
|
||||
cryptroot = {
|
||||
device = "/dev/vdc";
|
||||
device = "/dev/vdb";
|
||||
keyFile = "/etc/cryptroot.key";
|
||||
};
|
||||
};
|
||||
virtualisation.bootDevice = "/dev/mapper/cryptroot";
|
||||
virtualisation.rootDevice = "/dev/mapper/cryptroot";
|
||||
boot.initrd.secrets."/etc/cryptroot.key" = keyfile;
|
||||
};
|
||||
};
|
||||
@ -39,7 +39,7 @@ in {
|
||||
testScript = ''
|
||||
# Create encrypted volume
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
machine.succeed("cryptsetup luksFormat -q --iter-time=1 -d ${keyfile} /dev/vdc")
|
||||
machine.succeed("cryptsetup luksFormat -q --iter-time=1 -d ${keyfile} /dev/vdb")
|
||||
|
||||
# Boot from the encrypted disk
|
||||
machine.succeed("bootctl set-default nixos-generation-1-specialisation-boot-luks.conf")
|
||||
|
@ -19,10 +19,10 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
|
||||
specialisation.boot-luks.configuration = {
|
||||
boot.initrd.luks.devices = lib.mkVMOverride {
|
||||
# We have two disks and only type one password - key reuse is in place
|
||||
cryptroot.device = "/dev/vdc";
|
||||
cryptroot2.device = "/dev/vdd";
|
||||
cryptroot.device = "/dev/vdb";
|
||||
cryptroot2.device = "/dev/vdc";
|
||||
};
|
||||
virtualisation.bootDevice = "/dev/mapper/cryptroot";
|
||||
virtualisation.rootDevice = "/dev/mapper/cryptroot";
|
||||
# test mounting device unlocked in initrd after switching root
|
||||
virtualisation.fileSystems."/cryptroot2".device = "/dev/mapper/cryptroot2";
|
||||
};
|
||||
@ -31,9 +31,9 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
|
||||
testScript = ''
|
||||
# Create encrypted volume
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdb -")
|
||||
machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdc -")
|
||||
machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdd -")
|
||||
machine.succeed("echo -n supersecret | cryptsetup luksOpen -q /dev/vdd cryptroot2")
|
||||
machine.succeed("echo -n supersecret | cryptsetup luksOpen -q /dev/vdc cryptroot2")
|
||||
machine.succeed("mkfs.ext4 /dev/mapper/cryptroot2")
|
||||
|
||||
# Boot from the encrypted disk
|
||||
@ -47,7 +47,7 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
|
||||
machine.send_console("supersecret\n")
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
|
||||
assert "/dev/mapper/cryptroot on / type ext4" in machine.succeed("mount")
|
||||
assert "/dev/mapper/cryptroot on / type ext4" in machine.succeed("mount"), "/dev/mapper/cryptroot do not appear in mountpoints list"
|
||||
assert "/dev/mapper/cryptroot2 on /cryptroot2 type ext4" in machine.succeed("mount")
|
||||
'';
|
||||
})
|
||||
|
@ -21,11 +21,11 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
|
||||
specialisation.boot-luks.configuration = {
|
||||
boot.initrd.luks.devices = lib.mkVMOverride {
|
||||
cryptroot = {
|
||||
device = "/dev/vdc";
|
||||
device = "/dev/vdb";
|
||||
crypttabExtraOpts = [ "tpm2-device=auto" ];
|
||||
};
|
||||
};
|
||||
virtualisation.bootDevice = "/dev/mapper/cryptroot";
|
||||
virtualisation.rootDevice = "/dev/mapper/cryptroot";
|
||||
};
|
||||
};
|
||||
|
||||
@ -55,8 +55,8 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
|
||||
|
||||
# Create encrypted volume
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdc -")
|
||||
machine.succeed("PASSWORD=supersecret SYSTEMD_LOG_LEVEL=debug systemd-cryptenroll --tpm2-pcrs= --tpm2-device=auto /dev/vdc |& systemd-cat")
|
||||
machine.succeed("echo -n supersecret | cryptsetup luksFormat -q --iter-time=1 /dev/vdb -")
|
||||
machine.succeed("PASSWORD=supersecret SYSTEMD_LOG_LEVEL=debug systemd-cryptenroll --tpm2-pcrs= --tpm2-device=auto /dev/vdb |& systemd-cat")
|
||||
|
||||
# Boot from the encrypted disk
|
||||
machine.succeed("bootctl set-default nixos-generation-1-specialisation-boot-luks.conf")
|
||||
|
@ -20,18 +20,18 @@ import ./make-test-python.nix ({ lib, pkgs, ... }: {
|
||||
services.swraid = {
|
||||
enable = true;
|
||||
mdadmConf = ''
|
||||
ARRAY /dev/md0 devices=/dev/vdc,/dev/vdd
|
||||
ARRAY /dev/md0 devices=/dev/vdb,/dev/vdc
|
||||
'';
|
||||
};
|
||||
kernelModules = [ "raid0" ];
|
||||
};
|
||||
|
||||
specialisation.boot-swraid.configuration.virtualisation.bootDevice = "/dev/disk/by-label/testraid";
|
||||
specialisation.boot-swraid.configuration.virtualisation.rootDevice = "/dev/disk/by-label/testraid";
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
# Create RAID
|
||||
machine.succeed("mdadm --create --force /dev/md0 -n 2 --level=raid0 /dev/vdc /dev/vdd")
|
||||
machine.succeed("mdadm --create --force /dev/md0 -n 2 --level=raid0 /dev/vdb /dev/vdc")
|
||||
machine.succeed("mkfs.ext4 -L testraid /dev/md0")
|
||||
machine.succeed("mkdir -p /mnt && mount /dev/md0 /mnt && echo hello > /mnt/test && umount /mnt")
|
||||
|
||||
|
@ -116,15 +116,15 @@ let
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
machine.succeed(
|
||||
"zpool status",
|
||||
"parted --script /dev/vdb mklabel msdos",
|
||||
"parted --script /dev/vdb -- mkpart primary 1024M -1s",
|
||||
"parted --script /dev/vdc mklabel msdos",
|
||||
"parted --script /dev/vdc -- mkpart primary 1024M -1s",
|
||||
"parted --script /dev/vdd mklabel msdos",
|
||||
"parted --script /dev/vdd -- mkpart primary 1024M -1s",
|
||||
)
|
||||
|
||||
with subtest("sharesmb works"):
|
||||
machine.succeed(
|
||||
"zpool create rpool /dev/vdc1",
|
||||
"zpool create rpool /dev/vdb1",
|
||||
"zfs create -o mountpoint=legacy rpool/root",
|
||||
# shared datasets cannot have legacy mountpoint
|
||||
"zfs create rpool/shared_smb",
|
||||
@ -144,8 +144,8 @@ let
|
||||
with subtest("encryption works"):
|
||||
machine.succeed(
|
||||
'echo password | zpool create -O mountpoint=legacy '
|
||||
+ "-O encryption=aes-256-gcm -O keyformat=passphrase automatic /dev/vdc1",
|
||||
"zpool create -O mountpoint=legacy manual /dev/vdd1",
|
||||
+ "-O encryption=aes-256-gcm -O keyformat=passphrase automatic /dev/vdb1",
|
||||
"zpool create -O mountpoint=legacy manual /dev/vdc1",
|
||||
"echo otherpass | zfs create "
|
||||
+ "-o encryption=aes-256-gcm -o keyformat=passphrase manual/encrypted",
|
||||
"zfs create -o encryption=aes-256-gcm -o keyformat=passphrase "
|
||||
@ -175,7 +175,7 @@ let
|
||||
machine.succeed(
|
||||
"rm /etc/hostid",
|
||||
"zgenhostid deadcafe",
|
||||
"zpool create forcepool /dev/vdc1 -O mountpoint=legacy",
|
||||
"zpool create forcepool /dev/vdb1 -O mountpoint=legacy",
|
||||
"bootctl set-default nixos-generation-1-specialisation-forcepool.conf",
|
||||
"rm /etc/hostid",
|
||||
"sync",
|
||||
|
Loading…
Reference in New Issue
Block a user