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