at 15.09-beta 6.3 kB view raw
1# Systemd services for libvirtd. 2 3{ config, lib, pkgs, ... }: 4 5with lib; 6 7let 8 9 cfg = config.virtualisation.libvirtd; 10 vswitch = config.virtualisation.vswitch; 11 configFile = pkgs.writeText "libvirtd.conf" '' 12 unix_sock_group = "libvirtd" 13 unix_sock_rw_perms = "0770" 14 auth_unix_ro = "none" 15 auth_unix_rw = "none" 16 ${cfg.extraConfig} 17 ''; 18 19in 20 21{ 22 ###### interface 23 24 options = { 25 26 virtualisation.libvirtd.enable = 27 mkOption { 28 type = types.bool; 29 default = false; 30 description = 31 '' 32 This option enables libvirtd, a daemon that manages 33 virtual machines. Users in the "libvirtd" group can interact with 34 the daemon (e.g. to start or stop VMs) using the 35 <command>virsh</command> command line tool, among others. 36 ''; 37 }; 38 39 virtualisation.libvirtd.enableKVM = 40 mkOption { 41 type = types.bool; 42 default = true; 43 description = 44 '' 45 This option enables support for QEMU/KVM in libvirtd. 46 ''; 47 }; 48 49 virtualisation.libvirtd.extraConfig = 50 mkOption { 51 type = types.lines; 52 default = ""; 53 description = 54 '' 55 Extra contents appended to the libvirtd configuration file, 56 libvirtd.conf. 57 ''; 58 }; 59 60 virtualisation.libvirtd.extraOptions = 61 mkOption { 62 type = types.listOf types.str; 63 default = [ ]; 64 example = [ "--verbose" ]; 65 description = 66 '' 67 Extra command line arguments passed to libvirtd on startup. 68 ''; 69 }; 70 71 virtualisation.libvirtd.onShutdown = 72 mkOption { 73 type = types.enum ["shutdown" "suspend" ]; 74 default = "suspend"; 75 description = 76 '' 77 When shutting down / restarting the host what method should 78 be used to gracefully halt the guests. Setting to "shutdown" 79 will cause an ACPI shutdown of each guest. "suspend" will 80 attempt to save the state of the guests ready to restore on boot. 81 ''; 82 }; 83 84 85 }; 86 87 88 ###### implementation 89 90 config = mkIf cfg.enable { 91 92 environment.systemPackages = 93 [ pkgs.libvirt pkgs.netcat-openbsd ] 94 ++ optional cfg.enableKVM pkgs.qemu_kvm; 95 96 boot.kernelModules = [ "tun" ]; 97 98 systemd.services.libvirtd = 99 { description = "Libvirt Virtual Machine Management Daemon"; 100 101 wantedBy = [ "multi-user.target" ]; 102 after = [ "systemd-udev-settle.service" ] 103 ++ optional vswitch.enable "vswitchd.service"; 104 105 path = [ 106 pkgs.bridge-utils 107 pkgs.dmidecode 108 pkgs.dnsmasq 109 pkgs.ebtables 110 ] 111 ++ optional cfg.enableKVM pkgs.qemu_kvm 112 ++ optional vswitch.enable vswitch.package; 113 114 preStart = 115 '' 116 mkdir -p /var/log/libvirt/qemu -m 755 117 rm -f /var/run/libvirtd.pid 118 119 mkdir -p /var/lib/libvirt 120 mkdir -p /var/lib/libvirt/dnsmasq 121 122 chmod 755 /var/lib/libvirt 123 chmod 755 /var/lib/libvirt/dnsmasq 124 125 # Libvirt unfortunately writes mutable state (such as 126 # runtime changes to VM, network or filter configurations) 127 # to /etc. So we can't use environment.etc to make the 128 # default network and filter definitions available, since 129 # libvirt will then modify the originals in the Nix store. 130 # So here we copy them instead. Ugly. 131 for i in $(cd ${pkgs.libvirt}/etc && echo \ 132 libvirt/qemu/networks/*.xml libvirt/qemu/networks/autostart/*.xml \ 133 libvirt/nwfilter/*.xml ); 134 do 135 mkdir -p /etc/$(dirname $i) -m 755 136 cp -fpd ${pkgs.libvirt}/etc/$i /etc/$i 137 done 138 139 # libvirtd puts the full path of the emulator binary in the machine 140 # config file. But this path can unfortunately be garbage collected 141 # while still being used by the virtual machine. So update the 142 # emulator path on each startup to something valid (re-scan $PATH). 143 for file in /etc/libvirt/qemu/*.xml /etc/libvirt/lxc/*.xml; do 144 test -f "$file" || continue 145 # get (old) emulator path from config file 146 emulator=$(grep "^[[:space:]]*<emulator>" "$file" | sed 's,^[[:space:]]*<emulator>\(.*\)</emulator>.*,\1,') 147 # get a (definitely) working emulator path by re-scanning $PATH 148 new_emulator=$(PATH=${pkgs.libvirt}/libexec:$PATH command -v $(basename "$emulator")) 149 # write back 150 sed -i "s,^[[:space:]]*<emulator>.*, <emulator>$new_emulator</emulator> <!-- WARNING: emulator dirname is auto-updated by the nixos libvirtd module -->," "$file" 151 done 152 ''; # */ 153 154 serviceConfig.ExecStart = ''@${pkgs.libvirt}/sbin/libvirtd libvirtd --config "${configFile}" --daemon ${concatStringsSep " " cfg.extraOptions}''; 155 serviceConfig.Type = "forking"; 156 serviceConfig.KillMode = "process"; # when stopping, leave the VMs alone 157 158 # Wait until libvirtd is ready to accept requests. 159 postStart = 160 '' 161 for ((i = 0; i < 60; i++)); do 162 if ${pkgs.libvirt}/bin/virsh list > /dev/null; then exit 0; fi 163 sleep 1 164 done 165 exit 1 # !!! seems to be ignored 166 ''; 167 }; 168 169 jobs."libvirt-guests" = 170 { description = "Libvirt Virtual Machines"; 171 172 wantedBy = [ "multi-user.target" ]; 173 wants = [ "libvirtd.service" ]; 174 after = [ "libvirtd.service" ]; 175 176 restartIfChanged = false; 177 178 path = [ pkgs.gettext pkgs.libvirt pkgs.gawk ]; 179 180 preStart = 181 '' 182 mkdir -p /var/lock/subsys -m 755 183 ${pkgs.libvirt}/etc/rc.d/init.d/libvirt-guests start || true 184 ''; 185 186 postStop = 187 '' 188 export PATH=${pkgs.gettext}/bin:$PATH 189 export ON_SHUTDOWN=${cfg.onShutdown} 190 ${pkgs.libvirt}/etc/rc.d/init.d/libvirt-guests stop 191 ''; 192 193 serviceConfig.Type = "oneshot"; 194 serviceConfig.RemainAfterExit = true; 195 }; 196 197 users.extraGroups.libvirtd.gid = config.ids.gids.libvirtd; 198 199 }; 200 201}