at 18.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 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.hostPlatform.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 ++ lib.optionals (builtins.compareVersions cfg.package.version "4.10" >= 0) [ 246 # in V 4.10 oxenstored requires /etc/xen/oxenstored.conf to start 247 { source = "${cfg.package}/etc/xen/oxenstored.conf"; 248 target = "xen/oxenstored.conf"; 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 (builtins.compareVersions cfg.package.version "4.8" < 0) 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 (builtins.compareVersions cfg.package.version "4.8" < 0) '' 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 ((builtins.compareVersions cfg.package.version "4.8" >= 0)) " -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.package-qemu}/${cfg.package-qemu.qemu-system-i386} \ 343 -xen-attach -xen-domid 0 -name dom0 -M xenpv \ 344 -nographic -monitor /dev/null -serial /dev/null -parallel /dev/null 345 ''; 346 }; 347 348 349 systemd.services.xen-watchdog = { 350 description = "Xen Watchdog Daemon"; 351 wantedBy = [ "multi-user.target" ]; 352 after = [ "xen-qemu.service" "xen-domains.service" ]; 353 serviceConfig.ExecStart = "${cfg.package}/bin/xenwatchdogd 30 15"; 354 serviceConfig.Type = "forking"; 355 serviceConfig.RestartSec = "1"; 356 serviceConfig.Restart = "on-failure"; 357 }; 358 359 360 systemd.services.xen-bridge = { 361 description = "Xen bridge"; 362 wantedBy = [ "multi-user.target" ]; 363 before = [ "xen-domains.service" ]; 364 preStart = '' 365 mkdir -p /var/run/xen 366 touch /var/run/xen/dnsmasq.pid 367 touch /var/run/xen/dnsmasq.etherfile 368 touch /var/run/xen/dnsmasq.leasefile 369 370 IFS='-' read -a data <<< `${pkgs.sipcalc}/bin/sipcalc ${cfg.bridge.address}/${toString cfg.bridge.prefixLength} | grep Usable\ range` 371 export XEN_BRIDGE_IP_RANGE_START="${"\${data[1]//[[:blank:]]/}"}" 372 export XEN_BRIDGE_IP_RANGE_END="${"\${data[2]//[[:blank:]]/}"}" 373 374 IFS='-' read -a data <<< `${pkgs.sipcalc}/bin/sipcalc ${cfg.bridge.address}/${toString cfg.bridge.prefixLength} | grep Network\ address` 375 export XEN_BRIDGE_NETWORK_ADDRESS="${"\${data[1]//[[:blank:]]/}"}" 376 377 IFS='-' read -a data <<< `${pkgs.sipcalc}/bin/sipcalc ${cfg.bridge.address}/${toString cfg.bridge.prefixLength} | grep Network\ mask` 378 export XEN_BRIDGE_NETMASK="${"\${data[1]//[[:blank:]]/}"}" 379 380 echo "${cfg.bridge.address} host gw dns" > /var/run/xen/dnsmasq.hostsfile 381 382 cat <<EOF > /var/run/xen/dnsmasq.conf 383 no-daemon 384 pid-file=/var/run/xen/dnsmasq.pid 385 interface=${cfg.bridge.name} 386 except-interface=lo 387 bind-interfaces 388 auth-zone=xen.local,$XEN_BRIDGE_NETWORK_ADDRESS/${toString cfg.bridge.prefixLength} 389 domain=xen.local 390 addn-hosts=/var/run/xen/dnsmasq.hostsfile 391 expand-hosts 392 strict-order 393 no-hosts 394 bogus-priv 395 ${optionalString (!cfg.bridge.forwardDns) '' 396 no-resolv 397 no-poll 398 auth-server=dns.xen.local,${cfg.bridge.name} 399 ''} 400 filterwin2k 401 clear-on-reload 402 domain-needed 403 dhcp-hostsfile=/var/run/xen/dnsmasq.etherfile 404 dhcp-authoritative 405 dhcp-range=$XEN_BRIDGE_IP_RANGE_START,$XEN_BRIDGE_IP_RANGE_END 406 dhcp-no-override 407 no-ping 408 dhcp-leasefile=/var/run/xen/dnsmasq.leasefile 409 EOF 410 411 # DHCP 412 ${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 413 ${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 414 # DNS 415 ${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 416 ${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 417 418 ${pkgs.bridge-utils}/bin/brctl addbr ${cfg.bridge.name} 419 ${pkgs.inetutils}/bin/ifconfig ${cfg.bridge.name} ${cfg.bridge.address} 420 ${pkgs.inetutils}/bin/ifconfig ${cfg.bridge.name} netmask $XEN_BRIDGE_NETMASK 421 ${pkgs.inetutils}/bin/ifconfig ${cfg.bridge.name} up 422 ''; 423 serviceConfig.ExecStart = "${pkgs.dnsmasq}/bin/dnsmasq --conf-file=/var/run/xen/dnsmasq.conf"; 424 postStop = '' 425 IFS='-' read -a data <<< `${pkgs.sipcalc}/bin/sipcalc ${cfg.bridge.address}/${toString cfg.bridge.prefixLength} | grep Network\ address` 426 export XEN_BRIDGE_NETWORK_ADDRESS="${"\${data[1]//[[:blank:]]/}"}" 427 428 ${pkgs.inetutils}/bin/ifconfig ${cfg.bridge.name} down 429 ${pkgs.bridge-utils}/bin/brctl delbr ${cfg.bridge.name} 430 431 # DNS 432 ${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 433 ${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 434 # DHCP 435 ${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 436 ${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 437 ''; 438 }; 439 440 441 systemd.services.xen-domains = { 442 description = "Xen domains - automatically starts, saves and restores Xen domains"; 443 wantedBy = [ "multi-user.target" ]; 444 after = [ "xen-bridge.service" "xen-qemu.service" ]; 445 requires = [ "xen-bridge.service" "xen-qemu.service" ]; 446 ## To prevent a race between dhcpcd and xend's bridge setup script 447 ## (which renames eth* to peth* and recreates eth* as a virtual 448 ## device), start dhcpcd after xend. 449 before = [ "dhcpd.service" ]; 450 restartIfChanged = false; 451 serviceConfig.RemainAfterExit = "yes"; 452 path = [ cfg.package cfg.package-qemu ]; 453 environment.XENDOM_CONFIG = "${cfg.package}/etc/sysconfig/xendomains"; 454 preStart = "mkdir -p /var/lock/subsys -m 755"; 455 serviceConfig.ExecStart = "${cfg.package}/etc/init.d/xendomains start"; 456 serviceConfig.ExecStop = "${cfg.package}/etc/init.d/xendomains stop"; 457 }; 458 459 }; 460 461}