Merging from trunk

svn path=/nixos/branches/stdenv-updates/; revision=25176
This commit is contained in:
Lluís Batlle i Rossell 2010-12-17 14:59:04 +00:00
commit 3f7751b9c2
31 changed files with 911 additions and 166 deletions

View File

@ -0,0 +1,148 @@
<refentry xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude">
<refmeta>
<refentrytitle><command>nixos-build-vms</command></refentrytitle>
<manvolnum>8</manvolnum>
<refmiscinfo class="source">NixOS</refmiscinfo>
<!-- <refmiscinfo class="version"><xi:include href="version.txt" parse="text"/></refmiscinfo> -->
</refmeta>
<refnamediv>
<refname><command>nixos-build-vms</command></refname>
<refpurpose>build a network of virtual machines from a network of NixOS configurations</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>nixos-build-vms</command>
<arg><option>--use-backdoor</option></arg>
<arg><option>--show-trace</option></arg>
<arg><option>--no-out-link</option></arg>
<arg><option>--help</option></arg>
<arg choice="plain"><replaceable>network.nix</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsection><title>Description</title>
<para>This command builds a network of QEMU-KVM virtual machines of a Nix expression
specifying a network of NixOS machines. The virtual network can be started by
executing the <filename>bin/run-vms</filename> shell script that is generated by
this command. By default, a <filename>result</filename> symlink is produced that
points to the generated virtual network.
</para>
<para>This command also provides the <option>--use-backdoor</option> option,
which spawns UNIX domain sockets in the current working directory by using the
<command>socat</command> command. This allows
users to remotely script a generated virtual machine.</para>
<para>A network Nix expression has the following structure:
<screen>
{
test1 = {pkgs, config, ...}:
{
services.openssh.enable = true;
nixpkgs.system = "i686-linux";
deployment.targetHost = "test1.example.net";
# Other NixOS options
};
test2 = {pkgs, config, ...}:
{
services.openssh.enable = true;
services.httpd.enable = true;
environment.systemPackages = [ pkgs.lynx ];
nixpkgs.system = "x86_64-linux";
deployment.targetHost = "test2.example.net";
# Other NixOS options
};
}
</screen>
Each attribute in the expression represents a machine in the network
(e.g. <varname>test1</varname> and <varname>test2</varname>)
referring to a function defining a NixOS configuration.
In each NixOS configuration, two attributes have a special meaning.
The <varname>deployment.targetHost</varname> specifies the address
(domain name or IP address)
of the system which is used by <command>ssh</command> to perform
remote deployment operations. The <varname>nixpkgs.system</varname>
attribute can be used to specify an architecture for the target machine,
such as <varname>i686-linux</varname> which builds a 32-bit NixOS
configuration. Omitting this property will build the configuration
for the same architecture as the host system.
</para>
</refsection>
<refsection><title>Options</title>
<para>This command accepts the following options:</para>
<variablelist>
<varlistentry>
<term><option>--use-backdoor</option></term>
<listitem>
<para>Indicates that the backdoor must be enabled so that the VMs can be
accessed through a UNIX domain socket. The UNIX domain sockets are
created in the current working directory.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--show-trace</option></term>
<listitem>
<para>Shows a trace of the output.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--no-out-link</option></term>
<listitem>
<para>Do not create a 'result' symlink.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-h</option>, <option>--help</option></term>
<listitem>
<para>Shows the usage of this command to the user.</para>
</listitem>
</varlistentry>
</variablelist>
</refsection>
<refsection><title>Environment variables</title>
<variablelist>
<varlistentry>
<term><envar>NIXOS</envar></term>
<listitem>
<para>Path to the NixOS source tree. Defaults to
<filename>/etc/nixos/nixos</filename>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><envar>NIXPKGS_ALL</envar></term>
<listitem>
<para>Path to the Nixpkgs source tree. Defaults to
<filename>/etc/nixos/nixpkgs</filename>.</para>
</listitem>
</varlistentry>
</variablelist>
</refsection>
</refentry>

View File

@ -0,0 +1,135 @@
<refentry xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude">
<refmeta>
<refentrytitle><command>nixos-deploy-network</command></refentrytitle>
<manvolnum>8</manvolnum>
<refmiscinfo class="source">NixOS</refmiscinfo>
<!-- <refmiscinfo class="version"><xi:include href="version.txt" parse="text"/></refmiscinfo> -->
</refmeta>
<refnamediv>
<refname><command>nixos-deploy-network</command></refname>
<refpurpose>deploy a network of NixOS configurations into a network of machines</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>nixos-deploy-network</command>
<arg><option>--show-trace</option></arg>
<arg><option>--no-out-link</option></arg>
<arg><option>--help</option></arg>
<arg choice="plain"><replaceable>network.nix</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsection><title>Description</title>
<para>This command automatically deploys a network of NixOS
configurations into a network of machines.
First, it tries to build all the system derivations defined
in the network expression. Then it efficiently transfers
the closures to the machines in the network. Finally, the configurations
are activated. In case of a failure, a rollback is performed,
which brings all the updated configurations back into the previous
state.</para>
<para>A network Nix expression has the following structure:
<screen>
{
test1 = {pkgs, config, ...}:
{
services.openssh.enable = true;
nixpkgs.system = "i686-linux";
deployment.targetHost = "test1.example.net";
# Other NixOS options
};
test2 = {pkgs, config, ...}:
{
services.openssh.enable = true;
services.httpd.enable = true;
environment.systemPackages = [ pkgs.lynx ];
nixpkgs.system = "x86_64-linux";
deployment.targetHost = "test2.example.net";
# Other NixOS options
};
}
</screen>
Each attribute in the expression represents a machine in the network
(e.g. <varname>test1</varname> and <varname>test2</varname>)
referring to a function defining a NixOS configuration.
In each NixOS configuration, two attributes have a special meaning.
The <varname>deployment.targetHost</varname> specifies the address
(domain name or IP address)
of the system which is used by <command>ssh</command> to perform
remote deployment operations. The <varname>nixpkgs.system</varname>
attribute can be used to specify an architecture for the target machine,
such as <varname>i686-linux</varname> which builds a 32-bit NixOS
configuration. Omitting this property will build the configuration
for the same architecture as the host system.
</para>
</refsection>
<refsection><title>Options</title>
<para>This command accepts the following options:</para>
<variablelist>
<varlistentry>
<term><option>--show-trace</option></term>
<listitem>
<para>Shows a trace of the output.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--no-out-link</option></term>
<listitem>
<para>Do not create a 'result' symlink.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-h</option>, <option>--help</option></term>
<listitem>
<para>Shows the usage of this command to the user.</para>
</listitem>
</varlistentry>
</variablelist>
</refsection>
<refsection><title>Environment variables</title>
<variablelist>
<varlistentry>
<term><envar>NIXOS</envar></term>
<listitem>
<para>Path to the NixOS source tree. Defaults to
<filename>/etc/nixos/nixos</filename>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><envar>NIXPKGS_ALL</envar></term>
<listitem>
<para>Path to the Nixpkgs source tree. Defaults to
<filename>/etc/nixos/nixpkgs</filename>.</para>
</listitem>
</varlistentry>
</variablelist>
</refsection>
</refentry>

View File

@ -26,5 +26,6 @@
<xi:include href="man-configuration.xml" /> <xi:include href="man-configuration.xml" />
<xi:include href="man-nixos-rebuild.xml" /> <xi:include href="man-nixos-rebuild.xml" />
<xi:include href="man-nixos-option.xml" /> <xi:include href="man-nixos-option.xml" />
<xi:include href="man-nixos-deploy-network.xml" />
<xi:include href="man-nixos-build-vms.xml" />
</reference> </reference>

View File

@ -108,7 +108,7 @@ rec {
virtualisation.qemu.options = virtualisation.qemu.options =
lib.flip lib.concatMapStrings interfacesNumbered lib.flip lib.concatMapStrings interfacesNumbered
({ first, second }: qemuNICFlags second first ); ({ first, second }: qemuNICFlags second first m.second);
}; };
} }
) )

View File

@ -2,8 +2,8 @@
{ {
qemuNICFlags = nic: net: qemuNICFlags = nic: net: machine:
"-net nic,vlan=${toString nic},model=virtio " + "-net nic,vlan=${toString nic},macaddr=52:54:00:12:${toString net}:${toString machine},model=virtio " +
# Use 232.0.1.<vlan> as the multicast address to connect VMs on # Use 232.0.1.<vlan> as the multicast address to connect VMs on
# the same vlan, but allow it to be overriden using the # the same vlan, but allow it to be overriden using the
# $QEMU_MCAST_ADDR_<vlan> environment variable. The test driver # $QEMU_MCAST_ADDR_<vlan> environment variable. The test driver

View File

@ -19,11 +19,20 @@ for (my $n = 0; $n < 256; $n++) {
$ENV{"QEMU_MCAST_ADDR_$n"} = "$mcastPrefix.$n.$mcastSuffix"; $ENV{"QEMU_MCAST_ADDR_$n"} = "$mcastPrefix.$n.$mcastSuffix";
} }
my $showGraphics = defined $ENV{'DISPLAY'};
sub new { sub new {
my ($class, $args) = @_; my ($class, $args) = @_;
my $startCommand = $args->{startCommand}; my $startCommand = $args->{startCommand};
my $name = $args->{name};
if (!$name) {
$startCommand =~ /run-(.*)-vm$/;
$name = $1 || "machine";
}
if (!$startCommand) { if (!$startCommand) {
# !!! merge with qemu-vm.nix. # !!! merge with qemu-vm.nix.
$startCommand = $startCommand =
@ -34,12 +43,8 @@ sub new {
$startCommand .= "-cdrom $args->{cdrom} " $startCommand .= "-cdrom $args->{cdrom} "
if defined $args->{cdrom}; if defined $args->{cdrom};
$startCommand .= $args->{qemuFlags} || ""; $startCommand .= $args->{qemuFlags} || "";
} } else {
$startCommand = Cwd::abs_path $startCommand;
my $name = $args->{name};
if (!$name) {
$startCommand =~ /run-(.*)-vm$/;
$name = $1 || "machine";
} }
my $tmpDir = $ENV{'TMPDIR'} || "/tmp"; my $tmpDir = $ENV{'TMPDIR'} || "/tmp";
@ -51,7 +56,7 @@ sub new {
pid => 0, pid => 0,
connected => 0, connected => 0,
socket => undef, socket => undef,
stateDir => "$tmpDir/$name", stateDir => "$tmpDir/vm-state-$name",
monitor => undef, monitor => undef,
}; };
@ -121,12 +126,14 @@ sub start {
dup2(fileno($serialC), fileno(STDERR)); dup2(fileno($serialC), fileno(STDERR));
$ENV{TMPDIR} = $self->{stateDir}; $ENV{TMPDIR} = $self->{stateDir};
$ENV{USE_TMPDIR} = 1; $ENV{USE_TMPDIR} = 1;
$ENV{QEMU_OPTS} = "-nographic -no-reboot -monitor unix:./monitor -chardev socket,id=shell,path=./shell"; $ENV{QEMU_OPTS} =
"-no-reboot -monitor unix:./monitor -chardev socket,id=shell,path=./shell " .
($showGraphics ? "-serial stdio" : "-nographic");
$ENV{QEMU_NET_OPTS} = "guestfwd=tcp:10.0.2.6:23-chardev:shell"; $ENV{QEMU_NET_OPTS} = "guestfwd=tcp:10.0.2.6:23-chardev:shell";
$ENV{QEMU_KERNEL_PARAMS} = "hostTmpDir=$ENV{TMPDIR}"; $ENV{QEMU_KERNEL_PARAMS} = "hostTmpDir=$ENV{TMPDIR}";
chdir $self->{stateDir} or die; chdir $self->{stateDir} or die;
exec $self->{startCommand}; exec $self->{startCommand};
die; die "running VM script: $!";
} }
# Process serial line output. # Process serial line output.
@ -248,7 +255,8 @@ sub execute {
my $out = ""; my $out = "";
while (1) { while (1) {
my $line = readline($self->{socket}) or die "connection to VM lost unexpectedly"; my $line = readline($self->{socket});
die "connection to VM lost unexpectedly" unless defined $line;
#$self->log("got line: $line"); #$self->log("got line: $line");
if ($line =~ /^(.*)\|\!\=EOF\s+(\d+)$/) { if ($line =~ /^(.*)\|\!\=EOF\s+(\d+)$/) {
$out .= $1; $out .= $1;
@ -267,7 +275,7 @@ sub succeed {
my ($status, $out) = $self->execute($command); my ($status, $out) = $self->execute($command);
if ($status != 0) { if ($status != 0) {
$self->log("output: $out"); $self->log("output: $out");
die "command `$command' did not succeed (exit code $status)"; die "command `$command' did not succeed (exit code $status)\n";
} }
$res .= $out; $res .= $out;
} }
@ -404,7 +412,8 @@ sub unblock {
# Take a screenshot of the X server on :0.0. # Take a screenshot of the X server on :0.0.
sub screenshot { sub screenshot {
my ($self, $filename) = @_; my ($self, $filename) = @_;
$filename = "$ENV{'out'}/${filename}.png" if $filename =~ /^\w+$/; my $dir = $ENV{'out'} || Cwd::abs_path(".");
$filename = "$dir/${filename}.png" if $filename =~ /^\w+$/;
my $tmp = "${filename}.ppm"; my $tmp = "${filename}.ppm";
$self->sendMonitorCommand("screendump $tmp"); $self->sendMonitorCommand("screendump $tmp");
system("convert $tmp ${filename}") == 0 system("convert $tmp ${filename}") == 0

View File

@ -1,8 +1,13 @@
#! @perl@ -w -I@libDir@ -I@readline@
use strict; use strict;
use Machine; use Machine;
use Term::ReadLine;
$SIG{PIPE} = 'IGNORE'; # because Unix domain sockets may die unexpectedly $SIG{PIPE} = 'IGNORE'; # because Unix domain sockets may die unexpectedly
$ENV{PATH} = "@extraPath@:$ENV{PATH}";
STDERR->autoflush(1); STDERR->autoflush(1);
my %vms; my %vms;
@ -26,10 +31,13 @@ sub runTests {
eval "$context $ENV{tests}"; eval "$context $ENV{tests}";
die $@ if $@; die $@ if $@;
} else { } else {
while (<STDIN>) { my $term = Term::ReadLine->new('nixos-vm-test');
$term->ReadHistory;
while (defined ($_ = $term->readline("> "))) {
eval "$context $_\n"; eval "$context $_\n";
warn $@ if $@; warn $@ if $@;
} }
$term->WriteHistory;
} }
# Copy the kernel coverage data for each machine, if the kernel # Copy the kernel coverage data for each machine, if the kernel

View File

@ -8,6 +8,27 @@ rec {
inherit pkgs; inherit pkgs;
testDriver = stdenv.mkDerivation {
name = "nixos-test-driver";
buildCommand =
''
mkdir -p $out/bin
cp ${./test-driver/test-driver.pl} $out/bin/nixos-test-driver
chmod u+x $out/bin/nixos-test-driver
libDir=$out/lib/perl5/site_perl
mkdir -p $libDir
cp ${./test-driver/Machine.pm} $libDir/Machine.pm
substituteInPlace $out/bin/nixos-test-driver \
--subst-var-by perl "${perl}/bin/perl" \
--subst-var-by readline "${perlPackages.TermReadLineGnu}/lib/perl5/site_perl" \
--subst-var-by extraPath "${imagemagick}/bin" \
--subst-var libDir
'';
};
# Run an automated test suite in the given virtual network. # Run an automated test suite in the given virtual network.
# `network' must be the result of a call to the # `network' must be the result of a call to the
# `buildVirtualNetwork' function. `tests' is a Perl fragment # `buildVirtualNetwork' function. `tests' is a Perl fragment
@ -20,15 +41,13 @@ rec {
inherit tests; inherit tests;
buildInputs = [ pkgs.qemu_kvm pkgs.imagemagick ]; buildInputs = [ pkgs.qemu_kvm ];
buildCommand = buildCommand =
'' ''
mkdir $out
cp ${./test-driver/Machine.pm} Machine.pm
ensureDir $out/nix-support ensureDir $out/nix-support
${perl}/bin/perl ${./test-driver/test-driver.pl} ${network}/vms/*/bin/run-*-vm ${testDriver}/bin/nixos-test-driver ${network}/vms/*/bin/run-*-vm
for i in */coverage-data; do for i in */coverage-data; do
ensureDir $out/coverage-data ensureDir $out/coverage-data
@ -98,13 +117,38 @@ rec {
if t ? nodes then t.nodes else if t ? nodes then t.nodes else
if t ? machine then { machine = t.machine; } if t ? machine then { machine = t.machine; }
else { }; else { };
vms = buildVirtualNetwork { inherit nodes; }; vms = buildVirtualNetwork { inherit nodes; };
test = runTests vms
testScript =
# Call the test script with the computed nodes. # Call the test script with the computed nodes.
(if builtins.isFunction t.testScript then t.testScript { inherit (vms) nodes; } else t.testScript); if builtins.isFunction t.testScript
then t.testScript { inherit (vms) nodes; }
else t.testScript;
test = runTests vms testScript;
report = makeReport test; report = makeReport test;
# Generate a convenience wrapper for running the test driver
# interactively with the specified network.
driver = runCommand "nixos-test-driver"
{ buildInputs = [ makeWrapper];
inherit testScript;
}
''
mkdir -p $out/bin
ln -s ${vms}/bin/* $out/bin/
ln -s ${testDriver}/bin/* $out/bin/
wrapProgram $out/bin/nixos-test-driver \
--add-flags "${vms}/vms/*/bin/run-*-vm" \
--run "testScript=\"\$(cat $out/test-script)\"" \
--set testScript '"$testScript"'
echo "$testScript" > $out/test-script
''; # "
}; };
runInMachine = runInMachine =
{ drv { drv
, machine , machine
@ -140,7 +184,7 @@ rec {
export PATH=${qemu_kvm}/bin:${coreutils}/bin export PATH=${qemu_kvm}/bin:${coreutils}/bin
cp ${./test-driver/Machine.pm} Machine.pm cp ${./test-driver/Machine.pm} Machine.pm
export tests='${testscript}' export tests='${testscript}'
${perl}/bin/perl ${./test-driver/test-driver.pl} ${vms}/vms/*/bin/run-*-vm ${testDriver}/bin/nixos-test-driver ${vms}/vms/*/bin/run-*-vm
''; # */ ''; # */
in in
@ -152,6 +196,7 @@ rec {
origBuilder = attrs.builder; origBuilder = attrs.builder;
}); });
runInMachineWithX = { require ? [], ...}@args : runInMachineWithX = { require ? [], ...}@args :
let let
client = client =
@ -174,6 +219,7 @@ rec {
'' ; '' ;
} // args ); } // args );
simpleTest = as: (makeTest ({ ... }: as)).test; simpleTest = as: (makeTest ({ ... }: as)).test;
} }

View File

@ -10,10 +10,15 @@ let
'' ''
#! ${pkgs.stdenv.shell} #! ${pkgs.stdenv.shell}
action="$1" action="$1"
if [ "$action" = "resume" ]; then case "$action" in
${cfg.resumeCommands} hibernate|suspend)
${cfg.powerUpCommands} ${cfg.powerDownCommands}
fi ;;
thaw|resume)
${cfg.resumeCommands}
${cfg.powerUpCommands}
;;
esac
''; '';
in in
@ -50,6 +55,17 @@ in
it resumes from suspend or hibernation. it resumes from suspend or hibernation.
''; '';
}; };
powerDownCommands = mkOption {
default = "";
example = "${pkgs.hdparm}/sbin/hdparm -B 255 /dev/sda";
description =
''
Commands executed when the machine powers down. That is,
they're executed both when the system shuts down and when
it goes to suspend or hibernation.
'';
};
}; };

View File

@ -4,18 +4,18 @@
showUsage() showUsage()
{ {
echo "Usage: $0 -n network_expr -i infrastructure_expr" echo "Usage: $0 network_expr"
echo "Options:" echo "Options:"
echo echo
echo "-n,--network Network Nix expression which captures properties of machines in the network" echo "--use-backdoor Indicates that the backdoor must be enabled so that the VMs can be accessed through a UNIX domain socket"
echo "--use-backdoor Indicates that the backdoor must be enabled so that the VMs can be accessed through a UNIX domain socket" echo "--no-out-link Do not create a 'result' symlink"
echo "--show-trace Shows the output trace" echo "--show-trace Shows the output trace"
echo "-h,--help Shows the usage of this command" echo "-h,--help Shows the usage of this command"
} }
# Parse valid argument options # Parse valid argument options
PARAMS=`getopt -n $0 -o n:h -l network:,use-backdoor,show-trace,help -- "$@"` PARAMS=`getopt -n $0 -o h -l use-backdoor,no-out-link,show-trace,help -- "$@"`
if [ $? != 0 ] if [ $? != 0 ]
then then
@ -30,12 +30,12 @@ eval set -- "$PARAMS"
while [ "$1" != "--" ] while [ "$1" != "--" ]
do do
case "$1" in case "$1" in
-n|--network)
networkExpr=`readlink -f $2`
;;
--use-backdoor) --use-backdoor)
useBackdoorArg="--arg useBackdoor true" useBackdoorArg="--arg useBackdoor true"
;; ;;
--no-out-link)
noOutLinkArg="--no-out-link"
;;
--show-trace) --show-trace)
showTraceArg="--show-trace" showTraceArg="--show-trace"
;; ;;
@ -48,19 +48,23 @@ do
shift shift
done done
# Validate the given options shift
if [ "$networkExpr" = "" ] # Validate the given options
then
echo "ERROR: A network expression must be specified!" >&2
exit 1
fi
if [ -z "$NIXOS" ] if [ -z "$NIXOS" ]
then then
NIXOS=/etc/nixos/nixos NIXOS=/etc/nixos/nixos
fi fi
if [ "$@" = "" ]
then
echo "ERROR: A network expression must be specified!" >&2
exit 1
else
networkExpr=$(readlink -f $@)
fi
# Build a network of VMs # Build a network of VMs
nix-build $NIXOS/modules/installer/tools/nixos-build-vms/build-vms.nix --argstr networkExpr $networkExpr --argstr nixos $NIXOS --argstr nixpkgs $NIXPKGS_ALL $useBackdoorArg $showTraceArg nix-build $NIXOS/modules/installer/tools/nixos-build-vms/build-vms.nix --argstr networkExpr $networkExpr --argstr nixos $NIXOS --argstr nixpkgs $NIXPKGS_ALL $useBackdoorArg $noOutLinkArg $showTraceArg

View File

@ -1,8 +1,7 @@
{ nixos ? /etc/nixos/nixos { nixos ? /etc/nixos/nixos
, nixpkgs ? /etc/nixos/nixpkgs , nixpkgs ? /etc/nixos/nixpkgs
, networkExpr , networkExpr
, infrastructureExpr , targetProperty ? "targetHost"
, targetProperty ? "hostname"
}: }:
let let
@ -12,19 +11,17 @@ let
inherit (pkgs.lib) concatMapStrings; inherit (pkgs.lib) concatMapStrings;
network = import networkExpr; network = import networkExpr;
infrastructure = import infrastructureExpr;
generateRollbackSucceededPhase = network: infrastructure: configs: generateRollbackSucceededPhase = network: configs:
concatMapStrings (configurationName: concatMapStrings (configurationName:
let let
infrastructureElement = getAttr configurationName infrastructure;
config = getAttr configurationName configs; config = getAttr configurationName configs;
in in
'' ''
if [ "$rollback" != "$succeeded" ] if [ "$rollback" != "$succeeded" ]
then then
ssh $NIX_SSHOPTS ${getAttr targetProperty infrastructureElement} nix-env -p /nix/var/nix/profiles/system --rollback ssh $NIX_SSHOPTS ${getAttr targetProperty (config.deployment)} nix-env -p /nix/var/nix/profiles/system --rollback
ssh $NIX_SSHOPTS ${getAttr targetProperty infrastructureElement} /nix/var/nix/profiles/bin/switch-to-configuration switch ssh $NIX_SSHOPTS ${getAttr targetProperty (config.deployment)} /nix/var/nix/profiles/system/bin/switch-to-configuration switch
rollback=$((rollback + 1)) rollback=$((rollback + 1))
fi fi
@ -32,33 +29,31 @@ let
) (attrNames network) ) (attrNames network)
; ;
generateDistributionPhase = network: infrastructure: configs: generateDistributionPhase = network: configs:
concatMapStrings (configurationName: concatMapStrings (configurationName:
let let
infrastructureElement = getAttr configurationName infrastructure;
config = getAttr configurationName configs; config = getAttr configurationName configs;
in in
'' ''
echo "=== copy system closure to ${getAttr targetProperty infrastructureElement} ===" echo "=== copy system closure to ${getAttr targetProperty (config.deployment)} ==="
nix-copy-closure --to ${getAttr targetProperty infrastructureElement} ${config.system.build.toplevel} nix-copy-closure --to ${getAttr targetProperty (config.deployment)} ${config.system.build.toplevel}
'' ''
) (attrNames network) ) (attrNames network)
; ;
generateActivationPhase = network: infrastructure: configs: generateActivationPhase = network: configs:
concatMapStrings (configurationName: concatMapStrings (configurationName:
let let
infrastructureElement = getAttr configurationName infrastructure;
config = getAttr configurationName configs; config = getAttr configurationName configs;
in in
'' ''
echo "=== activating system configuration on ${getAttr targetProperty infrastructureElement} ===" echo "=== activating system configuration on ${getAttr targetProperty (config.deployment)} ==="
ssh $NIX_SSHOPTS ${getAttr targetProperty infrastructureElement} nix-env -p /nix/var/nix/profiles/system --set ${config.system.build.toplevel} || ssh $NIX_SSHOPTS ${getAttr targetProperty (config.deployment)} nix-env -p /nix/var/nix/profiles/system --set ${config.system.build.toplevel} ||
(ssh $NIX_SSHOPTS ${getAttr targetProperty infrastructureElement} nix-env -p /nix/var/nix/profiles/system --rollback; rollbackSucceeded) (ssh $NIX_SSHOPTS ${getAttr targetProperty (config.deployment)} nix-env -p /nix/var/nix/profiles/system --rollback; rollbackSucceeded)
ssh $NIX_SSHOPTS ${getAttr targetProperty infrastructureElement} /nix/var/nix/profiles/bin/switch-to-configuration switch || ssh $NIX_SSHOPTS ${getAttr targetProperty (config.deployment)} /nix/var/nix/profiles/system/bin/switch-to-configuration switch ||
( ssh $NIX_SSHOPTS ${getAttr targetProperty infrastructureElement} nix-env -p /nix/var/nix/profiles/system --rollback ( ssh $NIX_SSHOPTS ${getAttr targetProperty (config.deployment)} nix-env -p /nix/var/nix/profiles/system --rollback
ssh $NIX_SSHOPTS ${getAttr targetProperty infrastructureElement} /nix/var/nix/profiles/bin/switch-to-configuration switch ssh $NIX_SSHOPTS ${getAttr targetProperty (config.deployment)} /nix/var/nix/profiles/system/bin/switch-to-configuration switch
rollbackSucceeded rollbackSucceeded
) )
@ -67,25 +62,36 @@ let
) (attrNames network) ) (attrNames network)
; ;
evaluateMachines = network: infrastructure: evaluateMachines = network:
listToAttrs (map (configurationName: listToAttrs (map (configurationName:
let let
configuration = getAttr configurationName network; configuration = getAttr configurationName network;
system = (getAttr configurationName infrastructure).system;
in in
{ name = configurationName; { name = configurationName;
value = (import "${nixos}/lib/eval-config.nix" { value = (import "${nixos}/lib/eval-config.nix" {
inherit nixpkgs system; inherit nixpkgs;
modules = [ configuration ]; modules =
extraArgs = evaluateMachines network infrastructure; [ configuration
# Provide a default hostname and deployment target equal
# to the attribute name of the machine in the model.
{ key = "set-default-hostname";
networking.hostName = pkgs.lib.mkOverride 900 configurationName;
deployment.targetHost = pkgs.lib.mkOverride 900 configurationName;
}
];
extraArgs = evaluateMachines network;
}).config; } }).config; }
) (attrNames (network))) ) (attrNames (network)));
;
configs = evaluateMachines network infrastructure; configs = evaluateMachines network;
in in
pkgs.stdenv.mkDerivation { pkgs.stdenv.mkDerivation {
name = "deploy-script"; name = "deploy-script";
# This script has a zillion dependencies and is trivial to build, so
# we don't want to build it remotely.
preferLocalBuild = true;
buildCommand = buildCommand =
'' ''
ensureDir $out/bin ensureDir $out/bin
@ -95,18 +101,18 @@ pkgs.stdenv.mkDerivation {
rollbackSucceeded() rollbackSucceeded()
{ {
rollback=0 rollback=0
${generateRollbackSucceededPhase network infrastructure configs} ${generateRollbackSucceededPhase network configs}
} }
# Distribution phase # Distribution phase
${generateDistributionPhase network infrastructure configs} ${generateDistributionPhase network configs}
# Activation phase # Activation phase
succeeded=0 succeeded=0
${generateActivationPhase network infrastructure configs} ${generateActivationPhase network configs}
EOF EOF
chmod +x $out/bin/deploy-systems chmod +x $out/bin/deploy-systems
''; '';

View File

@ -4,18 +4,17 @@
showUsage() showUsage()
{ {
echo "Usage: $0 -n network_expr -i infrastructure_expr" echo "Usage: $0 network_expr"
echo "Options:" echo "Options:"
echo echo
echo "-n,--network Network Nix expression which captures properties of machines in the network" echo "--show-trace Shows an output trace"
echo "-i,--infrastructure Infrastructure Nix expression which captures properties of machines in the network" echo "--no-out-link Do not create a 'result' symlink"
echo "--show-trace Shows an output trace" echo "-h,--help Shows the usage of this command"
echo "-h,--help Shows the usage of this command"
} }
# Parse valid argument options # Parse valid argument options
PARAMS=`getopt -n $0 -o n:i:h -l network:,infrastructure:,show-trace,help -- "$@"` PARAMS=`getopt -n $0 -o h -l show-trace,no-out-link,help -- "$@"`
if [ $? != 0 ] if [ $? != 0 ]
then then
@ -30,15 +29,12 @@ eval set -- "$PARAMS"
while [ "$1" != "--" ] while [ "$1" != "--" ]
do do
case "$1" in case "$1" in
-n|--network)
networkExpr=`readlink -f $2`
;;
-i|--infrastructure)
infrastructureExpr=`readlink -f $2`
;;
--show-trace) --show-trace)
showTraceArg="--show-trace" showTraceArg="--show-trace"
;; ;;
--no-out-link)
noOutLinkArg="--no-out-link"
;;
-h|--help) -h|--help)
showUsage showUsage
exit 0 exit 0
@ -48,27 +44,24 @@ do
shift shift
done done
shift
# Validate the given options # Validate the given options
if [ "$infrastructureExpr" = "" ]
then
echo "ERROR: A infrastructure expression must be specified!" >&2
exit 1
fi
if [ "$networkExpr" = "" ]
then
echo "ERROR: A network expression must be specified!" >&2
exit 1
fi
if [ -z "$NIXOS" ] if [ -z "$NIXOS" ]
then then
NIXOS=/etc/nixos/nixos NIXOS=/etc/nixos/nixos
fi fi
if [ "$@" = "" ]
then
echo "ERROR: A network Nix expression must be specified!" >&2
exit 1
else
networkExpr=$(readlink -f $@)
fi
# Deploy the network # Deploy the network
nix-build $NIXOS/modules/installer/tools/nixos-deploy-network/deploy.nix --argstr networkExpr $networkExpr --argstr infrastructureExpr $infrastructureExpr $showTraceArg vms=`nix-build $NIXOS/modules/installer/tools/nixos-deploy-network/deploy.nix --argstr networkExpr $networkExpr $showTraceArg $noOutLinkArg`
./result/bin/deploy-systems $vms/bin/deploy-systems
rm -f result

View File

@ -7,6 +7,7 @@ use File::Basename;
my @attrs = (); my @attrs = ();
my @kernelModules = (); my @kernelModules = ();
my @initrdKernelModules = (); my @initrdKernelModules = ();
my @modulePackages = ();
sub debug { sub debug {
@ -93,6 +94,19 @@ sub pciCheck {
} }
} }
# broadcom STA driver (wl.ko)
# list taken from http://www.broadcom.com/docs/linux_sta/README.txt
if ($vendor eq "0x14e4" &&
($device eq "0x4311" || $device eq "0x4312" || $device eq "0x4313" ||
$device eq "0x4315" || $device eq "0x4327" || $device eq "0x4328" ||
$device eq "0x4329" || $device eq "0x432a" || $device eq "0x432b" ||
$device eq "0x432c" || $device eq "0x432d" || $device eq "0x4353" ||
$device eq "0x4357") )
{
push @modulePackages, "kernelPackages.broadcom_sta";
push @kernelModules, "wl";
}
# Can't rely on $module here, since the module may not be loaded # Can't rely on $module here, since the module may not be loaded
# due to missing firmware. Ideally we would check modules.pcimap # due to missing firmware. Ideally we would check modules.pcimap
# here. # here.
@ -222,6 +236,7 @@ sub multiLineList {
my $initrdKernelModules = toNixExpr(removeDups @initrdKernelModules); my $initrdKernelModules = toNixExpr(removeDups @initrdKernelModules);
my $kernelModules = toNixExpr(removeDups @kernelModules); my $kernelModules = toNixExpr(removeDups @kernelModules);
my $modulePackages = toNixExpr(removeDups @modulePackages);
my $attrs = multiLineList(" ", removeDups @attrs); my $attrs = multiLineList(" ", removeDups @attrs);
print <<EOF ; print <<EOF ;
@ -235,8 +250,12 @@ print <<EOF ;
"\${modulesPath}/installer/scan/not-detected.nix" "\${modulesPath}/installer/scan/not-detected.nix"
]; ];
boot.initrd.kernelModules = [ $initrdKernelModules ]; boot = rec {
boot.kernelModules = [ $kernelModules ]; initrd.kernelModules = [ $initrdKernelModules ];
kernelModules = [ $kernelModules ];
kernelPackages = pkgs.linuxPackages;
extraModulePackages = [ $modulePackages ];
};
nix.maxJobs = $cpus; nix.maxJobs = $cpus;

View File

@ -4,20 +4,12 @@ with pkgs.lib;
{ {
options = { options = {
deployment.targetHost = mkOption {
deployment = mkOption { default = config.networking.hostName;
description = '' description = ''
This option captures various custom attributes related to the configuration of the system, which This option specifies a hostname or IP address which can be used by nixos-deploy-network
are not directly used for building a system configuration. Usually these attributes to execute remote deployment operations.
are used by external tooling, such as the nixos-deploy-network tool or the Disnix Avahi
publisher.
''; '';
default = {};
example = {
description = "My production machine";
hostname = "my.test.org";
country = "NL";
};
}; };
}; };
} }

View File

@ -58,6 +58,8 @@ in
foldingAtHome = 37; foldingAtHome = 37;
sabnzbd = 38; sabnzbd = 38;
kdm = 39; kdm = 39;
ghostOne = 40;
git = 41;
# When adding a uid, make sure it doesn't match an existing gid. # When adding a uid, make sure it doesn't match an existing gid.
nixbld = 30000; # start of range of uids nixbld = 30000; # start of range of uids
@ -98,6 +100,8 @@ in
privoxy = 32; privoxy = 32;
disnix = 33; disnix = 33;
osgi = 34; osgi = 34;
ghostOne = 40;
git = 41;
# When adding a gid, make sure it doesn't match an existing uid. # When adding a gid, make sure it doesn't match an existing uid.
users = 100; users = 100;

View File

@ -52,6 +52,7 @@
./services/backup/sitecopy-backup.nix ./services/backup/sitecopy-backup.nix
./services/databases/mysql.nix ./services/databases/mysql.nix
./services/databases/postgresql.nix ./services/databases/postgresql.nix
./services/games/ghost-one.nix
./services/hardware/acpid.nix ./services/hardware/acpid.nix
./services/hardware/bluetooth.nix ./services/hardware/bluetooth.nix
./services/hardware/hal.nix ./services/hardware/hal.nix
@ -91,6 +92,7 @@
./services/networking/dhcpd.nix ./services/networking/dhcpd.nix
./services/networking/ejabberd.nix ./services/networking/ejabberd.nix
./services/networking/firewall.nix ./services/networking/firewall.nix
./services/networking/git-daemon.nix
./services/networking/gnunet.nix ./services/networking/gnunet.nix
./services/networking/gvpe.nix ./services/networking/gvpe.nix
./services/networking/gw6c.nix ./services/networking/gw6c.nix
@ -107,6 +109,7 @@
./services/networking/ssh/sshd.nix ./services/networking/ssh/sshd.nix
./services/networking/tftpd.nix ./services/networking/tftpd.nix
./services/networking/vsftpd.nix ./services/networking/vsftpd.nix
./services/networking/wakeonlan.nix
./services/networking/wicd.nix ./services/networking/wicd.nix
./services/networking/wpa_supplicant.nix ./services/networking/wpa_supplicant.nix
./services/networking/xinetd.nix ./services/networking/xinetd.nix

View File

@ -79,7 +79,7 @@ in zipModules ([]
# OpenSSH # OpenSSH
++ rename obsolete "services.sshd.ports" "services.openssh.ports" ++ rename obsolete "services.sshd.ports" "services.openssh.ports"
++ rename obsolete "services.sshd.enable" "services.openssh.enable" ++ rename alias "services.sshd.enable" "services.openssh.enable"
++ rename obsolete "services.sshd.allowSFTP" "services.openssh.allowSFTP" ++ rename obsolete "services.sshd.allowSFTP" "services.openssh.allowSFTP"
++ rename obsolete "services.sshd.forwardX11" "services.openssh.forwardX11" ++ rename obsolete "services.sshd.forwardX11" "services.openssh.forwardX11"
++ rename obsolete "services.sshd.gatewayPorts" "services.openssh.gatewayPorts" ++ rename obsolete "services.sshd.gatewayPorts" "services.openssh.gatewayPorts"

View File

@ -66,7 +66,7 @@ in
services.cron.systemCronJobs = map sitecopyCron config.services.sitecopy.backups; services.cron.systemCronJobs = map sitecopyCron config.services.sitecopy.backups;
system.activationScripts.sitecopyBackup = stringAfter [ "stdio" "systemConfig" "users" ] system.activationScripts.sitecopyBackup = stringAfter [ "stdio" "users" ]
'' ''
mkdir -m 0700 -p ${stateDir} mkdir -m 0700 -p ${stateDir}
chown root ${stateDir} chown root ${stateDir}

View File

@ -0,0 +1,105 @@
{pkgs, config, ...}:
with pkgs.lib;
let
cfg = config.services.ghostOne;
ghostUser = "ghostone";
stateDir = "/var/lib/ghost-one";
in
{
###### interface
options = {
services.ghostOne = {
enable = mkOption {
default = false;
description = "Enable Ghost-One Warcraft3 game hosting server.";
};
language = mkOption {
default = "English";
check = lang: elem lang [ "English" "Spanish" "Russian" "Serbian" "Turkish" ];
description = "The language of bot messages: English, Spanish, Russian, Serbian or Turkish.";
};
war3path = mkOption {
default = "";
description = ''
The path to your local Warcraft III directory, which must contain war3.exe, storm.dll, and game.dll.
'';
};
mappath = mkOption {
default = "";
description = ''
The path to the directory where you keep your map files. GHost One doesn't require
map files but if it has access to them it can send them to players and automatically
calculate most map config values. GHost One will search [bot_mappath + map_localpath]
for the map file (map_localpath is set in each map's config file).
'';
};
config = mkOption {
default = "";
description = "Extra configuration options.";
};
};
};
###### implementation
config = mkIf cfg.enable {
users.extraUsers = singleton
{ name = ghostUser;
uid = config.ids.uids.ghostOne;
description = "Ghost One game server user";
home = stateDir;
};
users.extraGroups = singleton
{ name = ghostUser;
gid = config.ids.gids.ghostOne;
};
services.ghostOne.config = ''
# bot_log = /dev/stderr
bot_language = ${pkgs.ghostOne}/share/ghost-one/languages/${cfg.language}.cfg
bot_war3path = ${cfg.war3path}
bot_mapcfgpath = mapcfgs
bot_savegamepath = savegames
bot_mappath = ${cfg.mappath}
bot_replaypath = replays
'';
jobs.ghostOne = {
name = "ghost-one";
script = ''
mkdir -p ${stateDir}
cd ${stateDir}
chown ${ghostUser}:${ghostUser} .
mkdir -p mapcfgs
chown ${ghostUser}:${ghostUser} mapcfgs
mkdir -p replays
chown ${ghostUser}:${ghostUser} replays
mkdir -p savegames
chown ${ghostUser}:${ghostUser} savegames
ln -sf ${pkgs.writeText "ghost.cfg" cfg.config} ghost.cfg
ln -sf ${pkgs.ghostOne}/share/ghost-one/ip-to-country.csv
${pkgs.su}/bin/su -s ${pkgs.stdenv.shell} ${ghostUser} \
-c "LANG=C ${pkgs.ghostOne}/bin/ghost++"
'';
};
};
}

View File

@ -34,6 +34,23 @@ in
description = "Whether to enable the DisnixWebService interface running on Apache Tomcat"; description = "Whether to enable the DisnixWebService interface running on Apache Tomcat";
}; };
publishInfrastructure = {
enable = mkOption {
default = false;
description = "Whether to publish capabilities/properties of this machine in as attributes in the infrastructure option";
};
enableAuthentication = mkOption {
default = false;
description = "Whether to publish authentication credentials through the infrastructure attribute (not recommended in combination with Avahi)";
};
};
infrastructure = mkOption {
default = {};
description = "List of name value pairs containing properties for the infrastructure model";
};
publishAvahi = mkOption { publishAvahi = mkOption {
default = false; default = false;
description = "Whether to publish capabilities/properties as a Disnix service through Avahi"; description = "Whether to publish capabilities/properties as a Disnix service through Avahi";
@ -47,62 +64,79 @@ in
###### implementation ###### implementation
config = mkIf cfg.enable { config = mkIf cfg.enable {
environment.systemPackages = [ pkgs.disnix ] ++ optional cfg.useWebServiceInterface pkgs.DisnixWebService; environment.systemPackages = [ pkgs.disnix ] ++ optional cfg.useWebServiceInterface pkgs.DisnixWebService;
services.dbus.enable = true; services.dbus.enable = true;
services.dbus.packages = [ pkgs.disnix ]; services.dbus.packages = [ pkgs.disnix ];
services.avahi.enable = cfg.publishAvahi;
services.tomcat.enable = cfg.useWebServiceInterface; services.tomcat.enable = cfg.useWebServiceInterface;
services.tomcat.extraGroups = [ "disnix" ]; services.tomcat.extraGroups = [ "disnix" ];
services.tomcat.javaOpts = "${optionalString cfg.useWebServiceInterface "-Djava.library.path=${pkgs.libmatthew_java}/lib/jni"} "; services.tomcat.javaOpts = "${optionalString cfg.useWebServiceInterface "-Djava.library.path=${pkgs.libmatthew_java}/lib/jni"} ";
services.tomcat.sharedLibs = [] services.tomcat.sharedLibs = optional cfg.useWebServiceInterface "${pkgs.DisnixWebService}/share/java/DisnixConnection.jar"
++ optional cfg.useWebServiceInterface "${pkgs.DisnixWebService}/share/java/DisnixConnection.jar"
++ optional cfg.useWebServiceInterface "${pkgs.dbus_java}/share/java/dbus.jar"; ++ optional cfg.useWebServiceInterface "${pkgs.dbus_java}/share/java/dbus.jar";
services.tomcat.webapps = [] ++ optional cfg.useWebServiceInterface pkgs.DisnixWebService; services.tomcat.webapps = optional cfg.useWebServiceInterface pkgs.DisnixWebService;
users.extraGroups = singleton users.extraGroups = singleton
{ name = "disnix"; { name = "disnix";
gid = config.ids.gids.disnix; gid = config.ids.gids.disnix;
}; };
jobs.disnix = services.disnix.infrastructure =
{ description = "Disnix server"; optionalAttrs (cfg.publishInfrastructure.enable)
( { hostname = config.networking.hostName;
targetHost = config.deployment.targetHost;
system = if config.nixpkgs.system == "" then builtins.currentSystem else config.nixpkgs.system;
}
// optionalAttrs (cfg.useWebServiceInterface) { targetEPR = "http://${config.deployment.targetHost}:8080/DisnixWebService/services/DisnixWebService"; }
// optionalAttrs (config.services.httpd.enable) { documentRoot = config.services.httpd.documentRoot; }
// optionalAttrs (config.services.mysql.enable) { mysqlPort = config.services.mysql.port; }
// optionalAttrs (config.services.tomcat.enable) { tomcatPort = 8080; }
// optionalAttrs (cfg.publishInfrastructure.enableAuthentication) (
optionalAttrs (config.services.mysql.enable) { mysqlUsername = "root"; mysqlPassword = builtins.readFile config.services.mysql.rootPassword; })
)
;
services.disnix.publishInfrastructure.enable = cfg.publishAvahi;
jobs = {
disnix =
{ description = "Disnix server";
startOn = "started dbus"; startOn = "started dbus";
script = script =
'' ''
export PATH=/var/run/current-system/sw/bin:/var/run/current-system/sw/sbin export PATH=/var/run/current-system/sw/bin:/var/run/current-system/sw/sbin
export HOME=/root export HOME=/root
${pkgs.disnix}/bin/disnix-service --activation-modules-dir=${disnix_activation_scripts}/libexec/disnix/activation-scripts ${pkgs.disnix}/bin/disnix-service --activation-modules-dir=${disnix_activation_scripts}/libexec/disnix/activation-scripts
''; '';
}; };
} // optionalAttrs cfg.publishAvahi {
} // disnixAvahi =
mkIf cfg.publishAvahi { { description = "Disnix Avahi publisher";
services.avahi.enable = true;
jobs.disnixAvahi =
{ description = "Disnix Avahi publisher";
startOn = "started avahi-daemon"; startOn = "started avahi-daemon";
exec = exec =
'' ''
${pkgs.avahi}/bin/avahi-publish-service disnix-$(${pkgs.nettools}/bin/hostname) _disnix._tcp 22 \ ${pkgs.avahi}/bin/avahi-publish-service disnix-${config.networking.hostName} _disnix._tcp 22 \
"hostname=\"$(${pkgs.nettools}/bin/hostname)\"" \ "mem=$(grep 'MemTotal:' /proc/meminfo | sed -e 's/kB//' -e 's/MemTotal://' -e 's/ //g')" \
"system=\"$(uname -m)-linux\"" \ "supportedTypes=[$(for i in ${disnix_activation_scripts}/libexec/disnix/activation-scripts/*; do echo -n " \"$(basename $i)\""; done) ]" \
"mem=$(grep 'MemTotal:' /proc/meminfo | sed -e 's/kB//' -e 's/MemTotal://' -e 's/ //g')" \ ${concatMapStrings (infrastructureAttrName:
${optionalString (cfg.useWebServiceInterface) ''"targetEPR=\"http://(${pkgs.nettools}/bin/hostname):8080/DisnixWebService/services/DisnixWebService\""''} \ let infrastructureAttrValue = getAttr infrastructureAttrName (cfg.infrastructure);
${optionalString (config.services.httpd.enable) ''"documentRoot=\"${config.services.httpd.documentRoot}\""''} \ in
${optionalString (config.services.mysql.enable) ''"mysqlPort=3306"''} \ if builtins.isInt infrastructureAttrValue then
${optionalString (config.services.tomcat.enable) ''"tomcatPort=8080"''} \ ''${infrastructureAttrName}=${infrastructureAttrValue} \
"supportedTypes=[$(for i in ${disnix_activation_scripts}/libexec/disnix/activation-scripts/*; do echo -n " \"$(basename $i)\""; done) ]" \ ''
${concatMapStrings (deploymentAttrName: let deploymentAttrValue = getAttr deploymentAttrName (config.deployment); in ''${deploymentAttrName}=\"${deploymentAttrValue}\" '' ) (attrNames (config.deployment))} else
''; ''${infrastructureAttrName}=\"${infrastructureAttrValue}\" \
}; ''
) (attrNames (cfg.infrastructure))}
'';
};
};
}; };
} }

View File

@ -5,6 +5,8 @@ with pkgs.lib;
let let
cfg = config.services.zabbixServer;
stateDir = "/var/run/zabbix"; stateDir = "/var/run/zabbix";
logDir = "/var/log/zabbix"; logDir = "/var/log/zabbix";
@ -19,9 +21,15 @@ let
PidFile = ${pidFile} PidFile = ${pidFile}
DBHost = ${cfg.dbServer}
DBName = zabbix DBName = zabbix
DBUser = zabbix DBUser = zabbix
${optionalString (cfg.dbPassword != "") ''
DBPassword = ${cfg.dbPassword}
''}
''; '';
in in
@ -39,11 +47,21 @@ in
''; '';
}; };
services.zabbixServer.dbServer = mkOption {
default = "localhost";
description = "Hostname or IP address of the database server.";
};
services.zabbixServer.dbPassword = mkOption {
default = "";
description = "Password used to connect to the database server.";
};
}; };
###### implementation ###### implementation
config = mkIf config.services.zabbixServer.enable { config = mkIf cfg.enable {
services.postgresql.enable = true; services.postgresql.enable = true;

View File

@ -80,10 +80,10 @@ in
# Only run dhclient on interfaces of type ARPHRD_ETHER # Only run dhclient on interfaces of type ARPHRD_ETHER
# (1), i.e. Ethernet. Ignore peth* devices; on Xen, # (1), i.e. Ethernet. Ignore peth* devices; on Xen,
# they're renamed physical Ethernet cards used for # they're renamed physical Ethernet cards used for
# bridging. # bridging. Likewise for vif* and tap*.
if [ "$(cat /sys/class/net/$i/type)" = 1 ]; then if [ "$(cat /sys/class/net/$i/type)" = 1 ]; then
if ! for j in ${toString ignoredInterfaces}; do echo $j; done | grep -F -x -q "$i" && if ! for j in ${toString ignoredInterfaces}; do echo $j; done | grep -F -x -q "$i" &&
! echo "$i" | grep -x -q "peth.*"; ! echo "$i" | grep -x -q "peth.*\|vif.*\|tap.*";
then then
echo "Running dhclient on $i" echo "Running dhclient on $i"
interfaces="$interfaces $i" interfaces="$interfaces $i"

View File

@ -0,0 +1,112 @@
{pkgs, config, ...}:
with pkgs.lib;
let
cfg = config.services.gitDaemon;
gitUser = "git";
in
{
###### interface
options = {
services.gitDaemon = {
enable = mkOption {
default = false;
description = ''
Enable Git daemon, which allows public hosting of git repositories
without any access controls. This is mostly intended for read-only access.
You can allow write access by setting daemon.receivepack configuration
item of the repository to true. This is solely meant for a closed LAN setting
where everybody is friendly.
If you need any access controls, use something else.
'';
};
basePath = mkOption {
default = "";
example = "/srv/git/";
description = ''
Remap all the path requests as relative to the given path. For example,
if you set base-path to /srv/git, then if you later try to pull
git://example.com/hello.git, Git daemon will interpret the path as /srv/git/hello.git.
'';
};
exportAll = mkOption {
default = false;
description = ''
Publish all directories that look like Git repositories (have the objects
and refs subdirectories), even if they do not have the git-daemon-export-ok file.
If disabled, you need to touch .git/git-daemon-export-ok in each repository
you want the daemon to publish.
Warning: enabling this without a repository whitelist or basePath
publishes every git repository you have.
'';
};
repositories = mkOption {
default = [];
example = [ "/srv/git" "/home/user/git/repo2" ];
description = ''
A whitelist of paths of git repositories, or directories containing repositories
all of which would be published. Paths must not end in "/".
Warning: leaving this empty and enabling exportAll publishes all
repositories in your filesystem or basePath if specified.
'';
};
listenAddress = mkOption {
default = "";
example = "example.com";
description = "Listen on a specific IP address or hostname.";
};
port = mkOption {
default = 9418;
description = "Port to listen on.";
};
options = mkOption {
default = "";
description = "Extra configuration options to be passed to Git daemon.";
};
};
};
###### implementation
config = mkIf cfg.enable {
users.extraUsers = singleton
{ name = gitUser;
uid = config.ids.uids.git;
description = "Git daemon user";
};
users.extraGroups = singleton
{ name = gitUser;
gid = config.ids.gids.git;
};
jobs.gitDaemon = {
name = "git-daemon";
startOn = "ip-up";
exec = "${pkgs.git}/bin/git daemon --reuseaddr "
+ (optionalString (cfg.basePath != "") "--basepath=${cfg.basePath} ")
+ (optionalString (cfg.listenAddress != "") "--listen=${cfg.listenAddress} ")
+ "--port=${toString cfg.port} --user=${gitUser} --group=${gitUser} ${cfg.options} "
+ "--verbose " + (optionalString cfg.exportAll "--export-all") + concatStringsSep " " cfg.repositories;
};
};
}

View File

@ -0,0 +1,56 @@
{ config, pkgs, ... }:
with pkgs.lib;
let
interfaces = config.services.wakeonlan.interfaces;
ethtool = "${pkgs.ethtool}/sbin/ethtool";
passwordParameter = password : if (password == "") then "" else
"sopass ${password}";
methodParameter = {method, password} :
if method == "magicpacket" then "wol g"
else if method == "password" then "wol s so ${passwordParameter password}"
else throw "Wake-On-Lan method not supported";
line = { interface, method ? "magicpacket", password ? "" }: ''
${ethtool} -s ${interface} ${methodParameter {inherit method password;}}
'';
concatStrings = fold (x: y: x + y) "";
lines = concatStrings (map (l: line l) interfaces);
in
{
###### interface
options = {
services.wakeonlan.interfaces = mkOption {
default = [ ];
example = [
{
interface = "eth0";
method = "password";
password = "00:11:22:33:44:55";
}
];
description = ''
Interfaces where to enable Wake-On-LAN, and how. Two methods available:
"magickey" and "password". The password has the shape of six bytes
in hexadecimal separated by a colon each. For more information,
check the ethtool manual.
'';
};
};
###### implementation
config.powerManagement.powerDownCommands = lines;
}

View File

@ -16,16 +16,16 @@ let
name = "additional-cups-backends"; name = "additional-cups-backends";
builder = pkgs.writeScript "additional-backends-builder.sh" '' builder = pkgs.writeScript "additional-backends-builder.sh" ''
PATH=${pkgs.coreutils}/bin PATH=${pkgs.coreutils}/bin
mkdir -p $out mkdir -pv $out
if [ ! -e ${pkgs.samba}/lib/cups/backend/smb ]; then if [ ! -e ${pkgs.samba}/lib/cups/backend/smb ]; then
mkdir -p $out/lib/cups/backend mkdir -pv $out/lib/cups/backend
ln -s ${pkgs.samba}/bin/smbspool $out/lib/cups/backend/smb ln -sv ${pkgs.samba}/bin/smbspool $out/lib/cups/backend/smb
fi fi
# Provide support for printing via HTTPS. # Provide support for printing via HTTPS.
if [ ! -e ${pkgs.cups}/lib/cups/backend/https ]; then if [ ! -e ${pkgs.cups}/lib/cups/backend/https ]; then
mkdir -p $out/lib/cups/backend mkdir -pv $out/lib/cups/backend
ln -s ${pkgs.cups}/lib/cups/backend/ipp $out/lib/cups/backend/https ln -sv ${pkgs.cups}/lib/cups/backend/ipp $out/lib/cups/backend/https
fi fi
''; '';
}; };
@ -147,7 +147,7 @@ in
exec = "${cups}/sbin/cupsd -c ${pkgs.writeText "cupsd.conf" cfg.cupsdConf} -F"; exec = "${cups}/sbin/cupsd -c ${pkgs.writeText "cupsd.conf" cfg.cupsdConf} -F";
}; };
services.printing.drivers = [ pkgs.cups pkgs.ghostscript additionalBackends ]; services.printing.drivers = [ pkgs.cups pkgs.cups_pdf_filter pkgs.ghostscript additionalBackends ];
services.printing.cupsdConf = services.printing.cupsdConf =
'' ''
LogLevel info LogLevel info

View File

@ -57,17 +57,22 @@ let
$wgArticlePath = "${config.articleUrlPrefix}/$1"; $wgArticlePath = "${config.articleUrlPrefix}/$1";
''} ''}
${optionalString config.enableUploads ''
$wgEnableUploads = true;
$wgUploadDirectory = "${config.uploadDir}";
''}
${config.extraConfig} ${config.extraConfig}
?> ?>
''; '';
# Unpack Mediawiki and put the config file in its root directory. # Unpack Mediawiki and put the config file in its root directory.
mediawikiRoot = pkgs.stdenv.mkDerivation rec { mediawikiRoot = pkgs.stdenv.mkDerivation rec {
name= "mediawiki-1.15.4"; name= "mediawiki-1.15.5";
src = pkgs.fetchurl { src = pkgs.fetchurl {
url = "http://download.wikimedia.org/mediawiki/1.15/${name}.tar.gz"; url = "http://download.wikimedia.org/mediawiki/1.15/${name}.tar.gz";
sha256 = "1blf79lhnaxixc8z96f9z4xi2jlg906ps3kd4x8b9ipg2dgl3vy9"; sha256 = "1d8afbdh3lsg54b69mnh6a47psb3lg978xpp277qs08yz15cjf7q";
}; };
buildPhase = "true"; buildPhase = "true";
@ -96,6 +101,16 @@ in
extraConfig = extraConfig =
'' ''
${optionalString config.enableUploads ''
Alias ${config.urlPrefix}/images ${config.uploadDir}
<Directory ${config.uploadDir}>
Order allow,deny
Allow from all
Options -Indexes
</Directory>
''}
Alias ${config.urlPrefix} ${mediawikiRoot} Alias ${config.urlPrefix} ${mediawikiRoot}
<Directory ${mediawikiRoot}> <Directory ${mediawikiRoot}>
@ -205,6 +220,17 @@ in
''; '';
}; };
enableUploads = mkOption {
default = false;
description = "Whether to enable file uploads.";
};
uploadDir = mkOption {
default = throw "You must specify `uploadDir'.";
example = "/data/mediawiki-upload";
description = "The directory that stores uploaded files.";
};
extraConfig = mkOption { extraConfig = mkOption {
default = ""; default = "";
example = example =

View File

@ -29,9 +29,11 @@ with pkgs.lib;
echo "<<< System shutdown >>>" echo "<<< System shutdown >>>"
fi fi
echo "" echo ""
${config.powerManagement.powerDownCommands}
export PATH=${pkgs.utillinux}/bin:${pkgs.utillinux}/sbin:$PATH export PATH=${pkgs.utillinux}/bin:${pkgs.utillinux}/sbin:$PATH
# Do an initial sync just in case. # Do an initial sync just in case.
sync sync

View File

@ -185,7 +185,7 @@ let
-m ${toString config.virtualisation.memorySize} \ -m ${toString config.virtualisation.memorySize} \
-net nic,vlan=0,model=virtio \ -net nic,vlan=0,model=virtio \
-chardev socket,id=samba,path=./samba \ -chardev socket,id=samba,path=./samba \
-net user,vlan=0,guestfwd=tcp:10.0.2.4:139-chardev:samba${if cfg.useBackdoor then ",guestfwd=tcp:10.0.2.6:23-chardev:shell" else ""}''${QEMU_NET_OPTS:+,$QEMU_NET_OPTS} \ -net user,vlan=0,guestfwd=tcp:10.0.2.4:445-chardev:samba${if cfg.useBackdoor then ",guestfwd=tcp:10.0.2.6:23-chardev:shell" else ""}''${QEMU_NET_OPTS:+,$QEMU_NET_OPTS} \
${if cfg.useBackdoor then "-chardev socket,id=shell,path=./shell" else ""} \ ${if cfg.useBackdoor then "-chardev socket,id=shell,path=./shell" else ""} \
${if cfg.useBootLoader then '' ${if cfg.useBootLoader then ''
-drive index=0,file=$NIX_DISK_IMAGE,if=virtio,cache=writeback,werror=report \ -drive index=0,file=$NIX_DISK_IMAGE,if=virtio,cache=writeback,werror=report \

View File

@ -79,6 +79,14 @@ in
# as it's loaded, so don't load it. # as it's loaded, so don't load it.
boot.blacklistedKernelModules = [ "radeonfb" ]; 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 = virtualisation.xen.bootParams =
[ "loglvl=all" "guest_loglvl=all" ] ++ [ "loglvl=all" "guest_loglvl=all" ] ++
optional (cfg.domain0MemorySize != 0) "dom0_mem=${toString cfg.domain0MemorySize}M"; optional (cfg.domain0MemorySize != 0) "dom0_mem=${toString cfg.domain0MemorySize}M";

View File

@ -93,7 +93,7 @@ let
'' ''
createDisk("harddisk", 4 * 1024); createDisk("harddisk", 4 * 1024);
my $machine = Machine->new({ hda => "harddisk", cdrom => glob("${iso}/iso/*.iso"), qemuFlags => '${qemuNICFlags 1 1}' }); my $machine = Machine->new({ hda => "harddisk", cdrom => glob("${iso}/iso/*.iso"), qemuFlags => '${qemuNICFlags 1 1 1}' });
$machine->start; $machine->start;
${optionalString testChannel '' ${optionalString testChannel ''

View File

@ -4,7 +4,7 @@ let
client = client =
{ config, pkgs, ... }: { config, pkgs, ... }:
{ fileSystems = pkgs.lib.mkOverrideTemplate 50 {} { fileSystems = pkgs.lib.mkOverride 50
[ { mountPoint = "/data"; [ { mountPoint = "/data";
device = "server:/data"; device = "server:/data";
fsType = "nfs"; fsType = "nfs";