diff --git a/nixos/modules/services/network-filesystems/moosefs.nix b/nixos/modules/services/network-filesystems/moosefs.nix index 2fef0bec378c..1c874e4108f6 100644 --- a/nixos/modules/services/network-filesystems/moosefs.nix +++ b/nixos/modules/services/network-filesystems/moosefs.nix @@ -26,7 +26,7 @@ let 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} @@ -37,19 +37,10 @@ let fi ''; - # master config file - masterCfg = settingsFormat.generate - "mfsmaster.cfg" cfg.master.settings; + masterCfg = settingsFormat.generate "mfsmaster.cfg" cfg.master.settings; + metaloggerCfg = settingsFormat.generate "mfsmetalogger.cfg" cfg.metalogger.settings; + chunkserverCfg = settingsFormat.generate "mfschunkserver.cfg" cfg.chunkserver.settings; - # metalogger config file - metaloggerCfg = settingsFormat.generate - "mfsmetalogger.cfg" cfg.metalogger.settings; - - # chunkserver config file - chunkserverCfg = settingsFormat.generate - "mfschunkserver.cfg" cfg.chunkserver.settings; - - # generic template for all daemons systemdService = name: extraConfig: configFile: { wantedBy = [ "multi-user.target" ]; wants = [ "network-online.target" ]; @@ -66,40 +57,43 @@ let in { ###### interface - options = { services.moosefs = { masterHost = lib.mkOption { type = lib.types.str; default = null; - description = "IP or DNS name of master host."; + 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 user moosefs instead of root."; + description = "Run daemons as moosefs user instead of root for better security."; }; - client.enable = lib.mkEnableOption "Moosefs client"; + client.enable = lib.mkEnableOption "MooseFS client"; master = { enable = lib.mkOption { type = lib.types.bool; description = '' - Enable Moosefs master daemon. - - You need to run `mfsmaster-init` on a freshly installed master server to - initialize the `DATA_PATH` directory. + 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 = "Paths to export (see mfsexports.cfg)."; + description = "Export definitions for MooseFS (see mfsexports.cfg)."; example = [ "* / rw,alldirs,admin,maproot=0:0" "* . rw" @@ -108,7 +102,7 @@ in { openFirewall = lib.mkOption { type = lib.types.bool; - description = "Whether to automatically open the necessary ports in the firewall."; + description = "Whether to automatically open required firewall ports for master service."; default = false; }; @@ -119,16 +113,15 @@ in { options.DATA_PATH = lib.mkOption { type = lib.types.str; default = "/var/lib/mfs"; - description = "Data storage directory."; + description = "Directory for storing master metadata."; }; }; - - description = "Contents of config file (mfsmaster.cfg)."; + description = "Master configuration options (mfsmaster.cfg)."; }; }; metalogger = { - enable = lib.mkEnableOption "Moosefs metalogger daemon"; + enable = lib.mkEnableOption "MooseFS metalogger daemon that maintains a backup copy of the master's metadata"; settings = lib.mkOption { type = lib.types.submodule { @@ -137,28 +130,27 @@ in { options.DATA_PATH = lib.mkOption { type = lib.types.str; default = "/var/lib/mfs"; - description = "Data storage directory"; + description = "Directory for storing metalogger data."; }; }; - - description = "Contents of metalogger config file (mfsmetalogger.cfg)."; + description = "Metalogger configuration options (mfsmetalogger.cfg)."; }; }; chunkserver = { - enable = lib.mkEnableOption "Moosefs chunkserver daemon"; + enable = lib.mkEnableOption "MooseFS chunkserver daemon that stores file data"; openFirewall = lib.mkOption { type = lib.types.bool; - description = "Whether to automatically open the necessary ports in the firewall."; + 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 to be used by chunkserver for storage (see mfshdd.cfg)."; - example = [ "/mnt/hdd1" ]; + default = null; + description = "Mount points used by chunkserver for data storage (see mfshdd.cfg)."; + example = [ "/mnt/hdd1" "/mnt/hdd2" ]; }; settings = lib.mkOption { @@ -168,29 +160,65 @@ in { options.DATA_PATH = lib.mkOption { type = lib.types.str; default = "/var/lib/mfs"; - description = "Directory for lock file."; + description = "Directory for lock files and other runtime data."; }; }; + description = "Chunkserver configuration options (mfschunkserver.cfg)."; + }; + }; - description = "Contents of chunkserver config file (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.") ]; - config = lib.mkIf ( cfg.client.enable || cfg.master.enable || cfg.metalogger.enable || cfg.chunkserver.enable ) { - - warnings = [ ( lib.mkIf (!cfg.runAsUser) "Running moosefs services as root is not recommended.") ]; - - # Service settings services.moosefs = { - master.settings = lib.mkIf cfg.master.enable { - WORKING_USER = mfsUser; - EXPORTS_FILENAME = toString ( pkgs.writeText "mfsexports.cfg" - (lib.concatStringsSep "\n" cfg.master.exports)); - }; + 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; @@ -205,11 +233,10 @@ in { }; }; - # Create system user account for daemons - users = lib.mkIf ( cfg.runAsUser && ( cfg.master.enable || cfg.metalogger.enable || cfg.chunkserver.enable ) ) { + 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"; + description = "MooseFS daemon user"; group = "moosefs"; }; groups.moosefs = {}; @@ -219,28 +246,74 @@ in { (lib.optional cfg.client.enable pkgs.moosefs) ++ (lib.optional cfg.master.enable initTool); - networking.firewall.allowedTCPPorts = - (lib.optionals cfg.master.openFirewall [ 9419 9420 9421 ]) ++ - (lib.optional cfg.chunkserver.openFirewall 9422); + 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) + ]; - # Ensure storage directories exist - systemd.tmpfiles.rules = - lib.optional cfg.master.enable "d ${cfg.master.settings.DATA_PATH} 0700 ${mfsUser} ${mfsUser}" - ++ lib.optional cfg.metalogger.enable "d ${cfg.metalogger.settings.DATA_PATH} 0700 ${mfsUser} ${mfsUser}" - ++ lib.optional cfg.chunkserver.enable "d ${cfg.chunkserver.settings.DATA_PATH} 0700 ${mfsUser} ${mfsUser}"; + systemd.tmpfiles.rules = [ + # Master directories + (lib.optionalString cfg.master.enable + "d ${cfg.master.settings.DATA_PATH} 0700 ${mfsUser} ${mfsUser} -") - # Service definitions - systemd.services.mfs-master = lib.mkIf cfg.master.enable - ( systemdService "master" { - TimeoutStartSec = 1800; - TimeoutStopSec = 1800; - Restart = "no"; - } masterCfg ); + # Metalogger directories + (lib.optionalString cfg.metalogger.enable + "d ${cfg.metalogger.settings.DATA_PATH} 0700 ${mfsUser} ${mfsUser} -") - systemd.services.mfs-metalogger = lib.mkIf cfg.metalogger.enable - ( systemdService "metalogger" { Restart = "on-abnormal"; } metaloggerCfg ); + # 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.mfs-chunkserver = lib.mkIf cfg.chunkserver.enable - ( systemdService "chunkserver" { Restart = "on-abnormal"; } chunkserverCfg ); - }; + 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"; + }; + }; + }) + ]; + }; } diff --git a/nixos/tests/moosefs.nix b/nixos/tests/moosefs.nix index ca351c3a8855..633e872f0d30 100644 --- a/nixos/tests/moosefs.nix +++ b/nixos/tests/moosefs.nix @@ -12,6 +12,7 @@ import ./make-test-python.nix ( services.moosefs.master = { enable = true; openFirewall = true; + autoInit = true; exports = [ "* / rw,alldirs,admin,maproot=0:0" "* . rw" @@ -76,8 +77,6 @@ import ./make-test-python.nix ( # prepare master server master.start() master.wait_for_unit("multi-user.target") - master.succeed("mfsmaster-init") - master.succeed("systemctl restart mfs-master") master.wait_for_unit("mfs-master.service") metalogger.wait_for_unit("mfs-metalogger.service")