# inspired from https://nixos.wiki/wiki/Virtualization_in_NixOS
{ config, pkgs, lib, ... }@args:
let
pools = {
niximages = {
type = "dir";
target = "/etc/libvirtd/base-images";
};
buildbot-disks = rec {
preStart = ''
mkdir -p ${target}
'';
type = "dir";
target = "/var/lib/libvirt/images/buildbot-disks";
};
zfspool = {
# pool-define-as --name zfspool --source-name zpool/libvirt --type zfs
type = "zfs";
xml = ''
zpool/libvirt
'';
};
};
networks = {
immae = {
bridgeNumber = "1";
ipRange = "192.168.100";
};
};
guests = {
caldance = {
pool = "zfspool";
cpus = "1";
memory = "2";
network = "immae";
diskSize = "10GiB";
extraDevicesXML = ''
'';
};
buildbot = {
pool = "zfspool";
cpus = "1";
memory = "3";
network = "immae";
diskSize = "10GiB";
destroyVolumeOnExit = true;
};
};
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-pool-${guest.pool}.service" "libvirtd-network-${guest.network}.service" ];
requires = [ "libvirtd.service" "libvirtd-pool-${guest.pool}.service" "libvirtd-network-${guest.network}.service" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = "yes";
};
script =
let
xml = pkgs.writeText "libvirt-guest-${name}.xml"
''
${name}
UUID
${guest.memory}
${guest.cpus}
hvm
/run/current-system/sw/bin/qemu-system-x86_64
${guest.extraDevicesXML or ""}
'';
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" ''
${name}
UUID
'';
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) // (lib.mapAttrs' (name: pool: lib.nameValuePair "libvirtd-pool-${name}" {
after = [ "libvirtd.service" ];
requires = [ "libvirtd.service" ];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = "yes";
};
script = let
xml = pkgs.writeText "libvirt-pool-${name}.xml" ''
${name}
UUID
${pool.xml or ""}
${if pool ? target then ''
${pool.target}
'' else ""}
'';
in pool.preStart or "" + ''
uuid="$(${pkgs.libvirt}/bin/virsh pool-uuid '${name}' || true)"
${pkgs.libvirt}/bin/virsh pool-define <(sed "s/UUID/$uuid/" '${xml}')
${pkgs.libvirt}/bin/virsh pool-start '${name}' || true
'';
}) pools);
}