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