Merge branch 'master' into staging
A few thousand rebuilds from master, again. Hydra: ?compare=1422362
This commit is contained in:
@@ -403,6 +403,7 @@
|
||||
./services/monitoring/vnstat.nix
|
||||
./services/monitoring/zabbix-agent.nix
|
||||
./services/monitoring/zabbix-server.nix
|
||||
./services/network-filesystems/beegfs.nix
|
||||
./services/network-filesystems/cachefilesd.nix
|
||||
./services/network-filesystems/davfs2.nix
|
||||
./services/network-filesystems/drbd.nix
|
||||
|
||||
@@ -1,39 +1,372 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
|
||||
with lib;
|
||||
with types;
|
||||
|
||||
let
|
||||
|
||||
# Converts a plan like
|
||||
# { "1d" = "1h"; "1w" = "1d"; }
|
||||
# into
|
||||
# "1d=>1h,1w=>1d"
|
||||
attrToPlan = attrs: concatStringsSep "," (builtins.attrValues (
|
||||
mapAttrs (n: v: "${n}=>${v}") attrs));
|
||||
|
||||
planDescription = ''
|
||||
The znapzend backup plan to use for the source.
|
||||
</para>
|
||||
<para>
|
||||
The plan specifies how often to backup and for how long to keep the
|
||||
backups. It consists of a series of retention periodes to interval
|
||||
associations:
|
||||
</para>
|
||||
<para>
|
||||
<literal>
|
||||
retA=>intA,retB=>intB,...
|
||||
</literal>
|
||||
</para>
|
||||
<para>
|
||||
Both intervals and retention periods are expressed in standard units
|
||||
of time or multiples of them. You can use both the full name or a
|
||||
shortcut according to the following listing:
|
||||
</para>
|
||||
<para>
|
||||
<literal>
|
||||
second|sec|s, minute|min, hour|h, day|d, week|w, month|mon|m, year|y
|
||||
</literal>
|
||||
</para>
|
||||
<para>
|
||||
See <citerefentry><refentrytitle>znapzendzetup</refentrytitle><manvolnum>1</manvolnum></citerefentry> for more info.
|
||||
'';
|
||||
planExample = "1h=>10min,1d=>1h,1w=>1d,1m=>1w,1y=>1m";
|
||||
|
||||
# A type for a string of the form number{b|k|M|G}
|
||||
mbufferSizeType = str // {
|
||||
check = x: str.check x && builtins.isList (builtins.match "^[0-9]+[bkMG]$" x);
|
||||
description = "string of the form number{b|k|M|G}";
|
||||
};
|
||||
|
||||
# Type for a string that must contain certain other strings (the list parameter).
|
||||
# Note that these would need regex escaping.
|
||||
stringContainingStrings = list: let
|
||||
matching = s: map (str: builtins.match ".*${str}.*" s) list;
|
||||
in str // {
|
||||
check = x: str.check x && all isList (matching x);
|
||||
description = "string containing all of the characters ${concatStringsSep ", " list}";
|
||||
};
|
||||
|
||||
timestampType = stringContainingStrings [ "%Y" "%m" "%d" "%H" "%M" "%S" ];
|
||||
|
||||
destType = srcConfig: submodule ({ name, ... }: {
|
||||
options = {
|
||||
|
||||
label = mkOption {
|
||||
type = str;
|
||||
description = "Label for this destination. Defaults to the attribute name.";
|
||||
};
|
||||
|
||||
plan = mkOption {
|
||||
type = str;
|
||||
description = planDescription;
|
||||
example = planExample;
|
||||
};
|
||||
|
||||
dataset = mkOption {
|
||||
type = str;
|
||||
description = "Dataset name to send snapshots to.";
|
||||
example = "tank/main";
|
||||
};
|
||||
|
||||
host = mkOption {
|
||||
type = nullOr str;
|
||||
description = ''
|
||||
Host to use for the destination dataset. Can be prefixed with
|
||||
<literal>user@</literal> to specify the ssh user.
|
||||
'';
|
||||
default = null;
|
||||
example = "john@example.com";
|
||||
};
|
||||
|
||||
presend = mkOption {
|
||||
type = nullOr str;
|
||||
description = ''
|
||||
Command to run before sending the snapshot to the destination.
|
||||
Intended to run a remote script via <command>ssh</command> on the
|
||||
destination, e.g. to bring up a backup disk or server or to put a
|
||||
zpool online/offline. See also <option>postsend</option>.
|
||||
'';
|
||||
default = null;
|
||||
example = "ssh root@bserv zpool import -Nf tank";
|
||||
};
|
||||
|
||||
postsend = mkOption {
|
||||
type = nullOr str;
|
||||
description = ''
|
||||
Command to run after sending the snapshot to the destination.
|
||||
Intended to run a remote script via <command>ssh</command> on the
|
||||
destination, e.g. to bring up a backup disk or server or to put a
|
||||
zpool online/offline. See also <option>presend</option>.
|
||||
'';
|
||||
default = null;
|
||||
example = "ssh root@bserv zpool export tank";
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
label = mkDefault name;
|
||||
plan = mkDefault srcConfig.plan;
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
|
||||
srcType = submodule ({ name, config, ... }: {
|
||||
options = {
|
||||
|
||||
enable = mkOption {
|
||||
type = bool;
|
||||
description = "Whether to enable this source.";
|
||||
default = true;
|
||||
};
|
||||
|
||||
recursive = mkOption {
|
||||
type = bool;
|
||||
description = "Whether to do recursive snapshots.";
|
||||
default = false;
|
||||
};
|
||||
|
||||
mbuffer = {
|
||||
enable = mkOption {
|
||||
type = bool;
|
||||
description = "Whether to use <command>mbuffer</command>.";
|
||||
default = false;
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = nullOr ints.u16;
|
||||
description = ''
|
||||
Port to use for <command>mbuffer</command>.
|
||||
</para>
|
||||
<para>
|
||||
If this is null, it will run <command>mbuffer</command> through
|
||||
ssh.
|
||||
</para>
|
||||
<para>
|
||||
If this is not null, it will run <command>mbuffer</command>
|
||||
directly through TCP, which is not encrypted but faster. In that
|
||||
case the given port needs to be open on the destination host.
|
||||
'';
|
||||
default = null;
|
||||
};
|
||||
|
||||
size = mkOption {
|
||||
type = mbufferSizeType;
|
||||
description = ''
|
||||
The size for <command>mbuffer</command>.
|
||||
Supports the units b, k, M, G.
|
||||
'';
|
||||
default = "1G";
|
||||
example = "128M";
|
||||
};
|
||||
};
|
||||
|
||||
presnap = mkOption {
|
||||
type = nullOr str;
|
||||
description = ''
|
||||
Command to run before snapshots are taken on the source dataset,
|
||||
e.g. for database locking/flushing. See also
|
||||
<option>postsnap</option>.
|
||||
'';
|
||||
default = null;
|
||||
example = literalExample ''
|
||||
''${pkgs.mariadb}/bin/mysql -e "set autocommit=0;flush tables with read lock;\\! ''${pkgs.coreutils}/bin/sleep 600" & ''${pkgs.coreutils}/bin/echo $! > /tmp/mariadblock.pid ; sleep 10
|
||||
'';
|
||||
};
|
||||
|
||||
postsnap = mkOption {
|
||||
type = nullOr str;
|
||||
description = ''
|
||||
Command to run after snapshots are taken on the source dataset,
|
||||
e.g. for database unlocking. See also <option>presnap</option>.
|
||||
'';
|
||||
default = null;
|
||||
example = literalExample ''
|
||||
''${pkgs.coreutils}/bin/kill `''${pkgs.coreutils}/bin/cat /tmp/mariadblock.pid`;''${pkgs.coreutils}/bin/rm /tmp/mariadblock.pid
|
||||
'';
|
||||
};
|
||||
|
||||
timestampFormat = mkOption {
|
||||
type = timestampType;
|
||||
description = ''
|
||||
The timestamp format to use for constructing snapshot names.
|
||||
The syntax is <literal>strftime</literal>-like. The string must
|
||||
consist of the mandatory <literal>%Y %m %d %H %M %S</literal>.
|
||||
Optionally <literal>- _ . :</literal> characters as well as any
|
||||
alphanumeric character are allowed. If suffixed by a
|
||||
<literal>Z</literal>, times will be in UTC.
|
||||
'';
|
||||
default = "%Y-%m-%d-%H%M%S";
|
||||
example = "znapzend-%m.%d.%Y-%H%M%SZ";
|
||||
};
|
||||
|
||||
sendDelay = mkOption {
|
||||
type = int;
|
||||
description = ''
|
||||
Specify delay (in seconds) before sending snaps to the destination.
|
||||
May be useful if you want to control sending time.
|
||||
'';
|
||||
default = 0;
|
||||
example = 60;
|
||||
};
|
||||
|
||||
plan = mkOption {
|
||||
type = str;
|
||||
description = planDescription;
|
||||
example = planExample;
|
||||
};
|
||||
|
||||
dataset = mkOption {
|
||||
type = str;
|
||||
description = "The dataset to use for this source.";
|
||||
example = "tank/home";
|
||||
};
|
||||
|
||||
destinations = mkOption {
|
||||
type = loaOf (destType config);
|
||||
description = "Additional destinations.";
|
||||
default = {};
|
||||
example = literalExample ''
|
||||
{
|
||||
local = {
|
||||
dataset = "btank/backup";
|
||||
presend = "zpool import -N btank";
|
||||
postsend = "zpool export btank";
|
||||
};
|
||||
remote = {
|
||||
host = "john@example.com";
|
||||
dataset = "tank/john";
|
||||
};
|
||||
};
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = {
|
||||
dataset = mkDefault name;
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
### Generating the configuration from here
|
||||
|
||||
cfg = config.services.znapzend;
|
||||
|
||||
onOff = b: if b then "on" else "off";
|
||||
nullOff = b: if isNull b then "off" else toString b;
|
||||
stripSlashes = replaceStrings [ "/" ] [ "." ];
|
||||
|
||||
attrsToFile = config: concatStringsSep "\n" (builtins.attrValues (
|
||||
mapAttrs (n: v: "${n}=${v}") config));
|
||||
|
||||
mkDestAttrs = dst: with dst;
|
||||
mapAttrs' (n: v: nameValuePair "dst_${label}${n}" v) ({
|
||||
"" = optionalString (! isNull host) "${host}:" + dataset;
|
||||
_plan = plan;
|
||||
} // optionalAttrs (presend != null) {
|
||||
_precmd = presend;
|
||||
} // optionalAttrs (postsend != null) {
|
||||
_pstcmd = postsend;
|
||||
});
|
||||
|
||||
mkSrcAttrs = srcCfg: with srcCfg; {
|
||||
enabled = onOff enable;
|
||||
mbuffer = with mbuffer; if enable then "${pkgs.mbuffer}/bin/mbuffer"
|
||||
+ optionalString (port != null) ":${toString port}" else "off";
|
||||
mbuffer_size = mbuffer.size;
|
||||
post_znap_cmd = nullOff postsnap;
|
||||
pre_znap_cmd = nullOff presnap;
|
||||
recursive = onOff recursive;
|
||||
src = dataset;
|
||||
src_plan = plan;
|
||||
tsformat = timestampFormat;
|
||||
zend_delay = toString sendDelay;
|
||||
} // fold (a: b: a // b) {} (
|
||||
map mkDestAttrs (builtins.attrValues destinations)
|
||||
);
|
||||
|
||||
files = mapAttrs' (n: srcCfg: let
|
||||
fileText = attrsToFile (mkSrcAttrs srcCfg);
|
||||
in {
|
||||
name = srcCfg.dataset;
|
||||
value = pkgs.writeText (stripSlashes srcCfg.dataset) fileText;
|
||||
}) cfg.zetup;
|
||||
|
||||
in
|
||||
{
|
||||
options = {
|
||||
services.znapzend = {
|
||||
enable = mkEnableOption "ZnapZend daemon";
|
||||
enable = mkEnableOption "ZnapZend ZFS backup daemon";
|
||||
|
||||
logLevel = mkOption {
|
||||
default = "debug";
|
||||
example = "warning";
|
||||
type = lib.types.enum ["debug" "info" "warning" "err" "alert"];
|
||||
description = "The log level when logging to file. Any of debug, info, warning, err, alert. Default in daemonized form is debug.";
|
||||
type = enum ["debug" "info" "warning" "err" "alert"];
|
||||
description = ''
|
||||
The log level when logging to file. Any of debug, info, warning, err,
|
||||
alert. Default in daemonized form is debug.
|
||||
'';
|
||||
};
|
||||
|
||||
logTo = mkOption {
|
||||
type = types.str;
|
||||
type = str;
|
||||
default = "syslog::daemon";
|
||||
example = "/var/log/znapzend.log";
|
||||
description = "Where to log to (syslog::<facility> or <filepath>).";
|
||||
description = ''
|
||||
Where to log to (syslog::<facility> or <filepath>).
|
||||
'';
|
||||
};
|
||||
|
||||
noDestroy = mkOption {
|
||||
type = types.bool;
|
||||
type = bool;
|
||||
default = false;
|
||||
description = "Does all changes to the filesystem except destroy.";
|
||||
};
|
||||
|
||||
autoCreation = mkOption {
|
||||
type = types.bool;
|
||||
type = bool;
|
||||
default = false;
|
||||
description = "Automatically create the destination dataset if it does not exists.";
|
||||
};
|
||||
|
||||
zetup = mkOption {
|
||||
type = loaOf srcType;
|
||||
description = "Znapzend configuration.";
|
||||
default = {};
|
||||
example = literalExample ''
|
||||
{
|
||||
"tank/home" = {
|
||||
# Make snapshots of tank/home every hour, keep those for 1 day,
|
||||
# keep every days snapshot for 1 month, etc.
|
||||
plan = "1d=>1h,1m=>1d,1y=>1m";
|
||||
recursive = true;
|
||||
# Send all those snapshots to john@example.com:rtank/john as well
|
||||
destinations.remote = {
|
||||
host = "john@example.com";
|
||||
dataset = "rtank/john";
|
||||
};
|
||||
};
|
||||
};
|
||||
'';
|
||||
};
|
||||
|
||||
pure = mkOption {
|
||||
type = bool;
|
||||
description = ''
|
||||
Do not persist any stateful znapzend setups. If this option is
|
||||
enabled, your previously set znapzend setups will be cleared and only
|
||||
the ones defined with this module will be applied.
|
||||
'';
|
||||
default = false;
|
||||
description = "Automatically create the dataset on dest if it does not exists.";
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -49,12 +382,30 @@ in
|
||||
|
||||
path = with pkgs; [ zfs mbuffer openssh ];
|
||||
|
||||
preStart = optionalString cfg.pure ''
|
||||
echo Resetting znapzend zetups
|
||||
${pkgs.znapzend}/bin/znapzendzetup list \
|
||||
| grep -oP '(?<=\*\*\* backup plan: ).*(?= \*\*\*)' \
|
||||
| xargs ${pkgs.znapzend}/bin/znapzendzetup delete
|
||||
'' + concatStringsSep "\n" (mapAttrsToList (dataset: config: ''
|
||||
echo Importing znapzend zetup ${config} for dataset ${dataset}
|
||||
${pkgs.znapzend}/bin/znapzendzetup import --write ${dataset} ${config}
|
||||
'') files);
|
||||
|
||||
serviceConfig = {
|
||||
ExecStart = "${pkgs.znapzend}/bin/znapzend --logto=${cfg.logTo} --loglevel=${cfg.logLevel} ${optionalString cfg.noDestroy "--nodestroy"} ${optionalString cfg.autoCreation "--autoCreation"}";
|
||||
ExecStart = let
|
||||
args = concatStringsSep " " [
|
||||
"--logto=${cfg.logTo}"
|
||||
"--loglevel=${cfg.logLevel}"
|
||||
(optionalString cfg.noDestroy "--nodestroy")
|
||||
(optionalString cfg.autoCreation "--autoCreation")
|
||||
]; in "${pkgs.znapzend}/bin/znapzend ${args}";
|
||||
ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
|
||||
Restart = "on-failure";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
meta.maintainers = with maintainers; [ infinisil ];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,343 @@
|
||||
{ config, lib, pkgs, ...} :
|
||||
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.beegfs;
|
||||
|
||||
# functions for the generations of config files
|
||||
|
||||
configMgmtd = name: cfg: pkgs.writeText "mgmt-${name}.conf" ''
|
||||
storeMgmtdDirectory = ${cfg.mgmtd.storeDir}
|
||||
storeAllowFirstRunInit = false
|
||||
connAuthFile = ${cfg.connAuthFile}
|
||||
connPortShift = ${toString cfg.connPortShift}
|
||||
|
||||
${cfg.mgmtd.extraConfig}
|
||||
'';
|
||||
|
||||
configAdmon = name: cfg: pkgs.writeText "admon-${name}.conf" ''
|
||||
sysMgmtdHost = ${cfg.mgmtdHost}
|
||||
connAuthFile = ${cfg.connAuthFile}
|
||||
connPortShift = ${toString cfg.connPortShift}
|
||||
|
||||
${cfg.admon.extraConfig}
|
||||
'';
|
||||
|
||||
configMeta = name: cfg: pkgs.writeText "meta-${name}.conf" ''
|
||||
storeMetaDirectory = ${cfg.meta.storeDir}
|
||||
sysMgmtdHost = ${cfg.mgmtdHost}
|
||||
connAuthFile = ${cfg.connAuthFile}
|
||||
connPortShift = ${toString cfg.connPortShift}
|
||||
storeAllowFirstRunInit = false
|
||||
|
||||
${cfg.mgmtd.extraConfig}
|
||||
'';
|
||||
|
||||
configStorage = name: cfg: pkgs.writeText "storage-${name}.conf" ''
|
||||
storeStorageDirectory = ${cfg.storage.storeDir}
|
||||
sysMgmtdHost = ${cfg.mgmtdHost}
|
||||
connAuthFile = ${cfg.connAuthFile}
|
||||
connPortShift = ${toString cfg.connPortShift}
|
||||
storeAllowFirstRunInit = false
|
||||
|
||||
${cfg.storage.extraConfig}
|
||||
'';
|
||||
|
||||
configHelperd = name: cfg: pkgs.writeText "helperd-${name}.conf" ''
|
||||
connAuthFile = ${cfg.connAuthFile}
|
||||
${cfg.helperd.extraConfig}
|
||||
'';
|
||||
|
||||
configClientFilename = name : "/etc/beegfs/client-${name}.conf";
|
||||
|
||||
configClient = name: cfg: ''
|
||||
sysMgmtdHost = ${cfg.mgmtdHost}
|
||||
connAuthFile = ${cfg.connAuthFile}
|
||||
connPortShift = ${toString cfg.connPortShift}
|
||||
|
||||
${cfg.client.extraConfig}
|
||||
'';
|
||||
|
||||
serviceList = [
|
||||
{ service = "admon"; cfgFile = configAdmon; }
|
||||
{ service = "meta"; cfgFile = configMeta; }
|
||||
{ service = "mgmtd"; cfgFile = configMgmtd; }
|
||||
{ service = "storage"; cfgFile = configStorage; }
|
||||
];
|
||||
|
||||
# functions to generate systemd.service entries
|
||||
|
||||
systemdEntry = service: cfgFile: (mapAttrs' ( name: cfg:
|
||||
(nameValuePair "beegfs-${service}-${name}" (mkIf cfg."${service}".enable {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
requires = [ "network-online.target" ];
|
||||
after = [ "network-online.target" ];
|
||||
serviceConfig = rec {
|
||||
ExecStart = ''
|
||||
${pkgs.beegfs}/bin/beegfs-${service} \
|
||||
cfgFile=${cfgFile name cfg} \
|
||||
pidFile=${PIDFile}
|
||||
'';
|
||||
PIDFile = "/run/beegfs-${service}-${name}.pid";
|
||||
TimeoutStopSec = "300";
|
||||
};
|
||||
}))) cfg);
|
||||
|
||||
systemdHelperd = mapAttrs' ( name: cfg:
|
||||
(nameValuePair "beegfs-helperd-${name}" (mkIf cfg.client.enable {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
requires = [ "network-online.target" ];
|
||||
after = [ "network-online.target" ];
|
||||
serviceConfig = rec {
|
||||
ExecStart = ''
|
||||
${pkgs.beegfs}/bin/beegfs-helperd \
|
||||
cfgFile=${configHelperd name cfg} \
|
||||
pidFile=${PIDFile}
|
||||
'';
|
||||
PIDFile = "/run/beegfs-helperd-${name}.pid";
|
||||
TimeoutStopSec = "300";
|
||||
};
|
||||
}))) cfg;
|
||||
|
||||
# wrappers to beegfs tools. Avoid typing path of config files
|
||||
utilWrappers = mapAttrsToList ( name: cfg:
|
||||
( pkgs.runCommand "beegfs-utils-${name}" { nativeBuildInputs = [ pkgs.makeWrapper ]; } ''
|
||||
mkdir -p $out/bin
|
||||
|
||||
makeWrapper ${pkgs.beegfs}/bin/beegfs-check-servers \
|
||||
$out/bin/beegfs-check-servers-${name} \
|
||||
--add-flags "-c ${configClientFilename name}" \
|
||||
--prefix PATH : ${lib.makeBinPath [ pkgs.beegfs ]}
|
||||
|
||||
makeWrapper ${pkgs.beegfs}/bin/beegfs-ctl \
|
||||
$out/bin/beegfs-ctl-${name} \
|
||||
--add-flags "--cfgFile=${configClientFilename name}"
|
||||
|
||||
makeWrapper ${pkgs.beegfs}/bin/beegfs-ctl \
|
||||
$out/bin/beegfs-df-${name} \
|
||||
--add-flags "--cfgFile=${configClientFilename name}" \
|
||||
--add-flags --listtargets \
|
||||
--add-flags --hidenodeid \
|
||||
--add-flags --pools \
|
||||
--add-flags --spaceinfo
|
||||
|
||||
makeWrapper ${pkgs.beegfs}/bin/beegfs-fsck \
|
||||
$out/bin/beegfs-fsck-${name} \
|
||||
--add-flags "--cfgFile=${configClientFilename name}"
|
||||
''
|
||||
)) cfg;
|
||||
in
|
||||
{
|
||||
###### interface
|
||||
|
||||
options = {
|
||||
services.beegfsEnable = mkEnableOption "BeeGFS";
|
||||
|
||||
services.beegfs = mkOption {
|
||||
default = {};
|
||||
description = ''
|
||||
BeeGFS configurations. Every mount point requires a separate configuration.
|
||||
'';
|
||||
type = with types; attrsOf (submodule ({ config, ... } : {
|
||||
options = {
|
||||
mgmtdHost = mkOption {
|
||||
type = types.str;
|
||||
default = null;
|
||||
example = "master";
|
||||
description = ''Hostname of managament host.'';
|
||||
};
|
||||
|
||||
connAuthFile = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
example = "/etc/my.key";
|
||||
description = "File containing shared secret authentication.";
|
||||
};
|
||||
|
||||
connPortShift = mkOption {
|
||||
type = types.int;
|
||||
default = 0;
|
||||
example = 5;
|
||||
description = ''
|
||||
For each additional beegfs configuration shift all
|
||||
service TCP/UDP ports by at least 5.
|
||||
'';
|
||||
};
|
||||
|
||||
client = {
|
||||
enable = mkEnableOption "BeeGFS client";
|
||||
|
||||
mount = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Create fstab entry automatically";
|
||||
};
|
||||
|
||||
mountPoint = mkOption {
|
||||
type = types.str;
|
||||
default = "/run/beegfs";
|
||||
description = ''
|
||||
Mount point under which the beegfs filesytem should be mounted.
|
||||
If mounted manually the mount option specifing the config file is needed:
|
||||
cfgFile=/etc/beegfs/beegfs-client-<name>.conf
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Additional lines for beegfs-client.conf.
|
||||
See documentation for further details.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
helperd = {
|
||||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Additional lines for beegfs-helperd.conf. See documentation
|
||||
for further details.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
mgmtd = {
|
||||
enable = mkEnableOption "BeeGFS mgmtd daemon";
|
||||
|
||||
storeDir = mkOption {
|
||||
type = types.path;
|
||||
default = null;
|
||||
example = "/data/beegfs-mgmtd";
|
||||
description = ''
|
||||
Data directory for mgmtd.
|
||||
Must not be shared with other beegfs daemons.
|
||||
This directory must exist and it must be initialized
|
||||
with beegfs-setup-mgmtd, e.g. "beegfs-setup-mgmtd -C -p <storeDir>"
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Additional lines for beegfs-mgmtd.conf. See documentation
|
||||
for further details.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
admon = {
|
||||
enable = mkEnableOption "BeeGFS admon daemon";
|
||||
|
||||
extraConfig = mkOption {
|
||||
type = types.lines;
|
||||
default = "";
|
||||
description = ''
|
||||
Additional lines for beegfs-admon.conf. See documentation
|
||||
for further details.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
meta = {
|
||||
enable = mkEnableOption "BeeGFS meta data daemon";
|
||||
|
||||
storeDir = mkOption {
|
||||
type = types.path;
|
||||
default = null;
|
||||
example = "/data/beegfs-meta";
|
||||
description = ''
|
||||
Data directory for meta data service.
|
||||
Must not be shared with other beegfs daemons.
|
||||
The underlying filesystem must be mounted with xattr turned on.
|
||||
This directory must exist and it must be initialized
|
||||
with beegfs-setup-meta, e.g.
|
||||
"beegfs-setup-meta -C -s <serviceID> -p <storeDir>"
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
description = ''
|
||||
Additional lines for beegfs-meta.conf. See documentation
|
||||
for further details.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
storage = {
|
||||
enable = mkEnableOption "BeeGFS storage daemon";
|
||||
|
||||
storeDir = mkOption {
|
||||
type = types.path;
|
||||
default = null;
|
||||
example = "/data/beegfs-storage";
|
||||
description = ''
|
||||
Data directories for storage service.
|
||||
Must not be shared with other beegfs daemons.
|
||||
The underlying filesystem must be mounted with xattr turned on.
|
||||
This directory must exist and it must be initialized
|
||||
with beegfs-setup-storage, e.g.
|
||||
"beegfs-setup-storage -C -s <serviceID> -i <storageTargetID> -p <storeDir>"
|
||||
'';
|
||||
};
|
||||
|
||||
extraConfig = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
description = ''
|
||||
Addional lines for beegfs-storage.conf. See documentation
|
||||
for further details.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
}));
|
||||
};
|
||||
};
|
||||
|
||||
###### implementation
|
||||
|
||||
config =
|
||||
mkIf config.services.beegfsEnable {
|
||||
|
||||
environment.systemPackages = utilWrappers;
|
||||
|
||||
# Put the client.conf files in /etc since they are needed
|
||||
# by the commandline tools
|
||||
environment.etc = mapAttrs' ( name: cfg:
|
||||
(nameValuePair "beegfs/client-${name}.conf" (mkIf (cfg.client.enable)
|
||||
{
|
||||
enable = true;
|
||||
text = configClient name cfg;
|
||||
}))) cfg;
|
||||
|
||||
# Kernel module, we need it only once per host.
|
||||
boot = mkIf (
|
||||
foldr (a: b: a || b) false
|
||||
(map (x: x.client.enable) (collect (x: x ? client) cfg)))
|
||||
{
|
||||
kernelModules = [ "beegfs" ];
|
||||
extraModulePackages = [ pkgs.linuxPackages.beegfs-module ];
|
||||
};
|
||||
|
||||
# generate fstab entries
|
||||
fileSystems = mapAttrs' (name: cfg:
|
||||
(nameValuePair cfg.client.mountPoint (optionalAttrs cfg.client.mount (mkIf cfg.client.enable {
|
||||
device = "beegfs_nodev";
|
||||
fsType = "beegfs";
|
||||
mountPoint = cfg.client.mountPoint;
|
||||
options = [ "cfgFile=${configClientFilename name}" "_netdev" ];
|
||||
})))) cfg;
|
||||
|
||||
# generate systemd services
|
||||
systemd.services = systemdHelperd //
|
||||
foldr (a: b: a // b) {}
|
||||
(map (x: systemdEntry x.service x.cfgFile) serviceList);
|
||||
};
|
||||
}
|
||||
@@ -3,16 +3,13 @@
|
||||
with lib;
|
||||
|
||||
let
|
||||
|
||||
xcfg = config.services.xserver;
|
||||
pcfg = config.hardware.pulseaudio;
|
||||
cfg = xcfg.desktopManager.xfce;
|
||||
|
||||
in
|
||||
|
||||
{
|
||||
options = {
|
||||
|
||||
services.xserver.desktopManager.xfce = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
@@ -125,9 +122,7 @@ in
|
||||
[ "/share/xfce4" "/share/themes" "/share/mime" "/share/desktop-directories" "/share/gtksourceview-2.0" ];
|
||||
|
||||
environment.variables.GIO_EXTRA_MODULES = [ "${pkgs.xfce.gvfs}/lib/gio/modules" ];
|
||||
environment.variables.GDK_PIXBUF_MODULE_FILE = [
|
||||
"$(echo ${pkgs.librsvg.out}/lib/gdk-pixbuf-*/*/loaders.cache)"
|
||||
];
|
||||
environment.variables.GDK_PIXBUF_MODULE_FILE = "${pkgs.librsvg.out}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache";
|
||||
|
||||
# Enable helpful DBus services.
|
||||
services.udisks2.enable = true;
|
||||
|
||||
@@ -170,7 +170,7 @@ in {
|
||||
|
||||
disableWhileTyping = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
default = false;
|
||||
description =
|
||||
''
|
||||
Disable input method while typing.
|
||||
|
||||
@@ -232,6 +232,7 @@ in rec {
|
||||
tests.atd = callTest tests/atd.nix {};
|
||||
tests.acme = callTest tests/acme.nix {};
|
||||
tests.avahi = callTest tests/avahi.nix {};
|
||||
tests.beegfs = callTest tests/beegfs.nix {};
|
||||
tests.bittorrent = callTest tests/bittorrent.nix {};
|
||||
tests.blivet = callTest tests/blivet.nix {};
|
||||
tests.boot = callSubTests tests/boot.nix {};
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
import ./make-test.nix ({ pkgs, ... } :
|
||||
|
||||
let
|
||||
connAuthFile="beegfs/auth-def.key";
|
||||
|
||||
client = { config, pkgs, lib, ... } : {
|
||||
networking.firewall.enable = false;
|
||||
services.beegfsEnable = true;
|
||||
services.beegfs.default = {
|
||||
mgmtdHost = "mgmt";
|
||||
connAuthFile = "/etc/${connAuthFile}";
|
||||
client = {
|
||||
mount = false;
|
||||
enable = true;
|
||||
};
|
||||
};
|
||||
|
||||
fileSystems = pkgs.lib.mkVMOverride # FIXME: this should be creatd by the module
|
||||
[ { mountPoint = "/beegfs";
|
||||
device = "default";
|
||||
fsType = "beegfs";
|
||||
options = [ "cfgFile=/etc/beegfs/client-default.conf" "_netdev" ];
|
||||
}
|
||||
];
|
||||
|
||||
environment.etc."${connAuthFile}" = {
|
||||
enable = true;
|
||||
text = "ThisIsALousySecret";
|
||||
mode = "0600";
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
server = service : { config, pkgs, lib, ... } : {
|
||||
networking.firewall.enable = false;
|
||||
boot.initrd.postDeviceCommands = ''
|
||||
${pkgs.e2fsprogs}/bin/mkfs.ext4 -L data /dev/vdb
|
||||
'';
|
||||
|
||||
virtualisation.emptyDiskImages = [ 4096 ];
|
||||
|
||||
fileSystems = pkgs.lib.mkVMOverride
|
||||
[ { mountPoint = "/data";
|
||||
device = "/dev/disk/by-label/data";
|
||||
fsType = "ext4";
|
||||
}
|
||||
];
|
||||
|
||||
environment.systemPackages = with pkgs; [ beegfs ];
|
||||
environment.etc."${connAuthFile}" = {
|
||||
enable = true;
|
||||
text = "ThisIsALousySecret";
|
||||
mode = "0600";
|
||||
};
|
||||
|
||||
services.beegfsEnable = true;
|
||||
services.beegfs.default = {
|
||||
mgmtdHost = "mgmt";
|
||||
connAuthFile = "/etc/${connAuthFile}";
|
||||
"${service}" = {
|
||||
enable = true;
|
||||
storeDir = "/data";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
in
|
||||
{
|
||||
name = "beegfs";
|
||||
|
||||
nodes = {
|
||||
meta = server "meta";
|
||||
mgmt = server "mgmtd";
|
||||
storage1 = server "storage";
|
||||
storage2 = server "storage";
|
||||
client1 = client;
|
||||
client2 = client;
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
# Initalize the data directories
|
||||
$mgmt->waitForUnit("default.target");
|
||||
$mgmt->succeed("beegfs-setup-mgmtd -C -f -p /data");
|
||||
$mgmt->succeed("systemctl start beegfs-mgmtd-default");
|
||||
|
||||
$meta->waitForUnit("default.target");
|
||||
$meta->succeed("beegfs-setup-meta -C -f -s 1 -p /data");
|
||||
$meta->succeed("systemctl start beegfs-meta-default");
|
||||
|
||||
$storage1->waitForUnit("default.target");
|
||||
$storage1->succeed("beegfs-setup-storage -C -f -s 1 -i 1 -p /data");
|
||||
$storage1->succeed("systemctl start beegfs-storage-default");
|
||||
|
||||
$storage2->waitForUnit("default.target");
|
||||
$storage2->succeed("beegfs-setup-storage -C -f -s 2 -i 2 -p /data");
|
||||
$storage2->succeed("systemctl start beegfs-storage-default");
|
||||
|
||||
#
|
||||
|
||||
# Basic test
|
||||
$client1->waitForUnit("beegfs.mount");
|
||||
$client1->succeed("beegfs-check-servers-default");
|
||||
$client1->succeed("echo test > /beegfs/test");
|
||||
$client2->waitForUnit("beegfs.mount");
|
||||
$client2->succeed("test -e /beegfs/test");
|
||||
$client2->succeed("cat /beegfs/test | grep test");
|
||||
|
||||
# test raid0/stripping
|
||||
$client1->succeed("dd if=/dev/urandom bs=1M count=10 of=/beegfs/striped");
|
||||
$client2->succeed("cat /beegfs/striped > /dev/null");
|
||||
|
||||
# check if fs is still healthy
|
||||
$client1->succeed("beegfs-fsck-default --checkfs");
|
||||
'';
|
||||
})
|
||||
Reference in New Issue
Block a user