master
go 531 lines 23.1 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package nvme
4
5 import (
6 "context"
7 "encoding/json"
8 "errors"
9 "fmt"
10 "os"
11 "strings"
12 "testing"
13
14 "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/collecttest"
15
16 "github.com/stretchr/testify/assert"
17 "github.com/stretchr/testify/require"
18 )
19
20 var (
21 dataConfigJSON, _ = os.ReadFile("testdata/config.json")
22 dataConfigYAML, _ = os.ReadFile("testdata/config.yaml")
23
24 dataNVMeListEmptyJson, _ = os.ReadFile("testdata/nvme-list-empty.json")
25
26 dataVer23NVMeListJson, _ = os.ReadFile("testdata/v2.3/nvme-list.json")
27 dataVer23NVMeSmartLogJson, _ = os.ReadFile("testdata/v2.3/nvme-smart-log.json")
28 dataVer23NVMeSmartLogStringJson, _ = os.ReadFile("testdata/v2.3/nvme-smart-log-string.json")
29 dataVer23NVMeSmartLogFloatJson, _ = os.ReadFile("testdata/v2.3/nvme-smart-log-float.json")
30
31 dataVer211NVMeListJson, _ = os.ReadFile("testdata/v2.11/nvme-list.json")
32 dataVer211NVMeSmartLogJson, _ = os.ReadFile("testdata/v2.11/nvme-smart-log.json")
33 )
34
35 func Test_testDataIsValid(t *testing.T) {
36 for name, data := range map[string][]byte{
37 "dataConfigJSON": dataConfigJSON,
38 "dataConfigYAML": dataConfigYAML,
39 "dataNVMeListEmptyJSON": dataNVMeListEmptyJson,
40 "dataNVMeListJSON": dataVer23NVMeListJson,
41 "dataNVMeSmartLogStringJSON": dataVer23NVMeSmartLogStringJson,
42 "dataNVMeSmartLogFloatJSON": dataVer23NVMeSmartLogFloatJson,
43 "dataVer211NVMeListJson": dataVer211NVMeListJson,
44 "dataVer211NVMeSmartLogJson": dataVer211NVMeSmartLogJson,
45 } {
46 require.NotNil(t, data, name)
47 }
48 }
49
50 func TestCollector_ConfigurationSerialize(t *testing.T) {
51 collecttest.TestConfigurationSerialize(t, &Collector{}, dataConfigJSON, dataConfigYAML)
52 }
53
54 func TestCollector_Init(t *testing.T) {
55 tests := map[string]struct {
56 config Config
57 wantFail bool
58 }{
59 "success with default config": {
60 wantFail: false,
61 config: New().Config,
62 },
63 }
64
65 for name, test := range tests {
66 t.Run(name, func(t *testing.T) {
67 collr := New()
68
69 if test.wantFail {
70 assert.Error(t, collr.Init(context.Background()))
71 } else {
72 assert.NoError(t, collr.Init(context.Background()))
73 }
74 })
75 }
76 }
77
78 func TestCollector_Charts(t *testing.T) {
79 assert.NotNil(t, New().Charts())
80 }
81
82 func TestCollector_Cleanup(t *testing.T) {
83 assert.NotPanics(t, func() { New().Cleanup(context.Background()) })
84 }
85
86 func TestCollector_Check(t *testing.T) {
87 tests := map[string]struct {
88 wantFail bool
89 prepare func(*Collector)
90 }{
91 "success if all calls successful": {
92 wantFail: false,
93 prepare: prepareCaseVer23OK,
94 },
95 "fails if 'nvme list' returns an empty list": {
96 wantFail: true,
97 prepare: prepareCaseEmptyList,
98 },
99 "fails if 'nvme list' returns an error": {
100 wantFail: true,
101 prepare: prepareCaseErrOnList,
102 },
103 "fails if 'nvme smart-log' returns an error": {
104 wantFail: true,
105 prepare: prepareCaseErrOnSmartLog,
106 },
107 }
108
109 for name, test := range tests {
110 t.Run(name, func(t *testing.T) {
111 collr := New()
112
113 test.prepare(collr)
114
115 if test.wantFail {
116 assert.Error(t, collr.Check(context.Background()))
117 } else {
118 assert.NoError(t, collr.Check(context.Background()))
119 }
120 })
121 }
122 }
123
124 func TestCollector_Collect(t *testing.T) {
125 type testCaseStep struct {
126 prepare func(*Collector)
127 check func(*testing.T, *Collector)
128 }
129
130 tests := map[string][]testCaseStep{
131 "v2.11: success if all calls successful": {
132 {
133 prepare: prepareCaseVer211OK,
134 check: func(t *testing.T, collr *Collector) {
135 mx := collr.Collect(context.Background())
136
137 expected := map[string]int64{
138 "device_nvme0_available_spare": 100,
139 "device_nvme0_controller_busy_time": 4172940,
140 "device_nvme0_critical_comp_time": 0,
141 "device_nvme0_critical_warning_available_spare": 0,
142 "device_nvme0_critical_warning_nvm_subsystem_reliability": 0,
143 "device_nvme0_critical_warning_persistent_memory_read_only": 0,
144 "device_nvme0_critical_warning_read_only": 0,
145 "device_nvme0_critical_warning_temp_threshold": 0,
146 "device_nvme0_critical_warning_volatile_mem_backup_failed": 0,
147 "device_nvme0_data_units_read": 91155062272000,
148 "device_nvme0_data_units_written": 987485941760000,
149 "device_nvme0_host_read_commands": 5808178366,
150 "device_nvme0_host_write_commands": 24273507789,
151 "device_nvme0_media_errors": 0,
152 "device_nvme0_num_err_log_entries": 212,
153 "device_nvme0_percentage_used": 30,
154 "device_nvme0_power_cycles": 99,
155 "device_nvme0_power_on_time": 87926400,
156 "device_nvme0_temperature": 50,
157 "device_nvme0_thm_temp1_total_time": 1474,
158 "device_nvme0_thm_temp1_trans_count": 3,
159 "device_nvme0_thm_temp2_total_time": 0,
160 "device_nvme0_thm_temp2_trans_count": 0,
161 "device_nvme0_unsafe_shutdowns": 49,
162 "device_nvme0_warning_temp_time": 0,
163 "device_nvme1_available_spare": 100,
164 "device_nvme1_controller_busy_time": 4172940,
165 "device_nvme1_critical_comp_time": 0,
166 "device_nvme1_critical_warning_available_spare": 0,
167 "device_nvme1_critical_warning_nvm_subsystem_reliability": 0,
168 "device_nvme1_critical_warning_persistent_memory_read_only": 0,
169 "device_nvme1_critical_warning_read_only": 0,
170 "device_nvme1_critical_warning_temp_threshold": 0,
171 "device_nvme1_critical_warning_volatile_mem_backup_failed": 0,
172 "device_nvme1_data_units_read": 91155062272000,
173 "device_nvme1_data_units_written": 987485941760000,
174 "device_nvme1_host_read_commands": 5808178366,
175 "device_nvme1_host_write_commands": 24273507789,
176 "device_nvme1_media_errors": 0,
177 "device_nvme1_num_err_log_entries": 212,
178 "device_nvme1_percentage_used": 30,
179 "device_nvme1_power_cycles": 99,
180 "device_nvme1_power_on_time": 87926400,
181 "device_nvme1_temperature": 50,
182 "device_nvme1_thm_temp1_total_time": 1474,
183 "device_nvme1_thm_temp1_trans_count": 3,
184 "device_nvme1_thm_temp2_total_time": 0,
185 "device_nvme1_thm_temp2_trans_count": 0,
186 "device_nvme1_unsafe_shutdowns": 49,
187 "device_nvme1_warning_temp_time": 0,
188 "device_nvme2_available_spare": 100,
189 "device_nvme2_controller_busy_time": 4172940,
190 "device_nvme2_critical_comp_time": 0,
191 "device_nvme2_critical_warning_available_spare": 0,
192 "device_nvme2_critical_warning_nvm_subsystem_reliability": 0,
193 "device_nvme2_critical_warning_persistent_memory_read_only": 0,
194 "device_nvme2_critical_warning_read_only": 0,
195 "device_nvme2_critical_warning_temp_threshold": 0,
196 "device_nvme2_critical_warning_volatile_mem_backup_failed": 0,
197 "device_nvme2_data_units_read": 91155062272000,
198 "device_nvme2_data_units_written": 987485941760000,
199 "device_nvme2_host_read_commands": 5808178366,
200 "device_nvme2_host_write_commands": 24273507789,
201 "device_nvme2_media_errors": 0,
202 "device_nvme2_num_err_log_entries": 212,
203 "device_nvme2_percentage_used": 30,
204 "device_nvme2_power_cycles": 99,
205 "device_nvme2_power_on_time": 87926400,
206 "device_nvme2_temperature": 50,
207 "device_nvme2_thm_temp1_total_time": 1474,
208 "device_nvme2_thm_temp1_trans_count": 3,
209 "device_nvme2_thm_temp2_total_time": 0,
210 "device_nvme2_thm_temp2_trans_count": 0,
211 "device_nvme2_unsafe_shutdowns": 49,
212 "device_nvme2_warning_temp_time": 0,
213 }
214
215 assert.Equal(t, expected, mx)
216 },
217 },
218 },
219 "v2.3: success if all calls successful": {
220 {
221 prepare: prepareCaseVer23OK,
222 check: func(t *testing.T, collr *Collector) {
223 mx := collr.Collect(context.Background())
224
225 expected := map[string]int64{
226 "device_nvme0_available_spare": 100,
227 "device_nvme0_controller_busy_time": 497040,
228 "device_nvme0_critical_comp_time": 0,
229 "device_nvme0_critical_warning_available_spare": 0,
230 "device_nvme0_critical_warning_nvm_subsystem_reliability": 0,
231 "device_nvme0_critical_warning_persistent_memory_read_only": 0,
232 "device_nvme0_critical_warning_read_only": 0,
233 "device_nvme0_critical_warning_temp_threshold": 0,
234 "device_nvme0_critical_warning_volatile_mem_backup_failed": 0,
235 "device_nvme0_data_units_read": 5068041216000,
236 "device_nvme0_data_units_written": 69712734208000,
237 "device_nvme0_host_read_commands": 313528805,
238 "device_nvme0_host_write_commands": 1928062610,
239 "device_nvme0_media_errors": 0,
240 "device_nvme0_num_err_log_entries": 110,
241 "device_nvme0_percentage_used": 2,
242 "device_nvme0_power_cycles": 64,
243 "device_nvme0_power_on_time": 17906400,
244 "device_nvme0_temperature": 36,
245 "device_nvme0_thm_temp1_total_time": 0,
246 "device_nvme0_thm_temp1_trans_count": 0,
247 "device_nvme0_thm_temp2_total_time": 0,
248 "device_nvme0_thm_temp2_trans_count": 0,
249 "device_nvme0_unsafe_shutdowns": 39,
250 "device_nvme0_warning_temp_time": 0,
251 "device_nvme1_available_spare": 100,
252 "device_nvme1_controller_busy_time": 497040,
253 "device_nvme1_critical_comp_time": 0,
254 "device_nvme1_critical_warning_available_spare": 0,
255 "device_nvme1_critical_warning_nvm_subsystem_reliability": 0,
256 "device_nvme1_critical_warning_persistent_memory_read_only": 0,
257 "device_nvme1_critical_warning_read_only": 0,
258 "device_nvme1_critical_warning_temp_threshold": 0,
259 "device_nvme1_critical_warning_volatile_mem_backup_failed": 0,
260 "device_nvme1_data_units_read": 5068041216000,
261 "device_nvme1_data_units_written": 69712734208000,
262 "device_nvme1_host_read_commands": 313528805,
263 "device_nvme1_host_write_commands": 1928062610,
264 "device_nvme1_media_errors": 0,
265 "device_nvme1_num_err_log_entries": 110,
266 "device_nvme1_percentage_used": 2,
267 "device_nvme1_power_cycles": 64,
268 "device_nvme1_power_on_time": 17906400,
269 "device_nvme1_temperature": 36,
270 "device_nvme1_thm_temp1_total_time": 0,
271 "device_nvme1_thm_temp1_trans_count": 0,
272 "device_nvme1_thm_temp2_total_time": 0,
273 "device_nvme1_thm_temp2_trans_count": 0,
274 "device_nvme1_unsafe_shutdowns": 39,
275 "device_nvme1_warning_temp_time": 0,
276 }
277
278 assert.Equal(t, expected, mx)
279 },
280 },
281 },
282 "v2.3: success if all calls successful with string values": {
283 {
284 prepare: prepareCaseVer23StringValuesOK,
285 check: func(t *testing.T, collr *Collector) {
286 mx := collr.Collect(context.Background())
287
288 expected := map[string]int64{
289 "device_nvme0_available_spare": 100,
290 "device_nvme0_controller_busy_time": 497040,
291 "device_nvme0_critical_comp_time": 0,
292 "device_nvme0_critical_warning_available_spare": 0,
293 "device_nvme0_critical_warning_nvm_subsystem_reliability": 0,
294 "device_nvme0_critical_warning_persistent_memory_read_only": 0,
295 "device_nvme0_critical_warning_read_only": 0,
296 "device_nvme0_critical_warning_temp_threshold": 0,
297 "device_nvme0_critical_warning_volatile_mem_backup_failed": 0,
298 "device_nvme0_data_units_read": 5068041216000,
299 "device_nvme0_data_units_written": 69712734208000,
300 "device_nvme0_host_read_commands": 313528805,
301 "device_nvme0_host_write_commands": 1928062610,
302 "device_nvme0_media_errors": 0,
303 "device_nvme0_num_err_log_entries": 110,
304 "device_nvme0_percentage_used": 2,
305 "device_nvme0_power_cycles": 64,
306 "device_nvme0_power_on_time": 17906400,
307 "device_nvme0_temperature": 36,
308 "device_nvme0_thm_temp1_total_time": 0,
309 "device_nvme0_thm_temp1_trans_count": 0,
310 "device_nvme0_thm_temp2_total_time": 0,
311 "device_nvme0_thm_temp2_trans_count": 0,
312 "device_nvme0_unsafe_shutdowns": 39,
313 "device_nvme0_warning_temp_time": 0,
314 "device_nvme1_available_spare": 100,
315 "device_nvme1_controller_busy_time": 497040,
316 "device_nvme1_critical_comp_time": 0,
317 "device_nvme1_critical_warning_available_spare": 0,
318 "device_nvme1_critical_warning_nvm_subsystem_reliability": 0,
319 "device_nvme1_critical_warning_persistent_memory_read_only": 0,
320 "device_nvme1_critical_warning_read_only": 0,
321 "device_nvme1_critical_warning_temp_threshold": 0,
322 "device_nvme1_critical_warning_volatile_mem_backup_failed": 0,
323 "device_nvme1_data_units_read": 5068041216000,
324 "device_nvme1_data_units_written": 69712734208000,
325 "device_nvme1_host_read_commands": 313528805,
326 "device_nvme1_host_write_commands": 1928062610,
327 "device_nvme1_media_errors": 0,
328 "device_nvme1_num_err_log_entries": 110,
329 "device_nvme1_percentage_used": 2,
330 "device_nvme1_power_cycles": 64,
331 "device_nvme1_power_on_time": 17906400,
332 "device_nvme1_temperature": 36,
333 "device_nvme1_thm_temp1_total_time": 0,
334 "device_nvme1_thm_temp1_trans_count": 0,
335 "device_nvme1_thm_temp2_total_time": 0,
336 "device_nvme1_thm_temp2_trans_count": 0,
337 "device_nvme1_unsafe_shutdowns": 39,
338 "device_nvme1_warning_temp_time": 0,
339 }
340
341 assert.Equal(t, expected, mx)
342 },
343 },
344 },
345 "v2.3: success if all calls successful with float values": {
346 {
347 prepare: prepareCaseVer23FloatValuesOK,
348 check: func(t *testing.T, collr *Collector) {
349 mx := collr.Collect(context.Background())
350
351 expected := map[string]int64{
352 "device_nvme0_available_spare": 100,
353 "device_nvme0_controller_busy_time": 497040,
354 "device_nvme0_critical_comp_time": 0,
355 "device_nvme0_critical_warning_available_spare": 0,
356 "device_nvme0_critical_warning_nvm_subsystem_reliability": 0,
357 "device_nvme0_critical_warning_persistent_memory_read_only": 0,
358 "device_nvme0_critical_warning_read_only": 0,
359 "device_nvme0_critical_warning_temp_threshold": 0,
360 "device_nvme0_critical_warning_volatile_mem_backup_failed": 0,
361 "device_nvme0_data_units_read": 5068041216000,
362 "device_nvme0_data_units_written": 69712734208000,
363 "device_nvme0_host_read_commands": 313528805,
364 "device_nvme0_host_write_commands": 1928062610,
365 "device_nvme0_media_errors": 0,
366 "device_nvme0_num_err_log_entries": 110,
367 "device_nvme0_percentage_used": 2,
368 "device_nvme0_power_cycles": 64,
369 "device_nvme0_power_on_time": 17906400,
370 "device_nvme0_temperature": 36,
371 "device_nvme0_thm_temp1_total_time": 0,
372 "device_nvme0_thm_temp1_trans_count": 0,
373 "device_nvme0_thm_temp2_total_time": 0,
374 "device_nvme0_thm_temp2_trans_count": 0,
375 "device_nvme0_unsafe_shutdowns": 39,
376 "device_nvme0_warning_temp_time": 0,
377 "device_nvme1_available_spare": 100,
378 "device_nvme1_controller_busy_time": 497040,
379 "device_nvme1_critical_comp_time": 0,
380 "device_nvme1_critical_warning_available_spare": 0,
381 "device_nvme1_critical_warning_nvm_subsystem_reliability": 0,
382 "device_nvme1_critical_warning_persistent_memory_read_only": 0,
383 "device_nvme1_critical_warning_read_only": 0,
384 "device_nvme1_critical_warning_temp_threshold": 0,
385 "device_nvme1_critical_warning_volatile_mem_backup_failed": 0,
386 "device_nvme1_data_units_read": 5068041216000,
387 "device_nvme1_data_units_written": 69712734208000,
388 "device_nvme1_host_read_commands": 313528805,
389 "device_nvme1_host_write_commands": 1928062610,
390 "device_nvme1_media_errors": 0,
391 "device_nvme1_num_err_log_entries": 110,
392 "device_nvme1_percentage_used": 2,
393 "device_nvme1_power_cycles": 64,
394 "device_nvme1_power_on_time": 17906400,
395 "device_nvme1_temperature": 36,
396 "device_nvme1_thm_temp1_total_time": 0,
397 "device_nvme1_thm_temp1_trans_count": 0,
398 "device_nvme1_thm_temp2_total_time": 0,
399 "device_nvme1_thm_temp2_trans_count": 0,
400 "device_nvme1_unsafe_shutdowns": 39,
401 "device_nvme1_warning_temp_time": 0,
402 }
403
404 assert.Equal(t, expected, mx)
405 },
406 },
407 },
408 "fail if 'nvme list' returns an empty list": {
409 {
410 prepare: prepareCaseEmptyList,
411 check: func(t *testing.T, collr *Collector) {
412 mx := collr.Collect(context.Background())
413
414 assert.Equal(t, (map[string]int64)(nil), mx)
415 },
416 },
417 },
418 "fail if 'nvme list' returns an error": {
419 {
420 prepare: prepareCaseErrOnList,
421 check: func(t *testing.T, collr *Collector) {
422 mx := collr.Collect(context.Background())
423
424 assert.Equal(t, (map[string]int64)(nil), mx)
425 },
426 },
427 },
428 "fail if 'nvme smart-log' returns an error": {
429 {
430 prepare: prepareCaseErrOnSmartLog,
431 check: func(t *testing.T, collr *Collector) {
432 mx := collr.Collect(context.Background())
433
434 assert.Equal(t, (map[string]int64)(nil), mx)
435 },
436 },
437 },
438 }
439
440 for name, test := range tests {
441 t.Run(name, func(t *testing.T) {
442 collr := New()
443
444 for i, step := range test {
445 t.Run(fmt.Sprintf("step[%d]", i), func(t *testing.T) {
446 step.prepare(collr)
447 step.check(t, collr)
448 })
449 }
450 })
451 }
452 }
453
454 func prepareCaseVer211OK(collr *Collector) {
455 collr.exec = &mockNVMeCLIExec{
456 dataList: dataVer211NVMeListJson,
457 dataSmartLog: dataVer211NVMeSmartLogJson,
458 }
459 }
460
461 func prepareCaseVer23OK(collr *Collector) {
462 collr.exec = &mockNVMeCLIExec{
463 dataList: dataVer23NVMeListJson,
464 dataSmartLog: dataVer23NVMeSmartLogJson,
465 }
466 }
467
468 func prepareCaseVer23StringValuesOK(collr *Collector) {
469 collr.exec = &mockNVMeCLIExec{
470 dataList: dataVer23NVMeListJson,
471 dataSmartLog: dataVer23NVMeSmartLogStringJson,
472 }
473 }
474
475 func prepareCaseVer23FloatValuesOK(collr *Collector) {
476 collr.exec = &mockNVMeCLIExec{
477 dataList: dataVer23NVMeListJson,
478 dataSmartLog: dataVer23NVMeSmartLogFloatJson,
479 }
480 }
481
482 func prepareCaseEmptyList(collr *Collector) {
483 collr.exec = &mockNVMeCLIExec{
484 dataList: dataNVMeListEmptyJson,
485 }
486 }
487
488 func prepareCaseErrOnList(collr *Collector) {
489 collr.exec = &mockNVMeCLIExec{errOnList: true}
490 }
491
492 func prepareCaseErrOnSmartLog(collr *Collector) {
493 collr.exec = &mockNVMeCLIExec{errOnSmartLog: true}
494 }
495
496 type mockNVMeCLIExec struct {
497 errOnList bool
498 errOnSmartLog bool
499
500 dataList []byte
501 dataSmartLog []byte
502 }
503
504 func (m *mockNVMeCLIExec) list() (*nvmeDeviceList, error) {
505 if m.errOnList {
506 return nil, errors.New("mock.list() error")
507 }
508
509 var v nvmeDeviceList
510 if err := json.Unmarshal(m.dataList, &v); err != nil {
511 return nil, err
512 }
513
514 return &v, nil
515 }
516
517 func (m *mockNVMeCLIExec) smartLog(device string) (*nvmeDeviceSmartLog, error) {
518 if m.errOnSmartLog {
519 return nil, errors.New("mock.smartLog() error")
520 }
521 if !strings.HasPrefix(device, "/dev/") {
522 return nil, errors.New("mock.smartLog() expects device path /dev/")
523 }
524
525 var v nvmeDeviceSmartLog
526 if err := json.Unmarshal(m.dataSmartLog, &v); err != nil {
527 return nil, err
528 }
529
530 return &v, nil
531 }