depot/third_party/nixpkgs/nixos/modules/services/network-filesystems/moosefs.nix

376 lines
12 KiB
Nix

{
config,
lib,
pkgs,
...
}:
let
cfg = config.services.moosefs;
mfsUser = if cfg.runAsUser then "moosefs" else "root";
settingsFormat =
let
listSep = " ";
allowedTypes = with lib.types; [
bool
int
float
str
];
valueToString =
val:
if lib.isList val then
lib.concatStringsSep listSep (map (x: valueToString x) val)
else if lib.isBool val then
(if val then "1" else "0")
else
toString val;
in
{
type =
with lib.types;
let
valueType =
oneOf (
[
(listOf valueType)
]
++ allowedTypes
)
// {
description = "Flat key-value file";
};
in
attrsOf valueType;
generate =
name: value:
pkgs.writeText name (
lib.concatStringsSep "\n" (lib.mapAttrsToList (key: val: "${key} = ${valueToString val}") value)
);
};
# Manual initialization tool
initTool = pkgs.writeShellScriptBin "mfsmaster-init" ''
if [ ! -e ${cfg.master.settings.DATA_PATH}/metadata.mfs ]; then
cp ${pkgs.moosefs}/var/mfs/metadata.mfs.empty ${cfg.master.settings.DATA_PATH}
chmod +w ${cfg.master.settings.DATA_PATH}/metadata.mfs.empty
${pkgs.moosefs}/bin/mfsmaster -a -c ${masterCfg} start
${pkgs.moosefs}/bin/mfsmaster -c ${masterCfg} stop
rm ${cfg.master.settings.DATA_PATH}/metadata.mfs.empty
fi
'';
masterCfg = settingsFormat.generate "mfsmaster.cfg" cfg.master.settings;
metaloggerCfg = settingsFormat.generate "mfsmetalogger.cfg" cfg.metalogger.settings;
chunkserverCfg = settingsFormat.generate "mfschunkserver.cfg" cfg.chunkserver.settings;
systemdService = name: extraConfig: configFile: {
wantedBy = [ "multi-user.target" ];
wants = [ "network-online.target" ];
after = [
"network.target"
"network-online.target"
];
serviceConfig = {
Type = "forking";
ExecStart = "${pkgs.moosefs}/bin/mfs${name} -c ${configFile} start";
ExecStop = "${pkgs.moosefs}/bin/mfs${name} -c ${configFile} stop";
ExecReload = "${pkgs.moosefs}/bin/mfs${name} -c ${configFile} reload";
PIDFile = "${cfg."${name}".settings.DATA_PATH}/.mfs${name}.lock";
} // extraConfig;
};
in
{
###### interface
options = {
services.moosefs = {
masterHost = lib.mkOption {
type = lib.types.str;
default = null;
description = "IP or DNS name of the MooseFS master server.";
};
runAsUser = lib.mkOption {
type = lib.types.bool;
default = true;
example = true;
description = "Run daemons as moosefs user instead of root for better security.";
};
client.enable = lib.mkEnableOption "MooseFS client";
master = {
enable = lib.mkOption {
type = lib.types.bool;
description = ''
Enable MooseFS master daemon.
The master server coordinates all MooseFS operations and stores metadata.
'';
default = false;
};
autoInit = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Whether to automatically initialize the master's metadata directory on first run. Use with caution.";
};
exports = lib.mkOption {
type = with lib.types; listOf str;
default = null;
description = "Export definitions for MooseFS (see mfsexports.cfg).";
example = [
"* / rw,alldirs,admin,maproot=0:0"
"* . rw"
];
};
openFirewall = lib.mkOption {
type = lib.types.bool;
description = "Whether to automatically open required firewall ports for master service.";
default = false;
};
settings = lib.mkOption {
type = lib.types.submodule {
freeformType = settingsFormat.type;
options.DATA_PATH = lib.mkOption {
type = lib.types.str;
default = "/var/lib/mfs";
description = "Directory for storing master metadata.";
};
};
description = "Master configuration options (mfsmaster.cfg).";
};
};
metalogger = {
enable = lib.mkEnableOption "MooseFS metalogger daemon that maintains a backup copy of the master's metadata";
settings = lib.mkOption {
type = lib.types.submodule {
freeformType = settingsFormat.type;
options.DATA_PATH = lib.mkOption {
type = lib.types.str;
default = "/var/lib/mfs";
description = "Directory for storing metalogger data.";
};
};
description = "Metalogger configuration options (mfsmetalogger.cfg).";
};
};
chunkserver = {
enable = lib.mkEnableOption "MooseFS chunkserver daemon that stores file data";
openFirewall = lib.mkOption {
type = lib.types.bool;
description = "Whether to automatically open required firewall ports for chunkserver service.";
default = false;
};
hdds = lib.mkOption {
type = with lib.types; listOf str;
default = null;
description = "Mount points used by chunkserver for data storage (see mfshdd.cfg).";
example = [
"/mnt/hdd1"
"/mnt/hdd2"
];
};
settings = lib.mkOption {
type = lib.types.submodule {
freeformType = settingsFormat.type;
options.DATA_PATH = lib.mkOption {
type = lib.types.str;
default = "/var/lib/mfs";
description = "Directory for lock files and other runtime data.";
};
};
description = "Chunkserver configuration options (mfschunkserver.cfg).";
};
};
cgiserver = {
enable = lib.mkEnableOption ''
MooseFS CGI server for web interface.
Warning: The CGI server interface should be properly secured from unauthorized access,
as it provides full control over your MooseFS installation.
'';
openFirewall = lib.mkOption {
type = lib.types.bool;
description = "Whether to automatically open the web interface port.";
default = false;
};
settings = lib.mkOption {
type = lib.types.submodule {
freeformType = settingsFormat.type;
options = {
BIND_HOST = lib.mkOption {
type = lib.types.str;
default = "0.0.0.0";
description = "IP address to bind CGI server to.";
};
PORT = lib.mkOption {
type = lib.types.port;
default = 9425;
description = "Port for CGI server to listen on.";
};
};
};
default = { };
description = "CGI server configuration options.";
};
};
};
};
###### implementation
config =
lib.mkIf
(
cfg.client.enable
|| cfg.master.enable
|| cfg.metalogger.enable
|| cfg.chunkserver.enable
|| cfg.cgiserver.enable
)
{
warnings = [ (lib.mkIf (!cfg.runAsUser) "Running MooseFS services as root is not recommended.") ];
services.moosefs = {
master.settings = lib.mkIf cfg.master.enable (
lib.mkMerge [
{
WORKING_USER = mfsUser;
EXPORTS_FILENAME = toString (
pkgs.writeText "mfsexports.cfg" (lib.concatStringsSep "\n" cfg.master.exports)
);
}
(lib.mkIf cfg.cgiserver.enable {
MFSCGISERV = toString cfg.cgiserver.settings.PORT;
})
]
);
metalogger.settings = lib.mkIf cfg.metalogger.enable {
WORKING_USER = mfsUser;
MASTER_HOST = cfg.masterHost;
};
chunkserver.settings = lib.mkIf cfg.chunkserver.enable {
WORKING_USER = mfsUser;
MASTER_HOST = cfg.masterHost;
HDD_CONF_FILENAME = toString (
pkgs.writeText "mfshdd.cfg" (lib.concatStringsSep "\n" cfg.chunkserver.hdds)
);
};
};
users =
lib.mkIf
(
cfg.runAsUser
&& (cfg.master.enable || cfg.metalogger.enable || cfg.chunkserver.enable || cfg.cgiserver.enable)
)
{
users.moosefs = {
isSystemUser = true;
description = "MooseFS daemon user";
group = "moosefs";
};
groups.moosefs = { };
};
environment.systemPackages =
(lib.optional cfg.client.enable pkgs.moosefs) ++ (lib.optional cfg.master.enable initTool);
networking.firewall.allowedTCPPorts = lib.mkMerge [
(lib.optionals cfg.master.openFirewall [
9419
9420
9421
])
(lib.optional cfg.chunkserver.openFirewall 9422)
(lib.optional (cfg.cgiserver.enable && cfg.cgiserver.openFirewall) cfg.cgiserver.settings.PORT)
];
systemd.tmpfiles.rules =
[
# Master directories
(lib.optionalString cfg.master.enable "d ${cfg.master.settings.DATA_PATH} 0700 ${mfsUser} ${mfsUser} -")
# Metalogger directories
(lib.optionalString cfg.metalogger.enable "d ${cfg.metalogger.settings.DATA_PATH} 0700 ${mfsUser} ${mfsUser} -")
# Chunkserver directories
(lib.optionalString cfg.chunkserver.enable "d ${cfg.chunkserver.settings.DATA_PATH} 0700 ${mfsUser} ${mfsUser} -")
]
++ lib.optionals (cfg.chunkserver.enable && cfg.chunkserver.hdds != null) (
map (dir: "d ${dir} 0755 ${mfsUser} ${mfsUser} -") cfg.chunkserver.hdds
);
systemd.services = lib.mkMerge [
(lib.mkIf cfg.master.enable {
mfs-master = (
lib.mkMerge [
(systemdService "master" {
TimeoutStartSec = 1800;
TimeoutStopSec = 1800;
Restart = "on-failure";
User = mfsUser;
} masterCfg)
{
preStart = lib.mkIf cfg.master.autoInit "${initTool}/bin/mfsmaster-init";
}
]
);
})
(lib.mkIf cfg.metalogger.enable {
mfs-metalogger = systemdService "metalogger" {
Restart = "on-abnormal";
User = mfsUser;
} metaloggerCfg;
})
(lib.mkIf cfg.chunkserver.enable {
mfs-chunkserver = systemdService "chunkserver" {
Restart = "on-abnormal";
User = mfsUser;
} chunkserverCfg;
})
(lib.mkIf cfg.cgiserver.enable {
mfs-cgiserv = {
description = "MooseFS CGI Server";
wantedBy = [ "multi-user.target" ];
after = [ "mfs-master.service" ];
serviceConfig = {
Type = "simple";
ExecStart = "${pkgs.moosefs}/bin/mfscgiserv -D /var/lib/mfs -f start";
ExecStop = "${pkgs.moosefs}/bin/mfscgiserv -D /var/lib/mfs stop";
Restart = "on-failure";
RestartSec = "30s";
User = mfsUser;
Group = mfsUser;
WorkingDirectory = "/var/lib/mfs";
};
};
})
];
};
}