1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6
7 cfg = config.virtualisation.libvirtd;
8 vswitch = config.virtualisation.vswitch;
9 configFile = pkgs.writeText "libvirtd.conf" ''
10 unix_sock_group = "libvirtd"
11 unix_sock_rw_perms = "0770"
12 auth_unix_ro = "none"
13 auth_unix_rw = "none"
14 ${cfg.extraConfig}
15 '';
16 qemuConfigFile = pkgs.writeText "qemu.conf" ''
17 ${optionalString cfg.qemuOvmf ''
18 nvram = ["/run/libvirt/nix-ovmf/OVMF_CODE.fd:/run/libvirt/nix-ovmf/OVMF_VARS.fd"]
19 ''}
20 ${optionalString (!cfg.qemuRunAsRoot) ''
21 user = "qemu-libvirtd"
22 group = "qemu-libvirtd"
23 ''}
24 ${cfg.qemuVerbatimConfig}
25 '';
26
27in {
28
29 ###### interface
30
31 options = {
32
33 virtualisation.libvirtd.enable = mkOption {
34 type = types.bool;
35 default = false;
36 description = ''
37 This option enables libvirtd, a daemon that manages
38 virtual machines. Users in the "libvirtd" group can interact with
39 the daemon (e.g. to start or stop VMs) using the
40 <command>virsh</command> command line tool, among others.
41 '';
42 };
43
44 virtualisation.libvirtd.qemuPackage = mkOption {
45 type = types.package;
46 default = pkgs.qemu;
47 description = ''
48 Qemu package to use with libvirt.
49 `pkgs.qemu` can emulate alien architectures (e.g. aarch64 on x86)
50 `pkgs.qemu_kvm` saves disk space allowing to emulate only host architectures.
51 '';
52 };
53
54 virtualisation.libvirtd.extraConfig = mkOption {
55 type = types.lines;
56 default = "";
57 description = ''
58 Extra contents appended to the libvirtd configuration file,
59 libvirtd.conf.
60 '';
61 };
62
63 virtualisation.libvirtd.qemuRunAsRoot = mkOption {
64 type = types.bool;
65 default = true;
66 description = ''
67 If true, libvirtd runs qemu as root.
68 If false, libvirtd runs qemu as unprivileged user qemu-libvirtd.
69 Changing this option to false may cause file permission issues
70 for existing guests. To fix these, manually change ownership
71 of affected files in /var/lib/libvirt/qemu to qemu-libvirtd.
72 '';
73 };
74
75 virtualisation.libvirtd.qemuVerbatimConfig = mkOption {
76 type = types.lines;
77 default = ''
78 namespaces = []
79 '';
80 description = ''
81 Contents written to the qemu configuration file, qemu.conf.
82 Make sure to include a proper namespace configuration when
83 supplying custom configuration.
84 '';
85 };
86
87 virtualisation.libvirtd.qemuOvmf = mkOption {
88 type = types.bool;
89 default = true;
90 description = ''
91 Allows libvirtd to take advantage of OVMF when creating new
92 QEMU VMs with UEFI boot.
93 '';
94 };
95
96 virtualisation.libvirtd.extraOptions = mkOption {
97 type = types.listOf types.str;
98 default = [ ];
99 example = [ "--verbose" ];
100 description = ''
101 Extra command line arguments passed to libvirtd on startup.
102 '';
103 };
104
105 virtualisation.libvirtd.onShutdown = mkOption {
106 type = types.enum ["shutdown" "suspend" ];
107 default = "suspend";
108 description = ''
109 When shutting down / restarting the host what method should
110 be used to gracefully halt the guests. Setting to "shutdown"
111 will cause an ACPI shutdown of each guest. "suspend" will
112 attempt to save the state of the guests ready to restore on boot.
113 '';
114 };
115
116 };
117
118
119 ###### implementation
120
121 config = mkIf cfg.enable {
122
123 environment.systemPackages = with pkgs; [ libvirt libressl.nc cfg.qemuPackage ];
124
125 boot.kernelModules = [ "tun" ];
126
127 users.groups.libvirtd.gid = config.ids.gids.libvirtd;
128
129 # libvirtd runs qemu as this user and group by default
130 users.extraGroups.qemu-libvirtd.gid = config.ids.gids.qemu-libvirtd;
131 users.extraUsers.qemu-libvirtd = {
132 uid = config.ids.uids.qemu-libvirtd;
133 isNormalUser = false;
134 group = "qemu-libvirtd";
135 };
136
137 systemd.packages = [ pkgs.libvirt ];
138
139 systemd.services.libvirtd = {
140 description = "Libvirt Virtual Machine Management Daemon";
141
142 wantedBy = [ "multi-user.target" ];
143 after = [ "systemd-udev-settle.service" ]
144 ++ optional vswitch.enable "vswitchd.service";
145
146 environment.LIBVIRTD_ARGS = ''--config "${configFile}" ${concatStringsSep " " cfg.extraOptions}'';
147
148 path = [ cfg.qemuPackage ] # libvirtd requires qemu-img to manage disk images
149 ++ optional vswitch.enable vswitch.package;
150
151 preStart = ''
152 mkdir -p /var/log/libvirt/qemu -m 755
153 rm -f /var/run/libvirtd.pid
154
155 mkdir -p /var/lib/libvirt
156 mkdir -p /var/lib/libvirt/dnsmasq
157
158 chmod 755 /var/lib/libvirt
159 chmod 755 /var/lib/libvirt/dnsmasq
160
161 # Copy default libvirt network config .xml files to /var/lib
162 # Files modified by the user will not be overwritten
163 for i in $(cd ${pkgs.libvirt}/var/lib && echo \
164 libvirt/qemu/networks/*.xml libvirt/qemu/networks/autostart/*.xml \
165 libvirt/nwfilter/*.xml );
166 do
167 mkdir -p /var/lib/$(dirname $i) -m 755
168 cp -npd ${pkgs.libvirt}/var/lib/$i /var/lib/$i
169 done
170
171 # Copy generated qemu config to libvirt directory
172 cp -f ${qemuConfigFile} /var/lib/libvirt/qemu.conf
173
174 # stable (not GC'able as in /nix/store) paths for using in <emulator> section of xml configs
175 mkdir -p /run/libvirt/nix-emulators
176 for emulator in ${pkgs.libvirt}/libexec/libvirt_lxc ${cfg.qemuPackage}/bin/qemu-kvm ${cfg.qemuPackage}/bin/qemu-system-*; do
177 ln -s --force "$emulator" /run/libvirt/nix-emulators/
178 done
179
180 ${optionalString cfg.qemuOvmf ''
181 mkdir -p /run/libvirt/nix-ovmf
182 ln -s --force ${pkgs.OVMF.fd}/FV/OVMF_CODE.fd /run/libvirt/nix-ovmf/
183 ln -s --force ${pkgs.OVMF.fd}/FV/OVMF_VARS.fd /run/libvirt/nix-ovmf/
184 ''}
185 '';
186
187 serviceConfig = {
188 Type = "notify";
189 KillMode = "process"; # when stopping, leave the VMs alone
190 Restart = "no";
191 };
192 restartIfChanged = false;
193 };
194
195 systemd.services.libvirt-guests = {
196 wantedBy = [ "multi-user.target" ];
197 path = with pkgs; [ coreutils libvirt gawk ];
198 restartIfChanged = false;
199 };
200
201 systemd.sockets.virtlogd = {
202 description = "Virtual machine log manager socket";
203 wantedBy = [ "sockets.target" ];
204 listenStreams = [ "/run/libvirt/virtlogd-sock" ];
205 };
206
207 systemd.services.virtlogd = {
208 description = "Virtual machine log manager";
209 serviceConfig.ExecStart = "@${pkgs.libvirt}/sbin/virtlogd virtlogd";
210 restartIfChanged = false;
211 };
212
213 systemd.sockets.virtlockd = {
214 description = "Virtual machine lock manager socket";
215 wantedBy = [ "sockets.target" ];
216 listenStreams = [ "/run/libvirt/virtlockd-sock" ];
217 };
218
219 systemd.services.virtlockd = {
220 description = "Virtual machine lock manager";
221 serviceConfig.ExecStart = "@${pkgs.libvirt}/sbin/virtlockd virtlockd";
222 restartIfChanged = false;
223 };
224 };
225}