aboutsummaryrefslogtreecommitdiff
path: root/nixpkgs/nixos/modules/virtualisation/xen-dom0.nix
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/nixos/modules/virtualisation/xen-dom0.nix')
-rw-r--r--nixpkgs/nixos/modules/virtualisation/xen-dom0.nix461
1 files changed, 461 insertions, 0 deletions
diff --git a/nixpkgs/nixos/modules/virtualisation/xen-dom0.nix b/nixpkgs/nixos/modules/virtualisation/xen-dom0.nix
new file mode 100644
index 00000000000..06d5c63476f
--- /dev/null
+++ b/nixpkgs/nixos/modules/virtualisation/xen-dom0.nix
@@ -0,0 +1,461 @@
+# Xen hypervisor (Dom0) support.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.virtualisation.xen;
+in
+
+{
+ ###### interface
+
+ options = {
+
+ virtualisation.xen.enable =
+ mkOption {
+ default = false;
+ type = types.bool;
+ description =
+ ''
+ Setting this option enables the Xen hypervisor, a
+ virtualisation technology that allows multiple virtual
+ machines, known as <emphasis>domains</emphasis>, to run
+ concurrently on the physical machine. NixOS runs as the
+ privileged <emphasis>Domain 0</emphasis>. This option
+ requires a reboot to take effect.
+ '';
+ };
+
+ virtualisation.xen.package = mkOption {
+ type = types.package;
+ defaultText = "pkgs.xen";
+ example = literalExample "pkgs.xen-light";
+ description = ''
+ The package used for Xen binary.
+ '';
+ relatedPackages = [ "xen" "xen-light" ];
+ };
+
+ virtualisation.xen.package-qemu = mkOption {
+ type = types.package;
+ defaultText = "pkgs.xen";
+ example = literalExample "pkgs.qemu_xen-light";
+ description = ''
+ The package with qemu binaries for dom0 qemu and xendomains.
+ '';
+ relatedPackages = [ "xen"
+ { name = "qemu_xen-light"; comment = "For use with pkgs.xen-light."; }
+ ];
+ };
+
+ virtualisation.xen.bootParams =
+ mkOption {
+ default = "";
+ description =
+ ''
+ Parameters passed to the Xen hypervisor at boot time.
+ '';
+ };
+
+ virtualisation.xen.domain0MemorySize =
+ mkOption {
+ default = 0;
+ example = 512;
+ description =
+ ''
+ Amount of memory (in MiB) allocated to Domain 0 on boot.
+ If set to 0, all memory is assigned to Domain 0.
+ '';
+ };
+
+ virtualisation.xen.bridge = {
+ name = mkOption {
+ default = "xenbr0";
+ description = ''
+ Name of bridge the Xen domUs connect to.
+ '';
+ };
+
+ address = mkOption {
+ type = types.str;
+ default = "172.16.0.1";
+ description = ''
+ IPv4 address of the bridge.
+ '';
+ };
+
+ prefixLength = mkOption {
+ type = types.addCheck types.int (n: n >= 0 && n <= 32);
+ default = 16;
+ description = ''
+ Subnet mask of the bridge interface, specified as the number of
+ bits in the prefix (<literal>24</literal>).
+ A DHCP server will provide IP addresses for the whole, remaining
+ subnet.
+ '';
+ };
+
+ forwardDns = mkOption {
+ default = false;
+ description = ''
+ If set to <literal>true</literal>, the DNS queries from the
+ hosts connected to the bridge will be forwarded to the DNS
+ servers specified in /etc/resolv.conf .
+ '';
+ };
+
+ };
+
+ virtualisation.xen.stored =
+ mkOption {
+ type = types.path;
+ description =
+ ''
+ Xen Store daemon to use. Defaults to oxenstored of the xen package.
+ '';
+ };
+
+ virtualisation.xen.domains = {
+ extraConfig = mkOption {
+ type = types.lines;
+ default = "";
+ description =
+ ''
+ Options defined here will override the defaults for xendomains.
+ The default options can be seen in the file included from
+ /etc/default/xendomains.
+ '';
+ };
+ };
+
+ virtualisation.xen.trace =
+ mkOption {
+ default = false;
+ description =
+ ''
+ Enable Xen tracing.
+ '';
+ };
+ };
+
+
+ ###### implementation
+
+ config = mkIf cfg.enable {
+ assertions = [ {
+ assertion = pkgs.stdenv.isx86_64;
+ message = "Xen currently not supported on ${pkgs.stdenv.hostPlatform.system}";
+ } {
+ assertion = config.boot.loader.grub.enable && (config.boot.loader.grub.efiSupport == false);
+ message = "Xen currently does not support EFI boot";
+ } ];
+
+ virtualisation.xen.package = mkDefault pkgs.xen;
+ virtualisation.xen.package-qemu = mkDefault pkgs.xen;
+ virtualisation.xen.stored = mkDefault "${cfg.package}/bin/oxenstored";
+
+ environment.systemPackages = [ cfg.package ];
+
+ # Make sure Domain 0 gets the required configuration
+ #boot.kernelPackages = pkgs.boot.kernelPackages.override { features={xen_dom0=true;}; };
+
+ boot.kernelModules =
+ [ "xen-evtchn" "xen-gntdev" "xen-gntalloc" "xen-blkback" "xen-netback"
+ "xen-pciback" "evtchn" "gntdev" "netbk" "blkbk" "xen-scsibk"
+ "usbbk" "pciback" "xen-acpi-processor" "blktap2" "tun" "netxen_nic"
+ "xen_wdt" "xen-acpi-processor" "xen-privcmd" "xen-scsiback"
+ "xenfs"
+ ];
+
+ # The xenfs module is needed in system.activationScripts.xen, but
+ # the modprobe command there fails silently. Include xenfs in the
+ # initrd as a work around.
+ boot.initrd.kernelModules = [ "xenfs" ];
+
+ # The radeonfb kernel module causes the screen to go black as soon
+ # as it's loaded, so don't load it.
+ boot.blacklistedKernelModules = [ "radeonfb" ];
+
+ # Increase the number of loopback devices from the default (8),
+ # which is way too small because every VM virtual disk requires a
+ # loopback device.
+ boot.extraModprobeConfig =
+ ''
+ options loop max_loop=64
+ '';
+
+ virtualisation.xen.bootParams = [] ++
+ optionals cfg.trace [ "loglvl=all" "guest_loglvl=all" ] ++
+ optional (cfg.domain0MemorySize != 0) "dom0_mem=${toString cfg.domain0MemorySize}M";
+
+ system.extraSystemBuilderCmds =
+ ''
+ ln -s ${cfg.package}/boot/xen.gz $out/xen.gz
+ echo "${toString cfg.bootParams}" > $out/xen-params
+ '';
+
+ # Mount the /proc/xen pseudo-filesystem.
+ system.activationScripts.xen =
+ ''
+ if [ -d /proc/xen ]; then
+ ${pkgs.kmod}/bin/modprobe xenfs 2> /dev/null
+ ${pkgs.utillinux}/bin/mountpoint -q /proc/xen || \
+ ${pkgs.utillinux}/bin/mount -t xenfs none /proc/xen
+ fi
+ '';
+
+ # Domain 0 requires a pvops-enabled kernel.
+ system.requiredKernelConfig = with config.lib.kernelConfig;
+ [ (isYes "XEN")
+ (isYes "X86_IO_APIC")
+ (isYes "ACPI")
+ (isYes "XEN_DOM0")
+ (isYes "PCI_XEN")
+ (isYes "XEN_DEV_EVTCHN")
+ (isYes "XENFS")
+ (isYes "XEN_COMPAT_XENFS")
+ (isYes "XEN_SYS_HYPERVISOR")
+ (isYes "XEN_GNTDEV")
+ (isYes "XEN_BACKEND")
+ (isModule "XEN_NETDEV_BACKEND")
+ (isModule "XEN_BLKDEV_BACKEND")
+ (isModule "XEN_PCIDEV_BACKEND")
+ (isYes "XEN_BALLOON")
+ (isYes "XEN_SCRUB_PAGES")
+ ];
+
+
+ environment.etc =
+ [ { source = "${cfg.package}/etc/xen/xl.conf";
+ target = "xen/xl.conf";
+ }
+ { source = "${cfg.package}/etc/xen/scripts";
+ target = "xen/scripts";
+ }
+ { text = ''
+ source ${cfg.package}/etc/default/xendomains
+
+ ${cfg.domains.extraConfig}
+ '';
+ target = "default/xendomains";
+ }
+ ]
+ ++ lib.optionals (builtins.compareVersions cfg.package.version "4.10" >= 0) [
+ # in V 4.10 oxenstored requires /etc/xen/oxenstored.conf to start
+ { source = "${cfg.package}/etc/xen/oxenstored.conf";
+ target = "xen/oxenstored.conf";
+ }
+ ];
+
+ # Xen provides udev rules.
+ services.udev.packages = [ cfg.package ];
+
+ services.udev.path = [ pkgs.bridge-utils pkgs.iproute ];
+
+ systemd.services.xen-store = {
+ description = "Xen Store Daemon";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network.target" "xen-store.socket" ];
+ requires = [ "xen-store.socket" ];
+ preStart = ''
+ export XENSTORED_ROOTDIR="/var/lib/xenstored"
+ rm -f "$XENSTORED_ROOTDIR"/tdb* &>/dev/null
+
+ mkdir -p /var/run
+ mkdir -p /var/log/xen # Running xl requires /var/log/xen and /var/lib/xen,
+ mkdir -p /var/lib/xen # so we create them here unconditionally.
+ grep -q control_d /proc/xen/capabilities
+ '';
+ serviceConfig = if (builtins.compareVersions cfg.package.version "4.8" < 0) then
+ { ExecStart = ''
+ ${cfg.stored}${optionalString cfg.trace " -T /var/log/xen/xenstored-trace.log"} --no-fork
+ '';
+ } else {
+ ExecStart = ''
+ ${cfg.package}/etc/xen/scripts/launch-xenstore
+ '';
+ Type = "notify";
+ RemainAfterExit = true;
+ NotifyAccess = "all";
+ };
+ postStart = ''
+ ${optionalString (builtins.compareVersions cfg.package.version "4.8" < 0) ''
+ time=0
+ timeout=30
+ # Wait for xenstored to actually come up, timing out after 30 seconds
+ while [ $time -lt $timeout ] && ! `${cfg.package}/bin/xenstore-read -s / >/dev/null 2>&1` ; do
+ time=$(($time+1))
+ sleep 1
+ done
+
+ # Exit if we timed out
+ if ! [ $time -lt $timeout ] ; then
+ echo "Could not start Xenstore Daemon"
+ exit 1
+ fi
+ ''}
+ echo "executing xen-init-dom0"
+ ${cfg.package}/lib/xen/bin/xen-init-dom0
+ '';
+ };
+
+ systemd.sockets.xen-store = {
+ description = "XenStore Socket for userspace API";
+ wantedBy = [ "sockets.target" ];
+ socketConfig = {
+ ListenStream = [ "/var/run/xenstored/socket" "/var/run/xenstored/socket_ro" ];
+ SocketMode = "0660";
+ SocketUser = "root";
+ SocketGroup = "root";
+ };
+ };
+
+
+ systemd.services.xen-console = {
+ description = "Xen Console Daemon";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "xen-store.service" ];
+ requires = [ "xen-store.service" ];
+ preStart = ''
+ mkdir -p /var/run/xen
+ ${optionalString cfg.trace "mkdir -p /var/log/xen"}
+ grep -q control_d /proc/xen/capabilities
+ '';
+ serviceConfig = {
+ ExecStart = ''
+ ${cfg.package}/bin/xenconsoled\
+ ${optionalString ((builtins.compareVersions cfg.package.version "4.8" >= 0)) " -i"}\
+ ${optionalString cfg.trace " --log=all --log-dir=/var/log/xen"}
+ '';
+ };
+ };
+
+
+ systemd.services.xen-qemu = {
+ description = "Xen Qemu Daemon";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "xen-console.service" ];
+ requires = [ "xen-store.service" ];
+ serviceConfig.ExecStart = ''
+ ${cfg.package-qemu}/${cfg.package-qemu.qemu-system-i386} \
+ -xen-attach -xen-domid 0 -name dom0 -M xenpv \
+ -nographic -monitor /dev/null -serial /dev/null -parallel /dev/null
+ '';
+ };
+
+
+ systemd.services.xen-watchdog = {
+ description = "Xen Watchdog Daemon";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "xen-qemu.service" "xen-domains.service" ];
+ serviceConfig.ExecStart = "${cfg.package}/bin/xenwatchdogd 30 15";
+ serviceConfig.Type = "forking";
+ serviceConfig.RestartSec = "1";
+ serviceConfig.Restart = "on-failure";
+ };
+
+
+ systemd.services.xen-bridge = {
+ description = "Xen bridge";
+ wantedBy = [ "multi-user.target" ];
+ before = [ "xen-domains.service" ];
+ preStart = ''
+ mkdir -p /var/run/xen
+ touch /var/run/xen/dnsmasq.pid
+ touch /var/run/xen/dnsmasq.etherfile
+ touch /var/run/xen/dnsmasq.leasefile
+
+ IFS='-' read -a data <<< `${pkgs.sipcalc}/bin/sipcalc ${cfg.bridge.address}/${toString cfg.bridge.prefixLength} | grep Usable\ range`
+ export XEN_BRIDGE_IP_RANGE_START="${"\${data[1]//[[:blank:]]/}"}"
+ export XEN_BRIDGE_IP_RANGE_END="${"\${data[2]//[[:blank:]]/}"}"
+
+ IFS='-' read -a data <<< `${pkgs.sipcalc}/bin/sipcalc ${cfg.bridge.address}/${toString cfg.bridge.prefixLength} | grep Network\ address`
+ export XEN_BRIDGE_NETWORK_ADDRESS="${"\${data[1]//[[:blank:]]/}"}"
+
+ IFS='-' read -a data <<< `${pkgs.sipcalc}/bin/sipcalc ${cfg.bridge.address}/${toString cfg.bridge.prefixLength} | grep Network\ mask`
+ export XEN_BRIDGE_NETMASK="${"\${data[1]//[[:blank:]]/}"}"
+
+ echo "${cfg.bridge.address} host gw dns" > /var/run/xen/dnsmasq.hostsfile
+
+ cat <<EOF > /var/run/xen/dnsmasq.conf
+ no-daemon
+ pid-file=/var/run/xen/dnsmasq.pid
+ interface=${cfg.bridge.name}
+ except-interface=lo
+ bind-interfaces
+ auth-zone=xen.local,$XEN_BRIDGE_NETWORK_ADDRESS/${toString cfg.bridge.prefixLength}
+ domain=xen.local
+ addn-hosts=/var/run/xen/dnsmasq.hostsfile
+ expand-hosts
+ strict-order
+ no-hosts
+ bogus-priv
+ ${optionalString (!cfg.bridge.forwardDns) ''
+ no-resolv
+ no-poll
+ auth-server=dns.xen.local,${cfg.bridge.name}
+ ''}
+ filterwin2k
+ clear-on-reload
+ domain-needed
+ dhcp-hostsfile=/var/run/xen/dnsmasq.etherfile
+ dhcp-authoritative
+ dhcp-range=$XEN_BRIDGE_IP_RANGE_START,$XEN_BRIDGE_IP_RANGE_END
+ dhcp-no-override
+ no-ping
+ dhcp-leasefile=/var/run/xen/dnsmasq.leasefile
+ EOF
+
+ # DHCP
+ ${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
+ ${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
+ # DNS
+ ${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
+ ${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
+
+ ${pkgs.bridge-utils}/bin/brctl addbr ${cfg.bridge.name}
+ ${pkgs.inetutils}/bin/ifconfig ${cfg.bridge.name} ${cfg.bridge.address}
+ ${pkgs.inetutils}/bin/ifconfig ${cfg.bridge.name} netmask $XEN_BRIDGE_NETMASK
+ ${pkgs.inetutils}/bin/ifconfig ${cfg.bridge.name} up
+ '';
+ serviceConfig.ExecStart = "${pkgs.dnsmasq}/bin/dnsmasq --conf-file=/var/run/xen/dnsmasq.conf";
+ postStop = ''
+ IFS='-' read -a data <<< `${pkgs.sipcalc}/bin/sipcalc ${cfg.bridge.address}/${toString cfg.bridge.prefixLength} | grep Network\ address`
+ export XEN_BRIDGE_NETWORK_ADDRESS="${"\${data[1]//[[:blank:]]/}"}"
+
+ ${pkgs.inetutils}/bin/ifconfig ${cfg.bridge.name} down
+ ${pkgs.bridge-utils}/bin/brctl delbr ${cfg.bridge.name}
+
+ # DNS
+ ${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
+ ${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
+ # DHCP
+ ${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
+ ${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
+ '';
+ };
+
+
+ systemd.services.xen-domains = {
+ description = "Xen domains - automatically starts, saves and restores Xen domains";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "xen-bridge.service" "xen-qemu.service" ];
+ requires = [ "xen-bridge.service" "xen-qemu.service" ];
+ ## To prevent a race between dhcpcd and xend's bridge setup script
+ ## (which renames eth* to peth* and recreates eth* as a virtual
+ ## device), start dhcpcd after xend.
+ before = [ "dhcpd.service" ];
+ restartIfChanged = false;
+ serviceConfig.RemainAfterExit = "yes";
+ path = [ cfg.package cfg.package-qemu ];
+ environment.XENDOM_CONFIG = "${cfg.package}/etc/sysconfig/xendomains";
+ preStart = "mkdir -p /var/lock/subsys -m 755";
+ serviceConfig.ExecStart = "${cfg.package}/etc/init.d/xendomains start";
+ serviceConfig.ExecStop = "${cfg.package}/etc/init.d/xendomains stop";
+ };
+
+ };
+
+}