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