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