1{ config, lib, pkgs, ...} : 2 3with lib; 4 5let 6 cfg = config.services.orangefs.server; 7 8 aliases = mapAttrsToList (alias: url: alias) cfg.servers; 9 10 # Maximum handle number is 2^63 11 maxHandle = 9223372036854775806; 12 13 # One range of handles for each meta/data instance 14 handleStep = maxHandle / (length aliases) / 2; 15 16 fileSystems = mapAttrsToList (name: fs: '' 17 <FileSystem> 18 Name ${name} 19 ID ${toString fs.id} 20 RootHandle ${toString fs.rootHandle} 21 22 ${fs.extraConfig} 23 24 <MetaHandleRanges> 25 ${concatStringsSep "\n" ( 26 imap0 (i: alias: 27 let 28 begin = i * handleStep + 3; 29 end = begin + handleStep - 1; 30 in "Range ${alias} ${toString begin}-${toString end}") aliases 31 )} 32 </MetaHandleRanges> 33 34 <DataHandleRanges> 35 ${concatStringsSep "\n" ( 36 imap0 (i: alias: 37 let 38 begin = i * handleStep + 3 + (length aliases) * handleStep; 39 end = begin + handleStep - 1; 40 in "Range ${alias} ${toString begin}-${toString end}") aliases 41 )} 42 </DataHandleRanges> 43 44 <StorageHints> 45 TroveSyncMeta ${if fs.troveSyncMeta then "yes" else "no"} 46 TroveSyncData ${if fs.troveSyncData then "yes" else "no"} 47 ${fs.extraStorageHints} 48 </StorageHints> 49 50 </FileSystem> 51 '') cfg.fileSystems; 52 53 configFile = '' 54 <Defaults> 55 LogType ${cfg.logType} 56 DataStorageSpace ${cfg.dataStorageSpace} 57 MetaDataStorageSpace ${cfg.metadataStorageSpace} 58 59 BMIModules ${concatStringsSep "," cfg.BMIModules} 60 ${cfg.extraDefaults} 61 </Defaults> 62 63 ${cfg.extraConfig} 64 65 <Aliases> 66 ${concatStringsSep "\n" (mapAttrsToList (alias: url: "Alias ${alias} ${url}") cfg.servers)} 67 </Aliases> 68 69 ${concatStringsSep "\n" fileSystems} 70 ''; 71 72in { 73 ###### interface 74 75 options = { 76 services.orangefs.server = { 77 enable = mkEnableOption "OrangeFS server"; 78 79 logType = mkOption { 80 type = with types; enum [ "file" "syslog" ]; 81 default = "syslog"; 82 description = "Destination for log messages."; 83 }; 84 85 dataStorageSpace = mkOption { 86 type = types.nullOr types.str; 87 default = null; 88 example = "/data/storage"; 89 description = "Directory for data storage."; 90 }; 91 92 metadataStorageSpace = mkOption { 93 type = types.nullOr types.str; 94 default = null; 95 example = "/data/meta"; 96 description = "Directory for meta data storage."; 97 }; 98 99 BMIModules = mkOption { 100 type = with types; listOf str; 101 default = [ "bmi_tcp" ]; 102 example = [ "bmi_tcp" "bmi_ib"]; 103 description = "List of BMI modules to load."; 104 }; 105 106 extraDefaults = mkOption { 107 type = types.lines; 108 default = ""; 109 description = "Extra config for <literal>&lt;Defaults&gt;</literal> section."; 110 }; 111 112 extraConfig = mkOption { 113 type = types.lines; 114 default = ""; 115 description = "Extra config for the global section."; 116 }; 117 118 servers = mkOption { 119 type = with types; attrsOf types.str; 120 default = {}; 121 example = '' 122 { 123 node1="tcp://node1:3334"; 124 node2="tcp://node2:3334"; 125 } 126 ''; 127 description = "URLs for storage server including port. The attribute names define the server alias."; 128 }; 129 130 fileSystems = mkOption { 131 description = '' 132 These options will create the <literal>&lt;FileSystem&gt;</literal> sections of config file. 133 ''; 134 default = { orangefs = {}; }; 135 defaultText = literalExample "{ orangefs = {}; }"; 136 example = literalExample '' 137 { 138 fs1 = { 139 id = 101; 140 }; 141 142 fs2 = { 143 id = 102; 144 }; 145 } 146 ''; 147 type = with types; attrsOf (submodule ({ ... } : { 148 options = { 149 id = mkOption { 150 type = types.int; 151 default = 1; 152 description = "File system ID (must be unique within configuration)."; 153 }; 154 155 rootHandle = mkOption { 156 type = types.int; 157 default = 3; 158 description = "File system root ID."; 159 }; 160 161 extraConfig = mkOption { 162 type = types.lines; 163 default = ""; 164 description = "Extra config for <literal>&lt;FileSystem&gt;</literal> section."; 165 }; 166 167 troveSyncMeta = mkOption { 168 type = types.bool; 169 default = true; 170 description = "Sync meta data."; 171 }; 172 173 troveSyncData = mkOption { 174 type = types.bool; 175 default = false; 176 description = "Sync data."; 177 }; 178 179 extraStorageHints = mkOption { 180 type = types.lines; 181 default = ""; 182 description = "Extra config for <literal>&lt;StorageHints&gt;</literal> section."; 183 }; 184 }; 185 })); 186 }; 187 }; 188 }; 189 190 ###### implementation 191 192 config = mkIf cfg.enable { 193 environment.systemPackages = [ pkgs.orangefs ]; 194 195 # orangefs daemon will run as user 196 users.users.orangefs.isSystemUser = true; 197 users.groups.orangefs = {}; 198 199 # To format the file system the config file is needed. 200 environment.etc."orangefs/server.conf" = { 201 text = configFile; 202 user = "orangefs"; 203 group = "orangefs"; 204 }; 205 206 systemd.services.orangefs-server = { 207 wantedBy = [ "multi-user.target" ]; 208 requires = [ "network-online.target" ]; 209 after = [ "network-online.target" ]; 210 211 serviceConfig = { 212 # Run as "simple" in forground mode. 213 # This is more reliable 214 ExecStart = '' 215 ${pkgs.orangefs}/bin/pvfs2-server -d \ 216 /etc/orangefs/server.conf 217 ''; 218 TimeoutStopSec = "120"; 219 User = "orangefs"; 220 Group = "orangefs"; 221 }; 222 }; 223 }; 224 225}