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 (lib.mdDoc "OrangeFS server");
78
79 logType = mkOption {
80 type = with types; enum [ "file" "syslog" ];
81 default = "syslog";
82 description = lib.mdDoc "Destination for log messages.";
83 };
84
85 dataStorageSpace = mkOption {
86 type = types.nullOr types.str;
87 default = null;
88 example = "/data/storage";
89 description = lib.mdDoc "Directory for data storage.";
90 };
91
92 metadataStorageSpace = mkOption {
93 type = types.nullOr types.str;
94 default = null;
95 example = "/data/meta";
96 description = lib.mdDoc "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 = lib.mdDoc "List of BMI modules to load.";
104 };
105
106 extraDefaults = mkOption {
107 type = types.lines;
108 default = "";
109 description = lib.mdDoc "Extra config for `<Defaults>` section.";
110 };
111
112 extraConfig = mkOption {
113 type = types.lines;
114 default = "";
115 description = lib.mdDoc "Extra config for the global section.";
116 };
117
118 servers = mkOption {
119 type = with types; attrsOf types.str;
120 default = {};
121 example = {
122 node1 = "tcp://node1:3334";
123 node2 = "tcp://node2:3334";
124 };
125 description = lib.mdDoc "URLs for storage server including port. The attribute names define the server alias.";
126 };
127
128 fileSystems = mkOption {
129 description = lib.mdDoc ''
130 These options will create the `<FileSystem>` sections of config file.
131 '';
132 default = { orangefs = {}; };
133 example = literalExpression ''
134 {
135 fs1 = {
136 id = 101;
137 };
138
139 fs2 = {
140 id = 102;
141 };
142 }
143 '';
144 type = with types; attrsOf (submodule ({ ... } : {
145 options = {
146 id = mkOption {
147 type = types.int;
148 default = 1;
149 description = lib.mdDoc "File system ID (must be unique within configuration).";
150 };
151
152 rootHandle = mkOption {
153 type = types.int;
154 default = 3;
155 description = lib.mdDoc "File system root ID.";
156 };
157
158 extraConfig = mkOption {
159 type = types.lines;
160 default = "";
161 description = lib.mdDoc "Extra config for `<FileSystem>` section.";
162 };
163
164 troveSyncMeta = mkOption {
165 type = types.bool;
166 default = true;
167 description = lib.mdDoc "Sync meta data.";
168 };
169
170 troveSyncData = mkOption {
171 type = types.bool;
172 default = false;
173 description = lib.mdDoc "Sync data.";
174 };
175
176 extraStorageHints = mkOption {
177 type = types.lines;
178 default = "";
179 description = lib.mdDoc "Extra config for `<StorageHints>` section.";
180 };
181 };
182 }));
183 };
184 };
185 };
186
187 ###### implementation
188
189 config = mkIf cfg.enable {
190 environment.systemPackages = [ pkgs.orangefs ];
191
192 # orangefs daemon will run as user
193 users.users.orangefs = {
194 isSystemUser = true;
195 group = "orangefs";
196 };
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 foreground 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}