python3Packages.orjson: Disable failing tests on 32 bit
[NixPkgs.git] / nixos / modules / services / monitoring / graphite.nix
blob8edb2ca099749574b0525991197311ed36afb6a2
1 { config, lib, options, pkgs, ... }:
3 with lib;
5 let
6   cfg = config.services.graphite;
7   opt = options.services.graphite;
8   writeTextOrNull = f: t: mapNullable (pkgs.writeTextDir f) t;
10   dataDir = cfg.dataDir;
11   staticDir = cfg.dataDir + "/static";
13   graphiteLocalSettingsDir = pkgs.runCommand "graphite_local_settings" {
14       inherit graphiteLocalSettings;
15       preferLocalBuild = true;
16     } ''
17     mkdir -p $out
18     ln -s $graphiteLocalSettings $out/graphite_local_settings.py
19   '';
21   graphiteLocalSettings = pkgs.writeText "graphite_local_settings.py" (
22     "STATIC_ROOT = '${staticDir}'\n" +
23     optionalString (config.time.timeZone != null) "TIME_ZONE = '${config.time.timeZone}'\n"
24     + cfg.web.extraConfig
25   );
27   seyrenConfig = {
28     SEYREN_URL = cfg.seyren.seyrenUrl;
29     MONGO_URL = cfg.seyren.mongoUrl;
30     GRAPHITE_URL = cfg.seyren.graphiteUrl;
31   } // cfg.seyren.extraConfig;
33   configDir = pkgs.buildEnv {
34     name = "graphite-config";
35     paths = lists.filter (el: el != null) [
36       (writeTextOrNull "carbon.conf" cfg.carbon.config)
37       (writeTextOrNull "storage-aggregation.conf" cfg.carbon.storageAggregation)
38       (writeTextOrNull "storage-schemas.conf" cfg.carbon.storageSchemas)
39       (writeTextOrNull "blacklist.conf" cfg.carbon.blacklist)
40       (writeTextOrNull "whitelist.conf" cfg.carbon.whitelist)
41       (writeTextOrNull "rewrite-rules.conf" cfg.carbon.rewriteRules)
42       (writeTextOrNull "relay-rules.conf" cfg.carbon.relayRules)
43       (writeTextOrNull "aggregation-rules.conf" cfg.carbon.aggregationRules)
44     ];
45   };
47   carbonOpts = name: with config.ids; ''
48     --nodaemon --syslog --prefix=${name} --pidfile /run/${name}/${name}.pid ${name}
49   '';
51   carbonEnv = {
52     PYTHONPATH = let
53       cenv = pkgs.python3.buildEnv.override {
54         extraLibs = [ pkgs.python3Packages.carbon ];
55       };
56     in "${cenv}/${pkgs.python3.sitePackages}";
57     GRAPHITE_ROOT = dataDir;
58     GRAPHITE_CONF_DIR = configDir;
59     GRAPHITE_STORAGE_DIR = dataDir;
60   };
62 in {
64   imports = [
65     (mkRemovedOptionModule ["services" "graphite" "api"] "")
66     (mkRemovedOptionModule ["services" "graphite" "beacon"] "")
67     (mkRemovedOptionModule ["services" "graphite" "pager"] "")
68   ];
70   ###### interface
72   options.services.graphite = {
73     dataDir = mkOption {
74       type = types.path;
75       default = "/var/db/graphite";
76       description = lib.mdDoc ''
77         Data directory for graphite.
78       '';
79     };
81     web = {
82       enable = mkOption {
83         description = lib.mdDoc "Whether to enable graphite web frontend.";
84         default = false;
85         type = types.bool;
86       };
88       listenAddress = mkOption {
89         description = lib.mdDoc "Graphite web frontend listen address.";
90         default = "127.0.0.1";
91         type = types.str;
92       };
94       port = mkOption {
95         description = lib.mdDoc "Graphite web frontend port.";
96         default = 8080;
97         type = types.int;
98       };
100       extraConfig = mkOption {
101         type = types.str;
102         default = "";
103         description = lib.mdDoc ''
104           Graphite webapp settings. See:
105           <http://graphite.readthedocs.io/en/latest/config-local-settings.html>
106         '';
107       };
108     };
110     carbon = {
111       config = mkOption {
112         description = lib.mdDoc "Content of carbon configuration file.";
113         default = ''
114           [cache]
115           # Listen on localhost by default for security reasons
116           UDP_RECEIVER_INTERFACE = 127.0.0.1
117           PICKLE_RECEIVER_INTERFACE = 127.0.0.1
118           LINE_RECEIVER_INTERFACE = 127.0.0.1
119           CACHE_QUERY_INTERFACE = 127.0.0.1
120           # Do not log every update
121           LOG_UPDATES = False
122           LOG_CACHE_HITS = False
123         '';
124         type = types.str;
125       };
127       enableCache = mkOption {
128         description = lib.mdDoc "Whether to enable carbon cache, the graphite storage daemon.";
129         default = false;
130         type = types.bool;
131       };
133       storageAggregation = mkOption {
134         description = lib.mdDoc "Defines how to aggregate data to lower-precision retentions.";
135         default = null;
136         type = types.nullOr types.str;
137         example = ''
138           [all_min]
139           pattern = \.min$
140           xFilesFactor = 0.1
141           aggregationMethod = min
142         '';
143       };
145       storageSchemas = mkOption {
146         description = lib.mdDoc "Defines retention rates for storing metrics.";
147         default = "";
148         type = types.nullOr types.str;
149         example = ''
150           [apache_busyWorkers]
151           pattern = ^servers\.www.*\.workers\.busyWorkers$
152           retentions = 15s:7d,1m:21d,15m:5y
153         '';
154       };
156       blacklist = mkOption {
157         description = lib.mdDoc "Any metrics received which match one of the experssions will be dropped.";
158         default = null;
159         type = types.nullOr types.str;
160         example = "^some\\.noisy\\.metric\\.prefix\\..*";
161       };
163       whitelist = mkOption {
164         description = lib.mdDoc "Only metrics received which match one of the experssions will be persisted.";
165         default = null;
166         type = types.nullOr types.str;
167         example = ".*";
168       };
170       rewriteRules = mkOption {
171         description = lib.mdDoc ''
172           Regular expression patterns that can be used to rewrite metric names
173           in a search and replace fashion.
174         '';
175         default = null;
176         type = types.nullOr types.str;
177         example = ''
178           [post]
179           _sum$ =
180           _avg$ =
181         '';
182       };
184       enableRelay = mkOption {
185         description = lib.mdDoc "Whether to enable carbon relay, the carbon replication and sharding service.";
186         default = false;
187         type = types.bool;
188       };
190       relayRules = mkOption {
191         description = lib.mdDoc "Relay rules are used to send certain metrics to a certain backend.";
192         default = null;
193         type = types.nullOr types.str;
194         example = ''
195           [example]
196           pattern = ^mydata\.foo\..+
197           servers = 10.1.2.3, 10.1.2.4:2004, myserver.mydomain.com
198         '';
199       };
201       enableAggregator = mkOption {
202         description = lib.mdDoc "Whether to enable carbon aggregator, the carbon buffering service.";
203         default = false;
204         type = types.bool;
205       };
207       aggregationRules = mkOption {
208         description = lib.mdDoc "Defines if and how received metrics will be aggregated.";
209         default = null;
210         type = types.nullOr types.str;
211         example = ''
212           <env>.applications.<app>.all.requests (60) = sum <env>.applications.<app>.*.requests
213           <env>.applications.<app>.all.latency (60) = avg <env>.applications.<app>.*.latency
214         '';
215       };
216     };
218     seyren = {
219       enable = mkOption {
220         description = lib.mdDoc "Whether to enable seyren service.";
221         default = false;
222         type = types.bool;
223       };
225       port = mkOption {
226         description = lib.mdDoc "Seyren listening port.";
227         default = 8081;
228         type = types.int;
229       };
231       seyrenUrl = mkOption {
232         default = "http://localhost:${toString cfg.seyren.port}/";
233         defaultText = literalExpression ''"http://localhost:''${toString config.${opt.seyren.port}}/"'';
234         description = lib.mdDoc "Host where seyren is accessible.";
235         type = types.str;
236       };
238       graphiteUrl = mkOption {
239         default = "http://${cfg.web.listenAddress}:${toString cfg.web.port}";
240         defaultText = literalExpression ''"http://''${config.${opt.web.listenAddress}}:''${toString config.${opt.web.port}}"'';
241         description = lib.mdDoc "Host where graphite service runs.";
242         type = types.str;
243       };
245       mongoUrl = mkOption {
246         default = "mongodb://${config.services.mongodb.bind_ip}:27017/seyren";
247         defaultText = literalExpression ''"mongodb://''${config.services.mongodb.bind_ip}:27017/seyren"'';
248         description = lib.mdDoc "Mongodb connection string.";
249         type = types.str;
250       };
252       extraConfig = mkOption {
253         default = {};
254         description = lib.mdDoc ''
255           Extra seyren configuration. See
256           <https://github.com/scobal/seyren#config>
257         '';
258         type = types.attrsOf types.str;
259         example = literalExpression ''
260           {
261             GRAPHITE_USERNAME = "user";
262             GRAPHITE_PASSWORD = "pass";
263           }
264         '';
265       };
266     };
267   };
269   ###### implementation
271   config = mkMerge [
272     (mkIf cfg.carbon.enableCache {
273       systemd.services.carbonCache = let name = "carbon-cache"; in {
274         description = "Graphite Data Storage Backend";
275         wantedBy = [ "multi-user.target" ];
276         after = [ "network.target" ];
277         environment = carbonEnv;
278         serviceConfig = {
279           RuntimeDirectory = name;
280           ExecStart = "${pkgs.python3Packages.twisted}/bin/twistd ${carbonOpts name}";
281           User = "graphite";
282           Group = "graphite";
283           PermissionsStartOnly = true;
284           PIDFile="/run/${name}/${name}.pid";
285         };
286         preStart = ''
287           install -dm0700 -o graphite -g graphite ${cfg.dataDir}
288           install -dm0700 -o graphite -g graphite ${cfg.dataDir}/whisper
289         '';
290       };
291     })
293     (mkIf cfg.carbon.enableAggregator {
294       systemd.services.carbonAggregator = let name = "carbon-aggregator"; in {
295         enable = cfg.carbon.enableAggregator;
296         description = "Carbon Data Aggregator";
297         wantedBy = [ "multi-user.target" ];
298         after = [ "network.target" ];
299         environment = carbonEnv;
300         serviceConfig = {
301           RuntimeDirectory = name;
302           ExecStart = "${pkgs.python3Packages.twisted}/bin/twistd ${carbonOpts name}";
303           User = "graphite";
304           Group = "graphite";
305           PIDFile="/run/${name}/${name}.pid";
306         };
307       };
308     })
310     (mkIf cfg.carbon.enableRelay {
311       systemd.services.carbonRelay = let name = "carbon-relay"; in {
312         description = "Carbon Data Relay";
313         wantedBy = [ "multi-user.target" ];
314         after = [ "network.target" ];
315         environment = carbonEnv;
316         serviceConfig = {
317           RuntimeDirectory = name;
318           ExecStart = "${pkgs.python3Packages.twisted}/bin/twistd ${carbonOpts name}";
319           User = "graphite";
320           Group = "graphite";
321           PIDFile="/run/${name}/${name}.pid";
322         };
323       };
324     })
326     (mkIf (cfg.carbon.enableCache || cfg.carbon.enableAggregator || cfg.carbon.enableRelay) {
327       environment.systemPackages = [
328         pkgs.python3Packages.carbon
329       ];
330     })
332     (mkIf cfg.web.enable ({
333       systemd.services.graphiteWeb = {
334         description = "Graphite Web Interface";
335         wantedBy = [ "multi-user.target" ];
336         after = [ "network.target" ];
337         path = [ pkgs.perl ];
338         environment = {
339           PYTHONPATH = let
340               penv = pkgs.python3.buildEnv.override {
341                 extraLibs = [
342                   pkgs.python3Packages.graphite-web
343                 ];
344               };
345               penvPack = "${penv}/${pkgs.python3.sitePackages}";
346             in concatStringsSep ":" [
347                  "${graphiteLocalSettingsDir}"
348                  "${penvPack}"
349                  # explicitly adding pycairo in path because it cannot be imported via buildEnv
350                  "${pkgs.python3Packages.pycairo}/${pkgs.python3.sitePackages}"
351                ];
352           DJANGO_SETTINGS_MODULE = "graphite.settings";
353           GRAPHITE_SETTINGS_MODULE = "graphite_local_settings";
354           GRAPHITE_CONF_DIR = configDir;
355           GRAPHITE_STORAGE_DIR = dataDir;
356           LD_LIBRARY_PATH = "${pkgs.cairo.out}/lib";
357         };
358         serviceConfig = {
359           ExecStart = ''
360             ${pkgs.python3Packages.waitress-django}/bin/waitress-serve-django \
361               --host=${cfg.web.listenAddress} --port=${toString cfg.web.port}
362           '';
363           User = "graphite";
364           Group = "graphite";
365           PermissionsStartOnly = true;
366         };
367         preStart = ''
368           if ! test -e ${dataDir}/db-created; then
369             mkdir -p ${dataDir}/{whisper/,log/webapp/}
370             chmod 0700 ${dataDir}/{whisper/,log/webapp/}
372             ${pkgs.python3Packages.django}/bin/django-admin.py migrate --noinput
374             chown -R graphite:graphite ${dataDir}
376             touch ${dataDir}/db-created
377           fi
379           # Only collect static files when graphite_web changes.
380           if ! [ "${dataDir}/current_graphite_web" -ef "${pkgs.python3Packages.graphite-web}" ]; then
381             mkdir -p ${staticDir}
382             ${pkgs.python3Packages.django}/bin/django-admin.py collectstatic  --noinput --clear
383             chown -R graphite:graphite ${staticDir}
384             ln -sfT "${pkgs.python3Packages.graphite-web}" "${dataDir}/current_graphite_web"
385           fi
386         '';
387       };
389       environment.systemPackages = [ pkgs.python3Packages.graphite-web ];
390     }))
392     (mkIf cfg.seyren.enable {
393       systemd.services.seyren = {
394         description = "Graphite Alerting Dashboard";
395         wantedBy = [ "multi-user.target" ];
396         after = [ "network.target" "mongodb.service" ];
397         environment = seyrenConfig;
398         serviceConfig = {
399           ExecStart = "${pkgs.seyren}/bin/seyren -httpPort ${toString cfg.seyren.port}";
400           WorkingDirectory = dataDir;
401           User = "graphite";
402           Group = "graphite";
403         };
404         preStart = ''
405           if ! test -e ${dataDir}/db-created; then
406             mkdir -p ${dataDir}
407             chown graphite:graphite ${dataDir}
408           fi
409         '';
410       };
412       services.mongodb.enable = mkDefault true;
413     })
415     (mkIf (
416       cfg.carbon.enableCache || cfg.carbon.enableAggregator || cfg.carbon.enableRelay ||
417       cfg.web.enable || cfg.seyren.enable
418      ) {
419       users.users.graphite = {
420         uid = config.ids.uids.graphite;
421         group = "graphite";
422         description = "Graphite daemon user";
423         home = dataDir;
424       };
425       users.groups.graphite.gid = config.ids.gids.graphite;
426     })
427   ];