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