vuls: init at 0.27.0
[NixPkgs.git] / nixos / modules / services / monitoring / scrutiny.nix
blob37a991674daead69e785a3dd41e01244320e1722
1 { config, lib, pkgs, ... }:
2 let
3   inherit (lib) maintainers;
4   inherit (lib.meta) getExe;
5   inherit (lib.modules) mkIf mkMerge;
6   inherit (lib.options) literalExpression mkEnableOption mkOption mkPackageOption;
7   inherit (lib.types) bool enum nullOr port str submodule;
9   cfg = config.services.scrutiny;
10   # Define the settings format used for this program
11   settingsFormat = pkgs.formats.yaml { };
14   options = {
15     services.scrutiny = {
16       enable = mkEnableOption "Scrutiny, a web application for drive monitoring";
18       package = mkPackageOption pkgs "scrutiny" { };
20       openFirewall = mkEnableOption "opening the default ports in the firewall for Scrutiny";
22       influxdb.enable = mkOption {
23         type = bool;
24         default = true;
25         description = ''
26           Enables InfluxDB on the host system using the `services.influxdb2` NixOS module
27           with default options.
29           If you already have InfluxDB configured, or wish to connect to an external InfluxDB
30           instance, disable this option.
31         '';
32       };
34       settings = mkOption {
35         description = ''
36           Scrutiny settings to be rendered into the configuration file.
38           See https://github.com/AnalogJ/scrutiny/blob/master/example.scrutiny.yaml.
39         '';
40         default = { };
41         type = submodule {
42           freeformType = settingsFormat.type;
44           options.web.listen.port = mkOption {
45             type = port;
46             default = 8080;
47             description = "Port for web application to listen on.";
48           };
50           options.web.listen.host = mkOption {
51             type = str;
52             default = "0.0.0.0";
53             description = "Interface address for web application to bind to.";
54           };
56           options.web.listen.basepath = mkOption {
57             type = str;
58             default = "";
59             example = "/scrutiny";
60             description = ''
61               If Scrutiny will be behind a path prefixed reverse proxy, you can override this
62               value to serve Scrutiny on a subpath.
63             '';
64           };
66           options.log.level = mkOption {
67             type = enum [ "INFO" "DEBUG" ];
68             default = "INFO";
69             description = "Log level for Scrutiny.";
70           };
72           options.web.influxdb.scheme = mkOption {
73             type = str;
74             default = "http";
75             description = "URL scheme to use when connecting to InfluxDB.";
76           };
78           options.web.influxdb.host = mkOption {
79             type = str;
80             default = "0.0.0.0";
81             description = "IP or hostname of the InfluxDB instance.";
82           };
84           options.web.influxdb.port = mkOption {
85             type = port;
86             default = 8086;
87             description = "The port of the InfluxDB instance.";
88           };
90           options.web.influxdb.tls.insecure_skip_verify = mkEnableOption "skipping TLS verification when connecting to InfluxDB";
92           options.web.influxdb.token = mkOption {
93             type = nullOr str;
94             default = null;
95             description = "Authentication token for connecting to InfluxDB.";
96           };
98           options.web.influxdb.org = mkOption {
99             type = nullOr str;
100             default = null;
101             description = "InfluxDB organisation under which to store data.";
102           };
104           options.web.influxdb.bucket = mkOption {
105             type = nullOr str;
106             default = null;
107             description = "InfluxDB bucket in which to store data.";
108           };
109         };
110       };
112       collector = {
113         enable = mkEnableOption "the Scrutiny metrics collector" // {
114           default = cfg.enable;
115           defaultText = lib.literalExpression "config.services.scrutiny.enable";
116         };
118         package = mkPackageOption pkgs "scrutiny-collector" { };
120         schedule = mkOption {
121           type = str;
122           default = "*:0/15";
123           description = ''
124             How often to run the collector in systemd calendar format.
125           '';
126         };
128         settings = mkOption {
129           description = ''
130             Collector settings to be rendered into the collector configuration file.
132             See https://github.com/AnalogJ/scrutiny/blob/master/example.collector.yaml.
133           '';
134           default = { };
135           type = submodule {
136             freeformType = settingsFormat.type;
138             options.host.id = mkOption {
139               type = nullOr str;
140               default = null;
141               description = "Host ID for identifying/labelling groups of disks";
142             };
144             options.api.endpoint = mkOption {
145               type = str;
146               default = "http://${cfg.settings.web.listen.host}:${toString cfg.settings.web.listen.port}";
147               defaultText = literalExpression ''"http://''${config.services.scrutiny.settings.web.listen.host}:''${config.services.scrutiny.settings.web.listen.port}"'';
148               description = "Scrutiny app API endpoint for sending metrics to.";
149             };
151             options.log.level = mkOption {
152               type = enum [ "INFO" "DEBUG" ];
153               default = "INFO";
154               description = "Log level for Scrutiny collector.";
155             };
156           };
157         };
158       };
159     };
160   };
162   config = mkMerge [
163     (mkIf cfg.enable {
164       services.influxdb2.enable = cfg.influxdb.enable;
166       networking.firewall = mkIf cfg.openFirewall {
167         allowedTCPPorts = [ cfg.settings.web.listen.port ];
168       };
170       systemd.services.scrutiny = {
171         description = "Hard Drive S.M.A.R.T Monitoring, Historical Trends & Real World Failure Thresholds";
172         wantedBy = [ "multi-user.target" ];
173         after = [ "network.target" ] ++ lib.optional cfg.influxdb.enable "influxdb2.service";
174         wants = lib.optional cfg.influxdb.enable "influxdb2.service";
175         environment = {
176           SCRUTINY_VERSION = "1";
177           SCRUTINY_WEB_DATABASE_LOCATION = "/var/lib/scrutiny/scrutiny.db";
178           SCRUTINY_WEB_SRC_FRONTEND_PATH = "${cfg.package}/share/scrutiny";
179         };
180         postStart = ''
181           for i in $(seq 300); do
182               if "${lib.getExe pkgs.curl}" --fail --silent --head "http://${cfg.settings.web.listen.host}:${toString cfg.settings.web.listen.port}" >/dev/null; then
183                   echo "Scrutiny is ready (port is open)"
184                   exit 0
185               fi
186               echo "Waiting for Scrutiny to open port..."
187               sleep 0.2
188           done
189           echo "Timeout waiting for Scrutiny to open port" >&2
190           exit 1
191         '';
192         serviceConfig = {
193           DynamicUser = true;
194           ExecStart = "${getExe cfg.package} start --config ${settingsFormat.generate "scrutiny.yaml" cfg.settings}";
195           Restart = "always";
196           StateDirectory = "scrutiny";
197           StateDirectoryMode = "0750";
198         };
199       };
200     })
201     (mkIf cfg.collector.enable {
202       services.smartd = {
203         enable = true;
204         extraOptions = [
205           "-A /var/log/smartd/"
206           "--interval=600"
207         ];
208       };
210       systemd = {
211         services.scrutiny-collector = {
212           description = "Scrutiny Collector Service";
213           after = lib.optional cfg.enable "scrutiny.service";
214           wants = lib.optional cfg.enable "scrutiny.service";
215           environment = {
216             COLLECTOR_VERSION = "1";
217             COLLECTOR_API_ENDPOINT = cfg.collector.settings.api.endpoint;
218           };
219           serviceConfig = {
220             Type = "oneshot";
221             ExecStart = "${getExe cfg.collector.package} run --config ${settingsFormat.generate "scrutiny-collector.yaml" cfg.collector.settings}";
222           };
223           startAt = cfg.collector.schedule;
224         };
226         timers.scrutiny-collector.timerConfig.Persistent = true;
227       };
228     })
229   ];
231   meta.maintainers = [ maintainers.jnsgruk ];