at 18.03-beta 16 kB view raw
1# Xen hypervisor (Dom0) support. 2 3{ config, lib, pkgs, ... }: 4 5with lib; 6 7let 8 cfg = config.virtualisation.xen; 9in 10 11{ 12 ###### interface 13 14 options = { 15 16 virtualisation.xen.enable = 17 mkOption { 18 default = false; 19 type = types.bool; 20 description = 21 '' 22 Setting this option enables the Xen hypervisor, a 23 virtualisation technology that allows multiple virtual 24 machines, known as <emphasis>domains</emphasis>, to run 25 concurrently on the physical machine. NixOS runs as the 26 privileged <emphasis>Domain 0</emphasis>. This option 27 requires a reboot to take effect. 28 ''; 29 }; 30 31 virtualisation.xen.package = mkOption { 32 type = types.package; 33 defaultText = "pkgs.xen"; 34 example = literalExample "pkgs.xen-light"; 35 description = '' 36 The package used for Xen binary. 37 ''; 38 relatedPackages = [ "xen" "xen-light" ]; 39 }; 40 41 virtualisation.xen.package-qemu = mkOption { 42 type = types.package; 43 defaultText = "pkgs.xen"; 44 example = literalExample "pkgs.qemu_xen-light"; 45 description = '' 46 The package with qemu binaries for dom0 qemu and xendomains. 47 ''; 48 relatedPackages = [ "xen" 49 { name = "qemu_xen-light"; comment = "For use with pkgs.xen-light."; } 50 ]; 51 }; 52 53 virtualisation.xen.bootParams = 54 mkOption { 55 default = ""; 56 description = 57 '' 58 Parameters passed to the Xen hypervisor at boot time. 59 ''; 60 }; 61 62 virtualisation.xen.domain0MemorySize = 63 mkOption { 64 default = 0; 65 example = 512; 66 description = 67 '' 68 Amount of memory (in MiB) allocated to Domain 0 on boot. 69 If set to 0, all memory is assigned to Domain 0. 70 ''; 71 }; 72 73 virtualisation.xen.bridge = { 74 name = mkOption { 75 default = "xenbr0"; 76 description = '' 77 Name of bridge the Xen domUs connect to. 78 ''; 79 }; 80 81 address = mkOption { 82 type = types.str; 83 default = "172.16.0.1"; 84 description = '' 85 IPv4 address of the bridge. 86 ''; 87 }; 88 89 prefixLength = mkOption { 90 type = types.addCheck types.int (n: n >= 0 && n <= 32); 91 default = 16; 92 description = '' 93 Subnet mask of the bridge interface, specified as the number of 94 bits in the prefix (<literal>24</literal>). 95 A DHCP server will provide IP addresses for the whole, remaining 96 subnet. 97 ''; 98 }; 99 100 forwardDns = mkOption { 101 default = false; 102 description = '' 103 If set to <literal>true</literal>, the DNS queries from the 104 hosts connected to the bridge will be forwarded to the DNS 105 servers specified in /etc/resolv.conf . 106 ''; 107 }; 108 109 }; 110 111 virtualisation.xen.stored = 112 mkOption { 113 type = types.path; 114 description = 115 '' 116 Xen Store daemon to use. Defaults to oxenstored of the xen package. 117 ''; 118 }; 119 120 virtualisation.xen.domains = { 121 extraConfig = mkOption { 122 type = types.string; 123 default = ""; 124 description = 125 '' 126 Options defined here will override the defaults for xendomains. 127 The default options can be seen in the file included from 128 /etc/default/xendomains. 129 ''; 130 }; 131 }; 132 133 virtualisation.xen.trace = 134 mkOption { 135 default = false; 136 description = 137 '' 138 Enable Xen tracing. 139 ''; 140 }; 141 }; 142 143 144 ###### implementation 145 146 config = mkIf cfg.enable { 147 assertions = [ { 148 assertion = pkgs.stdenv.isx86_64; 149 message = "Xen currently not supported on ${pkgs.stdenv.system}"; 150 } { 151 assertion = config.boot.loader.grub.enable && (config.boot.loader.grub.efiSupport == false); 152 message = "Xen currently does not support EFI boot"; 153 } ]; 154 155 virtualisation.xen.package = mkDefault pkgs.xen; 156 virtualisation.xen.package-qemu = mkDefault pkgs.xen; 157 virtualisation.xen.stored = mkDefault "${cfg.package}/bin/oxenstored"; 158 159 environment.systemPackages = [ cfg.package ]; 160 161 # Make sure Domain 0 gets the required configuration 162 #boot.kernelPackages = pkgs.boot.kernelPackages.override { features={xen_dom0=true;}; }; 163 164 boot.kernelModules = 165 [ "xen-evtchn" "xen-gntdev" "xen-gntalloc" "xen-blkback" "xen-netback" 166 "xen-pciback" "evtchn" "gntdev" "netbk" "blkbk" "xen-scsibk" 167 "usbbk" "pciback" "xen-acpi-processor" "blktap2" "tun" "netxen_nic" 168 "xen_wdt" "xen-acpi-processor" "xen-privcmd" "xen-scsiback" 169 "xenfs" 170 ]; 171 172 # The xenfs module is needed in system.activationScripts.xen, but 173 # the modprobe command there fails silently. Include xenfs in the 174 # initrd as a work around. 175 boot.initrd.kernelModules = [ "xenfs" ]; 176 177 # The radeonfb kernel module causes the screen to go black as soon 178 # as it's loaded, so don't load it. 179 boot.blacklistedKernelModules = [ "radeonfb" ]; 180 181 # Increase the number of loopback devices from the default (8), 182 # which is way too small because every VM virtual disk requires a 183 # loopback device. 184 boot.extraModprobeConfig = 185 '' 186 options loop max_loop=64 187 ''; 188 189 virtualisation.xen.bootParams = [] ++ 190 optionals cfg.trace [ "loglvl=all" "guest_loglvl=all" ] ++ 191 optional (cfg.domain0MemorySize != 0) "dom0_mem=${toString cfg.domain0MemorySize}M"; 192 193 system.extraSystemBuilderCmds = 194 '' 195 ln -s ${cfg.package}/boot/xen.gz $out/xen.gz 196 echo "${toString cfg.bootParams}" > $out/xen-params 197 ''; 198 199 # Mount the /proc/xen pseudo-filesystem. 200 system.activationScripts.xen = 201 '' 202 if [ -d /proc/xen ]; then 203 ${pkgs.kmod}/bin/modprobe xenfs 2> /dev/null 204 ${pkgs.utillinux}/bin/mountpoint -q /proc/xen || \ 205 ${pkgs.utillinux}/bin/mount -t xenfs none /proc/xen 206 fi 207 ''; 208 209 # Domain 0 requires a pvops-enabled kernel. 210 system.requiredKernelConfig = with config.lib.kernelConfig; 211 [ (isYes "XEN") 212 (isYes "X86_IO_APIC") 213 (isYes "ACPI") 214 (isYes "XEN_DOM0") 215 (isYes "PCI_XEN") 216 (isYes "XEN_DEV_EVTCHN") 217 (isYes "XENFS") 218 (isYes "XEN_COMPAT_XENFS") 219 (isYes "XEN_SYS_HYPERVISOR") 220 (isYes "XEN_GNTDEV") 221 (isYes "XEN_BACKEND") 222 (isModule "XEN_NETDEV_BACKEND") 223 (isModule "XEN_BLKDEV_BACKEND") 224 (isModule "XEN_PCIDEV_BACKEND") 225 (isYes "XEN_BALLOON") 226 (isYes "XEN_SCRUB_PAGES") 227 ]; 228 229 230 environment.etc = 231 [ { source = "${cfg.package}/etc/xen/xl.conf"; 232 target = "xen/xl.conf"; 233 } 234 { source = "${cfg.package}/etc/xen/scripts"; 235 target = "xen/scripts"; 236 } 237 { text = '' 238 source ${cfg.package}/etc/default/xendomains 239 240 ${cfg.domains.extraConfig} 241 ''; 242 target = "default/xendomains"; 243 } 244 ]; 245 246 # Xen provides udev rules. 247 services.udev.packages = [ cfg.package ]; 248 249 services.udev.path = [ pkgs.bridge-utils pkgs.iproute ]; 250 251 systemd.services.xen-store = { 252 description = "Xen Store Daemon"; 253 wantedBy = [ "multi-user.target" ]; 254 after = [ "network.target" "xen-store.socket" ]; 255 requires = [ "xen-store.socket" ]; 256 preStart = '' 257 export XENSTORED_ROOTDIR="/var/lib/xenstored" 258 rm -f "$XENSTORED_ROOTDIR"/tdb* &>/dev/null 259 260 mkdir -p /var/run 261 mkdir -p /var/log/xen # Running xl requires /var/log/xen and /var/lib/xen, 262 mkdir -p /var/lib/xen # so we create them here unconditionally. 263 grep -q control_d /proc/xen/capabilities 264 ''; 265 serviceConfig = if cfg.package.version < "4.8" then 266 { ExecStart = '' 267 ${cfg.stored}${optionalString cfg.trace " -T /var/log/xen/xenstored-trace.log"} --no-fork 268 ''; 269 } else { 270 ExecStart = '' 271 ${cfg.package}/etc/xen/scripts/launch-xenstore 272 ''; 273 Type = "notify"; 274 RemainAfterExit = true; 275 NotifyAccess = "all"; 276 }; 277 postStart = '' 278 ${optionalString (cfg.package.version < "4.8") '' 279 time=0 280 timeout=30 281 # Wait for xenstored to actually come up, timing out after 30 seconds 282 while [ $time -lt $timeout ] && ! `${cfg.package}/bin/xenstore-read -s / >/dev/null 2>&1` ; do 283 time=$(($time+1)) 284 sleep 1 285 done 286 287 # Exit if we timed out 288 if ! [ $time -lt $timeout ] ; then 289 echo "Could not start Xenstore Daemon" 290 exit 1 291 fi 292 ''} 293 echo "executing xen-init-dom0" 294 ${cfg.package}/lib/xen/bin/xen-init-dom0 295 ''; 296 }; 297 298 systemd.sockets.xen-store = { 299 description = "XenStore Socket for userspace API"; 300 wantedBy = [ "sockets.target" ]; 301 socketConfig = { 302 ListenStream = [ "/var/run/xenstored/socket" "/var/run/xenstored/socket_ro" ]; 303 SocketMode = "0660"; 304 SocketUser = "root"; 305 SocketGroup = "root"; 306 }; 307 }; 308 309 310 systemd.services.xen-console = { 311 description = "Xen Console Daemon"; 312 wantedBy = [ "multi-user.target" ]; 313 after = [ "xen-store.service" ]; 314 requires = [ "xen-store.service" ]; 315 preStart = '' 316 mkdir -p /var/run/xen 317 ${optionalString cfg.trace "mkdir -p /var/log/xen"} 318 grep -q control_d /proc/xen/capabilities 319 ''; 320 serviceConfig = { 321 ExecStart = '' 322 ${cfg.package}/bin/xenconsoled\ 323 ${optionalString ((cfg.package.version >= "4.8")) " -i"}\ 324 ${optionalString cfg.trace " --log=all --log-dir=/var/log/xen"} 325 ''; 326 }; 327 }; 328 329 330 systemd.services.xen-qemu = { 331 description = "Xen Qemu Daemon"; 332 wantedBy = [ "multi-user.target" ]; 333 after = [ "xen-console.service" ]; 334 requires = [ "xen-store.service" ]; 335 serviceConfig.ExecStart = '' 336 ${cfg.package-qemu}/${cfg.package-qemu.qemu-system-i386} \ 337 -xen-attach -xen-domid 0 -name dom0 -M xenpv \ 338 -nographic -monitor /dev/null -serial /dev/null -parallel /dev/null 339 ''; 340 }; 341 342 343 systemd.services.xen-watchdog = { 344 description = "Xen Watchdog Daemon"; 345 wantedBy = [ "multi-user.target" ]; 346 after = [ "xen-qemu.service" "xen-domains.service" ]; 347 serviceConfig.ExecStart = "${cfg.package}/bin/xenwatchdogd 30 15"; 348 serviceConfig.Type = "forking"; 349 serviceConfig.RestartSec = "1"; 350 serviceConfig.Restart = "on-failure"; 351 }; 352 353 354 systemd.services.xen-bridge = { 355 description = "Xen bridge"; 356 wantedBy = [ "multi-user.target" ]; 357 before = [ "xen-domains.service" ]; 358 preStart = '' 359 mkdir -p /var/run/xen 360 touch /var/run/xen/dnsmasq.pid 361 touch /var/run/xen/dnsmasq.etherfile 362 touch /var/run/xen/dnsmasq.leasefile 363 364 IFS='-' read -a data <<< `${pkgs.sipcalc}/bin/sipcalc ${cfg.bridge.address}/${toString cfg.bridge.prefixLength} | grep Usable\ range` 365 export XEN_BRIDGE_IP_RANGE_START="${"\${data[1]//[[:blank:]]/}"}" 366 export XEN_BRIDGE_IP_RANGE_END="${"\${data[2]//[[:blank:]]/}"}" 367 368 IFS='-' read -a data <<< `${pkgs.sipcalc}/bin/sipcalc ${cfg.bridge.address}/${toString cfg.bridge.prefixLength} | grep Network\ address` 369 export XEN_BRIDGE_NETWORK_ADDRESS="${"\${data[1]//[[:blank:]]/}"}" 370 371 IFS='-' read -a data <<< `${pkgs.sipcalc}/bin/sipcalc ${cfg.bridge.address}/${toString cfg.bridge.prefixLength} | grep Network\ mask` 372 export XEN_BRIDGE_NETMASK="${"\${data[1]//[[:blank:]]/}"}" 373 374 echo "${cfg.bridge.address} host gw dns" > /var/run/xen/dnsmasq.hostsfile 375 376 cat <<EOF > /var/run/xen/dnsmasq.conf 377 no-daemon 378 pid-file=/var/run/xen/dnsmasq.pid 379 interface=${cfg.bridge.name} 380 except-interface=lo 381 bind-interfaces 382 auth-zone=xen.local,$XEN_BRIDGE_NETWORK_ADDRESS/${toString cfg.bridge.prefixLength} 383 domain=xen.local 384 addn-hosts=/var/run/xen/dnsmasq.hostsfile 385 expand-hosts 386 strict-order 387 no-hosts 388 bogus-priv 389 ${optionalString (!cfg.bridge.forwardDns) '' 390 no-resolv 391 no-poll 392 auth-server=dns.xen.local,${cfg.bridge.name} 393 ''} 394 filterwin2k 395 clear-on-reload 396 domain-needed 397 dhcp-hostsfile=/var/run/xen/dnsmasq.etherfile 398 dhcp-authoritative 399 dhcp-range=$XEN_BRIDGE_IP_RANGE_START,$XEN_BRIDGE_IP_RANGE_END 400 dhcp-no-override 401 no-ping 402 dhcp-leasefile=/var/run/xen/dnsmasq.leasefile 403 EOF 404 405 # DHCP 406 ${pkgs.iptables}/bin/iptables -w -I INPUT -i ${cfg.bridge.name} -p tcp -s $XEN_BRIDGE_NETWORK_ADDRESS/${toString cfg.bridge.prefixLength} --sport 68 --dport 67 -j ACCEPT 407 ${pkgs.iptables}/bin/iptables -w -I INPUT -i ${cfg.bridge.name} -p udp -s $XEN_BRIDGE_NETWORK_ADDRESS/${toString cfg.bridge.prefixLength} --sport 68 --dport 67 -j ACCEPT 408 # DNS 409 ${pkgs.iptables}/bin/iptables -w -I INPUT -i ${cfg.bridge.name} -p tcp -d ${cfg.bridge.address} --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT 410 ${pkgs.iptables}/bin/iptables -w -I INPUT -i ${cfg.bridge.name} -p udp -d ${cfg.bridge.address} --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT 411 412 ${pkgs.bridge-utils}/bin/brctl addbr ${cfg.bridge.name} 413 ${pkgs.inetutils}/bin/ifconfig ${cfg.bridge.name} ${cfg.bridge.address} 414 ${pkgs.inetutils}/bin/ifconfig ${cfg.bridge.name} netmask $XEN_BRIDGE_NETMASK 415 ${pkgs.inetutils}/bin/ifconfig ${cfg.bridge.name} up 416 ''; 417 serviceConfig.ExecStart = "${pkgs.dnsmasq}/bin/dnsmasq --conf-file=/var/run/xen/dnsmasq.conf"; 418 postStop = '' 419 IFS='-' read -a data <<< `${pkgs.sipcalc}/bin/sipcalc ${cfg.bridge.address}/${toString cfg.bridge.prefixLength} | grep Network\ address` 420 export XEN_BRIDGE_NETWORK_ADDRESS="${"\${data[1]//[[:blank:]]/}"}" 421 422 ${pkgs.inetutils}/bin/ifconfig ${cfg.bridge.name} down 423 ${pkgs.bridge-utils}/bin/brctl delbr ${cfg.bridge.name} 424 425 # DNS 426 ${pkgs.iptables}/bin/iptables -w -D INPUT -i ${cfg.bridge.name} -p udp -d ${cfg.bridge.address} --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT 427 ${pkgs.iptables}/bin/iptables -w -D INPUT -i ${cfg.bridge.name} -p tcp -d ${cfg.bridge.address} --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT 428 # DHCP 429 ${pkgs.iptables}/bin/iptables -w -D INPUT -i ${cfg.bridge.name} -p udp -s $XEN_BRIDGE_NETWORK_ADDRESS/${toString cfg.bridge.prefixLength} --sport 68 --dport 67 -j ACCEPT 430 ${pkgs.iptables}/bin/iptables -w -D INPUT -i ${cfg.bridge.name} -p tcp -s $XEN_BRIDGE_NETWORK_ADDRESS/${toString cfg.bridge.prefixLength} --sport 68 --dport 67 -j ACCEPT 431 ''; 432 }; 433 434 435 systemd.services.xen-domains = { 436 description = "Xen domains - automatically starts, saves and restores Xen domains"; 437 wantedBy = [ "multi-user.target" ]; 438 after = [ "xen-bridge.service" "xen-qemu.service" ]; 439 requires = [ "xen-bridge.service" "xen-qemu.service" ]; 440 ## To prevent a race between dhcpcd and xend's bridge setup script 441 ## (which renames eth* to peth* and recreates eth* as a virtual 442 ## device), start dhcpcd after xend. 443 before = [ "dhcpd.service" ]; 444 restartIfChanged = false; 445 serviceConfig.RemainAfterExit = "yes"; 446 path = [ cfg.package cfg.package-qemu ]; 447 environment.XENDOM_CONFIG = "${cfg.package}/etc/sysconfig/xendomains"; 448 preStart = "mkdir -p /var/lock/subsys -m 755"; 449 serviceConfig.ExecStart = "${cfg.package}/etc/init.d/xendomains start"; 450 serviceConfig.ExecStop = "${cfg.package}/etc/init.d/xendomains stop"; 451 }; 452 453 }; 454 455}