1301bdb185
link to search.nixos.org instead of pulling package metadata out of pkgs. this lets us cache docs of a few more modules and provides easier access to package info from the HTML manual, but makes the manpage slightly less useful since package description are no longer rendered.
454 lines
16 KiB
Nix
454 lines
16 KiB
Nix
# Xen hypervisor (Dom0) support.
|
|
|
|
{ config, lib, pkgs, ... }:
|
|
|
|
with lib;
|
|
|
|
let
|
|
cfg = config.virtualisation.xen;
|
|
in
|
|
|
|
{
|
|
imports = [
|
|
(mkRemovedOptionModule [ "virtualisation" "xen" "qemu" ] "You don't need this option anymore, it will work without it.")
|
|
(mkRenamedOptionModule [ "virtualisation" "xen" "qemu-package" ] [ "virtualisation" "xen" "package-qemu" ])
|
|
];
|
|
|
|
###### 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 = literalExpression "pkgs.xen";
|
|
example = literalExpression "pkgs.xen-light";
|
|
description = ''
|
|
The package used for Xen binary.
|
|
'';
|
|
relatedPackages = [ "xen" "xen-light" ];
|
|
};
|
|
|
|
virtualisation.xen.package-qemu = mkOption {
|
|
type = types.package;
|
|
defaultText = literalExpression "pkgs.xen";
|
|
example = literalExpression "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 = [];
|
|
type = types.listOf types.str;
|
|
description =
|
|
''
|
|
Parameters passed to the Xen hypervisor at boot time.
|
|
'';
|
|
};
|
|
|
|
virtualisation.xen.domain0MemorySize =
|
|
mkOption {
|
|
default = 0;
|
|
example = 512;
|
|
type = types.addCheck types.int (n: n >= 0);
|
|
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";
|
|
type = types.str;
|
|
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 {
|
|
type = types.bool;
|
|
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 = mkEnableOption "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 ];
|
|
|
|
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.util-linux}/bin/mountpoint -q /proc/xen || \
|
|
${pkgs.util-linux}/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 =
|
|
{
|
|
"xen/xl.conf".source = "${cfg.package}/etc/xen/xl.conf";
|
|
"xen/scripts".source = "${cfg.package}/etc/xen/scripts";
|
|
"default/xendomains".text = ''
|
|
source ${cfg.package}/etc/default/xendomains
|
|
|
|
${cfg.domains.extraConfig}
|
|
'';
|
|
}
|
|
// optionalAttrs (builtins.compareVersions cfg.package.version "4.10" >= 0) {
|
|
# in V 4.10 oxenstored requires /etc/xen/oxenstored.conf to start
|
|
"xen/oxenstored.conf".source = "${cfg.package}/etc/xen/oxenstored.conf";
|
|
};
|
|
|
|
# Xen provides udev rules.
|
|
services.udev.packages = [ cfg.package ];
|
|
|
|
services.udev.path = [ pkgs.bridge-utils pkgs.iproute2 ];
|
|
|
|
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";
|
|
};
|
|
|
|
};
|
|
}
|