package collect import "testing" const smartATA = `{ "smartctl": {"version": [7, 4], "exit_status": 0, "messages": []}, "device": {"name": "/dev/sda", "type": "sat", "protocol": "ATA"}, "model_name": "WDC WD80EFZZ-68BTXN0", "serial_number": "WD-AAA", "firmware_version": "81.00A81", "user_capacity": {"blocks": 15628053168, "bytes": 8001563222016}, "rotation_rate": 5640, "smart_support": {"available": true, "enabled": true}, "smart_status": {"passed": true}, "ata_smart_data": { "self_test": {"status": {"value": 0, "string": "completed without error", "passed": true}} }, "ata_smart_attributes": {"table": [ {"id": 5, "name": "Reallocated_Sector_Ct", "value": 200, "worst": 200, "thresh": 140, "when_failed": "", "raw": {"value": 0, "string": "0"}}, {"id": 9, "name": "Power_On_Hours", "value": 90, "worst": 90, "thresh": 0, "when_failed": "", "raw": {"value": 7541, "string": "7541"}}, {"id": 194, "name": "Temperature_Celsius", "value": 116, "worst": 100, "thresh": 0, "when_failed": "", "raw": {"value": 34, "string": "34"}}, {"id": 197, "name": "Current_Pending_Sector", "value": 200, "worst": 200, "thresh": 0, "when_failed": "", "raw": {"value": 8, "string": "8"}}, {"id": 199, "name": "UDMA_CRC_Error_Count", "value": 200, "worst": 200, "thresh": 0, "when_failed": "", "raw": {"value": 2, "string": "2"}}, {"id": 188, "name": "Command_Timeout", "value": 100, "worst": 100, "thresh": 0, "when_failed": "", "raw": {"value": 196611, "string": "3 3 3"}} ]}, "power_on_time": {"hours": 7541}, "power_cycle_count": 21, "temperature": {"current": 34}, "ata_smart_self_test_log": {"standard": {"revision": 1, "table": [ {"type": {"value": 2, "string": "Extended offline"}, "status": {"value": 0, "string": "Completed without error", "passed": true}, "lifetime_hours": 7500}, {"type": {"value": 1, "string": "Short offline"}, "status": {"value": 0, "string": "Completed without error", "passed": true}, "lifetime_hours": 7400} ]}} }` const smartNVMe = `{ "smartctl": {"version": [7, 4], "exit_status": 0}, "device": {"name": "/dev/nvme0", "type": "nvme", "protocol": "NVMe"}, "model_name": "Samsung SSD 980 500GB", "serial_number": "S64ANL0T", "firmware_version": "2B4QFXO7", "user_capacity": {"blocks": 976773168, "bytes": 500107862016}, "smart_support": {"available": true, "enabled": true}, "smart_status": {"passed": true, "nvme": {"value": 0}}, "nvme_smart_health_information_log": { "critical_warning": 0, "temperature": 41, "available_spare": 100, "available_spare_threshold": 10, "percentage_used": 3, "media_errors": 0, "num_err_log_entries": 12, "unsafe_shutdowns": 30, "power_cycles": 300, "power_on_hours": 1234 }, "temperature": {"current": 41}, "power_cycle_count": 300, "power_on_time": {"hours": 1234}, "nvme_self_test_log": {"current_self_test_operation": {"value": 0, "string": "No self-test in progress"}, "table": [{"self_test_code": {"value": 1, "string": "Short"}, "self_test_result": {"value": 0, "string": "Completed without error"}, "power_on_hours": 1200}]} }` const smartStandby = `{ "smartctl": {"version": [7, 4], "exit_status": 2, "messages": [{"string": "Device is in STANDBY mode, exit(2)", "severity": "information"}]}, "device": {"name": "/dev/sdb", "type": "sat", "protocol": "ATA"} }` const smartOpenFail = `{ "smartctl": {"version": [7, 4], "exit_status": 2, "messages": [{"string": "Smartctl open device: /dev/sdz failed: No such device", "severity": "error"}]} }` const smartInProgress = `{ "smartctl": {"version": [7, 4], "exit_status": 0}, "device": {"protocol": "ATA"}, "serial_number": "X", "smart_support": {"available": true, "enabled": true}, "smart_status": {"passed": true}, "ata_smart_data": {"self_test": {"status": {"value": 249, "string": "in progress, 90% remaining", "remaining_percent": 90}}}, "ata_smart_self_test_log": {"standard": {"table": [ {"type": {"string": "Short offline"}, "status": {"string": "Completed: read failure", "passed": false}, "lifetime_hours": 10}]}} }` func TestParseSmartATA(t *testing.T) { s := parseSmart([]byte(smartATA)) if s.Error != "" || s.Standby { t.Fatalf("unexpected error/standby: %+v", s) } if s.Protocol != "ATA" || s.Serial != "WD-AAA" || !s.Supported || !s.Healthy { t.Errorf("basic fields: %+v", s) } if !s.HasTemp || s.TempC != 34 || s.PowerOnHrs != 7541 || s.PowerCycles != 21 { t.Errorf("temp/hours: %+v", s) } if a, ok := s.Attr(197); !ok || a.Raw != 8 { t.Errorf("attr 197: %+v %v", a, ok) } if a, ok := s.Attr(188); !ok || a.Raw != 3 { t.Errorf("attr 188 must be unpacked to the low word: %+v", a) } if s.LastTest == nil || s.LastTest.InProgress || !s.LastTest.Passed || s.LastTest.Type != "Extended offline" || s.LastTest.Hours != 7500 { t.Errorf("last test: %+v", s.LastTest) } } func TestParseSmartNVMe(t *testing.T) { s := parseSmart([]byte(smartNVMe)) if s.NVMe == nil { t.Fatalf("nvme log missing: %+v", s) } if s.NVMe.PercentageUsed != 3 || s.NVMe.AvailableSpare != 100 || s.NVMe.ErrLogEntries != 12 { t.Errorf("nvme: %+v", s.NVMe) } if s.TempC != 41 || s.PowerOnHrs != 1234 || !s.Healthy { t.Errorf("fields: %+v", s) } if s.LastTest == nil || s.LastTest.Type != "Short" || !s.LastTest.Passed { t.Errorf("last test: %+v", s.LastTest) } } func TestParseSmartStandby(t *testing.T) { s := parseSmart([]byte(smartStandby)) if !s.Standby || s.Error != "" { t.Errorf("standby not detected: %+v", s) } } func TestParseSmartOpenFail(t *testing.T) { s := parseSmart([]byte(smartOpenFail)) if s.Standby || s.Error == "" || s.Supported { t.Errorf("open failure not reported: %+v", s) } } func TestParseSmartInProgress(t *testing.T) { s := parseSmart([]byte(smartInProgress)) if s.LastTest == nil || !s.LastTest.InProgress || s.LastTest.Remaining != 90 { t.Errorf("in-progress test: %+v", s.LastTest) } } func TestParseSmartGarbage(t *testing.T) { s := parseSmart([]byte("not json")) if s.Error == "" { t.Errorf("expected parse error") } } func TestParseLsblk(t *testing.T) { in := `{"blockdevices": [ {"name":"sda","path":"/dev/sda","type":"disk","model":"WDC WD80EFZZ-68BTXN0","serial":"WD-AAA","size":8001563222016,"rota":true,"tran":"sata"}, {"name":"nvme0n1","path":"/dev/nvme0n1","type":"disk","model":"Samsung SSD 980 500GB","serial":"S64ANL0T","size":500107862016,"rota":false,"tran":"nvme"}, {"name":"loop0","path":"/dev/loop0","type":"loop","model":null,"serial":null,"size":4096,"rota":false,"tran":null}, {"name":"zd0","path":"/dev/zd0","type":"disk","model":null,"serial":null,"size":4096,"rota":false,"tran":null}, {"name":"sr0","path":"/dev/sr0","type":"rom","model":"DVD","serial":null,"size":1024,"rota":true,"tran":"sata"} ]}` disks, err := parseLsblk([]byte(in)) if err != nil { t.Fatal(err) } if len(disks) != 2 || disks[0].Name != "sda" || !disks[0].Rota || disks[1].Tran != "nvme" || disks[1].Rota { t.Errorf("disks = %+v", disks) } // older util-linux prints rota as a string old := `{"blockdevices": [{"name":"sda","type":"disk","size":"1000","rota":"1","tran":"sata"}]}` disks, err = parseLsblk([]byte(old)) if err != nil || len(disks) != 1 || !disks[0].Rota || disks[0].Path != "/dev/sda" { t.Errorf("old lsblk: %+v %v", disks, err) } }