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
17in {
18
19 ###### interface
20
21 options = {
22
23 virtualisation.libvirtd.enable = mkOption {
24 type = types.bool;
25 default = false;
26 description = ''
27 This option enables libvirtd, a daemon that manages
28 virtual machines. Users in the "libvirtd" group can interact with
29 the daemon (e.g. to start or stop VMs) using the
30 <command>virsh</command> command line tool, among others.
31 '';
32 };
33
34 virtualisation.libvirtd.enableKVM = mkOption {
35 type = types.bool;
36 default = true;
37 description = ''
38 This option enables support for QEMU/KVM in libvirtd.
39 '';
40 };
41
42 virtualisation.libvirtd.extraConfig = mkOption {
43 type = types.lines;
44 default = "";
45 description = ''
46 Extra contents appended to the libvirtd configuration file,
47 libvirtd.conf.
48 '';
49 };
50
51 virtualisation.libvirtd.extraOptions = mkOption {
52 type = types.listOf types.str;
53 default = [ ];
54 example = [ "--verbose" ];
55 description = ''
56 Extra command line arguments passed to libvirtd on startup.
57 '';
58 };
59
60 virtualisation.libvirtd.onShutdown = mkOption {
61 type = types.enum ["shutdown" "suspend" ];
62 default = "suspend";
63 description = ''
64 When shutting down / restarting the host what method should
65 be used to gracefully halt the guests. Setting to "shutdown"
66 will cause an ACPI shutdown of each guest. "suspend" will
67 attempt to save the state of the guests ready to restore on boot.
68 '';
69 };
70
71 };
72
73
74 ###### implementation
75
76 config = mkIf cfg.enable {
77
78 environment.systemPackages =
79 [ pkgs.libvirt pkgs.netcat-openbsd ]
80 ++ optional cfg.enableKVM pkgs.qemu_kvm;
81
82 boot.kernelModules = [ "tun" ];
83
84 users.extraGroups.libvirtd.gid = config.ids.gids.libvirtd;
85
86 systemd.services.libvirtd = {
87 description = "Libvirt Virtual Machine Management Daemon";
88
89 wantedBy = [ "multi-user.target" ];
90 after = [ "systemd-udev-settle.service" ]
91 ++ optional vswitch.enable "vswitchd.service";
92
93 path = [
94 pkgs.bridge-utils
95 pkgs.dmidecode
96 pkgs.dnsmasq
97 pkgs.ebtables
98 ]
99 ++ optional cfg.enableKVM pkgs.qemu_kvm
100 ++ optional vswitch.enable vswitch.package;
101
102 preStart = ''
103 mkdir -p /var/log/libvirt/qemu -m 755
104 rm -f /var/run/libvirtd.pid
105
106 mkdir -p /var/lib/libvirt
107 mkdir -p /var/lib/libvirt/dnsmasq
108
109 chmod 755 /var/lib/libvirt
110 chmod 755 /var/lib/libvirt/dnsmasq
111
112 # Copy default libvirt network config .xml files to /var/lib
113 # Files modified by the user will not be overwritten
114 for i in $(cd ${pkgs.libvirt}/var/lib && echo \
115 libvirt/qemu/networks/*.xml libvirt/qemu/networks/autostart/*.xml \
116 libvirt/nwfilter/*.xml );
117 do
118 mkdir -p /var/lib/$(dirname $i) -m 755
119 cp -npd ${pkgs.libvirt}/var/lib/$i /var/lib/$i
120 done
121
122 # libvirtd puts the full path of the emulator binary in the machine
123 # config file. But this path can unfortunately be garbage collected
124 # while still being used by the virtual machine. So update the
125 # emulator path on each startup to something valid (re-scan $PATH).
126 for file in /etc/libvirt/qemu/*.xml /etc/libvirt/lxc/*.xml; do
127 test -f "$file" || continue
128 # get (old) emulator path from config file
129 emulator=$(grep "^[[:space:]]*<emulator>" "$file" | sed 's,^[[:space:]]*<emulator>\(.*\)</emulator>.*,\1,')
130 # get a (definitely) working emulator path by re-scanning $PATH
131 new_emulator=$(PATH=${pkgs.libvirt}/libexec:$PATH command -v $(basename "$emulator"))
132 # write back
133 sed -i "s,^[[:space:]]*<emulator>.*, <emulator>$new_emulator</emulator> <!-- WARNING: emulator dirname is auto-updated by the nixos libvirtd module -->," "$file"
134 done
135 ''; # */
136
137 serviceConfig = {
138 ExecStart = ''@${pkgs.libvirt}/sbin/libvirtd libvirtd --config "${configFile}" ${concatStringsSep " " cfg.extraOptions}'';
139 Type = "notify";
140 KillMode = "process"; # when stopping, leave the VMs alone
141 Restart = "on-failure";
142 };
143 };
144
145 systemd.sockets.virtlogd = {
146 description = "Virtual machine log manager socket";
147 wantedBy = [ "sockets.target" ];
148 listenStreams = [ "/run/libvirt/virtlogd-sock" ];
149 };
150
151 systemd.services.virtlogd = {
152 description = "Virtual machine log manager";
153 serviceConfig.ExecStart = "@${pkgs.libvirt}/sbin/virtlogd virtlogd";
154 };
155
156 systemd.sockets.virtlockd = {
157 description = "Virtual machine lock manager socket";
158 wantedBy = [ "sockets.target" ];
159 listenStreams = [ "/run/libvirt/virtlockd-sock" ];
160 };
161
162 systemd.services.virtlockd = {
163 description = "Virtual machine lock manager";
164 serviceConfig.ExecStart = "@${pkgs.libvirt}/sbin/virtlockd virtlockd";
165 };
166 };
167}