grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / virtualisation / proxmox-image.nix
blobd390c78432ae74dbfde25b126452e6078e678d6a
1 { config, pkgs, lib, ... }:
3 with lib;
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-single";
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       };
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     cloudInit = {
162       enable = mkOption {
163         type = types.bool;
164         default = true;
165         description = ''
166           Whether the VM should accept cloud init configurations from PVE.
167         '';
168       };
169       defaultStorage = mkOption {
170         default = "local-lvm";
171         example = "tank";
172         type = types.str;
173         description = ''
174           Default storage name for cloud init drive.
175         '';
176       };
177       device = mkOption {
178         default = "ide2";
179         example = "scsi0";
180         type = types.str;
181         description = ''
182           Bus/device to which the cloud init drive is attached.
183         '';
184       };
185     };
186   };
188   config = let
189     cfg = config.proxmox;
190     cfgLine = name: value: ''
191       ${name}: ${builtins.toString value}
192     '';
193     virtio0Storage = builtins.head (builtins.split ":" cfg.qemuConf.virtio0);
194     cfgFile = fileName: properties: pkgs.writeTextDir fileName ''
195       # generated by NixOS
196       ${lib.concatStrings (lib.mapAttrsToList cfgLine properties)}
197       #qmdump#map:virtio0:drive-virtio0:${virtio0Storage}:raw:
198     '';
199     inherit (cfg) partitionTableType;
200     supportEfi = partitionTableType == "efi" || partitionTableType == "hybrid";
201     supportBios = partitionTableType == "legacy" || partitionTableType == "hybrid" || partitionTableType == "legacy+gpt";
202     hasBootPartition = partitionTableType == "efi" || partitionTableType == "hybrid";
203     hasNoFsPartition = partitionTableType == "hybrid" || partitionTableType == "legacy+gpt";
204   in {
205     assertions = [
206       {
207         assertion = config.boot.loader.systemd-boot.enable -> config.proxmox.qemuConf.bios == "ovmf";
208         message = "systemd-boot requires 'ovmf' bios";
209       }
210       {
211         assertion = partitionTableType == "efi" -> config.proxmox.qemuConf.bios == "ovmf";
212         message = "'efi' disk partitioning requires 'ovmf' bios";
213       }
214       {
215         assertion = partitionTableType == "legacy" -> config.proxmox.qemuConf.bios == "seabios";
216         message = "'legacy' disk partitioning requires 'seabios' bios";
217       }
218       {
219         assertion = partitionTableType == "legacy+gpt" -> config.proxmox.qemuConf.bios == "seabios";
220         message = "'legacy+gpt' disk partitioning requires 'seabios' bios";
221       }
222     ];
223     system.build.VMA = import ../../lib/make-disk-image.nix {
224       name = "proxmox-${cfg.filenameSuffix}";
225       inherit (cfg) partitionTableType;
226       postVM = let
227         # Build qemu with PVE's patch that adds support for the VMA format
228         vma = (pkgs.qemu_kvm.override {
229           alsaSupport = false;
230           pulseSupport = false;
231           sdlSupport = false;
232           jackSupport = false;
233           gtkSupport = false;
234           vncSupport = false;
235           smartcardSupport = false;
236           spiceSupport = false;
237           ncursesSupport = false;
238           libiscsiSupport = false;
239           tpmSupport = false;
240           numaSupport = false;
241           seccompSupport = false;
242           guestAgentSupport = false;
243         }).overrideAttrs ( super: rec {
244           # Check https://github.com/proxmox/pve-qemu/tree/master for the version
245           # of qemu and patch to use
246           version = "9.0.0";
247           src = pkgs.fetchurl {
248             url = "https://download.qemu.org/qemu-${version}.tar.xz";
249             hash = "sha256-MnCKxmww2MiSYz6paMdxwcdtWX1w3erSGg0izPOG2mk=";
250           };
251           patches = [
252             # Proxmox' VMA tool is published as a particular patch upon QEMU
253             "${pkgs.fetchFromGitHub {
254               owner = "proxmox";
255               repo = "pve-qemu";
256               rev = "14afbdd55f04d250bd679ca1ad55d3f47cd9d4c8";
257               hash = "sha256-lSJQA5SHIHfxJvMLIID2drv2H43crTPMNIlIT37w9Nc=";
258             }}/debian/patches/pve/0027-PVE-Backup-add-vma-backup-format-code.patch"
259           ];
261           buildInputs = super.buildInputs ++ [ pkgs.libuuid ];
262           nativeBuildInputs = super.nativeBuildInputs ++ [ pkgs.perl ];
264         });
265       in
266       ''
267         ${vma}/bin/vma create "vzdump-qemu-${cfg.filenameSuffix}.vma" \
268           -c ${cfgFile "qemu-server.conf" (cfg.qemuConf // cfg.qemuExtraConf)}/qemu-server.conf drive-virtio0=$diskImage
269         rm $diskImage
270         ${pkgs.zstd}/bin/zstd "vzdump-qemu-${cfg.filenameSuffix}.vma"
271         mv "vzdump-qemu-${cfg.filenameSuffix}.vma.zst" $out/
273         mkdir -p $out/nix-support
274         echo "file vma $out/vzdump-qemu-${cfg.filenameSuffix}.vma.zst" > $out/nix-support/hydra-build-products
275       '';
276       inherit (cfg.qemuConf) additionalSpace diskSize bootSize;
277       format = "raw";
278       inherit config lib pkgs;
279     };
281     boot = {
282       growPartition = true;
283       kernelParams = [ "console=ttyS0" ];
284       loader.grub = {
285         device = lib.mkDefault (if (hasNoFsPartition || supportBios) then
286           # Even if there is a separate no-fs partition ("/dev/disk/by-partlabel/no-fs" i.e. "/dev/vda2"),
287           # which will be used the bootloader, do not set it as loader.grub.device.
288           # GRUB installation fails, unless the whole disk is selected.
289           "/dev/vda"
290         else
291           "nodev");
292         efiSupport = lib.mkDefault supportEfi;
293         efiInstallAsRemovable = lib.mkDefault supportEfi;
294       };
296       loader.timeout = 0;
297       initrd.availableKernelModules = [ "uas" "virtio_blk" "virtio_pci" ];
298     };
300     fileSystems."/" = {
301       device = "/dev/disk/by-label/nixos";
302       autoResize = true;
303       fsType = "ext4";
304     };
305     fileSystems."/boot" = lib.mkIf hasBootPartition {
306       device = "/dev/disk/by-label/ESP";
307       fsType = "vfat";
308     };
310     networking = mkIf cfg.cloudInit.enable {
311       hostName = mkForce "";
312       useDHCP = false;
313     };
315     services = {
316       cloud-init = mkIf cfg.cloudInit.enable {
317         enable = true;
318         network.enable = true;
319       };
320       sshd.enable = mkDefault true;
321       qemuGuest.enable = true;
322     };
324     proxmox.qemuExtraConf.${cfg.cloudInit.device} = "${cfg.cloudInit.defaultStorage}:vm-9999-cloudinit,media=cdrom";
325   };