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 # Copy default libvirt network config .xml files to /var/lib 126 # Files modified by the user will not be overwritten 127 for i in $(cd ${pkgs.libvirt}/var/lib && echo \ 128 libvirt/qemu/networks/*.xml libvirt/qemu/networks/autostart/*.xml \ 129 libvirt/nwfilter/*.xml ); 130 do 131 mkdir -p /var/lib/$(dirname $i) -m 755 132 cp -npd ${pkgs.libvirt}/var/lib/$i /var/lib/$i 133 done 134 135 # libvirtd puts the full path of the emulator binary in the machine 136 # config file. But this path can unfortunately be garbage collected 137 # while still being used by the virtual machine. So update the 138 # emulator path on each startup to something valid (re-scan $PATH). 139 for file in /etc/libvirt/qemu/*.xml /etc/libvirt/lxc/*.xml; do 140 test -f "$file" || continue 141 # get (old) emulator path from config file 142 emulator=$(grep "^[[:space:]]*<emulator>" "$file" | sed 's,^[[:space:]]*<emulator>\(.*\)</emulator>.*,\1,') 143 # get a (definitely) working emulator path by re-scanning $PATH 144 new_emulator=$(PATH=${pkgs.libvirt}/libexec:$PATH command -v $(basename "$emulator")) 145 # write back 146 sed -i "s,^[[:space:]]*<emulator>.*, <emulator>$new_emulator</emulator> <!-- WARNING: emulator dirname is auto-updated by the nixos libvirtd module -->," "$file" 147 done 148 ''; # */ 149 150 serviceConfig.ExecStart = ''@${pkgs.libvirt}/sbin/libvirtd libvirtd --config "${configFile}" --daemon ${concatStringsSep " " cfg.extraOptions}''; 151 serviceConfig.Type = "forking"; 152 serviceConfig.KillMode = "process"; # when stopping, leave the VMs alone 153 154 # Wait until libvirtd is ready to accept requests. 155 postStart = 156 '' 157 for ((i = 0; i < 60; i++)); do 158 if ${pkgs.libvirt}/bin/virsh list > /dev/null; then exit 0; fi 159 sleep 1 160 done 161 exit 1 # !!! seems to be ignored 162 ''; 163 }; 164 165 systemd.services."libvirt-guests" = { 166 description = "Libvirt Virtual Machines"; 167 168 wantedBy = [ "multi-user.target" ]; 169 wants = [ "libvirtd.service" ]; 170 after = [ "libvirtd.service" ]; 171 172 restartIfChanged = false; 173 174 path = with pkgs; [ gettext libvirt gawk ]; 175 176 preStart = '' 177 mkdir -p /var/lock/subsys -m 755 178 ${pkgs.libvirt}/etc/rc.d/init.d/libvirt-guests start || true 179 ''; 180 181 postStop = '' 182 export PATH=${pkgs.gettext}/bin:$PATH 183 export ON_SHUTDOWN=${cfg.onShutdown} 184 ${pkgs.libvirt}/etc/rc.d/init.d/libvirt-guests stop 185 ''; 186 187 serviceConfig = { 188 Type = "oneshot"; 189 RemainAfterExit = true; 190 }; 191 }; 192 193 users.extraGroups.libvirtd.gid = config.ids.gids.libvirtd; 194 195 }; 196 197}