master
go 760 lines 34.8 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package smartctl
4
5 import (
6 "bytes"
7 "context"
8 "fmt"
9 "os"
10 "testing"
11 "time"
12
13 "github.com/stretchr/testify/assert"
14 "github.com/stretchr/testify/require"
15 "github.com/tidwall/gjson"
16
17 "github.com/netdata/netdata/go/plugins/pkg/confopt"
18 "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/collecttest"
19 )
20
21 var (
22 dataConfigJSON, _ = os.ReadFile("testdata/config.json")
23 dataConfigYAML, _ = os.ReadFile("testdata/config.yaml")
24
25 dataTypeSataScan, _ = os.ReadFile("testdata/type-sat/scan.json")
26 dataTypeSataDeviceHDDSda, _ = os.ReadFile("testdata/type-sat/device-hdd-sda.json")
27 dataTypeSataDeviceSSDSdc, _ = os.ReadFile("testdata/type-sat/device-ssd-sdc.json")
28
29 dataTypeNvmeScan, _ = os.ReadFile("testdata/type-nvme/scan.json")
30 dataTypeNvmeDeviceNvme0, _ = os.ReadFile("testdata/type-nvme/device-nvme0.json")
31 dataTypeNvmeDeviceNvme1, _ = os.ReadFile("testdata/type-nvme/device-nvme1.json")
32
33 dataTypeScsiScan, _ = os.ReadFile("testdata/type-scsi/scan.json")
34 dataTypeScsiDeviceSda, _ = os.ReadFile("testdata/type-scsi/device-sda.json")
35 )
36
37 func Test_testDataIsValid(t *testing.T) {
38 for name, data := range map[string][]byte{
39 "dataConfigJSON": dataConfigJSON,
40 "dataConfigYAML": dataConfigYAML,
41
42 "dataTypeSataScan": dataTypeSataScan,
43 "dataTypeSataDeviceHDDSda": dataTypeSataDeviceHDDSda,
44 "dataTypeSataDeviceSSDSdc": dataTypeSataDeviceSSDSdc,
45
46 "dataTypeNvmeScan": dataTypeNvmeScan,
47 "dataTypeNvmeDeviceNvme0": dataTypeNvmeDeviceNvme0,
48 "dataTypeNvmeDeviceNvme1": dataTypeNvmeDeviceNvme1,
49
50 "dataTypeScsiScan": dataTypeScsiScan,
51 "dataTypeScsiDeviceSda": dataTypeScsiDeviceSda,
52 } {
53 require.NotNil(t, data, name)
54 }
55 }
56
57 func TestCollector_ConfigurationSerialize(t *testing.T) {
58 collecttest.TestConfigurationSerialize(t, &Collector{}, dataConfigJSON, dataConfigYAML)
59 }
60
61 func TestCollector_Init(t *testing.T) {
62 tests := map[string]struct {
63 config Config
64 wantFail bool
65 }{
66 "fails if invalid power mode": {
67 wantFail: true,
68 config: func() Config {
69 cfg := New().Config
70 cfg.NoCheckPowerMode = "invalid"
71 return cfg
72 }(),
73 },
74 "success with default config": {
75 wantFail: false,
76 config: New().Config,
77 },
78 }
79
80 for name, test := range tests {
81 t.Run(name, func(t *testing.T) {
82 collr := New()
83 collr.Config = test.config
84
85 if test.wantFail {
86 assert.Error(t, collr.Init(context.Background()))
87 } else {
88 assert.NoError(t, collr.Init(context.Background()))
89 }
90 })
91 }
92 }
93
94 func TestCollector_Cleanup(t *testing.T) {
95 tests := map[string]struct {
96 prepare func() *Collector
97 }{
98 "not initialized exec": {
99 prepare: func() *Collector {
100 return New()
101 },
102 },
103 "after check": {
104 prepare: func() *Collector {
105 collr := New()
106 collr.exec = prepareMockOkTypeSata()
107 _ = collr.Check(context.Background())
108 return collr
109 },
110 },
111 "after collect": {
112 prepare: func() *Collector {
113 collr := New()
114 collr.exec = prepareMockOkTypeSata()
115 _ = collr.Collect(context.Background())
116 return collr
117 },
118 },
119 }
120
121 for name, test := range tests {
122 t.Run(name, func(t *testing.T) {
123 collr := test.prepare()
124
125 assert.NotPanics(t, func() { collr.Cleanup(context.Background()) })
126 })
127 }
128 }
129
130 func TestCollector_Check(t *testing.T) {
131 tests := map[string]struct {
132 prepareMock func() *mockSmartctlCliExec
133 wantFail bool
134 }{
135 "success type sata devices": {
136 wantFail: false,
137 prepareMock: prepareMockOkTypeSata,
138 },
139 "success type nvme devices": {
140 wantFail: false,
141 prepareMock: prepareMockOkTypeNvme,
142 },
143 "error on scan": {
144 wantFail: true,
145 prepareMock: prepareMockErrOnScan,
146 },
147 "unexpected response on scan": {
148 wantFail: true,
149 prepareMock: prepareMockUnexpectedResponse,
150 },
151 "empty response on scan": {
152 wantFail: true,
153 prepareMock: prepareMockEmptyResponse,
154 },
155 }
156
157 for name, test := range tests {
158 t.Run(name, func(t *testing.T) {
159 collr := New()
160 mock := test.prepareMock()
161 collr.exec = mock
162
163 if test.wantFail {
164 assert.Error(t, collr.Check(context.Background()))
165 } else {
166 assert.NoError(t, collr.Check(context.Background()))
167 }
168 })
169 }
170 }
171
172 func TestCollector_Collect(t *testing.T) {
173 tests := map[string]struct {
174 prepareMock func() *mockSmartctlCliExec
175 prepareConfig func() Config
176 wantMetrics map[string]int64
177 wantCharts int
178 }{
179 "success type sata devices": {
180 prepareMock: prepareMockOkTypeSata,
181 wantCharts: 68,
182 wantMetrics: map[string]int64{
183 "device_sda_type_sat_ata_smart_error_log_summary_count": 0,
184 "device_sda_type_sat_attr_current_pending_sector_decoded": 0,
185 "device_sda_type_sat_attr_current_pending_sector_normalized": 100,
186 "device_sda_type_sat_attr_current_pending_sector_raw": 0,
187 "device_sda_type_sat_attr_load_cycle_count_decoded": 360,
188 "device_sda_type_sat_attr_load_cycle_count_normalized": 100,
189 "device_sda_type_sat_attr_load_cycle_count_raw": 360,
190 "device_sda_type_sat_attr_offline_uncorrectable_decoded": 0,
191 "device_sda_type_sat_attr_offline_uncorrectable_normalized": 100,
192 "device_sda_type_sat_attr_offline_uncorrectable_raw": 0,
193 "device_sda_type_sat_attr_power-off_retract_count_decoded": 360,
194 "device_sda_type_sat_attr_power-off_retract_count_normalized": 100,
195 "device_sda_type_sat_attr_power-off_retract_count_raw": 360,
196 "device_sda_type_sat_attr_power_cycle_count_decoded": 12,
197 "device_sda_type_sat_attr_power_cycle_count_normalized": 100,
198 "device_sda_type_sat_attr_power_cycle_count_raw": 12,
199 "device_sda_type_sat_attr_power_on_hours_decoded": 8244,
200 "device_sda_type_sat_attr_power_on_hours_normalized": 99,
201 "device_sda_type_sat_attr_power_on_hours_raw": 8244,
202 "device_sda_type_sat_attr_raw_read_error_rate_decoded": 0,
203 "device_sda_type_sat_attr_raw_read_error_rate_normalized": 100,
204 "device_sda_type_sat_attr_raw_read_error_rate_raw": 0,
205 "device_sda_type_sat_attr_reallocated_event_count_decoded": 0,
206 "device_sda_type_sat_attr_reallocated_event_count_normalized": 100,
207 "device_sda_type_sat_attr_reallocated_event_count_raw": 0,
208 "device_sda_type_sat_attr_reallocated_sector_ct_decoded": 0,
209 "device_sda_type_sat_attr_reallocated_sector_ct_normalized": 100,
210 "device_sda_type_sat_attr_reallocated_sector_ct_raw": 0,
211 "device_sda_type_sat_attr_seek_error_rate_decoded": 0,
212 "device_sda_type_sat_attr_seek_error_rate_normalized": 100,
213 "device_sda_type_sat_attr_seek_error_rate_raw": 0,
214 "device_sda_type_sat_attr_seek_time_performance_decoded": 15,
215 "device_sda_type_sat_attr_seek_time_performance_normalized": 140,
216 "device_sda_type_sat_attr_seek_time_performance_raw": 15,
217 "device_sda_type_sat_attr_spin_retry_count_decoded": 0,
218 "device_sda_type_sat_attr_spin_retry_count_normalized": 100,
219 "device_sda_type_sat_attr_spin_retry_count_raw": 0,
220 "device_sda_type_sat_attr_spin_up_time_decoded": 281,
221 "device_sda_type_sat_attr_spin_up_time_normalized": 86,
222 "device_sda_type_sat_attr_spin_up_time_raw": 25788088601,
223 "device_sda_type_sat_attr_start_stop_count_decoded": 12,
224 "device_sda_type_sat_attr_start_stop_count_normalized": 100,
225 "device_sda_type_sat_attr_start_stop_count_raw": 12,
226 "device_sda_type_sat_attr_temperature_celsius_decoded": 49,
227 "device_sda_type_sat_attr_temperature_celsius_normalized": 43,
228 "device_sda_type_sat_attr_temperature_celsius_raw": 240519741489,
229 "device_sda_type_sat_attr_throughput_performance_decoded": 48,
230 "device_sda_type_sat_attr_throughput_performance_normalized": 148,
231 "device_sda_type_sat_attr_throughput_performance_raw": 48,
232 "device_sda_type_sat_attr_udma_crc_error_count_decoded": 0,
233 "device_sda_type_sat_attr_udma_crc_error_count_normalized": 100,
234 "device_sda_type_sat_attr_udma_crc_error_count_raw": 0,
235 "device_sda_type_sat_attr_unknown_attribute_decoded": 100,
236 "device_sda_type_sat_attr_unknown_attribute_normalized": 100,
237 "device_sda_type_sat_attr_unknown_attribute_raw": 100,
238 "device_sda_type_sat_power_cycle_count": 12,
239 "device_sda_type_sat_power_on_time": 29678400,
240 "device_sda_type_sat_smart_status_failed": 0,
241 "device_sda_type_sat_smart_status_passed": 1,
242 "device_sda_type_sat_temperature": 49,
243 "device_sdc_type_sat_ata_smart_error_log_summary_count": 0,
244 "device_sdc_type_sat_attr_available_reservd_space_decoded": 100,
245 "device_sdc_type_sat_attr_available_reservd_space_normalized": 100,
246 "device_sdc_type_sat_attr_available_reservd_space_raw": 100,
247 "device_sdc_type_sat_attr_command_timeout_decoded": 0,
248 "device_sdc_type_sat_attr_command_timeout_normalized": 100,
249 "device_sdc_type_sat_attr_command_timeout_raw": 0,
250 "device_sdc_type_sat_attr_end-to-end_error_decoded": 0,
251 "device_sdc_type_sat_attr_end-to-end_error_normalized": 100,
252 "device_sdc_type_sat_attr_end-to-end_error_raw": 0,
253 "device_sdc_type_sat_attr_media_wearout_indicator_decoded": 65406,
254 "device_sdc_type_sat_attr_media_wearout_indicator_normalized": 100,
255 "device_sdc_type_sat_attr_media_wearout_indicator_raw": 65406,
256 "device_sdc_type_sat_attr_power_cycle_count_decoded": 13,
257 "device_sdc_type_sat_attr_power_cycle_count_normalized": 100,
258 "device_sdc_type_sat_attr_power_cycle_count_raw": 13,
259 "device_sdc_type_sat_attr_power_on_hours_decoded": 8244,
260 "device_sdc_type_sat_attr_power_on_hours_normalized": 100,
261 "device_sdc_type_sat_attr_power_on_hours_raw": 8244,
262 "device_sdc_type_sat_attr_reallocated_sector_ct_decoded": 0,
263 "device_sdc_type_sat_attr_reallocated_sector_ct_normalized": 100,
264 "device_sdc_type_sat_attr_reallocated_sector_ct_raw": 0,
265 "device_sdc_type_sat_attr_reported_uncorrect_decoded": 0,
266 "device_sdc_type_sat_attr_reported_uncorrect_normalized": 100,
267 "device_sdc_type_sat_attr_reported_uncorrect_raw": 0,
268 "device_sdc_type_sat_attr_temperature_celsius_decoded": 27,
269 "device_sdc_type_sat_attr_temperature_celsius_normalized": 73,
270 "device_sdc_type_sat_attr_temperature_celsius_raw": 184684970011,
271 "device_sdc_type_sat_attr_total_lbas_read_decoded": 76778,
272 "device_sdc_type_sat_attr_total_lbas_read_normalized": 253,
273 "device_sdc_type_sat_attr_total_lbas_read_raw": 76778,
274 "device_sdc_type_sat_attr_total_lbas_written_decoded": 173833,
275 "device_sdc_type_sat_attr_total_lbas_written_normalized": 253,
276 "device_sdc_type_sat_attr_total_lbas_written_raw": 173833,
277 "device_sdc_type_sat_attr_udma_crc_error_count_decoded": 0,
278 "device_sdc_type_sat_attr_udma_crc_error_count_normalized": 100,
279 "device_sdc_type_sat_attr_udma_crc_error_count_raw": 0,
280 "device_sdc_type_sat_attr_unknown_attribute_decoded": 0,
281 "device_sdc_type_sat_attr_unknown_attribute_normalized": 0,
282 "device_sdc_type_sat_attr_unknown_attribute_raw": 0,
283 "device_sdc_type_sat_attr_unknown_ssd_attribute_decoded": 4694419309637,
284 "device_sdc_type_sat_attr_unknown_ssd_attribute_normalized": 4,
285 "device_sdc_type_sat_attr_unknown_ssd_attribute_raw": 4694419309637,
286 "device_sdc_type_sat_power_cycle_count": 13,
287 "device_sdc_type_sat_power_on_time": 29678400,
288 "device_sdc_type_sat_smart_status_failed": 0,
289 "device_sdc_type_sat_smart_status_passed": 1,
290 "device_sdc_type_sat_temperature": 27,
291 },
292 },
293 "success type sata devices concurrent": {
294 prepareMock: prepareMockOkTypeSata,
295 prepareConfig: func() Config {
296 cfg := New().Config
297 cfg.ConcurrentScans = 2
298 return cfg
299 },
300 wantCharts: 68,
301 wantMetrics: map[string]int64{
302 "device_sda_type_sat_ata_smart_error_log_summary_count": 0,
303 "device_sda_type_sat_attr_current_pending_sector_decoded": 0,
304 "device_sda_type_sat_attr_current_pending_sector_normalized": 100,
305 "device_sda_type_sat_attr_current_pending_sector_raw": 0,
306 "device_sda_type_sat_attr_load_cycle_count_decoded": 360,
307 "device_sda_type_sat_attr_load_cycle_count_normalized": 100,
308 "device_sda_type_sat_attr_load_cycle_count_raw": 360,
309 "device_sda_type_sat_attr_offline_uncorrectable_decoded": 0,
310 "device_sda_type_sat_attr_offline_uncorrectable_normalized": 100,
311 "device_sda_type_sat_attr_offline_uncorrectable_raw": 0,
312 "device_sda_type_sat_attr_power-off_retract_count_decoded": 360,
313 "device_sda_type_sat_attr_power-off_retract_count_normalized": 100,
314 "device_sda_type_sat_attr_power-off_retract_count_raw": 360,
315 "device_sda_type_sat_attr_power_cycle_count_decoded": 12,
316 "device_sda_type_sat_attr_power_cycle_count_normalized": 100,
317 "device_sda_type_sat_attr_power_cycle_count_raw": 12,
318 "device_sda_type_sat_attr_power_on_hours_decoded": 8244,
319 "device_sda_type_sat_attr_power_on_hours_normalized": 99,
320 "device_sda_type_sat_attr_power_on_hours_raw": 8244,
321 "device_sda_type_sat_attr_raw_read_error_rate_decoded": 0,
322 "device_sda_type_sat_attr_raw_read_error_rate_normalized": 100,
323 "device_sda_type_sat_attr_raw_read_error_rate_raw": 0,
324 "device_sda_type_sat_attr_reallocated_event_count_decoded": 0,
325 "device_sda_type_sat_attr_reallocated_event_count_normalized": 100,
326 "device_sda_type_sat_attr_reallocated_event_count_raw": 0,
327 "device_sda_type_sat_attr_reallocated_sector_ct_decoded": 0,
328 "device_sda_type_sat_attr_reallocated_sector_ct_normalized": 100,
329 "device_sda_type_sat_attr_reallocated_sector_ct_raw": 0,
330 "device_sda_type_sat_attr_seek_error_rate_decoded": 0,
331 "device_sda_type_sat_attr_seek_error_rate_normalized": 100,
332 "device_sda_type_sat_attr_seek_error_rate_raw": 0,
333 "device_sda_type_sat_attr_seek_time_performance_decoded": 15,
334 "device_sda_type_sat_attr_seek_time_performance_normalized": 140,
335 "device_sda_type_sat_attr_seek_time_performance_raw": 15,
336 "device_sda_type_sat_attr_spin_retry_count_decoded": 0,
337 "device_sda_type_sat_attr_spin_retry_count_normalized": 100,
338 "device_sda_type_sat_attr_spin_retry_count_raw": 0,
339 "device_sda_type_sat_attr_spin_up_time_decoded": 281,
340 "device_sda_type_sat_attr_spin_up_time_normalized": 86,
341 "device_sda_type_sat_attr_spin_up_time_raw": 25788088601,
342 "device_sda_type_sat_attr_start_stop_count_decoded": 12,
343 "device_sda_type_sat_attr_start_stop_count_normalized": 100,
344 "device_sda_type_sat_attr_start_stop_count_raw": 12,
345 "device_sda_type_sat_attr_temperature_celsius_decoded": 49,
346 "device_sda_type_sat_attr_temperature_celsius_normalized": 43,
347 "device_sda_type_sat_attr_temperature_celsius_raw": 240519741489,
348 "device_sda_type_sat_attr_throughput_performance_decoded": 48,
349 "device_sda_type_sat_attr_throughput_performance_normalized": 148,
350 "device_sda_type_sat_attr_throughput_performance_raw": 48,
351 "device_sda_type_sat_attr_udma_crc_error_count_decoded": 0,
352 "device_sda_type_sat_attr_udma_crc_error_count_normalized": 100,
353 "device_sda_type_sat_attr_udma_crc_error_count_raw": 0,
354 "device_sda_type_sat_attr_unknown_attribute_decoded": 100,
355 "device_sda_type_sat_attr_unknown_attribute_normalized": 100,
356 "device_sda_type_sat_attr_unknown_attribute_raw": 100,
357 "device_sda_type_sat_power_cycle_count": 12,
358 "device_sda_type_sat_power_on_time": 29678400,
359 "device_sda_type_sat_smart_status_failed": 0,
360 "device_sda_type_sat_smart_status_passed": 1,
361 "device_sda_type_sat_temperature": 49,
362 "device_sdc_type_sat_ata_smart_error_log_summary_count": 0,
363 "device_sdc_type_sat_attr_available_reservd_space_decoded": 100,
364 "device_sdc_type_sat_attr_available_reservd_space_normalized": 100,
365 "device_sdc_type_sat_attr_available_reservd_space_raw": 100,
366 "device_sdc_type_sat_attr_command_timeout_decoded": 0,
367 "device_sdc_type_sat_attr_command_timeout_normalized": 100,
368 "device_sdc_type_sat_attr_command_timeout_raw": 0,
369 "device_sdc_type_sat_attr_end-to-end_error_decoded": 0,
370 "device_sdc_type_sat_attr_end-to-end_error_normalized": 100,
371 "device_sdc_type_sat_attr_end-to-end_error_raw": 0,
372 "device_sdc_type_sat_attr_media_wearout_indicator_decoded": 65406,
373 "device_sdc_type_sat_attr_media_wearout_indicator_normalized": 100,
374 "device_sdc_type_sat_attr_media_wearout_indicator_raw": 65406,
375 "device_sdc_type_sat_attr_power_cycle_count_decoded": 13,
376 "device_sdc_type_sat_attr_power_cycle_count_normalized": 100,
377 "device_sdc_type_sat_attr_power_cycle_count_raw": 13,
378 "device_sdc_type_sat_attr_power_on_hours_decoded": 8244,
379 "device_sdc_type_sat_attr_power_on_hours_normalized": 100,
380 "device_sdc_type_sat_attr_power_on_hours_raw": 8244,
381 "device_sdc_type_sat_attr_reallocated_sector_ct_decoded": 0,
382 "device_sdc_type_sat_attr_reallocated_sector_ct_normalized": 100,
383 "device_sdc_type_sat_attr_reallocated_sector_ct_raw": 0,
384 "device_sdc_type_sat_attr_reported_uncorrect_decoded": 0,
385 "device_sdc_type_sat_attr_reported_uncorrect_normalized": 100,
386 "device_sdc_type_sat_attr_reported_uncorrect_raw": 0,
387 "device_sdc_type_sat_attr_temperature_celsius_decoded": 27,
388 "device_sdc_type_sat_attr_temperature_celsius_normalized": 73,
389 "device_sdc_type_sat_attr_temperature_celsius_raw": 184684970011,
390 "device_sdc_type_sat_attr_total_lbas_read_decoded": 76778,
391 "device_sdc_type_sat_attr_total_lbas_read_normalized": 253,
392 "device_sdc_type_sat_attr_total_lbas_read_raw": 76778,
393 "device_sdc_type_sat_attr_total_lbas_written_decoded": 173833,
394 "device_sdc_type_sat_attr_total_lbas_written_normalized": 253,
395 "device_sdc_type_sat_attr_total_lbas_written_raw": 173833,
396 "device_sdc_type_sat_attr_udma_crc_error_count_decoded": 0,
397 "device_sdc_type_sat_attr_udma_crc_error_count_normalized": 100,
398 "device_sdc_type_sat_attr_udma_crc_error_count_raw": 0,
399 "device_sdc_type_sat_attr_unknown_attribute_decoded": 0,
400 "device_sdc_type_sat_attr_unknown_attribute_normalized": 0,
401 "device_sdc_type_sat_attr_unknown_attribute_raw": 0,
402 "device_sdc_type_sat_attr_unknown_ssd_attribute_decoded": 4694419309637,
403 "device_sdc_type_sat_attr_unknown_ssd_attribute_normalized": 4,
404 "device_sdc_type_sat_attr_unknown_ssd_attribute_raw": 4694419309637,
405 "device_sdc_type_sat_power_cycle_count": 13,
406 "device_sdc_type_sat_power_on_time": 29678400,
407 "device_sdc_type_sat_smart_status_failed": 0,
408 "device_sdc_type_sat_smart_status_passed": 1,
409 "device_sdc_type_sat_temperature": 27,
410 },
411 },
412 "success type nvme devices": {
413 prepareMock: prepareMockOkTypeNvme,
414 wantCharts: 4,
415 wantMetrics: map[string]int64{
416 "device_nvme0_type_nvme_power_cycle_count": 2,
417 "device_nvme0_type_nvme_power_on_time": 11206800,
418 "device_nvme0_type_nvme_smart_status_failed": 0,
419 "device_nvme0_type_nvme_smart_status_passed": 1,
420 "device_nvme0_type_nvme_temperature": 39,
421 },
422 },
423 "success type nvme devices with extra": {
424 prepareMock: prepareMockOkTypeNvme,
425 prepareConfig: func() Config {
426 cfg := New().Config
427 cfg.ExtraDevices = []ConfigExtraDevice{
428 {Name: "/dev/nvme1", Type: "nvme"},
429 }
430 return cfg
431 },
432 wantCharts: 8,
433 wantMetrics: map[string]int64{
434 "device_nvme0_type_nvme_power_cycle_count": 2,
435 "device_nvme0_type_nvme_power_on_time": 11206800,
436 "device_nvme0_type_nvme_smart_status_failed": 0,
437 "device_nvme0_type_nvme_smart_status_passed": 1,
438 "device_nvme0_type_nvme_temperature": 39,
439 "device_nvme1_type_nvme_power_cycle_count": 5,
440 "device_nvme1_type_nvme_power_on_time": 17038800,
441 "device_nvme1_type_nvme_smart_status_failed": 0,
442 "device_nvme1_type_nvme_smart_status_passed": 1,
443 "device_nvme1_type_nvme_temperature": 36,
444 },
445 },
446 "success type scsi devices": {
447 prepareMock: prepareMockOkTypeScsi,
448 wantCharts: 7,
449 wantMetrics: map[string]int64{
450 "device_sda_type_scsi_power_cycle_count": 4,
451 "device_sda_type_scsi_power_on_time": 5908920,
452 "device_sda_type_scsi_scsi_error_log_read_total_errors_corrected": 647736,
453 "device_sda_type_scsi_scsi_error_log_read_total_uncorrected_errors": 0,
454 "device_sda_type_scsi_scsi_error_log_verify_total_errors_corrected": 0,
455 "device_sda_type_scsi_scsi_error_log_verify_total_uncorrected_errors": 0,
456 "device_sda_type_scsi_scsi_error_log_write_total_errors_corrected": 0,
457 "device_sda_type_scsi_scsi_error_log_write_total_uncorrected_errors": 0,
458 "device_sda_type_scsi_smart_status_failed": 0,
459 "device_sda_type_scsi_smart_status_passed": 1,
460 "device_sda_type_scsi_temperature": 34,
461 },
462 },
463 "success type sata devices non-fatal exit status": {
464 prepareMock: prepareMockOkTypeSataNonFatalExitStatus,
465 wantCharts: 68,
466 wantMetrics: map[string]int64{
467 "device_sda_type_sat_ata_smart_error_log_summary_count": 0,
468 "device_sda_type_sat_attr_current_pending_sector_decoded": 0,
469 "device_sda_type_sat_attr_current_pending_sector_normalized": 100,
470 "device_sda_type_sat_attr_current_pending_sector_raw": 0,
471 "device_sda_type_sat_attr_load_cycle_count_decoded": 360,
472 "device_sda_type_sat_attr_load_cycle_count_normalized": 100,
473 "device_sda_type_sat_attr_load_cycle_count_raw": 360,
474 "device_sda_type_sat_attr_offline_uncorrectable_decoded": 0,
475 "device_sda_type_sat_attr_offline_uncorrectable_normalized": 100,
476 "device_sda_type_sat_attr_offline_uncorrectable_raw": 0,
477 "device_sda_type_sat_attr_power-off_retract_count_decoded": 360,
478 "device_sda_type_sat_attr_power-off_retract_count_normalized": 100,
479 "device_sda_type_sat_attr_power-off_retract_count_raw": 360,
480 "device_sda_type_sat_attr_power_cycle_count_decoded": 12,
481 "device_sda_type_sat_attr_power_cycle_count_normalized": 100,
482 "device_sda_type_sat_attr_power_cycle_count_raw": 12,
483 "device_sda_type_sat_attr_power_on_hours_decoded": 8244,
484 "device_sda_type_sat_attr_power_on_hours_normalized": 99,
485 "device_sda_type_sat_attr_power_on_hours_raw": 8244,
486 "device_sda_type_sat_attr_raw_read_error_rate_decoded": 0,
487 "device_sda_type_sat_attr_raw_read_error_rate_normalized": 100,
488 "device_sda_type_sat_attr_raw_read_error_rate_raw": 0,
489 "device_sda_type_sat_attr_reallocated_event_count_decoded": 0,
490 "device_sda_type_sat_attr_reallocated_event_count_normalized": 100,
491 "device_sda_type_sat_attr_reallocated_event_count_raw": 0,
492 "device_sda_type_sat_attr_reallocated_sector_ct_decoded": 0,
493 "device_sda_type_sat_attr_reallocated_sector_ct_normalized": 100,
494 "device_sda_type_sat_attr_reallocated_sector_ct_raw": 0,
495 "device_sda_type_sat_attr_seek_error_rate_decoded": 0,
496 "device_sda_type_sat_attr_seek_error_rate_normalized": 100,
497 "device_sda_type_sat_attr_seek_error_rate_raw": 0,
498 "device_sda_type_sat_attr_seek_time_performance_decoded": 15,
499 "device_sda_type_sat_attr_seek_time_performance_normalized": 140,
500 "device_sda_type_sat_attr_seek_time_performance_raw": 15,
501 "device_sda_type_sat_attr_spin_retry_count_decoded": 0,
502 "device_sda_type_sat_attr_spin_retry_count_normalized": 100,
503 "device_sda_type_sat_attr_spin_retry_count_raw": 0,
504 "device_sda_type_sat_attr_spin_up_time_decoded": 281,
505 "device_sda_type_sat_attr_spin_up_time_normalized": 86,
506 "device_sda_type_sat_attr_spin_up_time_raw": 25788088601,
507 "device_sda_type_sat_attr_start_stop_count_decoded": 12,
508 "device_sda_type_sat_attr_start_stop_count_normalized": 100,
509 "device_sda_type_sat_attr_start_stop_count_raw": 12,
510 "device_sda_type_sat_attr_temperature_celsius_decoded": 49,
511 "device_sda_type_sat_attr_temperature_celsius_normalized": 43,
512 "device_sda_type_sat_attr_temperature_celsius_raw": 240519741489,
513 "device_sda_type_sat_attr_throughput_performance_decoded": 48,
514 "device_sda_type_sat_attr_throughput_performance_normalized": 148,
515 "device_sda_type_sat_attr_throughput_performance_raw": 48,
516 "device_sda_type_sat_attr_udma_crc_error_count_decoded": 0,
517 "device_sda_type_sat_attr_udma_crc_error_count_normalized": 100,
518 "device_sda_type_sat_attr_udma_crc_error_count_raw": 0,
519 "device_sda_type_sat_attr_unknown_attribute_decoded": 100,
520 "device_sda_type_sat_attr_unknown_attribute_normalized": 100,
521 "device_sda_type_sat_attr_unknown_attribute_raw": 100,
522 "device_sda_type_sat_power_cycle_count": 12,
523 "device_sda_type_sat_power_on_time": 29678400,
524 "device_sda_type_sat_smart_status_failed": 0,
525 "device_sda_type_sat_smart_status_passed": 1,
526 "device_sda_type_sat_temperature": 49,
527 "device_sdc_type_sat_ata_smart_error_log_summary_count": 0,
528 "device_sdc_type_sat_attr_available_reservd_space_decoded": 100,
529 "device_sdc_type_sat_attr_available_reservd_space_normalized": 100,
530 "device_sdc_type_sat_attr_available_reservd_space_raw": 100,
531 "device_sdc_type_sat_attr_command_timeout_decoded": 0,
532 "device_sdc_type_sat_attr_command_timeout_normalized": 100,
533 "device_sdc_type_sat_attr_command_timeout_raw": 0,
534 "device_sdc_type_sat_attr_end-to-end_error_decoded": 0,
535 "device_sdc_type_sat_attr_end-to-end_error_normalized": 100,
536 "device_sdc_type_sat_attr_end-to-end_error_raw": 0,
537 "device_sdc_type_sat_attr_media_wearout_indicator_decoded": 65406,
538 "device_sdc_type_sat_attr_media_wearout_indicator_normalized": 100,
539 "device_sdc_type_sat_attr_media_wearout_indicator_raw": 65406,
540 "device_sdc_type_sat_attr_power_cycle_count_decoded": 13,
541 "device_sdc_type_sat_attr_power_cycle_count_normalized": 100,
542 "device_sdc_type_sat_attr_power_cycle_count_raw": 13,
543 "device_sdc_type_sat_attr_power_on_hours_decoded": 8244,
544 "device_sdc_type_sat_attr_power_on_hours_normalized": 100,
545 "device_sdc_type_sat_attr_power_on_hours_raw": 8244,
546 "device_sdc_type_sat_attr_reallocated_sector_ct_decoded": 0,
547 "device_sdc_type_sat_attr_reallocated_sector_ct_normalized": 100,
548 "device_sdc_type_sat_attr_reallocated_sector_ct_raw": 0,
549 "device_sdc_type_sat_attr_reported_uncorrect_decoded": 0,
550 "device_sdc_type_sat_attr_reported_uncorrect_normalized": 100,
551 "device_sdc_type_sat_attr_reported_uncorrect_raw": 0,
552 "device_sdc_type_sat_attr_temperature_celsius_decoded": 27,
553 "device_sdc_type_sat_attr_temperature_celsius_normalized": 73,
554 "device_sdc_type_sat_attr_temperature_celsius_raw": 184684970011,
555 "device_sdc_type_sat_attr_total_lbas_read_decoded": 76778,
556 "device_sdc_type_sat_attr_total_lbas_read_normalized": 253,
557 "device_sdc_type_sat_attr_total_lbas_read_raw": 76778,
558 "device_sdc_type_sat_attr_total_lbas_written_decoded": 173833,
559 "device_sdc_type_sat_attr_total_lbas_written_normalized": 253,
560 "device_sdc_type_sat_attr_total_lbas_written_raw": 173833,
561 "device_sdc_type_sat_attr_udma_crc_error_count_decoded": 0,
562 "device_sdc_type_sat_attr_udma_crc_error_count_normalized": 100,
563 "device_sdc_type_sat_attr_udma_crc_error_count_raw": 0,
564 "device_sdc_type_sat_attr_unknown_attribute_decoded": 0,
565 "device_sdc_type_sat_attr_unknown_attribute_normalized": 0,
566 "device_sdc_type_sat_attr_unknown_attribute_raw": 0,
567 "device_sdc_type_sat_attr_unknown_ssd_attribute_decoded": 4694419309637,
568 "device_sdc_type_sat_attr_unknown_ssd_attribute_normalized": 4,
569 "device_sdc_type_sat_attr_unknown_ssd_attribute_raw": 4694419309637,
570 "device_sdc_type_sat_power_cycle_count": 13,
571 "device_sdc_type_sat_power_on_time": 29678400,
572 "device_sdc_type_sat_smart_status_failed": 0,
573 "device_sdc_type_sat_smart_status_passed": 1,
574 "device_sdc_type_sat_temperature": 27,
575 },
576 },
577 "error on scan": {
578 prepareMock: prepareMockErrOnScan,
579 },
580 "unexpected response on scan": {
581 prepareMock: prepareMockUnexpectedResponse,
582 },
583 "empty response on scan": {
584 prepareMock: prepareMockEmptyResponse,
585 },
586 }
587
588 for name, test := range tests {
589 t.Run(name, func(t *testing.T) {
590 collr := New()
591 if test.prepareConfig != nil {
592 collr.Config = test.prepareConfig()
593 }
594 mock := test.prepareMock()
595 collr.exec = mock
596 collr.ScanEvery = confopt.Duration(time.Microsecond * 1)
597 collr.PollDevicesEvery = confopt.Duration(time.Microsecond * 1)
598
599 var mx map[string]int64
600 for range 10 {
601 mx = collr.Collect(context.Background())
602 }
603
604 assert.Equal(t, test.wantMetrics, mx)
605
606 assert.Len(t, *collr.Charts(), test.wantCharts, "wantCharts")
607
608 collecttest.TestMetricsHasAllChartsDims(t, collr.Charts(), mx)
609 })
610 }
611 }
612
613 func prepareMockOkTypeSata() *mockSmartctlCliExec {
614 return &mockSmartctlCliExec{
615 errOnScan: false,
616 scanData: dataTypeSataScan,
617 deviceDataFunc: func(deviceName, deviceType, powerMode string) ([]byte, error) {
618 if deviceType != "sat" {
619 return nil, fmt.Errorf("unexpected device type %s", deviceType)
620 }
621 switch deviceName {
622 case "/dev/sda":
623 return dataTypeSataDeviceHDDSda, nil
624 case "/dev/sdc":
625 return dataTypeSataDeviceSSDSdc, nil
626 default:
627 return nil, fmt.Errorf("unexpected device name %s", deviceName)
628 }
629 },
630 }
631 }
632
633 func prepareMockOkTypeNvme() *mockSmartctlCliExec {
634 return &mockSmartctlCliExec{
635 errOnScan: false,
636 scanData: dataTypeNvmeScan,
637 deviceDataFunc: func(deviceName, deviceType, powerMode string) ([]byte, error) {
638 if deviceType != "nvme" {
639 return nil, fmt.Errorf("unexpected device type %s", deviceType)
640 }
641 switch deviceName {
642 case "/dev/nvme0":
643 return dataTypeNvmeDeviceNvme0, nil
644 case "/dev/nvme1":
645 return dataTypeNvmeDeviceNvme1, nil
646 default:
647 return nil, fmt.Errorf("unexpected device name %s", deviceName)
648 }
649 },
650 }
651 }
652
653 func prepareMockOkTypeScsi() *mockSmartctlCliExec {
654 return &mockSmartctlCliExec{
655 errOnScan: false,
656 scanData: dataTypeScsiScan,
657 deviceDataFunc: func(deviceName, deviceType, powerMode string) ([]byte, error) {
658 if deviceType != "scsi" {
659 return nil, fmt.Errorf("unexpected device type %s", deviceType)
660 }
661 switch deviceName {
662 case "/dev/sda":
663 return dataTypeScsiDeviceSda, nil
664 default:
665 return nil, fmt.Errorf("unexpected device name %s", deviceName)
666 }
667 },
668 }
669 }
670
671 func prepareMockOkTypeSataNonFatalExitStatus() *mockSmartctlCliExec {
672 return &mockSmartctlCliExec{
673 errOnScan: false,
674 scanData: dataTypeSataScan,
675 deviceDataFunc: func(deviceName, deviceType, powerMode string) ([]byte, error) {
676 if deviceType != "sat" {
677 return nil, fmt.Errorf("unexpected device type %s", deviceType)
678 }
679 // Simulate smartctl exit status 32 (bit 5): some attributes were <= threshold in the past.
680 // The data is still valid and should be processed.
681 var data []byte
682 switch deviceName {
683 case "/dev/sda":
684 data = bytes.Replace(dataTypeSataDeviceHDDSda, []byte(`"exit_status": 0`), []byte(`"exit_status": 32`), 1)
685 case "/dev/sdc":
686 data = bytes.Replace(dataTypeSataDeviceSSDSdc, []byte(`"exit_status": 0`), []byte(`"exit_status": 32`), 1)
687 default:
688 return nil, fmt.Errorf("unexpected device name %s", deviceName)
689 }
690
691 // Verify that the modified payload actually encodes smartctl.exit_status == 32.
692 v := gjson.GetBytes(data, "smartctl.exit_status")
693 if !v.Exists() || v.Int() != 32 {
694 panic("prepareMockOkTypeSataNonFatalExitStatus: failed to construct payload with smartctl.exit_status == 32")
695 }
696 return data, fmt.Errorf("exit status 32")
697 },
698 }
699 }
700
701 func prepareMockErrOnScan() *mockSmartctlCliExec {
702 return &mockSmartctlCliExec{
703 errOnScan: true,
704 }
705 }
706
707 func prepareMockUnexpectedResponse() *mockSmartctlCliExec {
708 return &mockSmartctlCliExec{
709 scanData: []byte(randomJsonData),
710 deviceDataFunc: func(_, _, _ string) ([]byte, error) { return []byte(randomJsonData), nil },
711 }
712 }
713
714 func prepareMockEmptyResponse() *mockSmartctlCliExec {
715 return &mockSmartctlCliExec{}
716 }
717
718 type mockSmartctlCliExec struct {
719 errOnScan bool
720 scanData []byte
721 deviceDataFunc func(deviceName, deviceType, powerMode string) ([]byte, error)
722 }
723
724 func (m *mockSmartctlCliExec) scan(_ bool) (*gjson.Result, error) {
725 if m.errOnScan {
726 return nil, fmt.Errorf("mock.scan() error")
727 }
728 res := gjson.ParseBytes(m.scanData)
729 return &res, nil
730 }
731
732 func (m *mockSmartctlCliExec) deviceInfo(deviceName, deviceType, powerMode string) (*gjson.Result, error) {
733 if m.deviceDataFunc == nil {
734 return nil, nil
735 }
736 bs, err := m.deviceDataFunc(deviceName, deviceType, powerMode)
737 if len(bs) == 0 {
738 return nil, err
739 }
740 res := gjson.ParseBytes(bs)
741 return &res, err
742 }
743
744 var randomJsonData = `
745 {
746 "elephant": {
747 "burn": false,
748 "mountain": true,
749 "fog": false,
750 "skin": -1561907625,
751 "burst": "anyway",
752 "shadow": 1558616893
753 },
754 "start": "ever",
755 "base": 2093056027,
756 "mission": -2007590351,
757 "victory": 999053756,
758 "die": false
759 }
760 `