modules/virtualisation: add shared options, merge various diskSize options (#341058)

+3
.git-blame-ignore-revs
···
# percona: apply nixfmt
8d14fa2886fec877690c6d28cfcdba4503dbbcea
+
+
# nixos/virtualisation: format image-related files
+
88b285c01d84de82c0b2b052fd28eaf6709c2d26
+130 -94
nixos/maintainers/scripts/ec2/amazon-image.nix
···
-
{ config, lib, pkgs, ... }:
+
{
+
config,
+
lib,
+
pkgs,
+
...
+
}:
let
-
inherit (lib) mkOption optionalString types versionAtLeast;
+
inherit (lib)
+
mkOption
+
optionalString
+
types
+
versionAtLeast
+
;
inherit (lib.options) literalExpression;
cfg = config.amazonImage;
amiBootMode = if config.ec2.efi then "uefi" else "legacy-bios";
-
-
in {
-
-
imports = [ ../../../modules/virtualisation/amazon-image.nix ];
+
in
+
{
+
imports = [
+
../../../modules/virtualisation/amazon-image.nix
+
../../../modules/virtualisation/disk-size-option.nix
+
(lib.mkRenamedOptionModuleWith {
+
sinceRelease = 2411;
+
from = [
+
"amazonImage"
+
"sizeMB"
+
];
+
to = [
+
"virtualisation"
+
"diskSize"
+
];
+
})
+
];
# Amazon recommends setting this to the highest possible value for a good EBS
# experience, which prior to 4.15 was 255.
# https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/nvme-ebs-volumes.html#timeout-nvme-ebs-volumes
config.boot.kernelParams =
-
let timeout =
-
if versionAtLeast config.boot.kernelPackages.kernel.version "4.15"
-
then "4294967295"
-
else "255";
-
in [ "nvme_core.io_timeout=${timeout}" ];
+
let
+
timeout =
+
if versionAtLeast config.boot.kernelPackages.kernel.version "4.15" then "4294967295" else "255";
+
in
+
[ "nvme_core.io_timeout=${timeout}" ];
options.amazonImage = {
name = mkOption {
···
}
]
'';
-
default = [];
+
default = [ ];
description = ''
This option lists files to be copied to fixed locations in the
generated image. Glob patterns work.
'';
};
-
sizeMB = mkOption {
-
type = with types; either (enum [ "auto" ]) int;
-
default = 3072;
-
example = 8192;
-
description = "The size in MB of the image";
-
};
-
format = mkOption {
-
type = types.enum [ "raw" "qcow2" "vpc" ];
+
type = types.enum [
+
"raw"
+
"qcow2"
+
"vpc"
+
];
default = "vpc";
description = "The image format to output";
};
};
-
config.system.build.amazonImage = let
-
configFile = pkgs.writeText "configuration.nix"
-
''
+
# Use a priority just below mkOptionDefault (1500) instead of lib.mkDefault
+
# to avoid breaking existing configs using that.
+
config.virtualisation.diskSize = lib.mkOverride 1490 (3 * 1024);
+
config.virtualisation.diskSizeAutoSupported = !config.ec2.zfs.enable;
+
+
config.system.build.amazonImage =
+
let
+
configFile = pkgs.writeText "configuration.nix" ''
{ modulesPath, ... }: {
imports = [ "''${modulesPath}/virtualisation/amazon-image.nix" ];
${optionalString config.ec2.efi ''
···
}
'';
-
zfsBuilder = import ../../../lib/make-multi-disk-zfs-image.nix {
-
inherit lib config configFile pkgs;
-
inherit (cfg) contents format name;
+
zfsBuilder = import ../../../lib/make-multi-disk-zfs-image.nix {
+
inherit
+
lib
+
config
+
configFile
+
pkgs
+
;
+
inherit (cfg) contents format name;
-
includeChannel = true;
+
includeChannel = true;
-
bootSize = 1000; # 1G is the minimum EBS volume
+
bootSize = 1000; # 1G is the minimum EBS volume
-
rootSize = cfg.sizeMB;
-
rootPoolProperties = {
-
ashift = 12;
-
autoexpand = "on";
-
};
+
rootSize = config.virtualisation.diskSize;
+
rootPoolProperties = {
+
ashift = 12;
+
autoexpand = "on";
+
};
-
datasets = config.ec2.zfs.datasets;
+
datasets = config.ec2.zfs.datasets;
-
postVM = ''
-
extension=''${rootDiskImage##*.}
-
friendlyName=$out/${cfg.name}
-
rootDisk="$friendlyName.root.$extension"
-
bootDisk="$friendlyName.boot.$extension"
-
mv "$rootDiskImage" "$rootDisk"
-
mv "$bootDiskImage" "$bootDisk"
+
postVM = ''
+
extension=''${rootDiskImage##*.}
+
friendlyName=$out/${cfg.name}
+
rootDisk="$friendlyName.root.$extension"
+
bootDisk="$friendlyName.boot.$extension"
+
mv "$rootDiskImage" "$rootDisk"
+
mv "$bootDiskImage" "$bootDisk"
-
mkdir -p $out/nix-support
-
echo "file ${cfg.format} $bootDisk" >> $out/nix-support/hydra-build-products
-
echo "file ${cfg.format} $rootDisk" >> $out/nix-support/hydra-build-products
+
mkdir -p $out/nix-support
+
echo "file ${cfg.format} $bootDisk" >> $out/nix-support/hydra-build-products
+
echo "file ${cfg.format} $rootDisk" >> $out/nix-support/hydra-build-products
-
${pkgs.jq}/bin/jq -n \
-
--arg system_label ${lib.escapeShellArg config.system.nixos.label} \
-
--arg system ${lib.escapeShellArg pkgs.stdenv.hostPlatform.system} \
-
--arg root_logical_bytes "$(${pkgs.qemu_kvm}/bin/qemu-img info --output json "$rootDisk" | ${pkgs.jq}/bin/jq '."virtual-size"')" \
-
--arg boot_logical_bytes "$(${pkgs.qemu_kvm}/bin/qemu-img info --output json "$bootDisk" | ${pkgs.jq}/bin/jq '."virtual-size"')" \
-
--arg boot_mode "${amiBootMode}" \
-
--arg root "$rootDisk" \
-
--arg boot "$bootDisk" \
-
'{}
-
| .label = $system_label
-
| .boot_mode = $boot_mode
-
| .system = $system
-
| .disks.boot.logical_bytes = $boot_logical_bytes
-
| .disks.boot.file = $boot
-
| .disks.root.logical_bytes = $root_logical_bytes
-
| .disks.root.file = $root
-
' > $out/nix-support/image-info.json
-
'';
-
};
+
${pkgs.jq}/bin/jq -n \
+
--arg system_label ${lib.escapeShellArg config.system.nixos.label} \
+
--arg system ${lib.escapeShellArg pkgs.stdenv.hostPlatform.system} \
+
--arg root_logical_bytes "$(${pkgs.qemu_kvm}/bin/qemu-img info --output json "$rootDisk" | ${pkgs.jq}/bin/jq '."virtual-size"')" \
+
--arg boot_logical_bytes "$(${pkgs.qemu_kvm}/bin/qemu-img info --output json "$bootDisk" | ${pkgs.jq}/bin/jq '."virtual-size"')" \
+
--arg boot_mode "${amiBootMode}" \
+
--arg root "$rootDisk" \
+
--arg boot "$bootDisk" \
+
'{}
+
| .label = $system_label
+
| .boot_mode = $boot_mode
+
| .system = $system
+
| .disks.boot.logical_bytes = $boot_logical_bytes
+
| .disks.boot.file = $boot
+
| .disks.root.logical_bytes = $root_logical_bytes
+
| .disks.root.file = $root
+
' > $out/nix-support/image-info.json
+
'';
+
};
-
extBuilder = import ../../../lib/make-disk-image.nix {
-
inherit lib config configFile pkgs;
+
extBuilder = import ../../../lib/make-disk-image.nix {
+
inherit
+
lib
+
config
+
configFile
+
pkgs
+
;
-
inherit (cfg) contents format name;
+
inherit (cfg) contents format name;
-
fsType = "ext4";
-
partitionTableType = if config.ec2.efi then "efi" else "legacy+gpt";
+
fsType = "ext4";
+
partitionTableType = if config.ec2.efi then "efi" else "legacy+gpt";
-
diskSize = cfg.sizeMB;
+
inherit (config.virtualisation) diskSize;
-
postVM = ''
-
extension=''${diskImage##*.}
-
friendlyName=$out/${cfg.name}.$extension
-
mv "$diskImage" "$friendlyName"
-
diskImage=$friendlyName
+
postVM = ''
+
extension=''${diskImage##*.}
+
friendlyName=$out/${cfg.name}.$extension
+
mv "$diskImage" "$friendlyName"
+
diskImage=$friendlyName
-
mkdir -p $out/nix-support
-
echo "file ${cfg.format} $diskImage" >> $out/nix-support/hydra-build-products
+
mkdir -p $out/nix-support
+
echo "file ${cfg.format} $diskImage" >> $out/nix-support/hydra-build-products
-
${pkgs.jq}/bin/jq -n \
-
--arg system_label ${lib.escapeShellArg config.system.nixos.label} \
-
--arg system ${lib.escapeShellArg pkgs.stdenv.hostPlatform.system} \
-
--arg logical_bytes "$(${pkgs.qemu_kvm}/bin/qemu-img info --output json "$diskImage" | ${pkgs.jq}/bin/jq '."virtual-size"')" \
-
--arg boot_mode "${amiBootMode}" \
-
--arg file "$diskImage" \
-
'{}
-
| .label = $system_label
-
| .boot_mode = $boot_mode
-
| .system = $system
-
| .logical_bytes = $logical_bytes
-
| .file = $file
-
| .disks.root.logical_bytes = $logical_bytes
-
| .disks.root.file = $file
-
' > $out/nix-support/image-info.json
-
'';
-
};
-
in if config.ec2.zfs.enable then zfsBuilder else extBuilder;
+
${pkgs.jq}/bin/jq -n \
+
--arg system_label ${lib.escapeShellArg config.system.nixos.label} \
+
--arg system ${lib.escapeShellArg pkgs.stdenv.hostPlatform.system} \
+
--arg logical_bytes "$(${pkgs.qemu_kvm}/bin/qemu-img info --output json "$diskImage" | ${pkgs.jq}/bin/jq '."virtual-size"')" \
+
--arg boot_mode "${amiBootMode}" \
+
--arg file "$diskImage" \
+
'{}
+
| .label = $system_label
+
| .boot_mode = $boot_mode
+
| .system = $system
+
| .logical_bytes = $logical_bytes
+
| .file = $file
+
| .disks.root.logical_bytes = $logical_bytes
+
| .disks.root.file = $file
+
' > $out/nix-support/image-info.json
+
'';
+
};
+
in
+
if config.ec2.zfs.enable then zfsBuilder else extBuilder;
meta.maintainers = with lib.maintainers; [ arianvp ];
}
+35 -18
nixos/maintainers/scripts/openstack/openstack-image-zfs.nix
···
# nix-build '<nixpkgs/nixos>' -A config.system.build.openstackImage --arg configuration "{ imports = [ ./nixos/maintainers/scripts/openstack/openstack-image.nix ]; }"
-
{ config, lib, pkgs, ... }:
+
{
+
config,
+
lib,
+
pkgs,
+
...
+
}:
let
inherit (lib) mkOption types;
copyChannel = true;
···
{
imports = [
../../../modules/virtualisation/openstack-config.nix
+
../../../modules/virtualisation/disk-size-option.nix
+
(lib.mkRenamedOptionModuleWith {
+
sinceRelease = 2411;
+
from = [
+
"openstackImage"
+
"sizeMB"
+
];
+
to = [
+
"virtualisation"
+
"diskSize"
+
];
+
})
] ++ (lib.optional copyChannel ../../../modules/installer/cd-dvd/channel.nix);
-
options.openstackImage = {
name = mkOption {
···
ramMB = mkOption {
type = types.int;
-
default = 1024;
+
default = (3 * 1024);
description = "RAM allocation for build VM";
};
-
sizeMB = mkOption {
-
type = types.int;
-
default = 8192;
-
description = "The size in MB of the image";
-
};
-
format = mkOption {
-
type = types.enum [ "raw" "qcow2" ];
+
type = types.enum [
+
"raw"
+
"qcow2"
+
];
default = "qcow2";
description = "The image format to output";
};
···
};
};
+
# Use a priority just below mkOptionDefault (1500) instead of lib.mkDefault
+
# to avoid breaking existing configs using that.
+
virtualisation.diskSize = lib.mkOverride 1490 (8 * 1024);
+
virtualisation.diskSizeAutoSupported = false;
+
system.build.openstackImage = import ../../../lib/make-single-disk-zfs-image.nix {
inherit lib config;
inherit (cfg) contents format name;
pkgs = import ../../../.. { inherit (pkgs) system; }; # ensure we use the regular qemu-kvm package
-
configFile = pkgs.writeText "configuration.nix"
-
''
-
{ modulesPath, ... }: {
-
imports = [ "''${modulesPath}/virtualisation/openstack-config.nix" ];
-
openstack.zfs.enable = true;
-
}
-
'';
+
configFile = pkgs.writeText "configuration.nix" ''
+
{ modulesPath, ... }: {
+
imports = [ "''${modulesPath}/virtualisation/openstack-config.nix" ];
+
openstack.zfs.enable = true;
+
}
+
'';
includeChannel = copyChannel;
bootSize = 1000;
memSize = cfg.ramMB;
-
rootSize = cfg.sizeMB;
+
rootSize = config.virtualisation.diskSize;
rootPoolProperties = {
ashift = 12;
autoexpand = "on";
+30 -13
nixos/modules/virtualisation/azure-image.nix
···
-
{ config, lib, pkgs, ... }:
+
{
+
config,
+
lib,
+
pkgs,
+
...
+
}:
with lib;
let
cfg = config.virtualisation.azureImage;
in
{
-
imports = [ ./azure-common.nix ];
+
imports = [
+
./azure-common.nix
+
./disk-size-option.nix
+
(lib.mkRenamedOptionModuleWith {
+
sinceRelease = 2411;
+
from = [
+
"virtualisation"
+
"azureImage"
+
"diskSize"
+
];
+
to = [
+
"virtualisation"
+
"diskSize"
+
];
+
})
+
];
options.virtualisation.azureImage = {
-
diskSize = mkOption {
-
type = with types; either (enum [ "auto" ]) int;
-
default = "auto";
-
example = 2048;
-
description = ''
-
Size of disk image. Unit is MB.
-
'';
-
};
-
bootSize = mkOption {
type = types.int;
default = 256;
···
};
vmGeneration = mkOption {
-
type = with types; enum [ "v1" "v2" ];
+
type =
+
with types;
+
enum [
+
"v1"
+
"v2"
+
];
default = "v1";
description = ''
VM Generation to use.
···
bootSize = "${toString cfg.bootSize}M";
partitionTableType = if cfg.vmGeneration == "v2" then "efi" else "legacy";
-
inherit (cfg) diskSize contents;
+
inherit (cfg) contents;
+
inherit (config.virtualisation) diskSize;
inherit config lib pkgs;
};
};
+48 -26
nixos/modules/virtualisation/digital-ocean-image.nix
···
-
{ config, lib, pkgs, ... }:
+
{
+
config,
+
lib,
+
pkgs,
+
...
+
}:
with lib;
let
···
in
{
-
imports = [ ./digital-ocean-config.nix ];
+
imports = [
+
./digital-ocean-config.nix
+
./disk-size-option.nix
+
(lib.mkRenamedOptionModuleWith {
+
sinceRelease = 2411;
+
from = [
+
"virtualisation"
+
"digitalOceanImage"
+
"diskSize"
+
];
+
to = [
+
"virtualisation"
+
"diskSize"
+
];
+
})
+
];
options = {
-
virtualisation.digitalOceanImage.diskSize = mkOption {
-
type = with types; either (enum [ "auto" ]) int;
-
default = "auto";
-
example = 4096;
-
description = ''
-
Size of disk image. Unit is MB.
-
'';
-
};
-
virtualisation.digitalOceanImage.configFile = mkOption {
type = with types; nullOr path;
default = null;
···
};
virtualisation.digitalOceanImage.compressionMethod = mkOption {
-
type = types.enum [ "gzip" "bzip2" ];
+
type = types.enum [
+
"gzip"
+
"bzip2"
+
];
default = "gzip";
example = "bzip2";
description = ''
···
#### implementation
config = {
-
system.build.digitalOceanImage = import ../../lib/make-disk-image.nix {
name = "digital-ocean-image";
format = "qcow2";
-
postVM = let
-
compress = {
-
"gzip" = "${pkgs.gzip}/bin/gzip";
-
"bzip2" = "${pkgs.bzip2}/bin/bzip2";
-
}.${cfg.compressionMethod};
-
in ''
-
${compress} $diskImage
-
'';
-
configFile = if cfg.configFile == null
-
then config.virtualisation.digitalOcean.defaultConfigFile
-
else cfg.configFile;
-
inherit (cfg) diskSize;
+
postVM =
+
let
+
compress =
+
{
+
"gzip" = "${pkgs.gzip}/bin/gzip";
+
"bzip2" = "${pkgs.bzip2}/bin/bzip2";
+
}
+
.${cfg.compressionMethod};
+
in
+
''
+
${compress} $diskImage
+
'';
+
configFile =
+
if cfg.configFile == null then
+
config.virtualisation.digitalOcean.defaultConfigFile
+
else
+
cfg.configFile;
+
inherit (config.virtualisation) diskSize;
inherit config lib pkgs;
};
};
-
meta.maintainers = with maintainers; [ arianvp eamsden ];
+
meta.maintainers = with maintainers; [
+
arianvp
+
eamsden
+
];
}
+38
nixos/modules/virtualisation/disk-size-option.nix
···
+
{ lib, config, ... }:
+
let
+
t = lib.types;
+
in
+
{
+
options = {
+
virtualisation.diskSizeAutoSupported = lib.mkOption {
+
type = t.bool;
+
default = true;
+
description = ''
+
Whether the current image builder or vm runner supports `virtualisation.diskSize = "auto".`
+
'';
+
internal = true;
+
};
+
+
virtualisation.diskSize = lib.mkOption {
+
type = t.either (t.enum [ "auto" ]) t.ints.positive;
+
default = if config.virtualisation.diskSizeAutoSupported then "auto" else 1024;
+
defaultText = "\"auto\" if diskSizeAutoSupported, else 1024";
+
description = ''
+
The disk size in megabytes of the virtual machine.
+
'';
+
};
+
};
+
+
config =
+
let
+
inherit (config.virtualisation) diskSize diskSizeAutoSupported;
+
in
+
{
+
assertions = [
+
{
+
assertion = diskSize != "auto" || diskSizeAutoSupported;
+
message = "Setting virtualisation.diskSize to `auto` is not supported by the current image build or vm runner; use an explicit size.";
+
}
+
];
+
};
+
}
+30 -13
nixos/modules/virtualisation/google-compute-image.nix
···
-
{ config, lib, pkgs, ... }:
+
{
+
config,
+
lib,
+
pkgs,
+
...
+
}:
with lib;
let
···
in
{
-
imports = [ ./google-compute-config.nix ];
+
imports = [
+
./google-compute-config.nix
+
./disk-size-option.nix
+
(lib.mkRenamedOptionModuleWith {
+
sinceRelease = 2411;
+
from = [
+
"virtualisation"
+
"googleComputeImage"
+
"diskSize"
+
];
+
to = [
+
"virtualisation"
+
"diskSize"
+
];
+
})
+
];
options = {
-
virtualisation.googleComputeImage.diskSize = mkOption {
-
type = with types; either (enum [ "auto" ]) int;
-
default = "auto";
-
example = 1536;
-
description = ''
-
Size of disk image. Unit is MB.
-
'';
-
};
-
virtualisation.googleComputeImage.configFile = mkOption {
type = with types; nullOr str;
default = null;
···
system.build.googleComputeImage = import ../../lib/make-disk-image.nix {
name = "google-compute-image";
postVM = ''
-
PATH=$PATH:${with pkgs; lib.makeBinPath [ gnutar gzip ]}
+
PATH=$PATH:${
+
with pkgs;
+
lib.makeBinPath [
+
gnutar
+
gzip
+
]
+
}
pushd $out
mv $diskImage disk.raw
tar -Sc disk.raw | gzip -${toString cfg.compressionLevel} > \
···
format = "raw";
configFile = if cfg.configFile == null then defaultConfigFile else cfg.configFile;
partitionTableType = if cfg.efi then "efi" else "legacy";
-
inherit (cfg) diskSize;
+
inherit (config.virtualisation) diskSize;
inherit config lib pkgs;
};
+28 -11
nixos/modules/virtualisation/hyperv-image.nix
···
-
{ config, pkgs, lib, ... }:
+
{
+
config,
+
pkgs,
+
lib,
+
...
+
}:
with lib;
let
cfg = config.hyperv;
+
in
+
{
-
in {
+
imports = [
+
./disk-size-option.nix
+
(lib.mkRenamedOptionModuleWith {
+
sinceRelease = 2411;
+
from = [
+
"hyperv"
+
"baseImageSize"
+
];
+
to = [
+
"virtualisation"
+
"diskSize"
+
];
+
})
+
];
+
options = {
hyperv = {
-
baseImageSize = mkOption {
-
type = with types; either (enum [ "auto" ]) int;
-
default = "auto";
-
example = 2048;
-
description = ''
-
The size of the hyper-v base image in MiB.
-
'';
-
};
vmDerivationName = mkOption {
type = types.str;
default = "nixos-hyperv-${config.system.nixos.label}-${pkgs.stdenv.hostPlatform.system}";
···
};
config = {
+
# Use a priority just below mkOptionDefault (1500) instead of lib.mkDefault
+
# to avoid breaking existing configs using that.
+
virtualisation.diskSize = lib.mkOverride 1490 (4 * 1024);
+
system.build.hypervImage = import ../../lib/make-disk-image.nix {
name = cfg.vmDerivationName;
postVM = ''
···
rm $diskImage
'';
format = "raw";
-
diskSize = cfg.baseImageSize;
+
inherit (config.virtualisation) diskSize;
partitionTableType = "efi";
inherit config lib pkgs;
};
+23 -11
nixos/modules/virtualisation/linode-image.nix
···
-
{ config, lib, pkgs, ... }:
+
{
+
config,
+
lib,
+
pkgs,
+
...
+
}:
with lib;
let
···
'';
in
{
-
imports = [ ./linode-config.nix ];
+
imports = [
+
./linode-config.nix
+
./disk-size-option.nix
+
(lib.mkRenamedOptionModuleWith {
+
sinceRelease = 2411;
+
from = [
+
"virtualisation"
+
"linodeImage"
+
"diskSize"
+
];
+
to = [
+
"virtualisation"
+
"diskSize"
+
];
+
})
+
];
options = {
-
virtualisation.linodeImage.diskSize = mkOption {
-
type = with types; either (enum (singleton "auto")) ints.positive;
-
default = "auto";
-
example = 1536;
-
description = ''
-
Size of disk image in MB.
-
'';
-
};
virtualisation.linodeImage.configFile = mkOption {
type = with types; nullOr str;
···
format = "raw";
partitionTableType = "none";
configFile = if cfg.configFile == null then defaultConfigFile else cfg.configFile;
-
inherit (cfg) diskSize;
+
inherit (config.virtualisation) diskSize;
inherit config lib pkgs;
};
};
+16 -3
nixos/modules/virtualisation/oci-image.nix
···
-
{ config, lib, pkgs, ... }:
+
{
+
config,
+
lib,
+
pkgs,
+
...
+
}:
let
cfg = config.oci;
···
imports = [ ./oci-common.nix ];
config = {
+
# Use a priority just below mkOptionDefault (1500) instead of lib.mkDefault
+
# to avoid breaking existing configs using that.
+
virtualisation.diskSize = lib.mkOverride 1490 (8 * 1024);
+
virtualisation.diskSizeAutoSupported = false;
+
system.build.OCIImage = import ../../lib/make-disk-image.nix {
inherit config lib pkgs;
-
inherit (cfg) diskSize;
+
inherit (config.virtualisation) diskSize;
name = "oci-image";
configFile = ./oci-config-user.nix;
format = "qcow2";
···
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
-
path = [ pkgs.coreutils pkgs.curl ];
+
path = [
+
pkgs.coreutils
+
pkgs.curl
+
];
script = ''
mkdir -m 0700 -p /root/.ssh
if [ -f /root/.ssh/authorized_keys ]; then
+19 -7
nixos/modules/virtualisation/oci-options.nix
···
-
{ config, lib, pkgs, ... }:
+
{
+
lib,
+
...
+
}:
{
+
imports = [
+
./disk-size-option.nix
+
(lib.mkRenamedOptionModuleWith {
+
sinceRelease = 2411;
+
from = [
+
"oci"
+
"diskSize"
+
];
+
to = [
+
"virtualisation"
+
"diskSize"
+
];
+
})
+
];
+
options = {
oci = {
efi = lib.mkOption {
···
description = ''
Whether the OCI instance is using EFI.
'';
-
};
-
diskSize = lib.mkOption {
-
type = lib.types.int;
-
default = 8192;
-
description = "Size of the disk image created in MB.";
-
example = "diskSize = 12 * 1024; # 12GiB";
};
};
};
+187 -143
nixos/modules/virtualisation/proxmox-image.nix
···
-
{ config, pkgs, lib, ... }:
+
{
+
config,
+
pkgs,
+
lib,
+
...
+
}:
with lib;
-
{
+
imports = [
+
./disk-size-option.nix
+
(lib.mkRenamedOptionModuleWith {
+
sinceRelease = 2411;
+
from = [
+
"proxmox"
+
"qemuConf"
+
"diskSize"
+
];
+
to = [
+
"virtualisation"
+
"diskSize"
+
];
+
})
+
];
+
options.proxmox = {
qemuConf = {
# essential configs
···
'';
};
bios = mkOption {
-
type = types.enum [ "seabios" "ovmf" ];
+
type = types.enum [
+
"seabios"
+
"ovmf"
+
];
default = "seabios";
description = ''
Select BIOS implementation (seabios = Legacy BIOS, ovmf = UEFI).
···
either "efi" or "hybrid".
'';
};
-
diskSize = mkOption {
-
type = types.str;
-
default = "auto";
-
example = "20480";
-
description = ''
-
The size of the disk, in megabytes.
-
if "auto" size is calculated based on the contents copied to it and
-
additionalSpace is taken into account.
-
'';
-
};
net0 = mkOption {
type = types.commas;
default = "virtio=00:00:00:00:00:00,bridge=vmbr0,firewall=1";
···
};
};
qemuExtraConf = mkOption {
-
type = with types; attrsOf (oneOf [ str int ]);
-
default = {};
+
type =
+
with types;
+
attrsOf (oneOf [
+
str
+
int
+
]);
+
default = { };
example = literalExpression ''
{
cpu = "host";
···
'';
};
partitionTableType = mkOption {
-
type = types.enum [ "efi" "hybrid" "legacy" "legacy+gpt" ];
+
type = types.enum [
+
"efi"
+
"hybrid"
+
"legacy"
+
"legacy+gpt"
+
];
description = ''
Partition table type to use. See make-disk-image.nix partitionTableType for details.
Defaults to 'legacy' for 'proxmox.qemuConf.bios="seabios"' (default), other bios values defaults to 'efi'.
···
};
};
-
config = let
-
cfg = config.proxmox;
-
cfgLine = name: value: ''
-
${name}: ${builtins.toString value}
-
'';
-
virtio0Storage = builtins.head (builtins.split ":" cfg.qemuConf.virtio0);
-
cfgFile = fileName: properties: pkgs.writeTextDir fileName ''
-
# generated by NixOS
-
${lib.concatStrings (lib.mapAttrsToList cfgLine properties)}
-
#qmdump#map:virtio0:drive-virtio0:${virtio0Storage}:raw:
-
'';
-
inherit (cfg) partitionTableType;
-
supportEfi = partitionTableType == "efi" || partitionTableType == "hybrid";
-
supportBios = partitionTableType == "legacy" || partitionTableType == "hybrid" || partitionTableType == "legacy+gpt";
-
hasBootPartition = partitionTableType == "efi" || partitionTableType == "hybrid";
-
hasNoFsPartition = partitionTableType == "hybrid" || partitionTableType == "legacy+gpt";
-
in {
-
assertions = [
-
{
-
assertion = config.boot.loader.systemd-boot.enable -> config.proxmox.qemuConf.bios == "ovmf";
-
message = "systemd-boot requires 'ovmf' bios";
-
}
-
{
-
assertion = partitionTableType == "efi" -> config.proxmox.qemuConf.bios == "ovmf";
-
message = "'efi' disk partitioning requires 'ovmf' bios";
-
}
-
{
-
assertion = partitionTableType == "legacy" -> config.proxmox.qemuConf.bios == "seabios";
-
message = "'legacy' disk partitioning requires 'seabios' bios";
-
}
-
{
-
assertion = partitionTableType == "legacy+gpt" -> config.proxmox.qemuConf.bios == "seabios";
-
message = "'legacy+gpt' disk partitioning requires 'seabios' bios";
-
}
-
];
-
system.build.VMA = import ../../lib/make-disk-image.nix {
-
name = "proxmox-${cfg.filenameSuffix}";
+
config =
+
let
+
cfg = config.proxmox;
+
cfgLine = name: value: ''
+
${name}: ${builtins.toString value}
+
'';
+
virtio0Storage = builtins.head (builtins.split ":" cfg.qemuConf.virtio0);
+
cfgFile =
+
fileName: properties:
+
pkgs.writeTextDir fileName ''
+
# generated by NixOS
+
${lib.concatStrings (lib.mapAttrsToList cfgLine properties)}
+
#qmdump#map:virtio0:drive-virtio0:${virtio0Storage}:raw:
+
'';
inherit (cfg) partitionTableType;
-
postVM = let
-
# Build qemu with PVE's patch that adds support for the VMA format
-
vma = (pkgs.qemu_kvm.override {
-
alsaSupport = false;
-
pulseSupport = false;
-
sdlSupport = false;
-
jackSupport = false;
-
gtkSupport = false;
-
vncSupport = false;
-
smartcardSupport = false;
-
spiceSupport = false;
-
ncursesSupport = false;
-
libiscsiSupport = false;
-
tpmSupport = false;
-
numaSupport = false;
-
seccompSupport = false;
-
guestAgentSupport = false;
-
}).overrideAttrs ( super: rec {
-
# Check https://github.com/proxmox/pve-qemu/tree/master for the version
-
# of qemu and patch to use
-
version = "9.0.0";
-
src = pkgs.fetchurl {
-
url = "https://download.qemu.org/qemu-${version}.tar.xz";
-
hash = "sha256-MnCKxmww2MiSYz6paMdxwcdtWX1w3erSGg0izPOG2mk=";
-
};
-
patches = [
-
# Proxmox' VMA tool is published as a particular patch upon QEMU
-
"${pkgs.fetchFromGitHub {
-
owner = "proxmox";
-
repo = "pve-qemu";
-
rev = "14afbdd55f04d250bd679ca1ad55d3f47cd9d4c8";
-
hash = "sha256-lSJQA5SHIHfxJvMLIID2drv2H43crTPMNIlIT37w9Nc=";
-
}}/debian/patches/pve/0027-PVE-Backup-add-vma-backup-format-code.patch"
-
];
-
-
buildInputs = super.buildInputs ++ [ pkgs.libuuid ];
-
nativeBuildInputs = super.nativeBuildInputs ++ [ pkgs.perl ];
+
supportEfi = partitionTableType == "efi" || partitionTableType == "hybrid";
+
supportBios =
+
partitionTableType == "legacy"
+
|| partitionTableType == "hybrid"
+
|| partitionTableType == "legacy+gpt";
+
hasBootPartition = partitionTableType == "efi" || partitionTableType == "hybrid";
+
hasNoFsPartition = partitionTableType == "hybrid" || partitionTableType == "legacy+gpt";
+
in
+
{
+
assertions = [
+
{
+
assertion = config.boot.loader.systemd-boot.enable -> config.proxmox.qemuConf.bios == "ovmf";
+
message = "systemd-boot requires 'ovmf' bios";
+
}
+
{
+
assertion = partitionTableType == "efi" -> config.proxmox.qemuConf.bios == "ovmf";
+
message = "'efi' disk partitioning requires 'ovmf' bios";
+
}
+
{
+
assertion = partitionTableType == "legacy" -> config.proxmox.qemuConf.bios == "seabios";
+
message = "'legacy' disk partitioning requires 'seabios' bios";
+
}
+
{
+
assertion = partitionTableType == "legacy+gpt" -> config.proxmox.qemuConf.bios == "seabios";
+
message = "'legacy+gpt' disk partitioning requires 'seabios' bios";
+
}
+
];
+
system.build.VMA = import ../../lib/make-disk-image.nix {
+
name = "proxmox-${cfg.filenameSuffix}";
+
inherit (cfg) partitionTableType;
+
postVM =
+
let
+
# Build qemu with PVE's patch that adds support for the VMA format
+
vma =
+
(pkgs.qemu_kvm.override {
+
alsaSupport = false;
+
pulseSupport = false;
+
sdlSupport = false;
+
jackSupport = false;
+
gtkSupport = false;
+
vncSupport = false;
+
smartcardSupport = false;
+
spiceSupport = false;
+
ncursesSupport = false;
+
libiscsiSupport = false;
+
tpmSupport = false;
+
numaSupport = false;
+
seccompSupport = false;
+
guestAgentSupport = false;
+
}).overrideAttrs
+
(super: rec {
+
# Check https://github.com/proxmox/pve-qemu/tree/master for the version
+
# of qemu and patch to use
+
version = "9.0.0";
+
src = pkgs.fetchurl {
+
url = "https://download.qemu.org/qemu-${version}.tar.xz";
+
hash = "sha256-MnCKxmww2MiSYz6paMdxwcdtWX1w3erSGg0izPOG2mk=";
+
};
+
patches = [
+
# Proxmox' VMA tool is published as a particular patch upon QEMU
+
"${
+
pkgs.fetchFromGitHub {
+
owner = "proxmox";
+
repo = "pve-qemu";
+
rev = "14afbdd55f04d250bd679ca1ad55d3f47cd9d4c8";
+
hash = "sha256-lSJQA5SHIHfxJvMLIID2drv2H43crTPMNIlIT37w9Nc=";
+
}
+
}/debian/patches/pve/0027-PVE-Backup-add-vma-backup-format-code.patch"
+
];
-
});
-
in
-
''
-
${vma}/bin/vma create "vzdump-qemu-${cfg.filenameSuffix}.vma" \
-
-c ${cfgFile "qemu-server.conf" (cfg.qemuConf // cfg.qemuExtraConf)}/qemu-server.conf drive-virtio0=$diskImage
-
rm $diskImage
-
${pkgs.zstd}/bin/zstd "vzdump-qemu-${cfg.filenameSuffix}.vma"
-
mv "vzdump-qemu-${cfg.filenameSuffix}.vma.zst" $out/
+
buildInputs = super.buildInputs ++ [ pkgs.libuuid ];
+
nativeBuildInputs = super.nativeBuildInputs ++ [ pkgs.perl ];
-
mkdir -p $out/nix-support
-
echo "file vma $out/vzdump-qemu-${cfg.filenameSuffix}.vma.zst" > $out/nix-support/hydra-build-products
-
'';
-
inherit (cfg.qemuConf) additionalSpace diskSize bootSize;
-
format = "raw";
-
inherit config lib pkgs;
-
};
+
});
+
in
+
''
+
${vma}/bin/vma create "vzdump-qemu-${cfg.filenameSuffix}.vma" \
+
-c ${
+
cfgFile "qemu-server.conf" (cfg.qemuConf // cfg.qemuExtraConf)
+
}/qemu-server.conf drive-virtio0=$diskImage
+
rm $diskImage
+
${pkgs.zstd}/bin/zstd "vzdump-qemu-${cfg.filenameSuffix}.vma"
+
mv "vzdump-qemu-${cfg.filenameSuffix}.vma.zst" $out/
-
boot = {
-
growPartition = true;
-
kernelParams = [ "console=ttyS0" ];
-
loader.grub = {
-
device = lib.mkDefault (if (hasNoFsPartition || supportBios) then
-
# Even if there is a separate no-fs partition ("/dev/disk/by-partlabel/no-fs" i.e. "/dev/vda2"),
-
# which will be used the bootloader, do not set it as loader.grub.device.
-
# GRUB installation fails, unless the whole disk is selected.
-
"/dev/vda"
-
else
-
"nodev");
-
efiSupport = lib.mkDefault supportEfi;
-
efiInstallAsRemovable = lib.mkDefault supportEfi;
+
mkdir -p $out/nix-support
+
echo "file vma $out/vzdump-qemu-${cfg.filenameSuffix}.vma.zst" > $out/nix-support/hydra-build-products
+
'';
+
inherit (cfg.qemuConf) additionalSpace bootSize;
+
inherit (config.virtualisation) diskSize;
+
format = "raw";
+
inherit config lib pkgs;
};
-
loader.timeout = 0;
-
initrd.availableKernelModules = [ "uas" "virtio_blk" "virtio_pci" ];
-
};
+
boot = {
+
growPartition = true;
+
kernelParams = [ "console=ttyS0" ];
+
loader.grub = {
+
device = lib.mkDefault (
+
if (hasNoFsPartition || supportBios) then
+
# Even if there is a separate no-fs partition ("/dev/disk/by-partlabel/no-fs" i.e. "/dev/vda2"),
+
# which will be used the bootloader, do not set it as loader.grub.device.
+
# GRUB installation fails, unless the whole disk is selected.
+
"/dev/vda"
+
else
+
"nodev"
+
);
+
efiSupport = lib.mkDefault supportEfi;
+
efiInstallAsRemovable = lib.mkDefault supportEfi;
+
};
-
fileSystems."/" = {
-
device = "/dev/disk/by-label/nixos";
-
autoResize = true;
-
fsType = "ext4";
-
};
-
fileSystems."/boot" = lib.mkIf hasBootPartition {
-
device = "/dev/disk/by-label/ESP";
-
fsType = "vfat";
-
};
+
loader.timeout = 0;
+
initrd.availableKernelModules = [
+
"uas"
+
"virtio_blk"
+
"virtio_pci"
+
];
+
};
-
networking = mkIf cfg.cloudInit.enable {
-
hostName = mkForce "";
-
useDHCP = false;
-
};
+
fileSystems."/" = {
+
device = "/dev/disk/by-label/nixos";
+
autoResize = true;
+
fsType = "ext4";
+
};
+
fileSystems."/boot" = lib.mkIf hasBootPartition {
+
device = "/dev/disk/by-label/ESP";
+
fsType = "vfat";
+
};
+
+
networking = mkIf cfg.cloudInit.enable {
+
hostName = mkForce "";
+
useDHCP = false;
+
};
-
services = {
-
cloud-init = mkIf cfg.cloudInit.enable {
-
enable = true;
-
network.enable = true;
+
services = {
+
cloud-init = mkIf cfg.cloudInit.enable {
+
enable = true;
+
network.enable = true;
+
};
+
sshd.enable = mkDefault true;
+
qemuGuest.enable = true;
};
-
sshd.enable = mkDefault true;
-
qemuGuest.enable = true;
+
+
proxmox.qemuExtraConf.${cfg.cloudInit.device} = "${cfg.cloudInit.defaultStorage}:vm-9999-cloudinit,media=cdrom";
};
-
-
proxmox.qemuExtraConf.${cfg.cloudInit.device} = "${cfg.cloudInit.defaultStorage}:vm-9999-cloudinit,media=cdrom";
-
};
}
+926 -818
nixos/modules/virtualisation/qemu-vm.nix
···
# `config'. By default, the Nix store is shared read-only with the
# host, which makes (re)building VMs very efficient.
-
{ config, lib, pkgs, options, ... }:
+
{
+
config,
+
lib,
+
pkgs,
+
options,
+
...
+
}:
with lib;
···
consoles = lib.concatMapStringsSep " " (c: "console=${c}") cfg.qemu.consoles;
-
driveOpts = { ... }: {
+
driveOpts =
+
{ ... }:
+
{
-
options = {
+
options = {
-
file = mkOption {
-
type = types.str;
-
description = "The file image used for this drive.";
-
};
+
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.";
+
};
-
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.";
+
};
-
deviceExtraOpts = mkOption {
-
type = types.attrsOf types.str;
-
default = {};
-
description = "Extra options passed to device flag.";
-
};
+
name = mkOption {
+
type = types.nullOr types.str;
+
default = null;
+
description = "A name for the drive. Must be unique in the drives list. Not passed to qemu.";
+
};
-
name = mkOption {
-
type = types.nullOr types.str;
-
default = null;
-
description = "A name for the drive. Must be unique in the drives list. Not passed to qemu.";
};
};
-
};
+
selectPartitionTableLayout =
+
{ useEFIBoot, useDefaultFilesystems }:
+
if useDefaultFilesystems then if useEFIBoot then "efi" else "legacy" else "none";
-
selectPartitionTableLayout = { useEFIBoot, useDefaultFilesystems }:
-
if useDefaultFilesystems then
-
if useEFIBoot then "efi" else "legacy"
-
else "none";
-
-
driveCmdline = idx: { file, driveExtraOpts, deviceExtraOpts, ... }:
+
driveCmdline =
+
idx:
+
{
+
file,
+
driveExtraOpts,
+
deviceExtraOpts,
+
...
+
}:
let
drvId = "drive${toString idx}";
-
mkKeyValue = generators.mkKeyValueDefault {} "=";
+
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;
-
});
+
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}";
+
"-drive ${driveOpts} ${device}";
drivesCmdLine = drives: concatStringsSep "\\\n " (imap1 driveCmdline drives);
# Shell script to start the VM.
-
startVM =
-
''
-
#! ${hostPkgs.runtimeShell}
+
startVM = ''
+
#! ${hostPkgs.runtimeShell}
-
export PATH=${makeBinPath [ hostPkgs.coreutils ]}''${PATH:+:}$PATH
-
-
set -e
+
export PATH=${makeBinPath [ hostPkgs.coreutils ]}''${PATH:+:}$PATH
-
# Create an empty ext4 filesystem image. A filesystem image does not
-
# contain a partition table but just a filesystem.
-
createEmptyFilesystemImage() {
-
local name=$1
-
local size=$2
-
local temp=$(mktemp)
-
${qemu}/bin/qemu-img create -f raw "$temp" "$size"
-
${hostPkgs.e2fsprogs}/bin/mkfs.ext4 -L ${rootFilesystemLabel} "$temp"
-
${qemu}/bin/qemu-img convert -f raw -O qcow2 "$temp" "$name"
-
rm "$temp"
-
}
+
set -e
-
NIX_DISK_IMAGE=$(readlink -f "''${NIX_DISK_IMAGE:-${toString config.virtualisation.diskImage}}") || test -z "$NIX_DISK_IMAGE"
+
# Create an empty ext4 filesystem image. A filesystem image does not
+
# contain a partition table but just a filesystem.
+
createEmptyFilesystemImage() {
+
local name=$1
+
local size=$2
+
local temp=$(mktemp)
+
${qemu}/bin/qemu-img create -f raw "$temp" "$size"
+
${hostPkgs.e2fsprogs}/bin/mkfs.ext4 -L ${rootFilesystemLabel} "$temp"
+
${qemu}/bin/qemu-img convert -f raw -O qcow2 "$temp" "$name"
+
rm "$temp"
+
}
-
if test -n "$NIX_DISK_IMAGE" && ! test -e "$NIX_DISK_IMAGE"; then
-
echo "Disk image do not exist, creating the virtualisation disk image..."
+
NIX_DISK_IMAGE=$(readlink -f "''${NIX_DISK_IMAGE:-${toString config.virtualisation.diskImage}}") || test -z "$NIX_DISK_IMAGE"
-
${if (cfg.useBootLoader && cfg.useDefaultFilesystems) then ''
-
# Create a writable qcow2 image using the systemImage as a backing
-
# image.
+
if test -n "$NIX_DISK_IMAGE" && ! test -e "$NIX_DISK_IMAGE"; then
+
echo "Disk image do not exist, creating the virtualisation disk image..."
-
# CoW prevent size to be attributed to an image.
-
# FIXME: raise this issue to upstream.
-
${qemu}/bin/qemu-img create \
-
-f qcow2 \
-
-b ${systemImage}/nixos.qcow2 \
-
-F qcow2 \
-
"$NIX_DISK_IMAGE"
-
'' else if cfg.useDefaultFilesystems then ''
-
createEmptyFilesystemImage "$NIX_DISK_IMAGE" "${toString cfg.diskSize}M"
-
'' else ''
-
# Create an empty disk image without a filesystem.
-
${qemu}/bin/qemu-img create -f qcow2 "$NIX_DISK_IMAGE" "${toString cfg.diskSize}M"
-
''
-
}
-
echo "Virtualisation disk image created."
-
fi
+
${
+
if (cfg.useBootLoader && cfg.useDefaultFilesystems) then
+
''
+
# Create a writable qcow2 image using the systemImage as a backing
+
# image.
-
# Create a directory for storing temporary data of the running VM.
-
if [ -z "$TMPDIR" ] || [ -z "$USE_TMPDIR" ]; then
-
TMPDIR=$(mktemp -d nix-vm.XXXXXXXXXX --tmpdir)
-
fi
+
# CoW prevent size to be attributed to an image.
+
# FIXME: raise this issue to upstream.
+
${qemu}/bin/qemu-img create \
+
-f qcow2 \
+
-b ${systemImage}/nixos.qcow2 \
+
-F qcow2 \
+
"$NIX_DISK_IMAGE"
+
''
+
else if cfg.useDefaultFilesystems then
+
''
+
createEmptyFilesystemImage "$NIX_DISK_IMAGE" "${toString cfg.diskSize}M"
+
''
+
else
+
''
+
# Create an empty disk image without a filesystem.
+
${qemu}/bin/qemu-img create -f qcow2 "$NIX_DISK_IMAGE" "${toString cfg.diskSize}M"
+
''
+
}
+
echo "Virtualisation disk image created."
+
fi
-
${lib.optionalString (cfg.useNixStoreImage) ''
-
echo "Creating Nix store image..."
+
# Create a directory for storing temporary data of the running VM.
+
if [ -z "$TMPDIR" ] || [ -z "$USE_TMPDIR" ]; then
+
TMPDIR=$(mktemp -d nix-vm.XXXXXXXXXX --tmpdir)
+
fi
-
${hostPkgs.gnutar}/bin/tar --create \
-
--absolute-names \
-
--verbatim-files-from \
-
--transform 'flags=rSh;s|/nix/store/||' \
-
--transform 'flags=rSh;s|~nix~case~hack~[[:digit:]]\+||g' \
-
--files-from ${hostPkgs.closureInfo { rootPaths = [ config.system.build.toplevel regInfo ]; }}/store-paths \
-
| ${hostPkgs.erofs-utils}/bin/mkfs.erofs \
-
--quiet \
-
--force-uid=0 \
-
--force-gid=0 \
-
-L ${nixStoreFilesystemLabel} \
-
-U eb176051-bd15-49b7-9e6b-462e0b467019 \
-
-T 0 \
-
--tar=f \
-
"$TMPDIR"/store.img
+
${lib.optionalString (cfg.useNixStoreImage) ''
+
echo "Creating Nix store image..."
-
echo "Created Nix store image."
-
''
-
}
+
${hostPkgs.gnutar}/bin/tar --create \
+
--absolute-names \
+
--verbatim-files-from \
+
--transform 'flags=rSh;s|/nix/store/||' \
+
--transform 'flags=rSh;s|~nix~case~hack~[[:digit:]]\+||g' \
+
--files-from ${
+
hostPkgs.closureInfo {
+
rootPaths = [
+
config.system.build.toplevel
+
regInfo
+
];
+
}
+
}/store-paths \
+
| ${hostPkgs.erofs-utils}/bin/mkfs.erofs \
+
--quiet \
+
--force-uid=0 \
+
--force-gid=0 \
+
-L ${nixStoreFilesystemLabel} \
+
-U eb176051-bd15-49b7-9e6b-462e0b467019 \
+
-T 0 \
+
--tar=f \
+
"$TMPDIR"/store.img
-
# Create a directory for exchanging data with the VM.
-
mkdir -p "$TMPDIR/xchg"
+
echo "Created Nix store image."
+
''}
-
${lib.optionalString cfg.useHostCerts
-
''
-
mkdir -p "$TMPDIR/certs"
-
if [ -e "$NIX_SSL_CERT_FILE" ]; then
-
cp -L "$NIX_SSL_CERT_FILE" "$TMPDIR"/certs/ca-certificates.crt
-
else
-
echo \$NIX_SSL_CERT_FILE should point to a valid file if virtualisation.useHostCerts is enabled.
-
fi
-
''}
+
# Create a directory for exchanging data with the VM.
+
mkdir -p "$TMPDIR/xchg"
-
${lib.optionalString cfg.useEFIBoot
-
''
-
# Expose EFI variables, it's useful even when we are not using a bootloader (!).
-
# We might be interested in having EFI variable storage present even if we aren't booting via UEFI, hence
-
# no guard against `useBootLoader`. Examples:
-
# - testing PXE boot or other EFI applications
-
# - directbooting LinuxBoot, which `kexec()s` into a UEFI environment that can boot e.g. Windows
-
NIX_EFI_VARS=$(readlink -f "''${NIX_EFI_VARS:-${config.system.name}-efi-vars.fd}")
-
# VM needs writable EFI vars
-
if ! test -e "$NIX_EFI_VARS"; then
-
${if cfg.efi.keepVariables then
-
# We still need the EFI var from the make-disk-image derivation
-
# because our "switch-to-configuration" process might
-
# write into it and we want to keep this data.
-
''cp ${systemImage}/efi-vars.fd "$NIX_EFI_VARS"''
-
else
-
''cp ${cfg.efi.variables} "$NIX_EFI_VARS"''
-
}
-
chmod 0644 "$NIX_EFI_VARS"
-
fi
-
''}
+
${lib.optionalString cfg.useHostCerts ''
+
mkdir -p "$TMPDIR/certs"
+
if [ -e "$NIX_SSL_CERT_FILE" ]; then
+
cp -L "$NIX_SSL_CERT_FILE" "$TMPDIR"/certs/ca-certificates.crt
+
else
+
echo \$NIX_SSL_CERT_FILE should point to a valid file if virtualisation.useHostCerts is enabled.
+
fi
+
''}
-
${lib.optionalString cfg.tpm.enable ''
-
NIX_SWTPM_DIR=$(readlink -f "''${NIX_SWTPM_DIR:-${config.system.name}-swtpm}")
-
mkdir -p "$NIX_SWTPM_DIR"
-
${lib.getExe cfg.tpm.package} \
-
socket \
-
--tpmstate dir="$NIX_SWTPM_DIR" \
-
--ctrl type=unixio,path="$NIX_SWTPM_DIR"/socket,terminate \
-
--pid file="$NIX_SWTPM_DIR"/pid --daemon \
-
--tpm2 \
-
--log file="$NIX_SWTPM_DIR"/stdout,level=6
+
${lib.optionalString cfg.useEFIBoot ''
+
# Expose EFI variables, it's useful even when we are not using a bootloader (!).
+
# We might be interested in having EFI variable storage present even if we aren't booting via UEFI, hence
+
# no guard against `useBootLoader`. Examples:
+
# - testing PXE boot or other EFI applications
+
# - directbooting LinuxBoot, which `kexec()s` into a UEFI environment that can boot e.g. Windows
+
NIX_EFI_VARS=$(readlink -f "''${NIX_EFI_VARS:-${config.system.name}-efi-vars.fd}")
+
# VM needs writable EFI vars
+
if ! test -e "$NIX_EFI_VARS"; then
+
${
+
if cfg.efi.keepVariables then
+
# We still need the EFI var from the make-disk-image derivation
+
# because our "switch-to-configuration" process might
+
# write into it and we want to keep this data.
+
''cp ${systemImage}/efi-vars.fd "$NIX_EFI_VARS"''
+
else
+
''cp ${cfg.efi.variables} "$NIX_EFI_VARS"''
+
}
+
chmod 0644 "$NIX_EFI_VARS"
+
fi
+
''}
-
# Enable `fdflags` builtin in Bash
-
# We will need it to perform surgical modification of the file descriptor
-
# passed in the coprocess to remove `FD_CLOEXEC`, i.e. close the file descriptor
-
# on exec.
-
# If let alone, it will trigger the coprocess to read EOF when QEMU is `exec`
-
# at the end of this script. To work around that, we will just clear
-
# the `FD_CLOEXEC` bits as a first step.
-
enable -f ${hostPkgs.bash}/lib/bash/fdflags fdflags
-
# leave a dangling subprocess because the swtpm ctrl socket has
-
# "terminate" when the last connection disconnects, it stops swtpm.
-
# When qemu stops, or if the main shell process ends, the coproc will
-
# get signaled by virtue of the pipe between main and coproc ending.
-
# Which in turns triggers a socat connect-disconnect to swtpm which
-
# will stop it.
-
coproc waitingswtpm {
-
read || :
-
echo "" | ${lib.getExe hostPkgs.socat} STDIO UNIX-CONNECT:"$NIX_SWTPM_DIR"/socket
-
}
-
# Clear `FD_CLOEXEC` on the coprocess' file descriptor stdin.
-
fdflags -s-cloexec ''${waitingswtpm[1]}
-
''}
+
${lib.optionalString cfg.tpm.enable ''
+
NIX_SWTPM_DIR=$(readlink -f "''${NIX_SWTPM_DIR:-${config.system.name}-swtpm}")
+
mkdir -p "$NIX_SWTPM_DIR"
+
${lib.getExe cfg.tpm.package} \
+
socket \
+
--tpmstate dir="$NIX_SWTPM_DIR" \
+
--ctrl type=unixio,path="$NIX_SWTPM_DIR"/socket,terminate \
+
--pid file="$NIX_SWTPM_DIR"/pid --daemon \
+
--tpm2 \
+
--log file="$NIX_SWTPM_DIR"/stdout,level=6
-
cd "$TMPDIR"
+
# Enable `fdflags` builtin in Bash
+
# We will need it to perform surgical modification of the file descriptor
+
# passed in the coprocess to remove `FD_CLOEXEC`, i.e. close the file descriptor
+
# on exec.
+
# If let alone, it will trigger the coprocess to read EOF when QEMU is `exec`
+
# at the end of this script. To work around that, we will just clear
+
# the `FD_CLOEXEC` bits as a first step.
+
enable -f ${hostPkgs.bash}/lib/bash/fdflags fdflags
+
# leave a dangling subprocess because the swtpm ctrl socket has
+
# "terminate" when the last connection disconnects, it stops swtpm.
+
# When qemu stops, or if the main shell process ends, the coproc will
+
# get signaled by virtue of the pipe between main and coproc ending.
+
# Which in turns triggers a socat connect-disconnect to swtpm which
+
# will stop it.
+
coproc waitingswtpm {
+
read || :
+
echo "" | ${lib.getExe hostPkgs.socat} STDIO UNIX-CONNECT:"$NIX_SWTPM_DIR"/socket
+
}
+
# Clear `FD_CLOEXEC` on the coprocess' file descriptor stdin.
+
fdflags -s-cloexec ''${waitingswtpm[1]}
+
''}
-
${lib.optionalString (cfg.emptyDiskImages != []) "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
-
idx=$((idx + 1))
-
'')}
+
cd "$TMPDIR"
-
# Start QEMU.
-
exec ${qemu-common.qemuBinary qemu} \
-
-name ${config.system.name} \
-
-m ${toString config.virtualisation.memorySize} \
-
-smp ${toString config.virtualisation.cores} \
-
-device virtio-rng-pci \
-
${concatStringsSep " " config.virtualisation.qemu.networkingOptions} \
-
${concatStringsSep " \\\n "
-
(mapAttrsToList
-
(tag: share: "-virtfs local,path=${share.source},security_model=${share.securityModel},mount_tag=${tag}")
-
config.virtualisation.sharedDirectories)} \
-
${drivesCmdLine config.virtualisation.qemu.drives} \
-
${concatStringsSep " \\\n " config.virtualisation.qemu.options} \
-
$QEMU_OPTS \
-
"$@"
-
'';
+
${lib.optionalString (cfg.emptyDiskImages != [ ]) "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
+
idx=$((idx + 1))
+
'')}
+
# Start QEMU.
+
exec ${qemu-common.qemuBinary qemu} \
+
-name ${config.system.name} \
+
-m ${toString config.virtualisation.memorySize} \
+
-smp ${toString config.virtualisation.cores} \
+
-device virtio-rng-pci \
+
${concatStringsSep " " config.virtualisation.qemu.networkingOptions} \
+
${
+
concatStringsSep " \\\n " (
+
mapAttrsToList (
+
tag: share:
+
"-virtfs local,path=${share.source},security_model=${share.securityModel},mount_tag=${tag}"
+
) config.virtualisation.sharedDirectories
+
)
+
} \
+
${drivesCmdLine config.virtualisation.qemu.drives} \
+
${concatStringsSep " \\\n " config.virtualisation.qemu.options} \
+
$QEMU_OPTS \
+
"$@"
+
'';
regInfo = hostPkgs.closureInfo { rootPaths = config.virtualisation.additionalPaths; };
···
copyChannel = false;
OVMF = cfg.efi.OVMF;
};
-
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.`")
+
./disk-size-option.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 = {
virtualisation.fileSystems = options.fileSystems;
-
virtualisation.memorySize =
-
mkOption {
-
type = types.ints.positive;
-
default = 1024;
-
description = ''
-
The memory size in megabytes of the virtual machine.
-
'';
-
};
+
virtualisation.memorySize = mkOption {
+
type = types.ints.positive;
+
default = 1024;
+
description = ''
+
The memory size in megabytes of the virtual machine.
+
'';
+
};
-
virtualisation.msize =
-
mkOption {
-
type = types.ints.positive;
-
default = 16384;
-
description = ''
-
The msize (maximum packet size) option passed to 9p file systems, in
-
bytes. Increasing this should increase performance significantly,
-
at the cost of higher RAM usage.
-
'';
-
};
+
virtualisation.msize = mkOption {
+
type = types.ints.positive;
+
default = 16384;
+
description = ''
+
The msize (maximum packet size) option passed to 9p file systems, in
+
bytes. Increasing this should increase performance significantly,
+
at the cost of higher RAM usage.
+
'';
+
};
-
virtualisation.diskSize =
-
mkOption {
-
type = types.ints.positive;
-
default = 1024;
-
description = ''
-
The disk size in megabytes of the virtual machine.
-
'';
-
};
+
virtualisation.diskImage = mkOption {
+
type = types.nullOr types.str;
+
default = "./${config.system.name}.qcow2";
+
defaultText = literalExpression ''"./''${config.system.name}.qcow2"'';
+
description = ''
+
Path to the disk image containing the root filesystem.
+
The image will be created on startup if it does not
+
exist.
-
virtualisation.diskImage =
-
mkOption {
-
type = types.nullOr types.str;
-
default = "./${config.system.name}.qcow2";
-
defaultText = literalExpression ''"./''${config.system.name}.qcow2"'';
-
description = ''
-
Path to the disk image containing the root filesystem.
-
The image will be created on startup if it does not
-
exist.
+
If null, a tmpfs will be used as the root filesystem and
+
the VM's state will not be persistent.
+
'';
+
};
-
If null, a tmpfs will be used as the root filesystem and
-
the VM's state will not be persistent.
-
'';
-
};
-
-
virtualisation.bootLoaderDevice =
-
mkOption {
-
type = types.path;
-
default = "/dev/disk/by-id/virtio-${rootDriveSerialAttr}";
-
defaultText = literalExpression ''/dev/disk/by-id/virtio-${rootDriveSerialAttr}'';
-
example = "/dev/disk/by-id/virtio-boot-loader-device";
-
description = ''
-
The path (inside th VM) to the device to boot from when legacy booting.
-
'';
-
};
+
virtualisation.bootLoaderDevice = mkOption {
+
type = types.path;
+
default = "/dev/disk/by-id/virtio-${rootDriveSerialAttr}";
+
defaultText = literalExpression ''/dev/disk/by-id/virtio-${rootDriveSerialAttr}'';
+
example = "/dev/disk/by-id/virtio-boot-loader-device";
+
description = ''
+
The path (inside th VM) to the device to boot from when legacy booting.
+
'';
+
};
-
virtualisation.bootPartition =
-
mkOption {
-
type = types.nullOr types.path;
-
default = if cfg.useEFIBoot then "/dev/disk/by-label/${espFilesystemLabel}" else null;
-
defaultText = literalExpression ''if cfg.useEFIBoot then "/dev/disk/by-label/${espFilesystemLabel}" else null'';
-
example = "/dev/disk/by-label/esp";
-
description = ''
-
The path (inside the VM) to the device containing the EFI System Partition (ESP).
+
virtualisation.bootPartition = mkOption {
+
type = types.nullOr types.path;
+
default = if cfg.useEFIBoot then "/dev/disk/by-label/${espFilesystemLabel}" else null;
+
defaultText = literalExpression ''if cfg.useEFIBoot then "/dev/disk/by-label/${espFilesystemLabel}" else null'';
+
example = "/dev/disk/by-label/esp";
+
description = ''
+
The path (inside the VM) to the device containing the EFI System Partition (ESP).
-
If you are *not* booting from a UEFI firmware, this value is, by
-
default, `null`. The ESP is mounted to `boot.loader.efi.efiSysMountpoint`.
-
'';
-
};
+
If you are *not* booting from a UEFI firmware, this value is, by
+
default, `null`. The ESP is mounted to `boot.loader.efi.efiSysMountpoint`.
+
'';
+
};
-
virtualisation.rootDevice =
-
mkOption {
-
type = types.nullOr types.path;
-
default = "/dev/disk/by-label/${rootFilesystemLabel}";
-
defaultText = literalExpression ''/dev/disk/by-label/${rootFilesystemLabel}'';
-
example = "/dev/disk/by-label/nixos";
-
description = ''
-
The path (inside the VM) to the device containing the root filesystem.
-
'';
-
};
+
virtualisation.rootDevice = mkOption {
+
type = types.nullOr types.path;
+
default = "/dev/disk/by-label/${rootFilesystemLabel}";
+
defaultText = literalExpression ''/dev/disk/by-label/${rootFilesystemLabel}'';
+
example = "/dev/disk/by-label/nixos";
+
description = ''
+
The path (inside the VM) to the device containing the root filesystem.
+
'';
+
};
-
virtualisation.emptyDiskImages =
-
mkOption {
-
type = types.listOf types.ints.positive;
-
default = [];
-
description = ''
-
Additional disk images to provide to the VM. The value is
-
a list of size in megabytes of each disk. These disks are
-
writeable by the VM.
-
'';
-
};
+
virtualisation.emptyDiskImages = mkOption {
+
type = types.listOf types.ints.positive;
+
default = [ ];
+
description = ''
+
Additional disk images to provide to the VM. The value is
+
a list of size in megabytes of each disk. These disks are
+
writeable by the VM.
+
'';
+
};
-
virtualisation.graphics =
-
mkOption {
-
type = types.bool;
-
default = true;
-
description = ''
-
Whether to run QEMU with a graphics window, or in nographic mode.
-
Serial console will be enabled on both settings, but this will
-
change the preferred console.
-
'';
-
};
+
virtualisation.graphics = mkOption {
+
type = types.bool;
+
default = true;
+
description = ''
+
Whether to run QEMU with a graphics window, or in nographic mode.
+
Serial console will be enabled on both settings, but this will
+
change the preferred console.
+
'';
+
};
-
virtualisation.resolution =
-
mkOption {
-
type = options.services.xserver.resolutions.type.nestedTypes.elemType;
-
default = { x = 1024; y = 768; };
-
description = ''
-
The resolution of the virtual machine display.
-
'';
+
virtualisation.resolution = mkOption {
+
type = options.services.xserver.resolutions.type.nestedTypes.elemType;
+
default = {
+
x = 1024;
+
y = 768;
};
+
description = ''
+
The resolution of the virtual machine display.
+
'';
+
};
-
virtualisation.cores =
-
mkOption {
-
type = types.ints.positive;
-
default = 1;
-
description = ''
-
Specify the number of cores the guest is permitted to use.
-
The number can be higher than the available cores on the
-
host system.
-
'';
-
};
+
virtualisation.cores = mkOption {
+
type = types.ints.positive;
+
default = 1;
+
description = ''
+
Specify the number of cores the guest is permitted to use.
+
The number can be higher than the available cores on the
+
host system.
+
'';
+
};
-
virtualisation.sharedDirectories =
-
mkOption {
-
type = types.attrsOf
-
(types.submodule {
-
options.source = mkOption {
-
type = types.str;
-
description = "The path of the directory to share, can be a shell variable";
-
};
-
options.target = mkOption {
-
type = types.path;
-
description = "The mount point of the directory inside the virtual machine";
-
};
-
options.securityModel = mkOption {
-
type = types.enum [ "passthrough" "mapped-xattr" "mapped-file" "none" ];
-
default = "mapped-xattr";
-
description = ''
-
The security model to use for this share:
+
virtualisation.sharedDirectories = mkOption {
+
type = types.attrsOf (
+
types.submodule {
+
options.source = mkOption {
+
type = types.str;
+
description = "The path of the directory to share, can be a shell variable";
+
};
+
options.target = mkOption {
+
type = types.path;
+
description = "The mount point of the directory inside the virtual machine";
+
};
+
options.securityModel = mkOption {
+
type = types.enum [
+
"passthrough"
+
"mapped-xattr"
+
"mapped-file"
+
"none"
+
];
+
default = "mapped-xattr";
+
description = ''
+
The security model to use for this share:
-
- `passthrough`: files are stored using the same credentials as they are created on the guest (this requires QEMU to run as root)
-
- `mapped-xattr`: some of the file attributes like uid, gid, mode bits and link target are stored as file attributes
-
- `mapped-file`: the attributes are stored in the hidden .virtfs_metadata directory. Directories exported by this security model cannot interact with other unix tools
-
- `none`: same as "passthrough" except the sever won't report failures if it fails to set file attributes like ownership
-
'';
-
};
-
});
-
default = { };
-
example = {
-
my-share = { source = "/path/to/be/shared"; target = "/mnt/shared"; };
+
- `passthrough`: files are stored using the same credentials as they are created on the guest (this requires QEMU to run as root)
+
- `mapped-xattr`: some of the file attributes like uid, gid, mode bits and link target are stored as file attributes
+
- `mapped-file`: the attributes are stored in the hidden .virtfs_metadata directory. Directories exported by this security model cannot interact with other unix tools
+
- `none`: same as "passthrough" except the sever won't report failures if it fails to set file attributes like ownership
+
'';
+
};
+
}
+
);
+
default = { };
+
example = {
+
my-share = {
+
source = "/path/to/be/shared";
+
target = "/mnt/shared";
};
-
description = ''
-
An attributes set of directories that will be shared with the
-
virtual machine using VirtFS (9P filesystem over VirtIO).
-
The attribute name will be used as the 9P mount tag.
-
'';
};
+
description = ''
+
An attributes set of directories that will be shared with the
+
virtual machine using VirtFS (9P filesystem over VirtIO).
+
The attribute name will be used as the 9P mount tag.
+
'';
+
};
-
virtualisation.additionalPaths =
-
mkOption {
-
type = types.listOf types.path;
-
default = [];
-
description = ''
-
A list of paths whose closure should be made available to
-
the VM.
+
virtualisation.additionalPaths = mkOption {
+
type = types.listOf types.path;
+
default = [ ];
+
description = ''
+
A list of paths whose closure should be made available to
+
the VM.
-
When 9p is used, the closure is registered in the Nix
-
database in the VM. All other paths in the host Nix store
-
appear in the guest Nix store as well, but are considered
-
garbage (because they are not registered in the Nix
-
database of the guest).
+
When 9p is used, the closure is registered in the Nix
+
database in the VM. All other paths in the host Nix store
+
appear in the guest Nix store as well, but are considered
+
garbage (because they are not registered in the Nix
+
database of the guest).
-
When {option}`virtualisation.useNixStoreImage` is
-
set, the closure is copied to the Nix store image.
-
'';
-
};
+
When {option}`virtualisation.useNixStoreImage` is
+
set, the closure is copied to the Nix store image.
+
'';
+
};
virtualisation.forwardPorts = mkOption {
-
type = types.listOf
-
(types.submodule {
+
type = types.listOf (
+
types.submodule {
options.from = mkOption {
-
type = types.enum [ "host" "guest" ];
+
type = types.enum [
+
"host"
+
"guest"
+
];
default = "host";
description = ''
-
Controls the direction in which the ports are mapped:
+
Controls the direction in which the ports are mapped:
-
- `"host"` means traffic from the host ports
-
is forwarded to the given guest port.
-
- `"guest"` means traffic from the guest ports
-
is forwarded to the given host port.
-
'';
+
- `"host"` means traffic from the host ports
+
is forwarded to the given guest port.
+
- `"guest"` means traffic from the guest ports
+
is forwarded to the given host port.
+
'';
};
options.proto = mkOption {
-
type = types.enum [ "tcp" "udp" ];
+
type = types.enum [
+
"tcp"
+
"udp"
+
];
default = "tcp";
description = "The protocol to forward.";
};
···
type = types.port;
description = "The guest port to be mapped.";
};
-
});
-
default = [];
-
example = lib.literalExpression
-
''
+
}
+
);
+
default = [ ];
+
example = lib.literalExpression ''
[ # forward local port 2222 -> 22, to ssh into the VM
{ from = "host"; host.port = 2222; guest.port = 22; }
···
host.address = "127.0.0.1"; host.port = 80;
}
]
-
'';
+
'';
description = ''
-
When using the SLiRP user networking (default), this option allows to
-
forward ports to/from the host/guest.
+
When using the SLiRP user networking (default), this option allows to
+
forward ports to/from the host/guest.
-
::: {.warning}
-
If the NixOS firewall on the virtual machine is enabled, you also
-
have to open the guest ports to enable the traffic between host and
-
guest.
-
:::
+
::: {.warning}
+
If the NixOS firewall on the virtual machine is enabled, you also
+
have to open the guest ports to enable the traffic between host and
+
guest.
+
:::
-
::: {.note}
-
Currently QEMU supports only IPv4 forwarding.
-
:::
-
'';
+
::: {.note}
+
Currently QEMU supports only IPv4 forwarding.
+
:::
+
'';
};
-
virtualisation.restrictNetwork =
-
mkOption {
-
type = types.bool;
-
default = false;
-
example = true;
-
description = ''
-
If this option is enabled, the guest will be isolated, i.e. it will
-
not be able to contact the host and no guest IP packets will be
-
routed over the host to the outside. This option does not affect
-
any explicitly set forwarding rules.
-
'';
-
};
+
virtualisation.restrictNetwork = mkOption {
+
type = types.bool;
+
default = false;
+
example = true;
+
description = ''
+
If this option is enabled, the guest will be isolated, i.e. it will
+
not be able to contact the host and no guest IP packets will be
+
routed over the host to the outside. This option does not affect
+
any explicitly set forwarding rules.
+
'';
+
};
-
virtualisation.vlans =
-
mkOption {
-
type = types.listOf types.ints.unsigned;
-
default = if config.virtualisation.interfaces == {} then [ 1 ] else [ ];
-
defaultText = lib.literalExpression ''if config.virtualisation.interfaces == {} then [ 1 ] else [ ]'';
-
example = [ 1 2 ];
-
description = ''
-
Virtual networks to which the VM is connected. Each
-
number «N» in this list causes
-
the VM to have a virtual Ethernet interface attached to a
-
separate virtual network on which it will be assigned IP
-
address
-
`192.168.«N».«M»`,
-
where «M» is the index of this VM
-
in the list of VMs.
-
'';
-
};
+
virtualisation.vlans = mkOption {
+
type = types.listOf types.ints.unsigned;
+
default = if config.virtualisation.interfaces == { } then [ 1 ] else [ ];
+
defaultText = lib.literalExpression ''if config.virtualisation.interfaces == {} then [ 1 ] else [ ]'';
+
example = [
+
1
+
2
+
];
+
description = ''
+
Virtual networks to which the VM is connected. Each
+
number «N» in this list causes
+
the VM to have a virtual Ethernet interface attached to a
+
separate virtual network on which it will be assigned IP
+
address
+
`192.168.«N».«M»`,
+
where «M» is the index of this VM
+
in the list of VMs.
+
'';
+
};
virtualisation.interfaces = mkOption {
-
default = {};
+
default = { };
example = {
enp1s0.vlan = 1;
};
description = ''
Network interfaces to add to the VM.
'';
-
type = with types; attrsOf (submodule {
-
options = {
-
vlan = mkOption {
-
type = types.ints.unsigned;
-
description = ''
-
VLAN to which the network interface is connected.
-
'';
-
};
+
type =
+
with types;
+
attrsOf (submodule {
+
options = {
+
vlan = mkOption {
+
type = types.ints.unsigned;
+
description = ''
+
VLAN to which the network interface is connected.
+
'';
+
};
-
assignIP = mkOption {
-
type = types.bool;
-
default = false;
-
description = ''
-
Automatically assign an IP address to the network interface using the same scheme as
-
virtualisation.vlans.
-
'';
+
assignIP = mkOption {
+
type = types.bool;
+
default = false;
+
description = ''
+
Automatically assign an IP address to the network interface using the same scheme as
+
virtualisation.vlans.
+
'';
+
};
};
-
};
-
});
+
});
};
-
virtualisation.writableStore =
-
mkOption {
-
type = types.bool;
-
default = cfg.mountHostNixStore;
-
defaultText = literalExpression "cfg.mountHostNixStore";
-
description = ''
-
If enabled, the Nix store in the VM is made writable by
-
layering an overlay filesystem on top of the host's Nix
-
store.
+
virtualisation.writableStore = mkOption {
+
type = types.bool;
+
default = cfg.mountHostNixStore;
+
defaultText = literalExpression "cfg.mountHostNixStore";
+
description = ''
+
If enabled, the Nix store in the VM is made writable by
+
layering an overlay filesystem on top of the host's Nix
+
store.
-
By default, this is enabled if you mount a host Nix store.
-
'';
-
};
+
By default, this is enabled if you mount a host Nix store.
+
'';
+
};
-
virtualisation.writableStoreUseTmpfs =
-
mkOption {
-
type = types.bool;
-
default = true;
-
description = ''
-
Use a tmpfs for the writable store instead of writing to the VM's
-
own filesystem.
-
'';
-
};
+
virtualisation.writableStoreUseTmpfs = mkOption {
+
type = types.bool;
+
default = true;
+
description = ''
+
Use a tmpfs for the writable store instead of writing to the VM's
+
own filesystem.
+
'';
+
};
-
networking.primaryIPAddress =
-
mkOption {
-
type = types.str;
-
default = "";
-
internal = true;
-
description = "Primary IP address used in /etc/hosts.";
-
};
+
networking.primaryIPAddress = mkOption {
+
type = types.str;
+
default = "";
+
internal = true;
+
description = "Primary IP address used in /etc/hosts.";
+
};
-
networking.primaryIPv6Address =
-
mkOption {
-
type = types.str;
-
default = "";
-
internal = true;
-
description = "Primary IPv6 address used in /etc/hosts.";
-
};
+
networking.primaryIPv6Address = mkOption {
+
type = types.str;
+
default = "";
+
internal = true;
+
description = "Primary IPv6 address used in /etc/hosts.";
+
};
virtualisation.host.pkgs = mkOption {
type = options.nixpkgs.pkgs.type;
···
};
virtualisation.qemu = {
-
package =
-
mkOption {
-
type = types.package;
-
default = if hostPkgs.stdenv.hostPlatform.qemuArch == pkgs.stdenv.hostPlatform.qemuArch then hostPkgs.qemu_kvm else hostPkgs.qemu;
-
defaultText = literalExpression "if hostPkgs.stdenv.hostPlatform.qemuArch == pkgs.stdenv.hostPlatform.qemuArch then config.virtualisation.host.pkgs.qemu_kvm else config.virtualisation.host.pkgs.qemu";
-
example = literalExpression "pkgs.qemu_test";
-
description = "QEMU package to use.";
-
};
+
package = mkOption {
+
type = types.package;
+
default =
+
if hostPkgs.stdenv.hostPlatform.qemuArch == pkgs.stdenv.hostPlatform.qemuArch then
+
hostPkgs.qemu_kvm
+
else
+
hostPkgs.qemu;
+
defaultText = literalExpression "if hostPkgs.stdenv.hostPlatform.qemuArch == pkgs.stdenv.hostPlatform.qemuArch then config.virtualisation.host.pkgs.qemu_kvm else config.virtualisation.host.pkgs.qemu";
+
example = literalExpression "pkgs.qemu_test";
+
description = "QEMU package to use.";
+
};
-
options =
-
mkOption {
-
type = types.listOf types.str;
-
default = [];
-
example = [ "-vga std" ];
-
description = ''
-
Options passed to QEMU.
-
See [QEMU User Documentation](https://www.qemu.org/docs/master/system/qemu-manpage) for a complete list.
-
'';
-
};
+
options = mkOption {
+
type = types.listOf types.str;
+
default = [ ];
+
example = [ "-vga std" ];
+
description = ''
+
Options passed to QEMU.
+
See [QEMU User Documentation](https://www.qemu.org/docs/master/system/qemu-manpage) for a complete list.
+
'';
+
};
consoles = mkOption {
type = types.listOf types.str;
-
default = let
-
consoles = [ "${qemu-common.qemuSerialDevice},115200n8" "tty0" ];
-
in if cfg.graphics then consoles else reverseList consoles;
+
default =
+
let
+
consoles = [
+
"${qemu-common.qemuSerialDevice},115200n8"
+
"tty0"
+
];
+
in
+
if cfg.graphics then consoles else reverseList consoles;
example = [ "console=tty1" ];
description = ''
The output console devices to pass to the kernel command line via the
···
'';
};
-
networkingOptions =
-
mkOption {
-
type = types.listOf types.str;
-
default = [ ];
-
example = [
-
"-net nic,netdev=user.0,model=virtio"
-
"-netdev user,id=user.0,\${QEMU_NET_OPTS:+,$QEMU_NET_OPTS}"
-
];
-
description = ''
-
Networking-related command-line options that should be passed to qemu.
-
The default is to use userspace networking (SLiRP).
-
See the [QEMU Wiki on Networking](https://wiki.qemu.org/Documentation/Networking) for details.
+
networkingOptions = mkOption {
+
type = types.listOf types.str;
+
default = [ ];
+
example = [
+
"-net nic,netdev=user.0,model=virtio"
+
"-netdev user,id=user.0,\${QEMU_NET_OPTS:+,$QEMU_NET_OPTS}"
+
];
+
description = ''
+
Networking-related command-line options that should be passed to qemu.
+
The default is to use userspace networking (SLiRP).
+
See the [QEMU Wiki on Networking](https://wiki.qemu.org/Documentation/Networking) for details.
-
If you override this option, be advised to keep
-
`''${QEMU_NET_OPTS:+,$QEMU_NET_OPTS}` (as seen in the example)
-
to keep the default runtime behaviour.
-
'';
-
};
-
-
drives =
-
mkOption {
-
type = types.listOf (types.submodule driveOpts);
-
description = "Drives passed to qemu.";
-
};
-
-
diskInterface =
-
mkOption {
-
type = types.enum [ "virtio" "scsi" "ide" ];
-
default = "virtio";
-
example = "scsi";
-
description = "The interface used for the virtual hard disks.";
-
};
+
If you override this option, be advised to keep
+
`''${QEMU_NET_OPTS:+,$QEMU_NET_OPTS}` (as seen in the example)
+
to keep the default runtime behaviour.
+
'';
+
};
-
guestAgent.enable =
-
mkOption {
-
type = types.bool;
-
default = true;
-
description = ''
-
Enable the Qemu guest agent.
-
'';
-
};
+
drives = mkOption {
+
type = types.listOf (types.submodule driveOpts);
+
description = "Drives passed to qemu.";
+
};
-
virtioKeyboard =
-
mkOption {
-
type = types.bool;
-
default = true;
-
description = ''
-
Enable the virtio-keyboard device.
-
'';
-
};
-
};
+
diskInterface = mkOption {
+
type = types.enum [
+
"virtio"
+
"scsi"
+
"ide"
+
];
+
default = "virtio";
+
example = "scsi";
+
description = "The interface used for the virtual hard disks.";
+
};
-
virtualisation.useNixStoreImage =
-
mkOption {
+
guestAgent.enable = mkOption {
type = types.bool;
-
default = false;
+
default = true;
description = ''
-
Build and use a disk image for the Nix store, instead of
-
accessing the host's one through 9p.
-
-
For applications which do a lot of reads from the store,
-
this can drastically improve performance, but at the cost of
-
disk space and image build time.
-
-
The Nix store image is built just-in-time right before the VM is
-
started. Because it does not produce another derivation, the image is
-
not cached between invocations and never lands in the store or binary
-
cache.
-
-
If you want a full disk image with a partition table and a root
-
filesystem instead of only a store image, enable
-
{option}`virtualisation.useBootLoader` instead.
+
Enable the Qemu guest agent.
'';
};
-
virtualisation.mountHostNixStore =
-
mkOption {
+
virtioKeyboard = mkOption {
type = types.bool;
-
default = !cfg.useNixStoreImage && !cfg.useBootLoader;
-
defaultText = literalExpression "!cfg.useNixStoreImage && !cfg.useBootLoader";
+
default = true;
description = ''
-
Mount the host Nix store as a 9p mount.
+
Enable the virtio-keyboard device.
'';
};
+
};
-
virtualisation.directBoot = {
-
enable =
-
mkOption {
-
type = types.bool;
-
default = !cfg.useBootLoader;
-
defaultText = "!cfg.useBootLoader";
-
description = ''
-
If enabled, the virtual machine will boot directly into the kernel instead of through a bootloader.
-
Read more about this feature in the [QEMU documentation on Direct Linux Boot](https://qemu-project.gitlab.io/qemu/system/linuxboot.html)
+
virtualisation.useNixStoreImage = mkOption {
+
type = types.bool;
+
default = false;
+
description = ''
+
Build and use a disk image for the Nix store, instead of
+
accessing the host's one through 9p.
-
This is enabled by default.
-
If you want to test netboot, consider disabling this option.
-
Enable a bootloader with {option}`virtualisation.useBootLoader` if you need.
+
For applications which do a lot of reads from the store,
+
this can drastically improve performance, but at the cost of
+
disk space and image build time.
-
Relevant parameters such as those set in `boot.initrd` and `boot.kernelParams` are also passed to QEMU.
-
Additional parameters can be supplied on invocation through the environment variable `$QEMU_KERNEL_PARAMS`.
-
They are added to the `-append` option, see [QEMU User Documentation](https://www.qemu.org/docs/master/system/qemu-manpage) for details
-
For example, to let QEMU use the parent terminal as the serial console, set `QEMU_KERNEL_PARAMS="console=ttyS0"`.
+
The Nix store image is built just-in-time right before the VM is
+
started. Because it does not produce another derivation, the image is
+
not cached between invocations and never lands in the store or binary
+
cache.
-
This will not (re-)boot correctly into a system that has switched to a different configuration on disk.
-
'';
-
};
-
initrd =
-
mkOption {
-
type = types.str;
-
default = "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}";
-
defaultText = "\${config.system.build.initialRamdisk}/\${config.system.boot.loader.initrdFile}";
-
description = ''
-
In direct boot situations, you may want to influence the initrd to load
-
to use your own customized payload.
+
If you want a full disk image with a partition table and a root
+
filesystem instead of only a store image, enable
+
{option}`virtualisation.useBootLoader` instead.
+
'';
+
};
-
This is useful if you want to test the netboot image without
-
testing the firmware or the loading part.
-
'';
-
};
+
virtualisation.mountHostNixStore = mkOption {
+
type = types.bool;
+
default = !cfg.useNixStoreImage && !cfg.useBootLoader;
+
defaultText = literalExpression "!cfg.useNixStoreImage && !cfg.useBootLoader";
+
description = ''
+
Mount the host Nix store as a 9p mount.
+
'';
};
-
virtualisation.useBootLoader =
-
mkOption {
+
virtualisation.directBoot = {
+
enable = mkOption {
type = types.bool;
-
default = false;
+
default = !cfg.useBootLoader;
+
defaultText = "!cfg.useBootLoader";
description = ''
-
Use a boot loader to boot the system.
-
This allows, among other things, testing the boot loader.
+
If enabled, the virtual machine will boot directly into the kernel instead of through a bootloader.
+
Read more about this feature in the [QEMU documentation on Direct Linux Boot](https://qemu-project.gitlab.io/qemu/system/linuxboot.html)
-
If disabled, the kernel and initrd are directly booted,
-
forgoing any bootloader.
+
This is enabled by default.
+
If you want to test netboot, consider disabling this option.
+
Enable a bootloader with {option}`virtualisation.useBootLoader` if you need.
-
Check the documentation on {option}`virtualisation.directBoot.enable` for details.
-
'';
-
};
+
Relevant parameters such as those set in `boot.initrd` and `boot.kernelParams` are also passed to QEMU.
+
Additional parameters can be supplied on invocation through the environment variable `$QEMU_KERNEL_PARAMS`.
+
They are added to the `-append` option, see [QEMU User Documentation](https://www.qemu.org/docs/master/system/qemu-manpage) for details
+
For example, to let QEMU use the parent terminal as the serial console, set `QEMU_KERNEL_PARAMS="console=ttyS0"`.
-
virtualisation.installBootLoader =
-
mkOption {
-
type = types.bool;
-
default = cfg.useBootLoader && cfg.useDefaultFilesystems;
-
defaultText = "cfg.useBootLoader && cfg.useDefaultFilesystems";
+
This will not (re-)boot correctly into a system that has switched to a different configuration on disk.
+
'';
+
};
+
initrd = mkOption {
+
type = types.str;
+
default = "${config.system.build.initialRamdisk}/${config.system.boot.loader.initrdFile}";
+
defaultText = "\${config.system.build.initialRamdisk}/\${config.system.boot.loader.initrdFile}";
description = ''
-
Install boot loader to target image.
+
In direct boot situations, you may want to influence the initrd to load
+
to use your own customized payload.
-
This is best-effort and may break with unconventional partition setups.
-
Use `virtualisation.useDefaultFilesystems` for a known-working configuration.
+
This is useful if you want to test the netboot image without
+
testing the firmware or the loading part.
'';
};
+
};
+
+
virtualisation.useBootLoader = mkOption {
+
type = types.bool;
+
default = false;
+
description = ''
+
Use a boot loader to boot the system.
+
This allows, among other things, testing the boot loader.
+
+
If disabled, the kernel and initrd are directly booted,
+
forgoing any bootloader.
-
virtualisation.useEFIBoot =
-
mkOption {
-
type = types.bool;
-
default = false;
-
description = ''
-
If enabled, the virtual machine will provide a EFI boot
-
manager.
-
useEFIBoot is ignored if useBootLoader == false.
-
'';
-
};
+
Check the documentation on {option}`virtualisation.directBoot.enable` for details.
+
'';
+
};
+
+
virtualisation.installBootLoader = mkOption {
+
type = types.bool;
+
default = cfg.useBootLoader && cfg.useDefaultFilesystems;
+
defaultText = "cfg.useBootLoader && cfg.useDefaultFilesystems";
+
description = ''
+
Install boot loader to target image.
+
+
This is best-effort and may break with unconventional partition setups.
+
Use `virtualisation.useDefaultFilesystems` for a known-working configuration.
+
'';
+
};
+
+
virtualisation.useEFIBoot = mkOption {
+
type = types.bool;
+
default = false;
+
description = ''
+
If enabled, the virtual machine will provide a EFI boot
+
manager.
+
useEFIBoot is ignored if useBootLoader == false.
+
'';
+
};
virtualisation.efi = {
OVMF = mkOption {
type = types.package;
-
default = (pkgs.OVMF.override {
-
secureBoot = cfg.useSecureBoot;
-
}).fd;
-
defaultText = ''(pkgs.OVMF.override {
-
secureBoot = cfg.useSecureBoot;
-
}).fd'';
+
default =
+
(pkgs.OVMF.override {
+
secureBoot = cfg.useSecureBoot;
+
}).fd;
+
defaultText = ''
+
(pkgs.OVMF.override {
+
secureBoot = cfg.useSecureBoot;
+
}).fd'';
description = "OVMF firmware package, defaults to OVMF configured with secure boot if needed.";
};
···
default = cfg.efi.OVMF.firmware;
defaultText = literalExpression "cfg.efi.OVMF.firmware";
description = ''
-
Firmware binary for EFI implementation, defaults to OVMF.
-
'';
+
Firmware binary for EFI implementation, defaults to OVMF.
+
'';
};
variables = mkOption {
···
default = cfg.efi.OVMF.variables;
defaultText = literalExpression "cfg.efi.OVMF.variables";
description = ''
-
Platform-specific flash binary for EFI variables, implementation-dependent to the EFI firmware.
-
Defaults to OVMF.
-
'';
+
Platform-specific flash binary for EFI variables, implementation-dependent to the EFI firmware.
+
Defaults to OVMF.
+
'';
};
keepVariables = mkOption {
···
deviceModel = mkOption {
type = types.str;
-
default = ({
-
"i686-linux" = "tpm-tis";
-
"x86_64-linux" = "tpm-tis";
-
"ppc64-linux" = "tpm-spapr";
-
"armv7-linux" = "tpm-tis-device";
-
"aarch64-linux" = "tpm-tis-device";
-
}.${pkgs.stdenv.hostPlatform.system} or (throw "Unsupported system for TPM2 emulation in QEMU"));
+
default = (
+
{
+
"i686-linux" = "tpm-tis";
+
"x86_64-linux" = "tpm-tis";
+
"ppc64-linux" = "tpm-spapr";
+
"armv7-linux" = "tpm-tis-device";
+
"aarch64-linux" = "tpm-tis-device";
+
}
+
.${pkgs.stdenv.hostPlatform.system} or (throw "Unsupported system for TPM2 emulation in QEMU")
+
);
defaultText = ''
Based on the guest platform Linux system:
···
};
};
-
virtualisation.useDefaultFilesystems =
-
mkOption {
-
type = types.bool;
-
default = true;
-
description = ''
-
If enabled, the boot disk of the virtual machine will be
-
formatted and mounted with the default filesystems for
-
testing. Swap devices and LUKS will be disabled.
+
virtualisation.useDefaultFilesystems = mkOption {
+
type = types.bool;
+
default = true;
+
description = ''
+
If enabled, the boot disk of the virtual machine will be
+
formatted and mounted with the default filesystems for
+
testing. Swap devices and LUKS will be disabled.
-
If disabled, a root filesystem has to be specified and
-
formatted (for example in the initial ramdisk).
-
'';
-
};
+
If disabled, a root filesystem has to be specified and
+
formatted (for example in the initial ramdisk).
+
'';
+
};
-
virtualisation.useSecureBoot =
-
mkOption {
-
type = types.bool;
-
default = false;
-
description = ''
-
Enable Secure Boot support in the EFI firmware.
-
'';
-
};
+
virtualisation.useSecureBoot = mkOption {
+
type = types.bool;
+
default = false;
+
description = ''
+
Enable Secure Boot support in the EFI firmware.
+
'';
+
};
-
virtualisation.bios =
-
mkOption {
-
type = types.nullOr types.package;
-
default = null;
-
description = ''
-
An alternate BIOS (such as `qboot`) with which to start the VM.
-
Should contain a file named `bios.bin`.
-
If `null`, QEMU's builtin SeaBIOS will be used.
-
'';
-
};
+
virtualisation.bios = mkOption {
+
type = types.nullOr types.package;
+
default = null;
+
description = ''
+
An alternate BIOS (such as `qboot`) with which to start the VM.
+
Should contain a file named `bios.bin`.
+
If `null`, QEMU's builtin SeaBIOS will be used.
+
'';
+
};
-
virtualisation.useHostCerts =
-
mkOption {
-
type = types.bool;
-
default = false;
-
description = ''
-
If enabled, when `NIX_SSL_CERT_FILE` is set on the host,
-
pass the CA certificates from the host to the VM.
-
'';
-
};
+
virtualisation.useHostCerts = mkOption {
+
type = types.bool;
+
default = false;
+
description = ''
+
If enabled, when `NIX_SSL_CERT_FILE` is set on the host,
+
pass the CA certificates from the host to the VM.
+
'';
+
};
};
config = {
assertions =
-
lib.concatLists (lib.flip lib.imap cfg.forwardPorts (i: rule:
-
[
-
{ assertion = rule.from == "guest" -> rule.proto == "tcp";
-
message =
-
''
+
lib.concatLists (
+
lib.flip lib.imap cfg.forwardPorts (
+
i: rule: [
+
{
+
assertion = rule.from == "guest" -> rule.proto == "tcp";
+
message = ''
Invalid virtualisation.forwardPorts.<entry ${toString i}>.proto:
Guest forwarding supports only TCP connections.
'';
-
}
-
{ assertion = rule.from == "guest" -> lib.hasPrefix "10.0.2." rule.guest.address;
-
message =
-
''
+
}
+
{
+
assertion = rule.from == "guest" -> lib.hasPrefix "10.0.2." rule.guest.address;
+
message = ''
Invalid virtualisation.forwardPorts.<entry ${toString i}>.guest.address:
The address must be in the default VLAN (10.0.2.0/24).
'';
-
}
-
])) ++ [
-
{ assertion = pkgs.stdenv.hostPlatform.is32bit -> cfg.memorySize < 2047;
-
message = ''
-
virtualisation.memorySize is above 2047, but qemu is only able to allocate 2047MB RAM on 32bit max.
-
'';
-
}
-
{ assertion = cfg.directBoot.enable || cfg.directBoot.initrd == options.virtualisation.directBoot.initrd.default;
-
message =
-
''
-
You changed the default of `virtualisation.directBoot.initrd` but you are not
-
using QEMU direct boot. This initrd will not be used in your current
-
boot configuration.
+
}
+
]
+
)
+
)
+
++ [
+
{
+
assertion = pkgs.stdenv.hostPlatform.is32bit -> cfg.memorySize < 2047;
+
message = ''
+
virtualisation.memorySize is above 2047, but qemu is only able to allocate 2047MB RAM on 32bit max.
+
'';
+
}
+
{
+
assertion =
+
cfg.directBoot.enable || cfg.directBoot.initrd == options.virtualisation.directBoot.initrd.default;
+
message = ''
+
You changed the default of `virtualisation.directBoot.initrd` but you are not
+
using QEMU direct boot. This initrd will not be used in your current
+
boot configuration.
-
Either do not mutate `virtualisation.directBoot.initrd` or enable direct boot.
+
Either do not mutate `virtualisation.directBoot.initrd` or enable direct boot.
-
If you have a more advanced usecase, please open an issue or a pull request.
-
'';
-
}
-
{
-
assertion = cfg.installBootLoader -> config.system.switch.enable;
-
message = ''
-
`system.switch.enable` must be enabled for `virtualisation.installBootLoader` to work.
-
Please enable it in your configuration.
-
'';
-
}
-
];
+
If you have a more advanced usecase, please open an issue or a pull request.
+
'';
+
}
+
{
+
assertion = cfg.installBootLoader -> config.system.switch.enable;
+
message = ''
+
`system.switch.enable` must be enabled for `virtualisation.installBootLoader` to work.
+
Please enable it in your configuration.
+
'';
+
}
+
];
-
warnings =
-
optional (cfg.directBoot.enable && cfg.useBootLoader)
-
''
-
You enabled direct boot and a bootloader, QEMU will not boot your bootloader, rendering
-
`useBootLoader` useless. You might want to disable one of those options.
-
'';
+
warnings = optional (cfg.directBoot.enable && cfg.useBootLoader) ''
+
You enabled direct boot and a bootloader, QEMU will not boot your bootloader, rendering
+
`useBootLoader` useless. You might want to disable one of those options.
+
'';
# 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.
···
# allow `system.build.toplevel' to be included. (If we had a direct
# reference to ${regInfo} here, then we would get a cyclic
# dependency.)
-
boot.postBootCommands = lib.mkIf config.nix.enable
-
''
-
if [[ "$(cat /proc/cmdline)" =~ regInfo=([^ ]*) ]]; then
-
${config.nix.package.out}/bin/nix-store --load-db < ''${BASH_REMATCH[1]}
-
fi
-
'';
+
boot.postBootCommands = lib.mkIf config.nix.enable ''
+
if [[ "$(cat /proc/cmdline)" =~ regInfo=([^ ]*) ]]; then
+
${config.nix.package.out}/bin/nix-store --load-db < ''${BASH_REMATCH[1]}
+
fi
+
'';
boot.initrd.availableKernelModules =
optional (cfg.qemu.diskInterface == "scsi") "sym53c8xx"
···
virtualisation.qemu.networkingOptions =
let
-
forwardingOptions = flip concatMapStrings cfg.forwardPorts
-
({ proto, from, host, guest }:
-
if from == "host"
-
then "hostfwd=${proto}:${host.address}:${toString host.port}-" +
-
"${guest.address}:${toString guest.port},"
-
else "'guestfwd=${proto}:${guest.address}:${toString guest.port}-" +
-
"cmd:${pkgs.netcat}/bin/nc ${host.address} ${toString host.port}',"
-
);
+
forwardingOptions = flip concatMapStrings cfg.forwardPorts (
+
{
+
proto,
+
from,
+
host,
+
guest,
+
}:
+
if from == "host" then
+
"hostfwd=${proto}:${host.address}:${toString host.port}-"
+
+ "${guest.address}:${toString guest.port},"
+
else
+
"'guestfwd=${proto}:${guest.address}:${toString guest.port}-"
+
+ "cmd:${pkgs.netcat}/bin/nc ${host.address} ${toString host.port}',"
+
);
restrictNetworkOption = lib.optionalString cfg.restrictNetwork "restrict=on,";
in
···
"-device virtio-keyboard"
])
(mkIf pkgs.stdenv.hostPlatform.isx86 [
-
"-usb" "-device usb-tablet,bus=usb-bus.0"
+
"-usb"
+
"-device usb-tablet,bus=usb-bus.0"
])
(mkIf pkgs.stdenv.hostPlatform.isAarch [
-
"-device virtio-gpu-pci" "-device usb-ehci,id=usb0" "-device usb-kbd" "-device usb-tablet"
+
"-device virtio-gpu-pci"
+
"-device usb-ehci,id=usb0"
+
"-device usb-kbd"
+
"-device usb-tablet"
])
-
(let
-
alphaNumericChars = lowerChars ++ upperChars ++ (map toString (range 0 9));
-
# Replace all non-alphanumeric characters with underscores
-
sanitizeShellIdent = s: concatMapStrings (c: if builtins.elem c alphaNumericChars then c else "_") (stringToCharacters s);
-
in mkIf cfg.directBoot.enable [
-
"-kernel \${NIXPKGS_QEMU_KERNEL_${sanitizeShellIdent config.system.name}:-${config.system.build.toplevel}/kernel}"
-
"-initrd ${cfg.directBoot.initrd}"
-
''-append "$(cat ${config.system.build.toplevel}/kernel-params) init=${config.system.build.toplevel}/init regInfo=${regInfo}/registration ${consoles} $QEMU_KERNEL_PARAMS"''
-
])
+
(
+
let
+
alphaNumericChars = lowerChars ++ upperChars ++ (map toString (range 0 9));
+
# Replace all non-alphanumeric characters with underscores
+
sanitizeShellIdent =
+
s:
+
concatMapStrings (c: if builtins.elem c alphaNumericChars then c else "_") (stringToCharacters s);
+
in
+
mkIf cfg.directBoot.enable [
+
"-kernel \${NIXPKGS_QEMU_KERNEL_${sanitizeShellIdent config.system.name}:-${config.system.build.toplevel}/kernel}"
+
"-initrd ${cfg.directBoot.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 [
"-drive if=pflash,format=raw,unit=0,readonly=on,file=${cfg.efi.firmware}"
"-drive if=pflash,format=raw,unit=1,readonly=off,file=$NIX_EFI_VARS"
···
"-device ${cfg.tpm.deviceModel},tpmdev=tpm_dev_0"
])
(mkIf (pkgs.stdenv.hostPlatform.isx86 && cfg.efi.OVMF.systemManagementModeRequired) [
-
"-machine" "q35,smm=on"
-
"-global" "driver=cfi.pflash01,property=secure,value=on"
+
"-machine"
+
"q35,smm=on"
+
"-global"
+
"driver=cfi.pflash01,property=secure,value=on"
])
];
virtualisation.qemu.drives = mkMerge [
-
(mkIf (cfg.diskImage != null) [{
-
name = "root";
-
file = ''"$NIX_DISK_IMAGE"'';
-
driveExtraOpts.cache = "writeback";
-
driveExtraOpts.werror = "report";
-
deviceExtraOpts.bootindex = "1";
-
deviceExtraOpts.serial = rootDriveSerialAttr;
-
}])
-
(mkIf cfg.useNixStoreImage [{
-
name = "nix-store";
-
file = ''"$TMPDIR"/store.img'';
-
deviceExtraOpts.bootindex = "2";
-
driveExtraOpts.format = "raw";
-
}])
+
(mkIf (cfg.diskImage != null) [
+
{
+
name = "root";
+
file = ''"$NIX_DISK_IMAGE"'';
+
driveExtraOpts.cache = "writeback";
+
driveExtraOpts.werror = "report";
+
deviceExtraOpts.bootindex = "1";
+
deviceExtraOpts.serial = rootDriveSerialAttr;
+
}
+
])
+
(mkIf cfg.useNixStoreImage [
+
{
+
name = "nix-store";
+
file = ''"$TMPDIR"/store.img'';
+
deviceExtraOpts.bootindex = "2";
+
driveExtraOpts.format = "raw";
+
}
+
])
(imap0 (idx: _: {
file = "$(pwd)/empty${toString idx}.qcow2";
driveExtraOpts.werror = "report";
···
# override by setting `virtualisation.fileSystems = lib.mkForce { };`.
fileSystems = lib.mkIf (cfg.fileSystems != { }) (mkVMOverride cfg.fileSystems);
-
virtualisation.fileSystems = let
-
mkSharedDir = tag: share:
-
{
+
virtualisation.diskSizeAutoSupported = false;
+
+
virtualisation.fileSystems =
+
let
+
mkSharedDir = tag: share: {
name = share.target;
value.device = tag;
value.fsType = "9p";
value.neededForBoot = true;
-
value.options =
-
[ "trans=virtio" "version=9p2000.L" "msize=${toString cfg.msize}" "x-systemd.requires=modprobe@9pnet_virtio.service" ]
-
++ lib.optional (tag == "nix-store") "cache=loose";
-
};
-
in lib.mkMerge [
-
(lib.mapAttrs' mkSharedDir cfg.sharedDirectories)
-
{
-
"/" = lib.mkIf cfg.useDefaultFilesystems (if cfg.diskImage == null then {
-
device = "tmpfs";
-
fsType = "tmpfs";
-
} else {
-
device = cfg.rootDevice;
-
fsType = "ext4";
-
});
-
"/tmp" = lib.mkIf config.boot.tmp.useTmpfs {
-
device = "tmpfs";
-
fsType = "tmpfs";
-
neededForBoot = true;
-
# Sync with systemd's tmp.mount;
-
options = [ "mode=1777" "strictatime" "nosuid" "nodev" "size=${toString config.boot.tmp.tmpfsSize}" ];
+
value.options = [
+
"trans=virtio"
+
"version=9p2000.L"
+
"msize=${toString cfg.msize}"
+
"x-systemd.requires=modprobe@9pnet_virtio.service"
+
] ++ lib.optional (tag == "nix-store") "cache=loose";
};
-
"/nix/store" = lib.mkIf (cfg.useNixStoreImage || cfg.mountHostNixStore) (if cfg.writableStore then {
-
overlay = {
-
lowerdir = [ "/nix/.ro-store" ];
-
upperdir = "/nix/.rw-store/upper";
-
workdir = "/nix/.rw-store/work";
+
in
+
lib.mkMerge [
+
(lib.mapAttrs' mkSharedDir cfg.sharedDirectories)
+
{
+
"/" = lib.mkIf cfg.useDefaultFilesystems (
+
if cfg.diskImage == null then
+
{
+
device = "tmpfs";
+
fsType = "tmpfs";
+
}
+
else
+
{
+
device = cfg.rootDevice;
+
fsType = "ext4";
+
}
+
);
+
"/tmp" = lib.mkIf config.boot.tmp.useTmpfs {
+
device = "tmpfs";
+
fsType = "tmpfs";
+
neededForBoot = true;
+
# Sync with systemd's tmp.mount;
+
options = [
+
"mode=1777"
+
"strictatime"
+
"nosuid"
+
"nodev"
+
"size=${toString config.boot.tmp.tmpfsSize}"
+
];
};
-
} else {
-
device = "/nix/.ro-store";
-
options = [ "bind" ];
-
});
-
"/nix/.ro-store" = lib.mkIf cfg.useNixStoreImage {
-
device = "/dev/disk/by-label/${nixStoreFilesystemLabel}";
-
fsType = "erofs";
-
neededForBoot = true;
-
options = [ "ro" ];
-
};
-
"/nix/.rw-store" = lib.mkIf (cfg.writableStore && cfg.writableStoreUseTmpfs) {
-
fsType = "tmpfs";
-
options = [ "mode=0755" ];
-
neededForBoot = true;
-
};
-
"${config.boot.loader.efi.efiSysMountPoint}" = lib.mkIf (cfg.useBootLoader && cfg.bootPartition != null) {
-
device = cfg.bootPartition;
-
fsType = "vfat";
-
};
-
}
-
];
+
"/nix/store" = lib.mkIf (cfg.useNixStoreImage || cfg.mountHostNixStore) (
+
if cfg.writableStore then
+
{
+
overlay = {
+
lowerdir = [ "/nix/.ro-store" ];
+
upperdir = "/nix/.rw-store/upper";
+
workdir = "/nix/.rw-store/work";
+
};
+
}
+
else
+
{
+
device = "/nix/.ro-store";
+
options = [ "bind" ];
+
}
+
);
+
"/nix/.ro-store" = lib.mkIf cfg.useNixStoreImage {
+
device = "/dev/disk/by-label/${nixStoreFilesystemLabel}";
+
fsType = "erofs";
+
neededForBoot = true;
+
options = [ "ro" ];
+
};
+
"/nix/.rw-store" = lib.mkIf (cfg.writableStore && cfg.writableStoreUseTmpfs) {
+
fsType = "tmpfs";
+
options = [ "mode=0755" ];
+
neededForBoot = true;
+
};
+
"${config.boot.loader.efi.efiSysMountPoint}" =
+
lib.mkIf (cfg.useBootLoader && cfg.bootPartition != null)
+
{
+
device = cfg.bootPartition;
+
fsType = "vfat";
+
};
+
}
+
];
swapDevices = (if cfg.useDefaultFilesystems then mkVMOverride else mkDefault) [ ];
-
boot.initrd.luks.devices = (if cfg.useDefaultFilesystems then mkVMOverride else mkDefault) {};
+
boot.initrd.luks.devices = (if cfg.useDefaultFilesystems then mkVMOverride else mkDefault) { };
# Don't run ntpd in the guest. It should get the correct time from KVM.
services.timesyncd.enable = false;
services.qemuGuest.enable = cfg.qemu.guestAgent.enable;
-
system.build.vm = hostPkgs.runCommand "nixos-vm" {
-
preferLocalBuild = true;
-
meta.mainProgram = "run-${config.system.name}-vm";
-
}
-
''
-
mkdir -p $out/bin
-
ln -s ${config.system.build.toplevel} $out/system
-
ln -s ${hostPkgs.writeScript "run-nixos-vm" startVM} $out/bin/run-${config.system.name}-vm
-
'';
+
system.build.vm =
+
hostPkgs.runCommand "nixos-vm"
+
{
+
preferLocalBuild = true;
+
meta.mainProgram = "run-${config.system.name}-vm";
+
}
+
''
+
mkdir -p $out/bin
+
ln -s ${config.system.build.toplevel} $out/system
+
ln -s ${hostPkgs.writeScript "run-nixos-vm" startVM} $out/bin/run-${config.system.name}-vm
+
'';
# When building a regular system configuration, override whatever
# video driver the host uses.
services.xserver.videoDrivers = mkVMOverride [ "modesetting" ];
services.xserver.defaultDepth = mkVMOverride 0;
services.xserver.resolutions = mkVMOverride [ cfg.resolution ];
-
services.xserver.monitorSection =
-
''
-
# Set a higher refresh rate so that resolutions > 800x600 work.
-
HorizSync 30-140
-
VertRefresh 50-160
-
'';
+
services.xserver.monitorSection = ''
+
# Set a higher refresh rate so that resolutions > 800x600 work.
+
HorizSync 30-140
+
VertRefresh 50-160
+
'';
# Wireless won't work in the VM.
networking.wireless.enable = mkVMOverride false;
···
networking.usePredictableInterfaceNames = false;
-
system.requiredKernelConfig = with config.lib.kernelConfig;
-
[ (isEnabled "VIRTIO_BLK")
+
system.requiredKernelConfig =
+
with config.lib.kernelConfig;
+
[
+
(isEnabled "VIRTIO_BLK")
(isEnabled "VIRTIO_PCI")
(isEnabled "VIRTIO_NET")
(isEnabled "EXT4_FS")
···
(isYes "NET_CORE")
(isYes "INET")
(isYes "NETWORK_FILESYSTEMS")
-
] ++ optionals (!cfg.graphics) [
+
]
+
++ optionals (!cfg.graphics) [
(isYes "SERIAL_8250_CONSOLE")
(isYes "SERIAL_8250")
-
] ++ optionals (cfg.writableStore) [
+
]
+
++ optionals (cfg.writableStore) [
(isEnabled "OVERLAY_FS")
];
+135 -92
nixos/modules/virtualisation/virtualbox-image.nix
···
-
{ config, lib, pkgs, ... }:
+
{
+
config,
+
lib,
+
pkgs,
+
...
+
}:
let
cfg = config.virtualbox;
-
-
in {
+
in
+
{
+
imports = [
+
./disk-size-option.nix
+
(lib.mkRenamedOptionModuleWith {
+
sinceRelease = 2411;
+
from = [
+
"virtualbox"
+
"baseImageSize"
+
];
+
to = [
+
"virtualisation"
+
"diskSize"
+
];
+
})
+
];
options = {
virtualbox = {
-
baseImageSize = lib.mkOption {
-
type = with lib.types; either (enum [ "auto" ]) int;
-
default = "auto";
-
example = 50 * 1024;
-
description = ''
-
The size of the VirtualBox base image in MiB.
-
'';
-
};
baseImageFreeSpace = lib.mkOption {
-
type = with lib.types; int;
+
type = lib.types.int;
default = 30 * 1024;
description = ''
Free space in the VirtualBox base image in MiB.
···
'';
};
params = lib.mkOption {
-
type = with lib.types; attrsOf (oneOf [ str int bool (listOf str) ]);
+
type =
+
with lib.types;
+
attrsOf (oneOf [
+
str
+
int
+
bool
+
(listOf str)
+
]);
example = {
audio = "alsa";
rtcuseutc = "on";
···
'';
};
exportParams = lib.mkOption {
-
type = with lib.types; listOf (oneOf [ str int bool (listOf str) ]);
+
type =
+
with lib.types;
+
listOf (oneOf [
+
str
+
int
+
bool
+
(listOf str)
+
]);
example = [
-
"--vsys" "0" "--vendor" "ACME Inc."
+
"--vsys"
+
"0"
+
"--vendor"
+
"ACME Inc."
];
-
default = [];
+
default = [ ];
description = ''
Parameters passed to the Virtualbox export command.
···
mountPoint = "/home/demo/storage";
size = 100 * 1024;
};
-
type = lib.types.nullOr (lib.types.submodule {
-
options = {
-
size = lib.mkOption {
-
type = lib.types.int;
-
description = "Size in MiB";
+
type = lib.types.nullOr (
+
lib.types.submodule {
+
options = {
+
size = lib.mkOption {
+
type = lib.types.int;
+
description = "Size in MiB";
+
};
+
label = lib.mkOption {
+
type = lib.types.str;
+
default = "vm-extra-storage";
+
description = "Label for the disk partition";
+
};
+
mountPoint = lib.mkOption {
+
type = lib.types.str;
+
description = "Path where to mount this disk.";
+
};
};
-
label = lib.mkOption {
-
type = lib.types.str;
-
default = "vm-extra-storage";
-
description = "Label for the disk partition";
-
};
-
mountPoint = lib.mkOption {
-
type = lib.types.str;
-
description = "Path where to mount this disk.";
-
};
-
};
-
});
+
}
+
);
};
postExportCommands = lib.mkOption {
type = lib.types.lines;
···
'';
};
storageController = lib.mkOption {
-
type = with lib.types; attrsOf (oneOf [ str int bool (listOf str) ]);
+
type =
+
with lib.types;
+
attrsOf (oneOf [
+
str
+
int
+
bool
+
(listOf str)
+
]);
example = {
name = "SCSI";
add = "scsi";
···
};
config = {
+
# Use a priority just below mkOptionDefault (1500) instead of lib.mkDefault
+
# to avoid breaking existing configs using that.
+
virtualisation.diskSize = lib.mkOverride 1490 (50 * 1024);
virtualbox.params = lib.mkMerge [
(lib.mapAttrs (name: lib.mkDefault) {
···
inherit pkgs lib config;
partitionTableType = "legacy";
-
diskSize = cfg.baseImageSize;
+
inherit (config.virtualisation) diskSize;
additionalSpace = "${toString cfg.baseImageFreeSpace}M";
-
postVM =
-
''
-
export HOME=$PWD
-
export PATH=${pkgs.virtualbox}/bin:$PATH
+
postVM = ''
+
export HOME=$PWD
+
export PATH=${pkgs.virtualbox}/bin:$PATH
-
echo "converting image to VirtualBox format..."
-
VBoxManage convertfromraw $diskImage disk.vdi
+
echo "converting image to VirtualBox format..."
+
VBoxManage convertfromraw $diskImage disk.vdi
-
${lib.optionalString (cfg.extraDisk != null) ''
-
echo "creating extra disk: data-disk.raw"
-
dataDiskImage=data-disk.raw
-
truncate -s ${toString cfg.extraDisk.size}M $dataDiskImage
+
${lib.optionalString (cfg.extraDisk != null) ''
+
echo "creating extra disk: data-disk.raw"
+
dataDiskImage=data-disk.raw
+
truncate -s ${toString cfg.extraDisk.size}M $dataDiskImage
-
parted --script $dataDiskImage -- \
-
mklabel msdos \
-
mkpart primary ext4 1MiB -1
-
eval $(partx $dataDiskImage -o START,SECTORS --nr 1 --pairs)
-
mkfs.ext4 -F -L ${cfg.extraDisk.label} $dataDiskImage -E offset=$(sectorsToBytes $START) $(sectorsToKilobytes $SECTORS)K
-
echo "creating extra disk: data-disk.vdi"
-
VBoxManage convertfromraw $dataDiskImage data-disk.vdi
-
''}
+
parted --script $dataDiskImage -- \
+
mklabel msdos \
+
mkpart primary ext4 1MiB -1
+
eval $(partx $dataDiskImage -o START,SECTORS --nr 1 --pairs)
+
mkfs.ext4 -F -L ${cfg.extraDisk.label} $dataDiskImage -E offset=$(sectorsToBytes $START) $(sectorsToKilobytes $SECTORS)K
+
echo "creating extra disk: data-disk.vdi"
+
VBoxManage convertfromraw $dataDiskImage data-disk.vdi
+
''}
-
echo "creating VirtualBox VM..."
-
vmName="${cfg.vmName}";
-
VBoxManage createvm --name "$vmName" --register \
-
--ostype ${if pkgs.stdenv.hostPlatform.system == "x86_64-linux" then "Linux26_64" else "Linux26"}
-
VBoxManage modifyvm "$vmName" \
-
--memory ${toString cfg.memorySize} \
-
${lib.cli.toGNUCommandLineShell { } cfg.params}
-
VBoxManage storagectl "$vmName" ${lib.cli.toGNUCommandLineShell { } cfg.storageController}
-
VBoxManage storageattach "$vmName" --storagectl ${cfg.storageController.name} --port 0 --device 0 --type hdd \
-
--medium disk.vdi
-
${lib.optionalString (cfg.extraDisk != null) ''
-
VBoxManage storageattach "$vmName" --storagectl ${cfg.storageController.name} --port 1 --device 0 --type hdd \
-
--medium data-disk.vdi
-
''}
+
echo "creating VirtualBox VM..."
+
vmName="${cfg.vmName}";
+
VBoxManage createvm --name "$vmName" --register \
+
--ostype ${if pkgs.stdenv.hostPlatform.system == "x86_64-linux" then "Linux26_64" else "Linux26"}
+
VBoxManage modifyvm "$vmName" \
+
--memory ${toString cfg.memorySize} \
+
${lib.cli.toGNUCommandLineShell { } cfg.params}
+
VBoxManage storagectl "$vmName" ${lib.cli.toGNUCommandLineShell { } cfg.storageController}
+
VBoxManage storageattach "$vmName" --storagectl ${cfg.storageController.name} --port 0 --device 0 --type hdd \
+
--medium disk.vdi
+
${lib.optionalString (cfg.extraDisk != null) ''
+
VBoxManage storageattach "$vmName" --storagectl ${cfg.storageController.name} --port 1 --device 0 --type hdd \
+
--medium data-disk.vdi
+
''}
-
echo "exporting VirtualBox VM..."
-
mkdir -p $out
-
fn="$out/${cfg.vmFileName}"
-
VBoxManage export "$vmName" --output "$fn" --options manifest ${lib.escapeShellArgs cfg.exportParams}
-
${cfg.postExportCommands}
+
echo "exporting VirtualBox VM..."
+
mkdir -p $out
+
fn="$out/${cfg.vmFileName}"
+
VBoxManage export "$vmName" --output "$fn" --options manifest ${lib.escapeShellArgs cfg.exportParams}
+
${cfg.postExportCommands}
-
rm -v $diskImage
+
rm -v $diskImage
-
mkdir -p $out/nix-support
-
echo "file ova $fn" >> $out/nix-support/hydra-build-products
-
'';
+
mkdir -p $out/nix-support
+
echo "file ova $fn" >> $out/nix-support/hydra-build-products
+
'';
};
-
fileSystems = {
-
"/" = {
-
device = "/dev/disk/by-label/nixos";
-
autoResize = true;
-
fsType = "ext4";
-
};
-
} // (lib.optionalAttrs (cfg.extraDisk != null) {
-
${cfg.extraDisk.mountPoint} = {
-
device = "/dev/disk/by-label/" + cfg.extraDisk.label;
-
autoResize = true;
-
fsType = "ext4";
-
};
-
});
+
fileSystems =
+
{
+
"/" = {
+
device = "/dev/disk/by-label/nixos";
+
autoResize = true;
+
fsType = "ext4";
+
};
+
}
+
// (lib.optionalAttrs (cfg.extraDisk != null) {
+
${cfg.extraDisk.mountPoint} = {
+
device = "/dev/disk/by-label/" + cfg.extraDisk.label;
+
autoResize = true;
+
fsType = "ext4";
+
};
+
});
boot.growPartition = true;
boot.loader.grub.device = "/dev/sda";
-
swapDevices = [{
-
device = "/var/swap";
-
size = 2048;
-
}];
+
swapDevices = [
+
{
+
device = "/var/swap";
+
size = 2048;
+
}
+
];
virtualisation.virtualbox.guest.enable = true;
+1 -1
nixos/release.nix
···
[ configuration
versionModule
./maintainers/scripts/ec2/amazon-image.nix
-
({ ... }: { amazonImage.sizeMB = "auto"; })
+
({ ... }: { amazonImage.virtualisation.diskSize = "auto"; })
];
}).config.system.build.amazonImage)