Merge pull request #55645 from eonpatapon/qemu-vm-drives

nixos/qemu-vm: declarative drives
This commit is contained in:
Wout Mertens 2019-11-04 08:30:37 +01:00 committed by GitHub
commit 59e731b0ac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -23,24 +23,56 @@ let
cfg = config.virtualisation; cfg = config.virtualisation;
qemuGraphics = lib.optionalString (!cfg.graphics) "-nographic";
consoles = lib.concatMapStringsSep " " (c: "console=${c}") cfg.qemu.consoles; consoles = lib.concatMapStringsSep " " (c: "console=${c}") cfg.qemu.consoles;
# XXX: This is very ugly and in the future we really should use attribute driveOpts = { ... }: {
# sets to build ALL of the QEMU flags instead of this mixed mess of Nix
# expressions and shell script stuff. options = {
mkDiskIfaceDriveFlag = idx: driveArgs: let
inherit (cfg.qemu) diskInterface; file = mkOption {
# The drive identifier created by incrementing the index by one using the type = types.str;
# shell. description = "The file image used for this drive.";
drvId = "drive$((${idx} + 1))"; };
# NOTE: DO NOT shell escape, because this may contain shell variables.
commonArgs = "index=${idx},id=${drvId},${driveArgs}"; driveExtraOpts = mkOption {
isSCSI = diskInterface == "scsi"; type = types.attrsOf types.str;
devArgs = "${diskInterface}-hd,drive=${drvId}"; default = {};
args = "-drive ${commonArgs},if=none -device lsi53c895a -device ${devArgs}"; description = "Extra options passed to drive flag.";
in if isSCSI then args else "-drive ${commonArgs},if=${diskInterface}"; };
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. # Shell script to start the VM.
startVM = startVM =
@ -77,13 +109,11 @@ let
''} ''}
cd $TMPDIR cd $TMPDIR
idx=2 idx=0
extraDisks=""
${flip concatMapStrings cfg.emptyDiskImages (size: '' ${flip concatMapStrings cfg.emptyDiskImages (size: ''
if ! test -e "empty$idx.qcow2"; then if ! test -e "empty$idx.qcow2"; then
${qemu}/bin/qemu-img create -f qcow2 "empty$idx.qcow2" "${toString size}M" ${qemu}/bin/qemu-img create -f qcow2 "empty$idx.qcow2" "${toString size}M"
fi fi
extraDisks="$extraDisks ${mkDiskIfaceDriveFlag "$idx" "file=$(pwd)/empty$idx.qcow2,werror=report"}"
idx=$((idx + 1)) idx=$((idx + 1))
'')} '')}
@ -97,21 +127,7 @@ let
-virtfs local,path=/nix/store,security_model=none,mount_tag=store \ -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=$TMPDIR/xchg,security_model=none,mount_tag=xchg \
-virtfs local,path=''${SHARED_DIR:-$TMPDIR/xchg},security_model=none,mount_tag=shared \ -virtfs local,path=''${SHARED_DIR:-$TMPDIR/xchg},security_model=none,mount_tag=shared \
${if cfg.useBootLoader then '' ${drivesCmdLine config.virtualisation.qemu.drives} \
${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} \
${toString config.virtualisation.qemu.options} \ ${toString config.virtualisation.qemu.options} \
$QEMU_OPTS \ $QEMU_OPTS \
"$@" "$@"
@ -367,6 +383,12 @@ in
''; '';
}; };
drives =
mkOption {
type = types.listOf (types.submodule driveOpts);
description = "Drives passed to qemu.";
};
diskInterface = diskInterface =
mkOption { mkOption {
default = "virtio"; default = "virtio";
@ -476,8 +498,49 @@ in
# FIXME: Consolidate this one day. # FIXME: Consolidate this one day.
virtualisation.qemu.options = mkMerge [ 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.isi686 || pkgs.stdenv.isx86_64) [
(mkIf (pkgs.stdenv.isAarch32 || pkgs.stdenv.isAarch64) [ "-device virtio-gpu-pci" "-device usb-ehci,id=usb0" "-device usb-kbd" "-device usb-tablet" ]) "-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 # Mount the host filesystem via 9P, and bind-mount the Nix store