]> git.immae.eu Git - perso/Immae/Config/Nix.git/blobdiff - modules/private/system/dilion/vms.nix
First attempt at making declarative VMs
[perso/Immae/Config/Nix.git] / modules / private / system / dilion / vms.nix
diff --git a/modules/private/system/dilion/vms.nix b/modules/private/system/dilion/vms.nix
new file mode 100644 (file)
index 0000000..8d5a57b
--- /dev/null
@@ -0,0 +1,146 @@
+# inspired from https://nixos.wiki/wiki/Virtualization_in_NixOS
+{ config, pkgs, lib, ... }@args:
+let
+  networks = {
+    immae = {
+      bridgeNumber = "1";
+      ipRange = "192.168.100";
+    };
+  };
+  guests = {
+    caldance = {
+      pool = "zfspool";
+      cpus = "1";
+      memory = "2";
+      network = "immae";
+      diskSize = "10GiB";
+      extraDevicesXML = ''
+        <filesystem type="mount">
+          <source dir="/var/lib/caldance"/>
+          <target dir="home"/>
+        </filesystem>
+      '';
+    };
+    buildbot = {
+      pool = "zfspool";
+      cpus = "1";
+      memory = "3";
+      network = "immae";
+      diskSize = "10GiB";
+      destroyVolumeOnExit = true;
+      preStart = ''
+        if ! ${pkgs.libvirt}/bin/virsh pool-info --pool niximages &> /dev/null; then
+          pool-create-as --name niximages --type dir --target /etc/libvirtd/base-images/
+        fi
+        if ! ${pkgs.libvirt}/bin/virsh pool-info --pool buildbot-disks &> /dev/null; then
+          mkdir -p /var/lib/libvirt/images/buildbot-disks
+          pool-create-as --name buildbot-disks --type dir --target /var/lib/libvirt/images/buildbot-disks
+        fi
+      '';
+    };
+  };
+  toImage = f: "${import ./vms/base_image.nix f (args // { myEnv = config.myEnv; })}/nixos.qcow2";
+in
+{
+  environment.etc."libvirtd/base-images/nixos.qcow2".source = toImage ./vms/base_configuration.nix;
+  environment.etc."libvirtd/base-images/buildbot.qcow2".source = toImage ./vms/buildbot_configuration.nix;
+  systemd.services = lib.mapAttrs' (name: guest: lib.nameValuePair "libvirtd-guest-${name}" {
+    after = [ "libvirtd.service" "libvirtd-network-${guest.network}.service" ];
+    requires = [ "libvirtd.service" "libvirtd-network-${guest.network}.service" ];
+    wantedBy = [ "multi-user.target" ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = "yes";
+    };
+    script =
+      let
+        xml = pkgs.writeText "libvirt-guest-${name}.xml"
+        ''
+          <domain type="kvm">
+            <name>${name}</name>
+            <uuid>UUID</uuid>
+            <memory unit="GiB">${guest.memory}</memory>
+            <vcpu>${guest.cpus}</vcpu>
+            <os>
+              <type arch="x86_64">hvm</type>
+            </os>
+            <devices>
+              <emulator>/run/current-system/sw/bin/qemu-system-x86_64</emulator>
+              <disk type="volume">
+                <source pool="${guest.pool}" volume="guest-${name}" />
+                <target dev="vda" bus="virtio"/>
+              </disk>
+              ${guest.extraDevicesXML or ""}
+              <input type="keyboard" bus="usb"/>
+              <graphics type="vnc" port="-1" autoport="yes"/>
+              <interface type="network">
+                <source network="${guest.network}" />
+              </interface>
+            </devices>
+            <features>
+              <acpi/>
+            </features>
+          </domain>
+        '';
+      in
+      guest.preStart or "" + ''
+        if ! ${pkgs.libvirt}/bin/virsh vol-key 'guest-${name}' --pool ${guest.pool} &> /dev/null; then
+          ${pkgs.libvirt}/bin/virsh vol-create-as --pool ${guest.pool} --name 'guest-${name}' --capacity '${guest.diskSize}'
+          volume_path=$(${pkgs.libvirt}/bin/virsh vol-path --pool ${guest.pool} --vol 'guest-${name}')
+          ${pkgs.qemu}/bin/qemu-img convert /etc/libvirtd/base-images/nixos.qcow2 $volume_path
+        fi
+        uuid="$(${pkgs.libvirt}/bin/virsh domuuid '${name}' || true)"
+        ${pkgs.libvirt}/bin/virsh define <(sed "s/UUID/$uuid/" '${xml}')
+        ${pkgs.libvirt}/bin/virsh start '${name}'
+      '';
+    preStop = ''
+      ${pkgs.libvirt}/bin/virsh shutdown '${name}'
+      let "timeout = $(date +%s) + 10"
+      while [ "$(${pkgs.libvirt}/bin/virsh list --name | grep --count '^${name}$')" -gt 0 ]; do
+        if [ "$(date +%s)" -ge "$timeout" ]; then
+          # Meh, we warned it...
+          ${pkgs.libvirt}/bin/virsh destroy '${name}'
+        else
+          # The machine is still running, let's give it some time to shut down
+          sleep 0.5
+        fi
+      done
+    '' + lib.optionalString (guest.destroyVolumeOnExit or false) ''
+        if ${pkgs.libvirt}/bin/virsh vol-key 'guest-${name}' --pool ${guest.pool} &> /dev/null; then
+          ${pkgs.libvirt}/bin/virsh vol-wipe --pool ${guest.pool} --vol 'guest-${name}' || true
+          ${pkgs.libvirt}/bin/virsh vol-delete --pool ${guest.pool} --vol 'guest-${name}'
+        fi
+    '';
+  }) guests // (lib.mapAttrs' (name: network: lib.nameValuePair "libvirtd-network-${name}" {
+    after = [ "libvirtd.service" ];
+    requires = [ "libvirtd.service" ];
+    wantedBy = [ "multi-user.target" ];
+    serviceConfig = {
+      Type = "oneshot";
+      RemainAfterExit = "yes";
+    };
+    script = let
+      xml = pkgs.writeText "libvirt-network-${name}.xml" ''
+        <network>
+          <name>${name}</name>
+          <uuid>UUID</uuid>
+          <forward mode='nat' />
+          <bridge name='virbr${network.bridgeNumber}' />
+          <domain name='${name}' localOnly='yes'/>
+          <ip address='${network.ipRange}.1' netmask='255.255.255.0'>
+            <dhcp>
+              <range start='${network.ipRange}.2' end='${network.ipRange}.254'/>
+            </dhcp>
+          </ip>
+        </network>
+      '';
+    in ''
+      uuid="$(${pkgs.libvirt}/bin/virsh net-uuid '${name}' || true)"
+      ${pkgs.libvirt}/bin/virsh net-define <(sed "s/UUID/$uuid/" '${xml}')
+      ${pkgs.libvirt}/bin/virsh net-start '${name}'
+    '';
+    preStop = ''
+      ${pkgs.libvirt}/bin/virsh net-destroy '${name}'
+    '';
+  }) networks);
+}