Move all of NixOS to nixos/ in preparation of the repository merge
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
{ config, pkgs, modulesPath, ... }:
|
||||
|
||||
{
|
||||
imports = [ "${modulesPath}/virtualisation/amazon-image.nix" ];
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
|
||||
{
|
||||
imports = [ ../profiles/headless.nix ./ec2-data.nix ];
|
||||
|
||||
system.build.amazonImage =
|
||||
pkgs.vmTools.runInLinuxVM (
|
||||
pkgs.runCommand "amazon-image"
|
||||
{ preVM =
|
||||
''
|
||||
mkdir $out
|
||||
diskImage=$out/nixos.img
|
||||
${pkgs.vmTools.qemu}/bin/qemu-img create -f raw $diskImage "4G"
|
||||
mv closure xchg/
|
||||
'';
|
||||
buildInputs = [ pkgs.utillinux pkgs.perl ];
|
||||
exportReferencesGraph =
|
||||
[ "closure" config.system.build.toplevel ];
|
||||
}
|
||||
''
|
||||
# Create an empty filesystem and mount it.
|
||||
${pkgs.e2fsprogs}/sbin/mkfs.ext4 -L nixos /dev/vda
|
||||
${pkgs.e2fsprogs}/sbin/tune2fs -c 0 -i 0 /dev/vda
|
||||
mkdir /mnt
|
||||
mount /dev/vda /mnt
|
||||
|
||||
# The initrd expects these directories to exist.
|
||||
mkdir /mnt/dev /mnt/proc /mnt/sys
|
||||
|
||||
mount -o bind /proc /mnt/proc
|
||||
|
||||
# Copy all paths in the closure to the filesystem.
|
||||
storePaths=$(perl ${pkgs.pathsFromGraph} /tmp/xchg/closure)
|
||||
|
||||
mkdir -p /mnt/nix/store
|
||||
echo "copying everything (will take a while)..."
|
||||
cp -prd $storePaths /mnt/nix/store/
|
||||
|
||||
# Register the paths in the Nix database.
|
||||
printRegistration=1 perl ${pkgs.pathsFromGraph} /tmp/xchg/closure | \
|
||||
chroot /mnt ${config.environment.nix}/bin/nix-store --load-db
|
||||
|
||||
# Create the system profile to allow nixos-rebuild to work.
|
||||
chroot /mnt ${config.environment.nix}/bin/nix-env \
|
||||
-p /nix/var/nix/profiles/system --set ${config.system.build.toplevel}
|
||||
|
||||
# `nixos-rebuild' requires an /etc/NIXOS.
|
||||
mkdir -p /mnt/etc
|
||||
touch /mnt/etc/NIXOS
|
||||
|
||||
# `switch-to-configuration' requires a /bin/sh
|
||||
mkdir -p /mnt/bin
|
||||
ln -s ${config.system.build.binsh}/bin/sh /mnt/bin/sh
|
||||
|
||||
# Install a configuration.nix.
|
||||
mkdir -p /mnt/etc/nixos
|
||||
cp ${./amazon-config.nix} /mnt/etc/nixos/configuration.nix
|
||||
|
||||
# Generate the GRUB menu.
|
||||
chroot /mnt ${config.system.build.toplevel}/bin/switch-to-configuration boot
|
||||
|
||||
umount /mnt/proc
|
||||
umount /mnt
|
||||
''
|
||||
);
|
||||
|
||||
fileSystems."/".device = "/dev/disk/by-label/nixos";
|
||||
|
||||
boot.initrd.kernelModules = [ "xen-blkfront" ];
|
||||
boot.kernelModules = [ "xen-netfront" ];
|
||||
|
||||
# Generate a GRUB menu. Amazon's pv-grub uses this to boot our kernel/initrd.
|
||||
boot.loader.grub.version = 1;
|
||||
boot.loader.grub.device = "nodev";
|
||||
boot.loader.grub.timeout = 0;
|
||||
boot.loader.grub.extraPerEntryConfig = "root (hd0)";
|
||||
|
||||
boot.initrd.postDeviceCommands =
|
||||
''
|
||||
# Force udev to exit to prevent random "Device or resource busy
|
||||
# while trying to open /dev/xvda" errors from fsck.
|
||||
udevadm control --exit || true
|
||||
kill -9 -1
|
||||
'';
|
||||
|
||||
# Mount all formatted ephemeral disks and activate all swap devices.
|
||||
# We cannot do this with the ‘fileSystems’ and ‘swapDevices’ options
|
||||
# because the set of devices is dependent on the instance type
|
||||
# (e.g. "m1.large" has one ephemeral filesystem and one swap device,
|
||||
# while "m1.large" has two ephemeral filesystems and no swap
|
||||
# devices). Also, put /tmp and /var on /disk0, since it has a lot
|
||||
# more space than the root device. Similarly, "move" /nix to /disk0
|
||||
# by layering a unionfs-fuse mount on top of it so we have a lot more space for
|
||||
# Nix operations.
|
||||
boot.initrd.postMountCommands =
|
||||
''
|
||||
diskNr=0
|
||||
diskForUnionfs=
|
||||
for device in /dev/xvd[abcde]*; do
|
||||
if [ "$device" = /dev/xvda -o "$device" = /dev/xvda1 ]; then continue; fi
|
||||
fsType=$(blkid -o value -s TYPE "$device" || true)
|
||||
if [ "$fsType" = swap ]; then
|
||||
echo "activating swap device $device..."
|
||||
swapon "$device" || true
|
||||
elif [ "$fsType" = ext3 ]; then
|
||||
mp="/disk$diskNr"
|
||||
diskNr=$((diskNr + 1))
|
||||
echo "mounting $device on $mp..."
|
||||
if mountFS "$device" "$mp" "" ext3; then
|
||||
if [ -z "$diskForUnionfs" ]; then diskForUnionfs="$mp"; fi
|
||||
fi
|
||||
else
|
||||
echo "skipping unknown device type $device"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$diskForUnionfs" ]; then
|
||||
mkdir -m 755 -p $targetRoot/$diskForUnionfs/root
|
||||
|
||||
mkdir -m 1777 -p $targetRoot/$diskForUnionfs/root/tmp $targetRoot/tmp
|
||||
mount --bind $targetRoot/$diskForUnionfs/root/tmp $targetRoot/tmp
|
||||
|
||||
if [ ! -e $targetRoot/.ebs ]; then
|
||||
mkdir -m 755 -p $targetRoot/$diskForUnionfs/root/var $targetRoot/var
|
||||
mount --bind $targetRoot/$diskForUnionfs/root/var $targetRoot/var
|
||||
|
||||
mkdir -p /unionfs-chroot/ro-nix
|
||||
mount --rbind $targetRoot/nix /unionfs-chroot/ro-nix
|
||||
|
||||
mkdir -m 755 -p $targetRoot/$diskForUnionfs/root/nix
|
||||
mkdir -p /unionfs-chroot/rw-nix
|
||||
mount --rbind $targetRoot/$diskForUnionfs/root/nix /unionfs-chroot/rw-nix
|
||||
|
||||
unionfs -o allow_other,cow,nonempty,chroot=/unionfs-chroot,max_files=32768 /rw-nix=RW:/ro-nix=RO $targetRoot/nix
|
||||
fi
|
||||
fi
|
||||
'';
|
||||
|
||||
boot.initrd.extraUtilsCommands =
|
||||
''
|
||||
# We need swapon in the initrd.
|
||||
cp ${pkgs.utillinux}/sbin/swapon $out/bin
|
||||
'';
|
||||
|
||||
# Don't put old configurations in the GRUB menu. The user has no
|
||||
# way to select them anyway.
|
||||
boot.loader.grub.configurationLimit = 0;
|
||||
|
||||
# Allow root logins only using the SSH key that the user specified
|
||||
# at instance creation time.
|
||||
services.openssh.enable = true;
|
||||
services.openssh.permitRootLogin = "without-password";
|
||||
|
||||
# Force getting the hostname from EC2.
|
||||
networking.hostName = mkDefault "";
|
||||
|
||||
# Always include cryptsetup so that Charon can use it.
|
||||
environment.systemPackages = [ pkgs.cryptsetup ];
|
||||
|
||||
boot.initrd.supportedFilesystems = [ "unionfs-fuse" ];
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
# This module defines an Upstart job that obtains the SSH key and host
|
||||
# name of virtual machines running on Amazon EC2, Eucalyptus and
|
||||
# OpenStack Compute (Nova).
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
|
||||
{
|
||||
options = {
|
||||
ec2.metadata = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to allow access to EC2 metadata.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
|
||||
systemd.services."fetch-ec2-data" =
|
||||
{ description = "Fetch EC2 Data";
|
||||
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
before = [ "sshd.service" ];
|
||||
after = [ "network.target" ];
|
||||
|
||||
path = [ pkgs.curl pkgs.iproute ];
|
||||
|
||||
script =
|
||||
''
|
||||
ip route del blackhole 169.254.169.254/32 || true
|
||||
|
||||
curl="curl --retry 3 --retry-delay 0 --fail"
|
||||
|
||||
echo "setting host name..."
|
||||
${optionalString (config.networking.hostName == "") ''
|
||||
${pkgs.nettools}/bin/hostname $($curl http://169.254.169.254/1.0/meta-data/hostname)
|
||||
''}
|
||||
|
||||
# Don't download the SSH key if it has already been injected
|
||||
# into the image (a Nova feature).
|
||||
if ! [ -e /root/.ssh/authorized_keys ]; then
|
||||
echo "obtaining SSH key..."
|
||||
mkdir -p /root/.ssh
|
||||
$curl -o /root/key.pub http://169.254.169.254/1.0/meta-data/public-keys/0/openssh-key
|
||||
if [ $? -eq 0 -a -e /root/key.pub ]; then
|
||||
if ! grep -q -f /root/key.pub /root/.ssh/authorized_keys; then
|
||||
cat /root/key.pub >> /root/.ssh/authorized_keys
|
||||
echo "new key added to authorized_keys"
|
||||
fi
|
||||
chmod 600 /root/.ssh/authorized_keys
|
||||
rm -f /root/key.pub
|
||||
fi
|
||||
fi
|
||||
|
||||
# Extract the intended SSH host key for this machine from
|
||||
# the supplied user data, if available. Otherwise sshd will
|
||||
# generate one normally.
|
||||
$curl http://169.254.169.254/2011-01-01/user-data > /root/user-data || true
|
||||
key="$(sed 's/|/\n/g; s/SSH_HOST_DSA_KEY://; t; d' /root/user-data)"
|
||||
key_pub="$(sed 's/SSH_HOST_DSA_KEY_PUB://; t; d' /root/user-data)"
|
||||
if [ -n "$key" -a -n "$key_pub" -a ! -e /etc/ssh/ssh_host_dsa_key ]; then
|
||||
mkdir -m 0755 -p /etc/ssh
|
||||
(umask 077; echo "$key" > /etc/ssh/ssh_host_dsa_key)
|
||||
echo "$key_pub" > /etc/ssh/ssh_host_dsa_key.pub
|
||||
fi
|
||||
|
||||
${optionalString (! config.ec2.metadata) ''
|
||||
# Since the user data is sensitive, prevent it from being
|
||||
# accessed from now on.
|
||||
ip route add blackhole 169.254.169.254/32
|
||||
''}
|
||||
'';
|
||||
|
||||
serviceConfig.Type = "oneshot";
|
||||
serviceConfig.RemainAfterExit = true;
|
||||
};
|
||||
|
||||
systemd.services."print-host-key" =
|
||||
{ description = "Print SSH Host Key";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "sshd.service" ];
|
||||
script =
|
||||
''
|
||||
# Print the host public key on the console so that the user
|
||||
# can obtain it securely by parsing the output of
|
||||
# ec2-get-console-output.
|
||||
echo "-----BEGIN SSH HOST KEY FINGERPRINTS-----" > /dev/console
|
||||
${pkgs.openssh}/bin/ssh-keygen -l -f /etc/ssh/ssh_host_dsa_key.pub > /dev/console
|
||||
echo "-----END SSH HOST KEY FINGERPRINTS-----" > /dev/console
|
||||
'';
|
||||
serviceConfig.Type = "oneshot";
|
||||
serviceConfig.RemainAfterExit = true;
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
# Upstart jobs for libvirtd.
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
|
||||
let
|
||||
|
||||
cfg = config.virtualisation.libvirtd;
|
||||
configFile = pkgs.writeText "libvirtd.conf" ''
|
||||
unix_sock_group = "libvirtd"
|
||||
unix_sock_rw_perms = "0770"
|
||||
auth_unix_ro = "none"
|
||||
auth_unix_rw = "none"
|
||||
${cfg.extraConfig}
|
||||
'';
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
###### interface
|
||||
|
||||
options = {
|
||||
|
||||
virtualisation.libvirtd.enable =
|
||||
mkOption {
|
||||
default = false;
|
||||
description =
|
||||
''
|
||||
This option enables libvirtd, a daemon that manages
|
||||
virtual machines. Users in the "libvirtd" group can interact with
|
||||
the daemon (e.g. to start or stop VMs) using the
|
||||
<command>virsh</command> command line tool, among others.
|
||||
'';
|
||||
};
|
||||
|
||||
virtualisation.libvirtd.enableKVM =
|
||||
mkOption {
|
||||
default = true;
|
||||
description =
|
||||
''
|
||||
This option enables support for QEMU/KVM in libvirtd.
|
||||
'';
|
||||
};
|
||||
|
||||
virtualisation.libvirtd.extraConfig =
|
||||
mkOption {
|
||||
default = "";
|
||||
description =
|
||||
''
|
||||
Extra contents appended to the libvirtd configuration file,
|
||||
libvirtd.conf.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
###### implementation
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
environment.systemPackages =
|
||||
[ pkgs.libvirt ]
|
||||
++ optional cfg.enableKVM pkgs.qemu_kvm;
|
||||
|
||||
boot.kernelModules = [ "tun" ];
|
||||
|
||||
systemd.services.libvirtd =
|
||||
{ description = "Libvirt Virtual Machine Management Daemon";
|
||||
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "systemd-udev-settle.service" ];
|
||||
|
||||
path =
|
||||
[ pkgs.bridge_utils pkgs.dmidecode pkgs.dnsmasq
|
||||
pkgs.ebtables
|
||||
] ++ optional cfg.enableKVM pkgs.qemu_kvm;
|
||||
|
||||
preStart =
|
||||
''
|
||||
mkdir -p /var/log/libvirt/qemu -m 755
|
||||
rm -f /var/run/libvirtd.pid
|
||||
|
||||
mkdir -p /var/lib/libvirt -m 700
|
||||
mkdir -p /var/lib/libvirt/dnsmasq -m 700
|
||||
|
||||
# Libvirt unfortunately writes mutable state (such as
|
||||
# runtime changes to VM, network or filter configurations)
|
||||
# to /etc. So we can't use environment.etc to make the
|
||||
# default network and filter definitions available, since
|
||||
# libvirt will then modify the originals in the Nix store.
|
||||
# So here we copy them instead. Ugly.
|
||||
for i in $(cd ${pkgs.libvirt}/etc && echo \
|
||||
libvirt/qemu/networks/*.xml libvirt/qemu/networks/autostart/*.xml \
|
||||
libvirt/nwfilter/*.xml );
|
||||
do
|
||||
mkdir -p /etc/$(dirname $i) -m 755
|
||||
cp -fpd ${pkgs.libvirt}/etc/$i /etc/$i
|
||||
done
|
||||
''; # */
|
||||
|
||||
serviceConfig.ExecStart = ''@${pkgs.libvirt}/sbin/libvirtd libvirtd --config "${configFile}" --daemon --verbose'';
|
||||
serviceConfig.Type = "forking";
|
||||
serviceConfig.KillMode = "process"; # when stopping, leave the VMs alone
|
||||
|
||||
# Wait until libvirtd is ready to accept requests.
|
||||
postStart =
|
||||
''
|
||||
for ((i = 0; i < 60; i++)); do
|
||||
if ${pkgs.libvirt}/bin/virsh list > /dev/null; then exit 0; fi
|
||||
sleep 1
|
||||
done
|
||||
exit 1 # !!! seems to be ignored
|
||||
'';
|
||||
};
|
||||
|
||||
jobs."libvirt-guests" =
|
||||
{ description = "Libvirt Virtual Machines";
|
||||
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
wants = [ "libvirtd.service" ];
|
||||
after = [ "libvirtd.service" ];
|
||||
|
||||
# We want to suspend VMs only on shutdown, but Upstart is broken.
|
||||
#stopOn = "";
|
||||
|
||||
restartIfChanged = false;
|
||||
|
||||
path = [ pkgs.gettext pkgs.libvirt pkgs.gawk ];
|
||||
|
||||
preStart =
|
||||
''
|
||||
mkdir -p /var/lock/subsys -m 755
|
||||
${pkgs.libvirt}/etc/rc.d/init.d/libvirt-guests start || true
|
||||
'';
|
||||
|
||||
postStop = "${pkgs.libvirt}/etc/rc.d/init.d/libvirt-guests stop";
|
||||
|
||||
serviceConfig.Type = "oneshot";
|
||||
serviceConfig.RemainAfterExit = true;
|
||||
};
|
||||
|
||||
users.extraGroups.libvirtd.gid = config.ids.gids.libvirtd;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{ config, pkgs, modulesPath, ... }:
|
||||
|
||||
{
|
||||
imports = [ "${modulesPath}/virtualisation/nova-image.nix" ];
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
|
||||
{
|
||||
imports = [ ../profiles/qemu-guest.nix ../profiles/headless.nix ./ec2-data.nix ];
|
||||
|
||||
system.build.novaImage =
|
||||
pkgs.vmTools.runInLinuxVM (
|
||||
pkgs.runCommand "nova-image"
|
||||
{ preVM =
|
||||
''
|
||||
mkdir $out
|
||||
diskImage=$out/image
|
||||
${pkgs.vmTools.qemu}/bin/qemu-img create -f raw $diskImage "4G"
|
||||
mv closure xchg/
|
||||
'';
|
||||
buildInputs = [ pkgs.utillinux pkgs.perl ];
|
||||
exportReferencesGraph =
|
||||
[ "closure" config.system.build.toplevel ];
|
||||
}
|
||||
''
|
||||
# Create a single / partition.
|
||||
${pkgs.parted}/sbin/parted /dev/vda mklabel msdos
|
||||
${pkgs.parted}/sbin/parted /dev/vda -- mkpart primary ext2 1M -1s
|
||||
. /sys/class/block/vda1/uevent
|
||||
mknod /dev/vda1 b $MAJOR $MINOR
|
||||
|
||||
# Create an empty filesystem and mount it.
|
||||
${pkgs.e2fsprogs}/sbin/mkfs.ext3 -L nixos /dev/vda1
|
||||
${pkgs.e2fsprogs}/sbin/tune2fs -c 0 -i 0 /dev/vda1
|
||||
mkdir /mnt
|
||||
mount /dev/vda1 /mnt
|
||||
|
||||
# The initrd expects these directories to exist.
|
||||
mkdir /mnt/dev /mnt/proc /mnt/sys
|
||||
mount --bind /proc /mnt/proc
|
||||
mount --bind /dev /mnt/dev
|
||||
mount --bind /sys /mnt/sys
|
||||
|
||||
# Copy all paths in the closure to the filesystem.
|
||||
storePaths=$(perl ${pkgs.pathsFromGraph} /tmp/xchg/closure)
|
||||
|
||||
mkdir -p /mnt/nix/store
|
||||
${pkgs.rsync}/bin/rsync -av $storePaths /mnt/nix/store/
|
||||
|
||||
# Register the paths in the Nix database.
|
||||
printRegistration=1 perl ${pkgs.pathsFromGraph} /tmp/xchg/closure | \
|
||||
chroot /mnt ${config.environment.nix}/bin/nix-store --load-db
|
||||
|
||||
# Create the system profile to allow nixos-rebuild to work.
|
||||
chroot /mnt ${config.environment.nix}/bin/nix-env \
|
||||
-p /nix/var/nix/profiles/system --set ${config.system.build.toplevel}
|
||||
|
||||
# `nixos-rebuild' requires an /etc/NIXOS.
|
||||
mkdir -p /mnt/etc
|
||||
touch /mnt/etc/NIXOS
|
||||
|
||||
# Install a configuration.nix.
|
||||
mkdir -p /mnt/etc/nixos
|
||||
cp ${./nova-config.nix} /mnt/etc/nixos/configuration.nix
|
||||
|
||||
# Generate the GRUB menu.
|
||||
chroot /mnt ${config.system.build.toplevel}/bin/switch-to-configuration boot
|
||||
|
||||
umount /mnt/proc /mnt/dev /mnt/sys
|
||||
umount /mnt
|
||||
''
|
||||
);
|
||||
|
||||
fileSystems."/".device = "/dev/disk/by-label/nixos";
|
||||
|
||||
boot.kernelParams = [ "console=ttyS0" ];
|
||||
|
||||
boot.loader.grub.version = 2;
|
||||
boot.loader.grub.device = "/dev/vda";
|
||||
boot.loader.grub.timeout = 0;
|
||||
|
||||
# Put /tmp and /var on /ephemeral0, which has a lot more space.
|
||||
# Unfortunately we can't do this with the `fileSystems' option
|
||||
# because it has no support for creating the source of a bind
|
||||
# mount. Also, "move" /nix to /ephemeral0 by layering a unionfs-fuse
|
||||
# mount on top of it so we have a lot more space for Nix operations.
|
||||
/*
|
||||
boot.initrd.postMountCommands =
|
||||
''
|
||||
mkdir -m 1777 -p $targetRoot/ephemeral0/tmp
|
||||
mkdir -m 1777 -p $targetRoot/tmp
|
||||
mount --bind $targetRoot/ephemeral0/tmp $targetRoot/tmp
|
||||
|
||||
mkdir -m 755 -p $targetRoot/ephemeral0/var
|
||||
mkdir -m 755 -p $targetRoot/var
|
||||
mount --bind $targetRoot/ephemeral0/var $targetRoot/var
|
||||
|
||||
mkdir -p /unionfs-chroot/ro-nix
|
||||
mount --rbind $targetRoot/nix /unionfs-chroot/ro-nix
|
||||
|
||||
mkdir -p /unionfs-chroot/rw-nix
|
||||
mkdir -m 755 -p $targetRoot/ephemeral0/nix
|
||||
mount --rbind $targetRoot/ephemeral0/nix /unionfs-chroot/rw-nix
|
||||
unionfs -o allow_other,cow,nonempty,chroot=/unionfs-chroot,max_files=32768 /rw-nix=RW:/ro-nix=RO $targetRoot/nix
|
||||
'';
|
||||
|
||||
boot.initrd.supportedFilesystems = [ "unionfs-fuse" ];
|
||||
*/
|
||||
|
||||
# Since Nova allows VNC access to instances, it's nice to start to
|
||||
# start a few virtual consoles.
|
||||
services.mingetty.ttys = [ "tty1" "tty2" ];
|
||||
|
||||
# Allow root logins only using the SSH key that the user specified
|
||||
# at instance creation time.
|
||||
services.openssh.enable = true;
|
||||
services.openssh.permitRootLogin = "without-password";
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
# Module for Nova, a.k.a. OpenStack Compute.
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
|
||||
let
|
||||
|
||||
cfg = config.virtualisation.nova;
|
||||
|
||||
nova = pkgs.nova;
|
||||
|
||||
novaConf = pkgs.writeText "nova.conf"
|
||||
''
|
||||
--nodaemon
|
||||
--verbose
|
||||
${cfg.extraConfig}
|
||||
'';
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
|
||||
###### interface
|
||||
|
||||
options = {
|
||||
|
||||
virtualisation.nova.enableSingleNode =
|
||||
mkOption {
|
||||
default = false;
|
||||
description =
|
||||
''
|
||||
This option enables Nova, also known as OpenStack Compute,
|
||||
a cloud computing system, as a single-machine
|
||||
installation. That is, all of Nova's components are
|
||||
enabled on this machine, using SQLite as Nova's database.
|
||||
This is useful for evaluating and experimenting with Nova.
|
||||
However, for a real cloud computing environment, you'll
|
||||
want to enable some of Nova's services on other machines,
|
||||
and use a database such as MySQL.
|
||||
'';
|
||||
};
|
||||
|
||||
virtualisation.nova.extraConfig =
|
||||
mkOption {
|
||||
default = "";
|
||||
description =
|
||||
''
|
||||
Additional text appended to <filename>nova.conf</filename>,
|
||||
the main Nova configuration file.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
###### implementation
|
||||
|
||||
config = mkIf cfg.enableSingleNode {
|
||||
|
||||
environment.systemPackages = [ nova pkgs.euca2ools pkgs.novaclient ];
|
||||
|
||||
environment.etc =
|
||||
[ { source = novaConf;
|
||||
target = "nova/nova.conf";
|
||||
}
|
||||
];
|
||||
|
||||
# Nova requires libvirtd and RabbitMQ.
|
||||
virtualisation.libvirtd.enable = true;
|
||||
services.rabbitmq.enable = true;
|
||||
|
||||
# `qemu-nbd' required the `nbd' kernel module.
|
||||
boot.kernelModules = [ "nbd" ];
|
||||
|
||||
system.activationScripts.nova =
|
||||
''
|
||||
mkdir -m 755 -p /var/lib/nova
|
||||
mkdir -m 755 -p /var/lib/nova/networks
|
||||
mkdir -m 700 -p /var/lib/nova/instances
|
||||
mkdir -m 700 -p /var/lib/nova/keys
|
||||
|
||||
# Allow the CA certificate generation script (called by
|
||||
# nova-api) to work.
|
||||
mkdir -m 700 -p /var/lib/nova/CA /var/lib/nova/CA/private
|
||||
|
||||
# Initialise the SQLite database.
|
||||
${nova}/bin/nova-manage db sync
|
||||
'';
|
||||
|
||||
# `nova-api' receives and executes external client requests from
|
||||
# tools such as euca2ools. It listens on port 8773 (XML) and 8774
|
||||
# (JSON).
|
||||
jobs.nova_api =
|
||||
{ name = "nova-api";
|
||||
|
||||
description = "Nova API service";
|
||||
|
||||
startOn = "ip-up";
|
||||
|
||||
# `openssl' is required to generate the CA. `openssh' is
|
||||
# required to generate key pairs.
|
||||
path = [ pkgs.openssl pkgs.openssh pkgs.bash ];
|
||||
|
||||
respawn = false;
|
||||
|
||||
exec = "${nova}/bin/nova-api --flagfile=${novaConf} --api_paste_config=${nova}/etc/nova/api-paste.ini";
|
||||
};
|
||||
|
||||
# `nova-objectstore' is a simple image server. Useful if you're
|
||||
# not running the OpenStack Imaging Service (Swift). It serves
|
||||
# images placed in /var/lib/nova/images/.
|
||||
jobs.nova_objectstore =
|
||||
{ name = "nova-objectstore";
|
||||
|
||||
description = "Nova simple object store service";
|
||||
|
||||
startOn = "ip-up";
|
||||
|
||||
preStart =
|
||||
''
|
||||
mkdir -m 700 -p /var/lib/nova/images
|
||||
'';
|
||||
|
||||
exec = "${nova}/bin/nova-objectstore --flagfile=${novaConf}";
|
||||
};
|
||||
|
||||
# `nova-scheduler' schedules VM execution requests.
|
||||
jobs.nova_scheduler =
|
||||
{ name = "nova-scheduler";
|
||||
|
||||
description = "Nova scheduler service";
|
||||
|
||||
startOn = "ip-up";
|
||||
|
||||
exec = "${nova}/bin/nova-scheduler --flagfile=${novaConf}";
|
||||
};
|
||||
|
||||
# `nova-compute' starts and manages virtual machines.
|
||||
jobs.nova_compute =
|
||||
{ name = "nova-compute";
|
||||
|
||||
description = "Nova compute service";
|
||||
|
||||
startOn = "ip-up";
|
||||
|
||||
path =
|
||||
[ pkgs.sudo pkgs.vlan pkgs.nettools pkgs.iptables pkgs.qemu_kvm
|
||||
pkgs.e2fsprogs pkgs.utillinux pkgs.multipath_tools pkgs.iproute
|
||||
pkgs.bridge_utils
|
||||
];
|
||||
|
||||
exec = "${nova}/bin/nova-compute --flagfile=${novaConf}";
|
||||
};
|
||||
|
||||
# `nova-network' manages networks and allocates IP addresses.
|
||||
jobs.nova_network =
|
||||
{ name = "nova-network";
|
||||
|
||||
description = "Nova network service";
|
||||
|
||||
startOn = "ip-up";
|
||||
|
||||
path =
|
||||
[ pkgs.sudo pkgs.vlan pkgs.dnsmasq pkgs.nettools pkgs.iptables
|
||||
pkgs.iproute pkgs.bridge_utils pkgs.radvd
|
||||
];
|
||||
|
||||
exec = "${nova}/bin/nova-network --flagfile=${novaConf}";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
-device virtio-serial \
|
||||
-chardev socket,id=charconsole0,path=/tmp/nixos-socket,server,nowait \
|
||||
#-device virtconsole,chardev=charconsole0,id=console0 \
|
||||
-device virtserialport,chardev=chardev=charconsole0,id=serial0
|
||||
@@ -0,0 +1,421 @@
|
||||
# This module creates a virtual machine from the NixOS configuration.
|
||||
# Building the `config.system.build.vm' attribute gives you a command
|
||||
# that starts a KVM/QEMU VM running the NixOS configuration defined in
|
||||
# `config'. The Nix store is shared read-only with the host, which
|
||||
# makes (re)building VMs very efficient. However, it also means you
|
||||
# can't reconfigure the guest inside the guest - you need to rebuild
|
||||
# the VM in the host. On the other hand, the root filesystem is a
|
||||
# read/writable disk image persistent across VM reboots.
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
|
||||
let
|
||||
|
||||
vmName =
|
||||
if config.networking.hostName == ""
|
||||
then "noname"
|
||||
else config.networking.hostName;
|
||||
|
||||
cfg = config.virtualisation;
|
||||
|
||||
qemuGraphics = if cfg.graphics then "" else "-nographic";
|
||||
kernelConsole = if cfg.graphics then "" else "console=ttyS0";
|
||||
ttys = [ "tty1" "tty2" "tty3" "tty4" "tty5" "tty6" ];
|
||||
|
||||
# Shell script to start the VM.
|
||||
startVM =
|
||||
''
|
||||
#! ${pkgs.stdenv.shell}
|
||||
|
||||
NIX_DISK_IMAGE=$(readlink -f ''${NIX_DISK_IMAGE:-${config.virtualisation.diskImage}})
|
||||
|
||||
if ! test -e "$NIX_DISK_IMAGE"; then
|
||||
${pkgs.qemu_kvm}/bin/qemu-img create -f qcow2 "$NIX_DISK_IMAGE" \
|
||||
${toString config.virtualisation.diskSize}M || exit 1
|
||||
fi
|
||||
|
||||
# Create a directory for exchanging data with the VM.
|
||||
if [ -z "$TMPDIR" -o -z "$USE_TMPDIR" ]; then
|
||||
TMPDIR=$(mktemp -d nix-vm.XXXXXXXXXX --tmpdir)
|
||||
fi
|
||||
cd $TMPDIR
|
||||
mkdir -p $TMPDIR/xchg
|
||||
|
||||
idx=2
|
||||
extraDisks=""
|
||||
${flip concatMapStrings cfg.emptyDiskImages (size: ''
|
||||
${pkgs.qemu_kvm}/bin/qemu-img create -f raw "empty$idx" "${toString size}M"
|
||||
extraDisks="$extraDisks -drive index=$idx,file=$(pwd)/empty$idx,if=virtio,werror=report"
|
||||
idx=$((idx + 1))
|
||||
'')}
|
||||
|
||||
# Start QEMU.
|
||||
# "-boot menu=on" is there, because I don't know how to make qemu boot from 2nd hd.
|
||||
exec ${pkgs.qemu_kvm}/bin/qemu-kvm \
|
||||
-name ${vmName} \
|
||||
-m ${toString config.virtualisation.memorySize} \
|
||||
${optionalString (pkgs.stdenv.system == "x86_64-linux") "-cpu kvm64"} \
|
||||
-net nic,vlan=0,model=virtio \
|
||||
-net user,vlan=0''${QEMU_NET_OPTS:+,$QEMU_NET_OPTS} \
|
||||
-virtfs local,path=/nix/store,security_model=none,mount_tag=store \
|
||||
-virtfs local,path=$TMPDIR/xchg,security_model=none,mount_tag=xchg \
|
||||
-virtfs local,path=''${SHARED_DIR:-$TMPDIR/xchg},security_model=none,mount_tag=shared \
|
||||
${if cfg.useBootLoader then ''
|
||||
-drive index=0,id=drive1,file=$NIX_DISK_IMAGE,if=virtio,cache=writeback,werror=report \
|
||||
-drive index=1,id=drive2,file=${bootDisk}/disk.img,if=virtio,readonly \
|
||||
-boot menu=on
|
||||
'' else ''
|
||||
-drive file=$NIX_DISK_IMAGE,if=virtio,cache=writeback,werror=report \
|
||||
-kernel ${config.system.build.toplevel}/kernel \
|
||||
-initrd ${config.system.build.toplevel}/initrd \
|
||||
-append "$(cat ${config.system.build.toplevel}/kernel-params) init=${config.system.build.toplevel}/init regInfo=${regInfo} ${kernelConsole} $QEMU_KERNEL_PARAMS" \
|
||||
''} \
|
||||
$extraDisks \
|
||||
${qemuGraphics} \
|
||||
${toString config.virtualisation.qemu.options} \
|
||||
$QEMU_OPTS
|
||||
'';
|
||||
|
||||
|
||||
regInfo = pkgs.runCommand "reginfo"
|
||||
{ exportReferencesGraph =
|
||||
map (x: [("closure-" + baseNameOf x) x]) config.virtualisation.pathsInNixDB;
|
||||
buildInputs = [ pkgs.perl ];
|
||||
preferLocalBuild = true;
|
||||
}
|
||||
''
|
||||
printRegistration=1 perl ${pkgs.pathsFromGraph} closure-* > $out
|
||||
'';
|
||||
|
||||
|
||||
# Generate a hard disk image containing a /boot partition and GRUB
|
||||
# in the MBR. Used when the `useBootLoader' option is set.
|
||||
bootDisk =
|
||||
pkgs.vmTools.runInLinuxVM (
|
||||
pkgs.runCommand "nixos-boot-disk"
|
||||
{ preVM =
|
||||
''
|
||||
mkdir $out
|
||||
diskImage=$out/disk.img
|
||||
${pkgs.qemu_kvm}/bin/qemu-img create -f qcow2 $diskImage "32M"
|
||||
'';
|
||||
buildInputs = [ pkgs.utillinux ];
|
||||
}
|
||||
''
|
||||
# Create a single /boot partition.
|
||||
${pkgs.parted}/sbin/parted /dev/vda mklabel msdos
|
||||
${pkgs.parted}/sbin/parted /dev/vda -- mkpart primary ext2 1M -1s
|
||||
. /sys/class/block/vda1/uevent
|
||||
mknod /dev/vda1 b $MAJOR $MINOR
|
||||
. /sys/class/block/vda/uevent
|
||||
${pkgs.e2fsprogs}/sbin/mkfs.ext4 -L boot /dev/vda1
|
||||
${pkgs.e2fsprogs}/sbin/tune2fs -c 0 -i 0 /dev/vda1
|
||||
|
||||
# Mount /boot.
|
||||
mkdir /boot
|
||||
mount /dev/vda1 /boot
|
||||
|
||||
# This is needed for GRUB 0.97, which doesn't know about virtio devices.
|
||||
mkdir /boot/grub
|
||||
echo '(hd0) /dev/vda' > /boot/grub/device.map
|
||||
|
||||
# Install GRUB and generate the GRUB boot menu.
|
||||
touch /etc/NIXOS
|
||||
mkdir -p /nix/var/nix/profiles
|
||||
${config.system.build.toplevel}/bin/switch-to-configuration boot
|
||||
|
||||
umount /boot
|
||||
''
|
||||
);
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
imports = [ ../profiles/qemu-guest.nix ];
|
||||
|
||||
options = {
|
||||
|
||||
virtualisation.memorySize =
|
||||
mkOption {
|
||||
default = 384;
|
||||
description =
|
||||
''
|
||||
Memory size (M) of virtual machine.
|
||||
'';
|
||||
};
|
||||
|
||||
virtualisation.diskSize =
|
||||
mkOption {
|
||||
default = 512;
|
||||
description =
|
||||
''
|
||||
Disk size (M) of virtual machine.
|
||||
'';
|
||||
};
|
||||
|
||||
virtualisation.diskImage =
|
||||
mkOption {
|
||||
default = "./${vmName}.qcow2";
|
||||
description =
|
||||
''
|
||||
Path to the disk image containing the root filesystem.
|
||||
The image will be created on startup if it does not
|
||||
exist.
|
||||
'';
|
||||
};
|
||||
|
||||
virtualisation.emptyDiskImages =
|
||||
mkOption {
|
||||
default = [];
|
||||
type = types.listOf types.int;
|
||||
description =
|
||||
''
|
||||
Additional disk images to provide to the VM, the value is a list of
|
||||
sizes in megabytes the empty disk should be.
|
||||
|
||||
These disks are writeable by the VM and will be thrown away
|
||||
afterwards.
|
||||
'';
|
||||
};
|
||||
|
||||
virtualisation.graphics =
|
||||
mkOption {
|
||||
default = true;
|
||||
description =
|
||||
''
|
||||
Whether to run QEMU with a graphics window, or access
|
||||
the guest computer serial port through the host tty.
|
||||
'';
|
||||
};
|
||||
|
||||
virtualisation.pathsInNixDB =
|
||||
mkOption {
|
||||
default = [];
|
||||
description =
|
||||
''
|
||||
The list of paths whose closure is registered in the Nix
|
||||
database in the VM. All other paths in the host Nix store
|
||||
appear in the guest Nix store as well, but are considered
|
||||
garbage (because they are not registered in the Nix
|
||||
database in the guest).
|
||||
'';
|
||||
};
|
||||
|
||||
virtualisation.vlans =
|
||||
mkOption {
|
||||
default = [ 1 ];
|
||||
example = [ 1 2 ];
|
||||
description =
|
||||
''
|
||||
Virtual networks to which the VM is connected. Each
|
||||
number <replaceable>N</replaceable> in this list causes
|
||||
the VM to have a virtual Ethernet interface attached to a
|
||||
separate virtual network on which it will be assigned IP
|
||||
address
|
||||
<literal>192.168.<replaceable>N</replaceable>.<replaceable>M</replaceable></literal>,
|
||||
where <replaceable>M</replaceable> is the index of this VM
|
||||
in the list of VMs.
|
||||
'';
|
||||
};
|
||||
|
||||
virtualisation.writableStore =
|
||||
mkOption {
|
||||
default = false;
|
||||
description =
|
||||
''
|
||||
If enabled, the Nix store in the VM is made writable by
|
||||
layering a unionfs-fuse/tmpfs filesystem on top of the host's Nix
|
||||
store.
|
||||
'';
|
||||
};
|
||||
|
||||
virtualisation.writableStoreUseTmpfs =
|
||||
mkOption {
|
||||
default = true;
|
||||
description =
|
||||
''
|
||||
Use a tmpfs for the writable store instead of writing to the VM's
|
||||
own filesystem.
|
||||
'';
|
||||
};
|
||||
|
||||
networking.primaryIPAddress =
|
||||
mkOption {
|
||||
default = "";
|
||||
internal = true;
|
||||
description = "Primary IP address used in /etc/hosts.";
|
||||
};
|
||||
|
||||
virtualisation.qemu.options =
|
||||
mkOption {
|
||||
default = [];
|
||||
example = [ "-vga std" ];
|
||||
description = "Options passed to QEMU.";
|
||||
};
|
||||
|
||||
virtualisation.useBootLoader =
|
||||
mkOption {
|
||||
default = false;
|
||||
description =
|
||||
''
|
||||
If enabled, the virtual machine will be booted using the
|
||||
regular boot loader (i.e., GRUB 1 or 2). This allows
|
||||
testing of the boot loader. If
|
||||
disabled (the default), the VM directly boots the NixOS
|
||||
kernel and initial ramdisk, bypassing the boot loader
|
||||
altogether.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = {
|
||||
|
||||
boot.loader.grub.device = mkOverride 50 "/dev/vda";
|
||||
|
||||
boot.initrd.supportedFilesystems = optional cfg.writableStore "unionfs-fuse";
|
||||
|
||||
boot.initrd.extraUtilsCommands =
|
||||
''
|
||||
# We need mke2fs in the initrd.
|
||||
cp ${pkgs.e2fsprogs}/sbin/mke2fs $out/bin
|
||||
'';
|
||||
|
||||
boot.initrd.postDeviceCommands =
|
||||
''
|
||||
# If the disk image appears to be empty, run mke2fs to
|
||||
# initialise.
|
||||
FSTYPE=$(blkid -o value -s TYPE /dev/vda || true)
|
||||
if test -z "$FSTYPE"; then
|
||||
mke2fs -t ext4 /dev/vda
|
||||
fi
|
||||
'';
|
||||
|
||||
boot.initrd.postMountCommands =
|
||||
''
|
||||
# Mark this as a NixOS machinex.
|
||||
mkdir -p $targetRoot/etc
|
||||
echo -n > $targetRoot/etc/NIXOS
|
||||
|
||||
# Fix the permissions on /tmp.
|
||||
chmod 1777 $targetRoot/tmp
|
||||
|
||||
mkdir -p $targetRoot/boot
|
||||
mount -o remount,ro $targetRoot/nix/store
|
||||
${optionalString cfg.writableStore ''
|
||||
mkdir -p /unionfs-chroot/ro-store
|
||||
mount --rbind $targetRoot/nix/store /unionfs-chroot/ro-store
|
||||
|
||||
mkdir /unionfs-chroot/rw-store
|
||||
${if cfg.writableStoreUseTmpfs then ''
|
||||
mount -t tmpfs -o "mode=755" none /unionfs-chroot/rw-store
|
||||
'' else ''
|
||||
mkdir $targetRoot/.nix-rw-store
|
||||
mount --bind $targetRoot/.nix-rw-store /unionfs-chroot/rw-store
|
||||
''}
|
||||
|
||||
unionfs -o allow_other,cow,nonempty,chroot=/unionfs-chroot,max_files=32768,hide_meta_files /rw-store=RW:/ro-store=RO $targetRoot/nix/store
|
||||
''}
|
||||
'';
|
||||
|
||||
# After booting, register the closure of the paths in
|
||||
# `virtualisation.pathsInNixDB' in the Nix database in the VM. This
|
||||
# allows Nix operations to work in the VM. The path to the
|
||||
# registration file is passed through the kernel command line to
|
||||
# allow `system.build.toplevel' to be included. (If we had a direct
|
||||
# reference to ${regInfo} here, then we would get a cyclic
|
||||
# dependency.)
|
||||
boot.postBootCommands =
|
||||
''
|
||||
if [[ "$(cat /proc/cmdline)" =~ regInfo=([^ ]*) ]]; then
|
||||
${config.environment.nix}/bin/nix-store --load-db < ''${BASH_REMATCH[1]}
|
||||
fi
|
||||
'';
|
||||
|
||||
virtualisation.pathsInNixDB = [ config.system.build.toplevel ];
|
||||
|
||||
virtualisation.qemu.options = [ "-vga std" "-usbdevice tablet" ];
|
||||
|
||||
# Mount the host filesystem via 9P, and bind-mount the Nix store of
|
||||
# the host into our own filesystem. We use mkOverride to allow this
|
||||
# module to be applied to "normal" NixOS system configuration, where
|
||||
# the regular value for the `fileSystems' attribute should be
|
||||
# disregarded for the purpose of building a VM test image (since
|
||||
# those filesystems don't exist in the VM).
|
||||
fileSystems = mkOverride 10
|
||||
{ "/".device = "/dev/vda";
|
||||
"/nix/store" =
|
||||
{ device = "store";
|
||||
fsType = "9p";
|
||||
options = "trans=virtio,version=9p2000.L,msize=1048576,cache=loose";
|
||||
};
|
||||
"/tmp/xchg" =
|
||||
{ device = "xchg";
|
||||
fsType = "9p";
|
||||
options = "trans=virtio,version=9p2000.L,msize=1048576,cache=loose";
|
||||
neededForBoot = true;
|
||||
};
|
||||
"/tmp/shared" =
|
||||
{ device = "shared";
|
||||
fsType = "9p";
|
||||
options = "trans=virtio,version=9p2000.L,msize=1048576";
|
||||
neededForBoot = true;
|
||||
};
|
||||
} // optionalAttrs cfg.useBootLoader
|
||||
{ "/boot" =
|
||||
{ device = "/dev/disk/by-label/boot";
|
||||
fsType = "ext4";
|
||||
options = "ro";
|
||||
noCheck = true; # fsck fails on a r/o filesystem
|
||||
};
|
||||
};
|
||||
|
||||
swapDevices = mkOverride 50 [ ];
|
||||
|
||||
# Don't run ntpd in the guest. It should get the correct time from KVM.
|
||||
services.ntp.enable = false;
|
||||
|
||||
system.build.vm = pkgs.runCommand "nixos-vm" { preferLocalBuild = true; }
|
||||
''
|
||||
ensureDir $out/bin
|
||||
ln -s ${config.system.build.toplevel} $out/system
|
||||
ln -s ${pkgs.writeScript "run-nixos-vm" startVM} $out/bin/run-${vmName}-vm
|
||||
'';
|
||||
|
||||
# When building a regular system configuration, override whatever
|
||||
# video driver the host uses.
|
||||
services.xserver.videoDriver = mkOverride 50 null;
|
||||
services.xserver.videoDrivers = mkOverride 50 [ "vesa" ];
|
||||
services.xserver.defaultDepth = mkOverride 50 0;
|
||||
services.xserver.resolutions = mkOverride 50 [ { x = 1024; y = 768; } ];
|
||||
services.xserver.monitorSection =
|
||||
''
|
||||
# Set a higher refresh rate so that resolutions > 800x600 work.
|
||||
HorizSync 30-140
|
||||
VertRefresh 50-160
|
||||
'';
|
||||
|
||||
# Wireless won't work in the VM.
|
||||
networking.wireless.enable = mkOverride 50 false;
|
||||
|
||||
system.requiredKernelConfig = with config.lib.kernelConfig;
|
||||
[ (isEnabled "VIRTIO_BLK")
|
||||
(isEnabled "VIRTIO_PCI")
|
||||
(isEnabled "VIRTIO_NET")
|
||||
(isEnabled "EXT4_FS")
|
||||
(isYes "BLK_DEV")
|
||||
(isYes "PCI")
|
||||
(isYes "EXPERIMENTAL")
|
||||
(isYes "NETDEVICES")
|
||||
(isYes "NET_CORE")
|
||||
(isYes "INET")
|
||||
(isYes "NETWORK_FILESYSTEMS")
|
||||
] ++ optional (!cfg.graphics) [
|
||||
(isYes "SERIAL_8250_CONSOLE")
|
||||
(isYes "SERIAL_8250")
|
||||
];
|
||||
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
# Module for VirtualBox guests.
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
|
||||
let
|
||||
|
||||
cfg = config.services.virtualbox;
|
||||
kernel = config.boot.kernelPackages;
|
||||
|
||||
in
|
||||
|
||||
optionalAttrs (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) # ugly...
|
||||
{
|
||||
|
||||
###### interface
|
||||
|
||||
options = {
|
||||
|
||||
services.virtualbox = {
|
||||
|
||||
enable = mkOption {
|
||||
default = false;
|
||||
description = "Whether to enable the VirtualBox service and other guest additions.";
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
###### implementation
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
environment.systemPackages = [ kernel.virtualboxGuestAdditions ];
|
||||
|
||||
boot.extraModulePackages = [ kernel.virtualboxGuestAdditions ];
|
||||
|
||||
users.extraGroups.vboxsf.gid = config.ids.gids.vboxsf;
|
||||
|
||||
systemd.services.virtualbox =
|
||||
{ description = "VirtualBox Guest Services";
|
||||
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
requires = [ "dev-vboxguest.device" ];
|
||||
after = [ "dev-vboxguest.device" ];
|
||||
|
||||
unitConfig.ConditionVirtualization = "oracle";
|
||||
|
||||
serviceConfig.ExecStart = "@${kernel.virtualboxGuestAdditions}/sbin/VBoxService VBoxService --foreground";
|
||||
};
|
||||
|
||||
services.xserver.videoDrivers = mkOverride 50 [ "virtualbox" ];
|
||||
|
||||
services.xserver.config =
|
||||
''
|
||||
Section "InputDevice"
|
||||
Identifier "VBoxMouse"
|
||||
Driver "vboxmouse"
|
||||
EndSection
|
||||
'';
|
||||
|
||||
services.xserver.serverLayoutSection =
|
||||
''
|
||||
InputDevice "VBoxMouse"
|
||||
'';
|
||||
|
||||
services.xserver.displayManager.sessionCommands =
|
||||
''
|
||||
PATH=${makeSearchPath "bin" [ pkgs.gnugrep pkgs.which pkgs.xorg.xorgserver ]}:$PATH \
|
||||
${kernel.virtualboxGuestAdditions}/bin/VBoxClient-all
|
||||
'';
|
||||
|
||||
services.udev.extraRules =
|
||||
''
|
||||
# /dev/vboxuser is necessary for VBoxClient to work. Maybe we
|
||||
# should restrict this to logged-in users.
|
||||
KERNEL=="vboxuser", OWNER="root", GROUP="root", MODE="0666"
|
||||
|
||||
# Allow systemd dependencies on vboxguest.
|
||||
KERNEL=="vboxguest", TAG+="systemd"
|
||||
'';
|
||||
|
||||
# Make the ACPI Shutdown command to do the right thing.
|
||||
services.acpid.enable = true;
|
||||
services.acpid.powerEventCommands = "poweroff";
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
|
||||
{
|
||||
system.build.virtualBoxImage =
|
||||
pkgs.vmTools.runInLinuxVM (
|
||||
pkgs.runCommand "virtualbox-image"
|
||||
{ memSize = 768;
|
||||
preVM =
|
||||
''
|
||||
mkdir $out
|
||||
diskImage=$out/image
|
||||
${pkgs.vmTools.qemu}/bin/qemu-img create -f raw $diskImage "10G"
|
||||
mv closure xchg/
|
||||
'';
|
||||
postVM =
|
||||
''
|
||||
echo "creating VirtualBox disk image..."
|
||||
${pkgs.vmTools.qemu}/bin/qemu-img convert -f raw -O vdi $diskImage $out/disk.vdi
|
||||
rm $diskImage
|
||||
'';
|
||||
buildInputs = [ pkgs.utillinux pkgs.perl ];
|
||||
exportReferencesGraph =
|
||||
[ "closure" config.system.build.toplevel ];
|
||||
}
|
||||
''
|
||||
# Create a single / partition.
|
||||
${pkgs.parted}/sbin/parted /dev/vda mklabel msdos
|
||||
${pkgs.parted}/sbin/parted /dev/vda -- mkpart primary ext2 1M -1s
|
||||
. /sys/class/block/vda1/uevent
|
||||
mknod /dev/vda1 b $MAJOR $MINOR
|
||||
|
||||
# Create an empty filesystem and mount it.
|
||||
${pkgs.e2fsprogs}/sbin/mkfs.ext4 -L nixos /dev/vda1
|
||||
${pkgs.e2fsprogs}/sbin/tune2fs -c 0 -i 0 /dev/vda1
|
||||
mkdir /mnt
|
||||
mount /dev/vda1 /mnt
|
||||
|
||||
# The initrd expects these directories to exist.
|
||||
mkdir /mnt/dev /mnt/proc /mnt/sys
|
||||
mount --bind /proc /mnt/proc
|
||||
mount --bind /dev /mnt/dev
|
||||
mount --bind /sys /mnt/sys
|
||||
|
||||
# Copy all paths in the closure to the filesystem.
|
||||
storePaths=$(perl ${pkgs.pathsFromGraph} /tmp/xchg/closure)
|
||||
|
||||
echo "filling Nix store..."
|
||||
mkdir -p /mnt/nix/store
|
||||
set -f
|
||||
cp -prvd $storePaths /mnt/nix/store/
|
||||
|
||||
# Register the paths in the Nix database.
|
||||
printRegistration=1 perl ${pkgs.pathsFromGraph} /tmp/xchg/closure | \
|
||||
chroot /mnt ${config.environment.nix}/bin/nix-store --load-db
|
||||
|
||||
# Create the system profile to allow nixos-rebuild to work.
|
||||
chroot /mnt ${config.environment.nix}/bin/nix-env \
|
||||
-p /nix/var/nix/profiles/system --set ${config.system.build.toplevel}
|
||||
|
||||
# `nixos-rebuild' requires an /etc/NIXOS.
|
||||
mkdir -p /mnt/etc/nixos
|
||||
touch /mnt/etc/NIXOS
|
||||
|
||||
# `switch-to-configuration' requires a /bin/sh
|
||||
mkdir -p /mnt/bin
|
||||
ln -s ${config.system.build.binsh}/bin/sh /mnt/bin/sh
|
||||
|
||||
# Generate the GRUB menu.
|
||||
ln -s vda /dev/sda
|
||||
chroot /mnt ${config.system.build.toplevel}/bin/switch-to-configuration boot
|
||||
|
||||
umount /mnt/proc /mnt/dev /mnt/sys
|
||||
umount /mnt
|
||||
''
|
||||
);
|
||||
|
||||
system.build.virtualBoxOVA = pkgs.runCommand "virtualbox-ova"
|
||||
{ buildInputs = [ pkgs.linuxPackages.virtualbox ];
|
||||
vmName = "NixOS ${config.system.nixosVersion} (${pkgs.stdenv.system})";
|
||||
fileName = "nixos-${config.system.nixosVersion}-${pkgs.stdenv.system}.ova";
|
||||
}
|
||||
''
|
||||
echo "creating VirtualBox VM..."
|
||||
export HOME=$PWD
|
||||
VBoxManage createvm --name "$vmName" --register \
|
||||
--ostype ${if pkgs.stdenv.system == "x86_64-linux" then "Linux26_64" else "Linux26"}
|
||||
VBoxManage modifyvm "$vmName" \
|
||||
--memory 1536 --acpi on --vram 10 \
|
||||
--nictype1 virtio --nic1 nat \
|
||||
--audiocontroller ac97 --audio alsa \
|
||||
--rtcuseutc on \
|
||||
--usb on --mouse usbtablet
|
||||
VBoxManage storagectl "$vmName" --name SATA --add sata --sataportcount 4 --bootable on --hostiocache on
|
||||
VBoxManage storageattach "$vmName" --storagectl SATA --port 0 --device 0 --type hdd \
|
||||
--medium ${config.system.build.virtualBoxImage}/disk.vdi
|
||||
|
||||
echo "exporting VirtualBox VM..."
|
||||
mkdir -p $out
|
||||
VBoxManage export "$vmName" --output "$out/$fileName"
|
||||
'';
|
||||
|
||||
fileSystems."/".device = "/dev/disk/by-label/nixos";
|
||||
|
||||
boot.loader.grub.version = 2;
|
||||
boot.loader.grub.device = "/dev/sda";
|
||||
|
||||
services.virtualbox.enable = true;
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
# Xen hypervisor (Dom0) support.
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
with pkgs.lib;
|
||||
|
||||
let
|
||||
|
||||
cfg = config.virtualisation.xen;
|
||||
|
||||
xen = pkgs.xen;
|
||||
|
||||
xendConfig = pkgs.writeText "xend-config.sxp"
|
||||
''
|
||||
(loglevel DEBUG)
|
||||
(network-script network-bridge)
|
||||
(vif-script vif-bridge)
|
||||
'';
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
###### interface
|
||||
|
||||
options = {
|
||||
|
||||
virtualisation.xen.enable =
|
||||
mkOption {
|
||||
default = false;
|
||||
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.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.
|
||||
'';
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
###### implementation
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
|
||||
environment.systemPackages = [ xen ];
|
||||
|
||||
# Domain 0 requires a pvops-enabled kernel.
|
||||
boot.kernelPackages = pkgs.linuxPackages_3_2_xen;
|
||||
|
||||
boot.kernelModules =
|
||||
[ "xen_evtchn" "xen_gntdev" "xen_blkback" "xen_netback" "xen_pciback"
|
||||
"blktap" "tun"
|
||||
];
|
||||
|
||||
# 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 =
|
||||
[ "loglvl=all" "guest_loglvl=all" ] ++
|
||||
optional (cfg.domain0MemorySize != 0) "dom0_mem=${toString cfg.domain0MemorySize}M";
|
||||
|
||||
system.extraSystemBuilderCmds =
|
||||
''
|
||||
ln -s ${xen}/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.sysvtools}/bin/mountpoint -q /proc/xen || \
|
||||
${pkgs.utillinux}/bin/mount -t xenfs none /proc/xen
|
||||
fi
|
||||
'';
|
||||
|
||||
jobs.xend =
|
||||
{ description = "Xen control daemon";
|
||||
|
||||
startOn = "stopped udevtrigger";
|
||||
|
||||
path =
|
||||
[ pkgs.bridge_utils pkgs.gawk pkgs.iproute pkgs.nettools
|
||||
pkgs.utillinux pkgs.bash xen pkgs.pciutils pkgs.procps
|
||||
];
|
||||
|
||||
environment.XENCONSOLED_TRACE = "hv";
|
||||
|
||||
preStart =
|
||||
''
|
||||
mkdir -p /var/log/xen/console -m 0700
|
||||
|
||||
${xen}/sbin/xend start
|
||||
|
||||
# Wait until Xend is running.
|
||||
for ((i = 0; i < 60; i++)); do echo "waiting for xend..."; ${xen}/sbin/xend status && break; done
|
||||
|
||||
${xen}/sbin/xend status || exit 1
|
||||
'';
|
||||
|
||||
postStop = "${xen}/sbin/xend stop";
|
||||
};
|
||||
|
||||
jobs.xendomains =
|
||||
{ description = "Automatically starts, saves and restores Xen domains on startup/shutdown";
|
||||
|
||||
startOn = "started xend";
|
||||
|
||||
stopOn = "starting shutdown and stopping xend";
|
||||
|
||||
restartIfChanged = false;
|
||||
|
||||
path = [ pkgs.xen ];
|
||||
|
||||
environment.XENDOM_CONFIG = "${xen}/etc/sysconfig/xendomains";
|
||||
|
||||
preStart =
|
||||
''
|
||||
mkdir -p /var/lock/subsys -m 755
|
||||
${xen}/etc/init.d/xendomains start
|
||||
'';
|
||||
|
||||
postStop = "${xen}/etc/init.d/xendomains stop";
|
||||
};
|
||||
|
||||
# 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.
|
||||
jobs.dhcpcd.startOn = mkOverride 50 "started xend";
|
||||
|
||||
environment.etc =
|
||||
[ { source = xendConfig;
|
||||
target = "xen/xend-config.sxp";
|
||||
}
|
||||
{ source = "${xen}/etc/xen/scripts";
|
||||
target = "xen/scripts";
|
||||
}
|
||||
];
|
||||
|
||||
# Xen provides udev rules.
|
||||
services.udev.packages = [ xen ];
|
||||
|
||||
services.udev.path = [ pkgs.bridge_utils pkgs.iproute ];
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
# Common configuration for Xen DomU NixOS virtual machines.
|
||||
|
||||
{ config, pkgs, ... }:
|
||||
|
||||
{
|
||||
# We're being booted using pv-grub, which means that we need to
|
||||
# generate a GRUB 1 menu without actually installing GRUB.
|
||||
boot.loader.grub.version = 1;
|
||||
boot.loader.grub.device = "nodev";
|
||||
boot.loader.grub.extraPerEntryConfig = "root (hd0)";
|
||||
|
||||
boot.initrd.kernelModules = [ "xen-blkfront" ];
|
||||
|
||||
# Send syslog messages to the Xen console.
|
||||
services.syslogd.tty = "hvc0";
|
||||
|
||||
# Don't run ntpd, since we should get the correct time from Dom0.
|
||||
services.ntp.enable = false;
|
||||
}
|
||||
Reference in New Issue
Block a user