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}