From 376b2ef2b83be9671c4f24b7ff4ded335d5352d4 Mon Sep 17 00:00:00 2001 From: Jean-Philippe Braun Date: Tue, 12 Feb 2019 16:38:23 +0100 Subject: nixos/qemu-vm: declarative drives Adds `virtualisation.qemu.drives` option to specify drives to be used by qemu. Also fix boot when `virtualisation.useBootLoader` is set to true. Since the boot disk is second qemu doesn't boot on it. Added `bootindex=1` to the boot disk device. --- nixos/modules/virtualisation/qemu-vm.nix | 135 ++++++++++++++++++++++--------- 1 file changed, 99 insertions(+), 36 deletions(-) diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix index ed3431554be4..e313d2b411bb 100644 --- a/nixos/modules/virtualisation/qemu-vm.nix +++ b/nixos/modules/virtualisation/qemu-vm.nix @@ -23,24 +23,56 @@ let cfg = config.virtualisation; - qemuGraphics = lib.optionalString (!cfg.graphics) "-nographic"; - consoles = lib.concatMapStringsSep " " (c: "console=${c}") cfg.qemu.consoles; - # XXX: This is very ugly and in the future we really should use attribute - # sets to build ALL of the QEMU flags instead of this mixed mess of Nix - # expressions and shell script stuff. - mkDiskIfaceDriveFlag = idx: driveArgs: let - inherit (cfg.qemu) diskInterface; - # The drive identifier created by incrementing the index by one using the - # shell. - drvId = "drive$((${idx} + 1))"; - # NOTE: DO NOT shell escape, because this may contain shell variables. - commonArgs = "index=${idx},id=${drvId},${driveArgs}"; - isSCSI = diskInterface == "scsi"; - devArgs = "${diskInterface}-hd,drive=${drvId}"; - args = "-drive ${commonArgs},if=none -device lsi53c895a -device ${devArgs}"; - in if isSCSI then args else "-drive ${commonArgs},if=${diskInterface}"; + driveOpts = { ... }: { + + options = { + + file = mkOption { + type = types.str; + description = "The file image used for this drive."; + }; + + driveExtraOpts = mkOption { + type = types.attrsOf types.str; + default = {}; + description = "Extra options passed to drive flag."; + }; + + deviceExtraOpts = mkOption { + type = types.attrsOf types.str; + default = {}; + description = "Extra options passed to device flag."; + }; + + }; + + }; + + driveCmdline = idx: { file, driveExtraOpts, deviceExtraOpts, ... }: + let + drvId = "drive${toString idx}"; + mkKeyValue = generators.mkKeyValueDefault {} "="; + mkOpts = opts: concatStringsSep "," (mapAttrsToList mkKeyValue opts); + driveOpts = mkOpts (driveExtraOpts // { + index = idx; + id = drvId; + "if" = "none"; + inherit file; + }); + deviceOpts = mkOpts (deviceExtraOpts // { + drive = drvId; + }); + device = + if cfg.qemu.diskInterface == "scsi" then + "-device lsi53c895a -device scsi-hd,${deviceOpts}" + else + "-device virtio-blk-pci,${deviceOpts}"; + in + "-drive ${driveOpts} ${device}"; + + drivesCmdLine = drives: concatStringsSep " " (imap1 driveCmdline drives); # Shell script to start the VM. startVM = @@ -77,13 +109,11 @@ let ''} cd $TMPDIR - idx=2 - extraDisks="" + idx=0 ${flip concatMapStrings cfg.emptyDiskImages (size: '' if ! test -e "empty$idx.qcow2"; then ${qemu}/bin/qemu-img create -f qcow2 "empty$idx.qcow2" "${toString size}M" fi - extraDisks="$extraDisks ${mkDiskIfaceDriveFlag "$idx" "file=$(pwd)/empty$idx.qcow2,werror=report"}" idx=$((idx + 1)) '')} @@ -97,21 +127,7 @@ let -virtfs local,path=/nix/store,security_model=none,mount_tag=store \ -virtfs local,path=$TMPDIR/xchg,security_model=none,mount_tag=xchg \ -virtfs local,path=''${SHARED_DIR:-$TMPDIR/xchg},security_model=none,mount_tag=shared \ - ${if cfg.useBootLoader then '' - ${mkDiskIfaceDriveFlag "0" "file=$NIX_DISK_IMAGE,cache=writeback,werror=report"} \ - ${mkDiskIfaceDriveFlag "1" "file=$TMPDIR/disk.img,media=disk"} \ - ${if cfg.useEFIBoot then '' - -pflash $TMPDIR/bios.bin \ - '' else '' - ''} - '' else '' - ${mkDiskIfaceDriveFlag "0" "file=$NIX_DISK_IMAGE,cache=writeback,werror=report"} \ - -kernel ${config.system.build.toplevel}/kernel \ - -initrd ${config.system.build.toplevel}/initrd \ - -append "$(cat ${config.system.build.toplevel}/kernel-params) init=${config.system.build.toplevel}/init regInfo=${regInfo}/registration ${consoles} $QEMU_KERNEL_PARAMS" \ - ''} \ - $extraDisks \ - ${qemuGraphics} \ + ${drivesCmdLine config.virtualisation.qemu.drives} \ ${toString config.virtualisation.qemu.options} \ $QEMU_OPTS \ "$@" @@ -367,6 +383,12 @@ in ''; }; + drives = + mkOption { + type = types.listOf (types.submodule driveOpts); + description = "Drives passed to qemu."; + }; + diskInterface = mkOption { default = "virtio"; @@ -476,8 +498,49 @@ in # FIXME: Consolidate this one day. virtualisation.qemu.options = mkMerge [ - (mkIf (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) [ "-vga std" "-usb" "-device usb-tablet,bus=usb-bus.0" ]) - (mkIf (pkgs.stdenv.isAarch32 || pkgs.stdenv.isAarch64) [ "-device virtio-gpu-pci" "-device usb-ehci,id=usb0" "-device usb-kbd" "-device usb-tablet" ]) + (mkIf (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) [ + "-vga std" "-usb" "-device usb-tablet,bus=usb-bus.0" + ]) + (mkIf (pkgs.stdenv.isAarch32 || pkgs.stdenv.isAarch64) [ + "-device virtio-gpu-pci" "-device usb-ehci,id=usb0" "-device usb-kbd" "-device usb-tablet" + ]) + (mkIf (!cfg.useBootLoader) [ + "-kernel ${config.system.build.toplevel}/kernel" + "-initrd ${config.system.build.toplevel}/initrd" + ''-append "$(cat ${config.system.build.toplevel}/kernel-params) init=${config.system.build.toplevel}/init regInfo=${regInfo}/registration ${consoles} $QEMU_KERNEL_PARAMS"'' + ]) + (mkIf cfg.useEFIBoot [ + "-pflash $TMPDIR/bios.bin" + ]) + (mkIf (!cfg.graphics) [ + "-nographic" + ]) + ]; + + virtualisation.qemu.drives = mkMerge [ + (mkIf cfg.useBootLoader [ + { + file = "$NIX_DISK_IMAGE"; + driveExtraOpts.cache = "writeback"; + driveExtraOpts.werror = "report"; + } + { + file = "$TMPDIR/disk.img"; + driveExtraOpts.media = "disk"; + deviceExtraOpts.bootindex = "1"; + } + ]) + (mkIf (!cfg.useBootLoader) [ + { + file = "$NIX_DISK_IMAGE"; + driveExtraOpts.cache = "writeback"; + driveExtraOpts.werror = "report"; + } + ]) + (imap0 (idx: _: { + file = "$(pwd)/empty${toString idx}.qcow2"; + driveExtraOpts.werror = "report"; + }) cfg.emptyDiskImages) ]; # Mount the host filesystem via 9P, and bind-mount the Nix store -- cgit 1.4.1