1{ lib, pkgs, ... }:
2{
3 name = "nvme-rs";
4
5 meta = {
6 maintainers = with lib.maintainers; [ liberodark ];
7 };
8
9 nodes = {
10 monitor =
11 { config, pkgs, ... }:
12 {
13 virtualisation = {
14 emptyDiskImages = [
15 512
16 512
17 ];
18 };
19
20 environment.systemPackages = with pkgs; [
21 nvme-rs
22 jq
23 ];
24
25 services.nvme-rs = {
26 enable = true;
27 package = pkgs.nvme-rs;
28 settings = {
29 check_interval_secs = 60;
30
31 thresholds = {
32 temp_warning = 50;
33 temp_critical = 60;
34 wear_warning = 15;
35 wear_critical = 40;
36 spare_warning = 60;
37 error_threshold = 100;
38 };
39
40 email = {
41 smtp_server = "mail";
42 smtp_port = 25;
43 smtp_username = "nvme-monitor@example.com";
44 smtp_password_file = "/run/secrets/smtp-password";
45 from = "NVMe Monitor <nvme-monitor@example.com>";
46 to = "admin@example.com";
47 use_tls = false;
48 };
49 };
50 };
51
52 systemd.tmpfiles.rules = [
53 "f /run/secrets/smtp-password 0600 root root - testpassword"
54 ];
55
56 networking.firewall.enable = false;
57 };
58
59 mail =
60 { config, pkgs, ... }:
61 {
62 services.postfix = {
63 enable = true;
64 hostname = "mail";
65 domain = "example.com";
66
67 networks = [ "0.0.0.0/0" ];
68 relayDomains = [ "example.com" ];
69 localRecipients = [ "admin" ];
70
71 settings = {
72 main = {
73 inet_interfaces = "all";
74 inet_protocols = "ipv4";
75 smtpd_recipient_restrictions = "permit_mynetworks";
76 smtpd_relay_restrictions = "permit_mynetworks";
77 };
78 };
79 };
80
81 users.users.admin = {
82 isNormalUser = true;
83 home = "/home/admin";
84 };
85
86 networking.firewall = {
87 allowedTCPPorts = [ 25 ];
88 };
89 };
90
91 client =
92 { config, pkgs, ... }:
93 {
94 virtualisation = {
95 emptyDiskImages = [ 256 ];
96 };
97
98 environment.systemPackages = with pkgs; [
99 nvme-rs
100 jq
101 ];
102
103 environment.etc."nvme-rs/config.toml".text = ''
104 check_interval_secs = 3600
105
106 [thresholds]
107 temp_warning = 55
108 temp_critical = 65
109 wear_warning = 20
110 wear_critical = 50
111 spare_warning = 50
112 error_threshold = 5000
113 '';
114 };
115 };
116
117 testScript =
118 { nodes, ... }:
119 ''
120 import json
121
122 start_all()
123
124 for machine in [monitor, mail, client]:
125 machine.wait_for_unit("multi-user.target")
126
127 mail.wait_for_unit("postfix.service")
128 mail.wait_for_open_port(25)
129
130 client.succeed("nvme-rs check || true")
131 client.succeed("nvme-rs check --config /etc/nvme-rs/config.toml || true")
132
133 output = client.succeed("nvme-rs check --format json || echo '[]'")
134 data = json.loads(output)
135 assert isinstance(data, list), "JSON output should be a list"
136
137 monitor.wait_for_unit("nvme-rs.service")
138 monitor.succeed("systemctl is-active nvme-rs.service")
139
140 config_path = monitor.succeed(
141 "systemctl status nvme-rs | grep -oE '/nix/store[^ ]*nvme-rs.toml' | head -1"
142 ).strip()
143
144 if config_path:
145 monitor.succeed(f"grep 'check_interval_secs = 60' {config_path}")
146 monitor.succeed(f"grep 'temp_warning = 50' {config_path}")
147 monitor.succeed(f"grep 'smtp_server = \"mail\"' {config_path}")
148
149 logs = monitor.succeed("journalctl -u nvme-rs.service -n 20 --no-pager")
150 assert "Starting NVMe monitor daemon" in logs or "Check interval" in logs
151
152 monitor.succeed("test -f /run/secrets/smtp-password")
153
154 monitor.succeed("nc -zv mail 25")
155 monitor.fail("nvme-rs daemon --config /nonexistent.toml 2>&1 | grep -E 'Failed to read'")
156 '';
157}