at 23.11-beta 11 kB view raw
1{ config, pkgs, lib, ... }: 2 3with lib; 4 5{ 6 options.proxmox = { 7 qemuConf = { 8 # essential configs 9 boot = mkOption { 10 type = types.str; 11 default = ""; 12 example = "order=scsi0;net0"; 13 description = lib.mdDoc '' 14 Default boot device. PVE will try all devices in its default order if this value is empty. 15 ''; 16 }; 17 scsihw = mkOption { 18 type = types.str; 19 default = "virtio-scsi-pci"; 20 example = "lsi"; 21 description = lib.mdDoc '' 22 SCSI controller type. Must be one of the supported values given in 23 <https://pve.proxmox.com/wiki/Qemu/KVM_Virtual_Machines> 24 ''; 25 }; 26 virtio0 = mkOption { 27 type = types.str; 28 default = "local-lvm:vm-9999-disk-0"; 29 example = "ceph:vm-123-disk-0"; 30 description = lib.mdDoc '' 31 Configuration for the default virtio disk. It can be used as a cue for PVE to autodetect the target storage. 32 This parameter is required by PVE even if it isn't used. 33 ''; 34 }; 35 ostype = mkOption { 36 type = types.str; 37 default = "l26"; 38 description = lib.mdDoc '' 39 Guest OS type 40 ''; 41 }; 42 cores = mkOption { 43 type = types.ints.positive; 44 default = 1; 45 description = lib.mdDoc '' 46 Guest core count 47 ''; 48 }; 49 memory = mkOption { 50 type = types.ints.positive; 51 default = 1024; 52 description = lib.mdDoc '' 53 Guest memory in MB 54 ''; 55 }; 56 bios = mkOption { 57 type = types.enum [ "seabios" "ovmf" ]; 58 default = "seabios"; 59 description = '' 60 Select BIOS implementation (seabios = Legacy BIOS, ovmf = UEFI). 61 ''; 62 }; 63 64 # optional configs 65 name = mkOption { 66 type = types.str; 67 default = "nixos-${config.system.nixos.label}"; 68 description = lib.mdDoc '' 69 VM name 70 ''; 71 }; 72 additionalSpace = mkOption { 73 type = types.str; 74 default = "512M"; 75 example = "2048M"; 76 description = lib.mdDoc '' 77 additional disk space to be added to the image if diskSize "auto" 78 is used. 79 ''; 80 }; 81 bootSize = mkOption { 82 type = types.str; 83 default = "256M"; 84 example = "512M"; 85 description = lib.mdDoc '' 86 Size of the boot partition. Is only used if partitionTableType is 87 either "efi" or "hybrid". 88 ''; 89 }; 90 diskSize = mkOption { 91 type = types.str; 92 default = "auto"; 93 example = "20480"; 94 description = lib.mdDoc '' 95 The size of the disk, in megabytes. 96 if "auto" size is calculated based on the contents copied to it and 97 additionalSpace is taken into account. 98 ''; 99 }; 100 net0 = mkOption { 101 type = types.commas; 102 default = "virtio=00:00:00:00:00:00,bridge=vmbr0,firewall=1"; 103 description = lib.mdDoc '' 104 Configuration for the default interface. When restoring from VMA, check the 105 "unique" box to ensure device mac is randomized. 106 ''; 107 }; 108 serial0 = mkOption { 109 type = types.str; 110 default = "socket"; 111 example = "/dev/ttyS0"; 112 description = lib.mdDoc '' 113 Create a serial device inside the VM (n is 0 to 3), and pass through a host serial device (i.e. /dev/ttyS0), 114 or create a unix socket on the host side (use qm terminal to open a terminal connection). 115 ''; 116 }; 117 agent = mkOption { 118 type = types.bool; 119 apply = x: if x then "1" else "0"; 120 default = true; 121 description = lib.mdDoc '' 122 Expect guest to have qemu agent running 123 ''; 124 }; 125 }; 126 qemuExtraConf = mkOption { 127 type = with types; attrsOf (oneOf [ str int ]); 128 default = {}; 129 example = literalExpression '' 130 { 131 cpu = "host"; 132 onboot = 1; 133 } 134 ''; 135 description = lib.mdDoc '' 136 Additional options appended to qemu-server.conf 137 ''; 138 }; 139 partitionTableType = mkOption { 140 type = types.enum [ "efi" "hybrid" "legacy" "legacy+gpt" ]; 141 description = '' 142 Partition table type to use. See make-disk-image.nix partitionTableType for details. 143 Defaults to 'legacy' for 'proxmox.qemuConf.bios="seabios"' (default), other bios values defaults to 'efi'. 144 Use 'hybrid' to build grub-based hybrid bios+efi images. 145 ''; 146 default = if config.proxmox.qemuConf.bios == "seabios" then "legacy" else "efi"; 147 defaultText = lib.literalExpression ''if config.proxmox.qemuConf.bios == "seabios" then "legacy" else "efi"''; 148 example = "hybrid"; 149 }; 150 filenameSuffix = mkOption { 151 type = types.str; 152 default = config.proxmox.qemuConf.name; 153 example = "999-nixos_template"; 154 description = lib.mdDoc '' 155 Filename of the image will be vzdump-qemu-''${filenameSuffix}.vma.zstd. 156 This will also determine the default name of the VM on restoring the VMA. 157 Start this value with a number if you want the VMA to be detected as a backup of 158 any specific VMID. 159 ''; 160 }; 161 }; 162 163 config = let 164 cfg = config.proxmox; 165 cfgLine = name: value: '' 166 ${name}: ${builtins.toString value} 167 ''; 168 virtio0Storage = builtins.head (builtins.split ":" cfg.qemuConf.virtio0); 169 cfgFile = fileName: properties: pkgs.writeTextDir fileName '' 170 # generated by NixOS 171 ${lib.concatStrings (lib.mapAttrsToList cfgLine properties)} 172 #qmdump#map:virtio0:drive-virtio0:${virtio0Storage}:raw: 173 ''; 174 inherit (cfg) partitionTableType; 175 supportEfi = partitionTableType == "efi" || partitionTableType == "hybrid"; 176 supportBios = partitionTableType == "legacy" || partitionTableType == "hybrid" || partitionTableType == "legacy+gpt"; 177 hasBootPartition = partitionTableType == "efi" || partitionTableType == "hybrid"; 178 hasNoFsPartition = partitionTableType == "hybrid" || partitionTableType == "legacy+gpt"; 179 in { 180 assertions = [ 181 { 182 assertion = config.boot.loader.systemd-boot.enable -> config.proxmox.qemuConf.bios == "ovmf"; 183 message = "systemd-boot requires 'ovmf' bios"; 184 } 185 { 186 assertion = partitionTableType == "efi" -> config.proxmox.qemuConf.bios == "ovmf"; 187 message = "'efi' disk partitioning requires 'ovmf' bios"; 188 } 189 { 190 assertion = partitionTableType == "legacy" -> config.proxmox.qemuConf.bios == "seabios"; 191 message = "'legacy' disk partitioning requires 'seabios' bios"; 192 } 193 { 194 assertion = partitionTableType == "legacy+gpt" -> config.proxmox.qemuConf.bios == "seabios"; 195 message = "'legacy+gpt' disk partitioning requires 'seabios' bios"; 196 } 197 ]; 198 system.build.VMA = import ../../lib/make-disk-image.nix { 199 name = "proxmox-${cfg.filenameSuffix}"; 200 inherit (cfg) partitionTableType; 201 postVM = let 202 # Build qemu with PVE's patch that adds support for the VMA format 203 vma = (pkgs.qemu_kvm.override { 204 alsaSupport = false; 205 pulseSupport = false; 206 sdlSupport = false; 207 jackSupport = false; 208 gtkSupport = false; 209 vncSupport = false; 210 smartcardSupport = false; 211 spiceSupport = false; 212 ncursesSupport = false; 213 libiscsiSupport = false; 214 tpmSupport = false; 215 numaSupport = false; 216 seccompSupport = false; 217 guestAgentSupport = false; 218 }).overrideAttrs ( super: rec { 219 220 version = "7.2.1"; 221 src = pkgs.fetchurl { 222 url= "https://download.qemu.org/qemu-${version}.tar.xz"; 223 sha256 = "sha256-jIVpms+dekOl/immTN1WNwsMLRrQdLr3CYqCTReq1zs="; 224 }; 225 patches = [ 226 # Proxmox' VMA tool is published as a particular patch upon QEMU 227 (pkgs.fetchpatch { 228 url = 229 let 230 rev = "abb04bb6272c1202ca9face0827917552b9d06f6"; 231 path = "debian/patches/pve/0027-PVE-Backup-add-vma-backup-format-code.patch"; 232 in "https://git.proxmox.com/?p=pve-qemu.git;a=blob_plain;hb=${rev};f=${path}"; 233 hash = "sha256-3d0HHdvaExCry6zcULnziYnWIAnn24vECkI4sjj2BMg="; 234 }) 235 236 # Proxmox' VMA tool uses O_DIRECT which fails on tmpfs 237 # Filed to upstream issue tracker: https://bugzilla.proxmox.com/show_bug.cgi?id=4710 238 (pkgs.writeText "inline.patch" '' 239 --- a/vma-writer.c 2023-05-01 15:11:13.361341177 +0200 240 +++ b/vma-writer.c 2023-05-01 15:10:51.785293129 +0200 241 @@ -306,7 +306,7 @@ 242 /* try to use O_NONBLOCK */ 243 fcntl(vmaw->fd, F_SETFL, fcntl(vmaw->fd, F_GETFL)|O_NONBLOCK); 244 } else { 245 - oflags = O_NONBLOCK|O_DIRECT|O_WRONLY|O_EXCL; 246 + oflags = O_NONBLOCK|O_WRONLY|O_EXCL; 247 vmaw->fd = qemu_create(filename, oflags, 0644, errp); 248 } 249 '') 250 ]; 251 252 buildInputs = super.buildInputs ++ [ pkgs.libuuid ]; 253 nativeBuildInputs = super.nativeBuildInputs ++ [ pkgs.perl ]; 254 255 }); 256 in 257 '' 258 ${vma}/bin/vma create "vzdump-qemu-${cfg.filenameSuffix}.vma" \ 259 -c ${cfgFile "qemu-server.conf" (cfg.qemuConf // cfg.qemuExtraConf)}/qemu-server.conf drive-virtio0=$diskImage 260 rm $diskImage 261 ${pkgs.zstd}/bin/zstd "vzdump-qemu-${cfg.filenameSuffix}.vma" 262 mv "vzdump-qemu-${cfg.filenameSuffix}.vma.zst" $out/ 263 264 mkdir -p $out/nix-support 265 echo "file vma $out/vzdump-qemu-${cfg.filenameSuffix}.vma.zst" >> $out/nix-support/hydra-build-products 266 ''; 267 inherit (cfg.qemuConf) additionalSpace diskSize bootSize; 268 format = "raw"; 269 inherit config lib pkgs; 270 }; 271 272 boot = { 273 growPartition = true; 274 kernelParams = [ "console=ttyS0" ]; 275 loader.grub = { 276 device = lib.mkDefault (if (hasNoFsPartition || supportBios) then 277 # Even if there is a separate no-fs partition ("/dev/disk/by-partlabel/no-fs" i.e. "/dev/vda2"), 278 # which will be used the bootloader, do not set it as loader.grub.device. 279 # GRUB installation fails, unless the whole disk is selected. 280 "/dev/vda" 281 else 282 "nodev"); 283 efiSupport = lib.mkDefault supportEfi; 284 efiInstallAsRemovable = lib.mkDefault supportEfi; 285 }; 286 287 loader.timeout = 0; 288 initrd.availableKernelModules = [ "uas" "virtio_blk" "virtio_pci" ]; 289 }; 290 291 fileSystems."/" = { 292 device = "/dev/disk/by-label/nixos"; 293 autoResize = true; 294 fsType = "ext4"; 295 }; 296 fileSystems."/boot" = lib.mkIf hasBootPartition { 297 device = "/dev/disk/by-label/ESP"; 298 fsType = "vfat"; 299 }; 300 301 services.qemuGuest.enable = lib.mkDefault true; 302 }; 303}