at 23.11-pre 10 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 net0 = mkOption { 73 type = types.commas; 74 default = "virtio=00:00:00:00:00:00,bridge=vmbr0,firewall=1"; 75 description = lib.mdDoc '' 76 Configuration for the default interface. When restoring from VMA, check the 77 "unique" box to ensure device mac is randomized. 78 ''; 79 }; 80 serial0 = mkOption { 81 type = types.str; 82 default = "socket"; 83 example = "/dev/ttyS0"; 84 description = lib.mdDoc '' 85 Create a serial device inside the VM (n is 0 to 3), and pass through a host serial device (i.e. /dev/ttyS0), 86 or create a unix socket on the host side (use qm terminal to open a terminal connection). 87 ''; 88 }; 89 agent = mkOption { 90 type = types.bool; 91 apply = x: if x then "1" else "0"; 92 default = true; 93 description = lib.mdDoc '' 94 Expect guest to have qemu agent running 95 ''; 96 }; 97 }; 98 qemuExtraConf = mkOption { 99 type = with types; attrsOf (oneOf [ str int ]); 100 default = {}; 101 example = literalExpression ''{ 102 cpu = "host"; 103 onboot = 1; 104 }''; 105 description = lib.mdDoc '' 106 Additional options appended to qemu-server.conf 107 ''; 108 }; 109 partitionTableType = mkOption { 110 type = types.enum [ "efi" "hybrid" "legacy" "legacy+gpt" ]; 111 description = '' 112 Partition table type to use. See make-disk-image.nix partitionTableType for details. 113 Defaults to 'legacy' for 'proxmox.qemuConf.bios="seabios"' (default), other bios values defaults to 'efi'. 114 Use 'hybrid' to build grub-based hybrid bios+efi images. 115 ''; 116 default = if config.proxmox.qemuConf.bios == "seabios" then "legacy" else "efi"; 117 defaultText = lib.literalExpression ''if config.proxmox.qemuConf.bios == "seabios" then "legacy" else "efi"''; 118 example = "hybrid"; 119 }; 120 filenameSuffix = mkOption { 121 type = types.str; 122 default = config.proxmox.qemuConf.name; 123 example = "999-nixos_template"; 124 description = lib.mdDoc '' 125 Filename of the image will be vzdump-qemu-''${filenameSuffix}.vma.zstd. 126 This will also determine the default name of the VM on restoring the VMA. 127 Start this value with a number if you want the VMA to be detected as a backup of 128 any specific VMID. 129 ''; 130 }; 131 }; 132 133 config = let 134 cfg = config.proxmox; 135 cfgLine = name: value: '' 136 ${name}: ${builtins.toString value} 137 ''; 138 virtio0Storage = builtins.head (builtins.split ":" cfg.qemuConf.virtio0); 139 cfgFile = fileName: properties: pkgs.writeTextDir fileName '' 140 # generated by NixOS 141 ${lib.concatStrings (lib.mapAttrsToList cfgLine properties)} 142 #qmdump#map:virtio0:drive-virtio0:${virtio0Storage}:raw: 143 ''; 144 inherit (cfg) partitionTableType; 145 supportEfi = partitionTableType == "efi" || partitionTableType == "hybrid"; 146 supportBios = partitionTableType == "legacy" || partitionTableType == "hybrid" || partitionTableType == "legacy+gpt"; 147 hasBootPartition = partitionTableType == "efi" || partitionTableType == "hybrid"; 148 hasNoFsPartition = partitionTableType == "hybrid" || partitionTableType == "legacy+gpt"; 149 in { 150 assertions = [ 151 { 152 assertion = config.boot.loader.systemd-boot.enable -> config.proxmox.qemuConf.bios == "ovmf"; 153 message = "systemd-boot requires 'ovmf' bios"; 154 } 155 { 156 assertion = partitionTableType == "efi" -> config.proxmox.qemuConf.bios == "ovmf"; 157 message = "'efi' disk partitioning requires 'ovmf' bios"; 158 } 159 { 160 assertion = partitionTableType == "legacy" -> config.proxmox.qemuConf.bios == "seabios"; 161 message = "'legacy' disk partitioning requires 'seabios' bios"; 162 } 163 { 164 assertion = partitionTableType == "legacy+gpt" -> config.proxmox.qemuConf.bios == "seabios"; 165 message = "'legacy+gpt' disk partitioning requires 'seabios' bios"; 166 } 167 ]; 168 system.build.VMA = import ../../lib/make-disk-image.nix { 169 name = "proxmox-${cfg.filenameSuffix}"; 170 inherit partitionTableType; 171 postVM = let 172 # Build qemu with PVE's patch that adds support for the VMA format 173 vma = (pkgs.qemu_kvm.override { 174 alsaSupport = false; 175 pulseSupport = false; 176 sdlSupport = false; 177 jackSupport = false; 178 gtkSupport = false; 179 vncSupport = false; 180 smartcardSupport = false; 181 spiceSupport = false; 182 ncursesSupport = false; 183 libiscsiSupport = false; 184 tpmSupport = false; 185 numaSupport = false; 186 seccompSupport = false; 187 guestAgentSupport = false; 188 }).overrideAttrs ( super: rec { 189 190 version = "7.0.0"; 191 src = pkgs.fetchurl { 192 url= "https://download.qemu.org/qemu-${version}.tar.xz"; 193 sha256 = "sha256-9rN1x5UfcoQCeYsLqrsthkeMpT1Eztvvq74cRr9G+Dk="; 194 }; 195 patches = [ 196 # Proxmox' VMA tool is published as a particular patch upon QEMU 197 (pkgs.fetchpatch { 198 url = 199 let 200 rev = "1976ca460796f28447b41e3618e5c1e234035dd5"; 201 path = "debian/patches/pve/0026-PVE-Backup-add-vma-backup-format-code.patch"; 202 in "https://git.proxmox.com/?p=pve-qemu.git;a=blob_plain;hb=${rev};f=${path}"; 203 hash = "sha256-2Dz+ceTwrcyYYxi76RtyY3v15/2pwGcDhFuoZWlgbjc="; 204 }) 205 206 # Proxmox' VMA tool uses O_DIRECT which fails on tmpfs 207 # Filed to upstream issue tracker: https://bugzilla.proxmox.com/show_bug.cgi?id=4710 208 (pkgs.writeText "inline.patch" '' 209 --- a/vma-writer.c 2023-05-01 15:11:13.361341177 +0200 210 +++ b/vma-writer.c 2023-05-01 15:10:51.785293129 +0200 211 @@ -306,7 +306,7 @@ 212 /* try to use O_NONBLOCK */ 213 fcntl(vmaw->fd, F_SETFL, fcntl(vmaw->fd, F_GETFL)|O_NONBLOCK); 214 } else { 215 - oflags = O_NONBLOCK|O_DIRECT|O_WRONLY|O_EXCL; 216 + oflags = O_NONBLOCK|O_WRONLY|O_EXCL; 217 vmaw->fd = qemu_create(filename, oflags, 0644, errp); 218 } 219 '') 220 ]; 221 222 buildInputs = super.buildInputs ++ [ pkgs.libuuid ]; 223 224 }); 225 in 226 '' 227 ${vma}/bin/vma create "vzdump-qemu-${cfg.filenameSuffix}.vma" \ 228 -c ${cfgFile "qemu-server.conf" (cfg.qemuConf // cfg.qemuExtraConf)}/qemu-server.conf drive-virtio0=$diskImage 229 rm $diskImage 230 ${pkgs.zstd}/bin/zstd "vzdump-qemu-${cfg.filenameSuffix}.vma" 231 mv "vzdump-qemu-${cfg.filenameSuffix}.vma.zst" $out/ 232 233 mkdir -p $out/nix-support 234 echo "file vma $out/vzdump-qemu-${cfg.filenameSuffix}.vma.zst" >> $out/nix-support/hydra-build-products 235 ''; 236 format = "raw"; 237 inherit config lib pkgs; 238 }; 239 240 boot = { 241 growPartition = true; 242 kernelParams = [ "console=ttyS0" ]; 243 loader.grub = { 244 device = lib.mkDefault (if (hasNoFsPartition || supportBios) then 245 # Even if there is a separate no-fs partition ("/dev/disk/by-partlabel/no-fs" i.e. "/dev/vda2"), 246 # which will be used the bootloader, do not set it as loader.grub.device. 247 # GRUB installation fails, unless the whole disk is selected. 248 "/dev/vda" 249 else 250 "nodev"); 251 efiSupport = lib.mkDefault supportEfi; 252 efiInstallAsRemovable = lib.mkDefault supportEfi; 253 }; 254 255 loader.timeout = 0; 256 initrd.availableKernelModules = [ "uas" "virtio_blk" "virtio_pci" ]; 257 }; 258 259 fileSystems."/" = { 260 device = "/dev/disk/by-label/nixos"; 261 autoResize = true; 262 fsType = "ext4"; 263 }; 264 fileSystems."/boot" = lib.mkIf hasBootPartition { 265 device = "/dev/disk/by-label/ESP"; 266 fsType = "vfat"; 267 }; 268 269 services.qemuGuest.enable = lib.mkDefault true; 270 }; 271}