at 24.11-pre 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 = '' 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 = '' 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 = '' 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 = '' 39 Guest OS type 40 ''; 41 }; 42 cores = mkOption { 43 type = types.ints.positive; 44 default = 1; 45 description = '' 46 Guest core count 47 ''; 48 }; 49 memory = mkOption { 50 type = types.ints.positive; 51 default = 1024; 52 description = '' 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 = '' 69 VM name 70 ''; 71 }; 72 additionalSpace = mkOption { 73 type = types.str; 74 default = "512M"; 75 example = "2048M"; 76 description = '' 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 = '' 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 = '' 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 = '' 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 = '' 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 = '' 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 = '' 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 = '' 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}