Add smart input plugin for collecting S.M.A.R.T. data (#2449)
This commit is contained in:
committed by
Daniel Nelson
parent
002ccf3295
commit
e69c3f9d1c
426
plugins/inputs/smart/smart_test.go
Normal file
426
plugins/inputs/smart/smart_test.go
Normal file
@@ -0,0 +1,426 @@
|
||||
package smart
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
mockScanData = `/dev/ada0 -d atacam # /dev/ada0, ATA device
|
||||
`
|
||||
mockInfoAttributeData = `smartctl 6.5 2016-05-07 r4318 [Darwin 16.4.0 x86_64] (local build)
|
||||
Copyright (C) 2002-16, Bruce Allen, Christian Franke, www.smartmontools.org
|
||||
|
||||
CHECK POWER MODE not implemented, ignoring -n option
|
||||
=== START OF INFORMATION SECTION ===
|
||||
Model Family: Apple SD/SM/TS...E/F SSDs
|
||||
Device Model: APPLE SSD SM256E
|
||||
Serial Number: S0X5NZBC422720
|
||||
LU WWN Device Id: 5 002538 043584d30
|
||||
Firmware Version: CXM09A1Q
|
||||
User Capacity: 251,000,193,024 bytes [251 GB]
|
||||
Sector Sizes: 512 bytes logical, 4096 bytes physical
|
||||
Rotation Rate: Solid State Device
|
||||
Device is: In smartctl database [for details use: -P show]
|
||||
ATA Version is: ATA8-ACS T13/1699-D revision 4c
|
||||
SATA Version is: SATA 3.0, 6.0 Gb/s (current: 6.0 Gb/s)
|
||||
Local Time is: Thu Feb 9 16:48:45 2017 CET
|
||||
SMART support is: Available - device has SMART capability.
|
||||
SMART support is: Enabled
|
||||
|
||||
=== START OF READ SMART DATA SECTION ===
|
||||
SMART overall-health self-assessment test result: PASSED
|
||||
|
||||
=== START OF READ SMART DATA SECTION ===
|
||||
SMART Attributes Data Structure revision number: 1
|
||||
Vendor Specific SMART Attributes with Thresholds:
|
||||
ID# ATTRIBUTE_NAME FLAGS VALUE WORST THRESH FAIL RAW_VALUE
|
||||
1 Raw_Read_Error_Rate -O-RC- 200 200 000 - 0
|
||||
5 Reallocated_Sector_Ct PO--CK 100 100 000 - 0
|
||||
9 Power_On_Hours -O--CK 099 099 000 - 2988
|
||||
12 Power_Cycle_Count -O--CK 085 085 000 - 14879
|
||||
169 Unknown_Attribute PO--C- 253 253 010 - 2044932921600
|
||||
173 Wear_Leveling_Count -O--CK 185 185 100 - 957808640337
|
||||
190 Airflow_Temperature_Cel -O---K 055 040 045 Past 45 (Min/Max 43/57 #2689)
|
||||
192 Power-Off_Retract_Count -O--C- 097 097 000 - 14716
|
||||
194 Temperature_Celsius -O---K 066 021 000 - 34 (Min/Max 14/79)
|
||||
197 Current_Pending_Sector -O---K 100 100 000 - 0
|
||||
199 UDMA_CRC_Error_Count -O-RC- 200 200 000 - 0
|
||||
240 Head_Flying_Hours ------ 100 253 000 - 6585h+55m+23.234s
|
||||
||||||_ K auto-keep
|
||||
|||||__ C event count
|
||||
||||___ R error rate
|
||||
|||____ S speed/performance
|
||||
||_____ O updated online
|
||||
|______ P prefailure warning
|
||||
`
|
||||
)
|
||||
|
||||
func TestGatherAttributes(t *testing.T) {
|
||||
s := &Smart{
|
||||
Path: "smartctl",
|
||||
Attributes: true,
|
||||
}
|
||||
// overwriting exec commands with mock commands
|
||||
execCommand = fakeExecCommand
|
||||
var acc testutil.Accumulator
|
||||
|
||||
err := s.Gather(&acc)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 65, acc.NFields(), "Wrong number of fields gathered")
|
||||
|
||||
var testsAda0Attributes = []struct {
|
||||
fields map[string]interface{}
|
||||
tags map[string]string
|
||||
}{
|
||||
{
|
||||
map[string]interface{}{
|
||||
"value": int64(200),
|
||||
"worst": int64(200),
|
||||
"threshold": int64(0),
|
||||
"raw_value": int64(0),
|
||||
"exit_status": int(0),
|
||||
},
|
||||
map[string]string{
|
||||
"device": "/dev/ada0",
|
||||
"serial_no": "S0X5NZBC422720",
|
||||
"wwn": "5002538043584d30",
|
||||
"id": "1",
|
||||
"name": "Raw_Read_Error_Rate",
|
||||
"flags": "-O-RC-",
|
||||
"fail": "-",
|
||||
},
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"value": int64(100),
|
||||
"worst": int64(100),
|
||||
"threshold": int64(0),
|
||||
"raw_value": int64(0),
|
||||
"exit_status": int(0),
|
||||
},
|
||||
map[string]string{
|
||||
"device": "/dev/ada0",
|
||||
"serial_no": "S0X5NZBC422720",
|
||||
"wwn": "5002538043584d30",
|
||||
"id": "5",
|
||||
"name": "Reallocated_Sector_Ct",
|
||||
"flags": "PO--CK",
|
||||
"fail": "-",
|
||||
},
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"value": int64(99),
|
||||
"worst": int64(99),
|
||||
"threshold": int64(0),
|
||||
"raw_value": int64(2988),
|
||||
"exit_status": int(0),
|
||||
},
|
||||
map[string]string{
|
||||
"device": "/dev/ada0",
|
||||
"serial_no": "S0X5NZBC422720",
|
||||
"wwn": "5002538043584d30",
|
||||
"id": "9",
|
||||
"name": "Power_On_Hours",
|
||||
"flags": "-O--CK",
|
||||
"fail": "-",
|
||||
},
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"value": int64(85),
|
||||
"worst": int64(85),
|
||||
"threshold": int64(0),
|
||||
"raw_value": int64(14879),
|
||||
"exit_status": int(0),
|
||||
},
|
||||
map[string]string{
|
||||
"device": "/dev/ada0",
|
||||
"serial_no": "S0X5NZBC422720",
|
||||
"wwn": "5002538043584d30",
|
||||
"id": "12",
|
||||
"name": "Power_Cycle_Count",
|
||||
"flags": "-O--CK",
|
||||
"fail": "-",
|
||||
},
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"value": int64(253),
|
||||
"worst": int64(253),
|
||||
"threshold": int64(10),
|
||||
"raw_value": int64(2044932921600),
|
||||
"exit_status": int(0),
|
||||
},
|
||||
map[string]string{
|
||||
"device": "/dev/ada0",
|
||||
"serial_no": "S0X5NZBC422720",
|
||||
"wwn": "5002538043584d30",
|
||||
"id": "169",
|
||||
"name": "Unknown_Attribute",
|
||||
"flags": "PO--C-",
|
||||
"fail": "-",
|
||||
},
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"value": int64(185),
|
||||
"worst": int64(185),
|
||||
"threshold": int64(100),
|
||||
"raw_value": int64(957808640337),
|
||||
"exit_status": int(0),
|
||||
},
|
||||
map[string]string{
|
||||
"device": "/dev/ada0",
|
||||
"serial_no": "S0X5NZBC422720",
|
||||
"wwn": "5002538043584d30",
|
||||
"id": "173",
|
||||
"name": "Wear_Leveling_Count",
|
||||
"flags": "-O--CK",
|
||||
"fail": "-",
|
||||
},
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"value": int64(55),
|
||||
"worst": int64(40),
|
||||
"threshold": int64(45),
|
||||
"raw_value": int64(45),
|
||||
"exit_status": int(0),
|
||||
},
|
||||
map[string]string{
|
||||
"device": "/dev/ada0",
|
||||
"serial_no": "S0X5NZBC422720",
|
||||
"wwn": "5002538043584d30",
|
||||
"id": "190",
|
||||
"name": "Airflow_Temperature_Cel",
|
||||
"flags": "-O---K",
|
||||
"fail": "Past",
|
||||
},
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"value": int64(97),
|
||||
"worst": int64(97),
|
||||
"threshold": int64(0),
|
||||
"raw_value": int64(14716),
|
||||
"exit_status": int(0),
|
||||
},
|
||||
map[string]string{
|
||||
"device": "/dev/ada0",
|
||||
"serial_no": "S0X5NZBC422720",
|
||||
"wwn": "5002538043584d30",
|
||||
"id": "192",
|
||||
"name": "Power-Off_Retract_Count",
|
||||
"flags": "-O--C-",
|
||||
"fail": "-",
|
||||
},
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"value": int64(66),
|
||||
"worst": int64(21),
|
||||
"threshold": int64(0),
|
||||
"raw_value": int64(34),
|
||||
"exit_status": int(0),
|
||||
},
|
||||
map[string]string{
|
||||
"device": "/dev/ada0",
|
||||
"serial_no": "S0X5NZBC422720",
|
||||
"wwn": "5002538043584d30",
|
||||
"id": "194",
|
||||
"name": "Temperature_Celsius",
|
||||
"flags": "-O---K",
|
||||
"fail": "-",
|
||||
},
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"value": int64(100),
|
||||
"worst": int64(100),
|
||||
"threshold": int64(0),
|
||||
"raw_value": int64(0),
|
||||
"exit_status": int(0),
|
||||
},
|
||||
map[string]string{
|
||||
"device": "/dev/ada0",
|
||||
"serial_no": "S0X5NZBC422720",
|
||||
"wwn": "5002538043584d30",
|
||||
"id": "197",
|
||||
"name": "Current_Pending_Sector",
|
||||
"flags": "-O---K",
|
||||
"fail": "-",
|
||||
},
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"value": int64(200),
|
||||
"worst": int64(200),
|
||||
"threshold": int64(0),
|
||||
"raw_value": int64(0),
|
||||
"exit_status": int(0),
|
||||
},
|
||||
map[string]string{
|
||||
"device": "/dev/ada0",
|
||||
"serial_no": "S0X5NZBC422720",
|
||||
"wwn": "5002538043584d30",
|
||||
"id": "199",
|
||||
"name": "UDMA_CRC_Error_Count",
|
||||
"flags": "-O-RC-",
|
||||
"fail": "-",
|
||||
},
|
||||
},
|
||||
{
|
||||
map[string]interface{}{
|
||||
"value": int64(100),
|
||||
"worst": int64(253),
|
||||
"threshold": int64(0),
|
||||
"raw_value": int64(23709323),
|
||||
"exit_status": int(0),
|
||||
},
|
||||
map[string]string{
|
||||
"device": "/dev/ada0",
|
||||
"serial_no": "S0X5NZBC422720",
|
||||
"wwn": "5002538043584d30",
|
||||
"id": "240",
|
||||
"name": "Head_Flying_Hours",
|
||||
"flags": "------",
|
||||
"fail": "-",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testsAda0Attributes {
|
||||
acc.AssertContainsTaggedFields(t, "smart_attribute", test.fields, test.tags)
|
||||
}
|
||||
|
||||
// tags = map[string]string{}
|
||||
|
||||
var testsAda0Device = []struct {
|
||||
fields map[string]interface{}
|
||||
tags map[string]string
|
||||
}{
|
||||
{
|
||||
map[string]interface{}{
|
||||
"exit_status": int(0),
|
||||
"health_ok": bool(true),
|
||||
"read_error_rate": int64(0),
|
||||
"temp_c": int64(34),
|
||||
"udma_crc_errors": int64(0),
|
||||
},
|
||||
map[string]string{
|
||||
"device": "/dev/ada0",
|
||||
"model": "APPLE SSD SM256E",
|
||||
"serial_no": "S0X5NZBC422720",
|
||||
"wwn": "5002538043584d30",
|
||||
"enabled": "Enabled",
|
||||
"capacity": "251000193024",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testsAda0Device {
|
||||
acc.AssertContainsTaggedFields(t, "smart_device", test.fields, test.tags)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestGatherNoAttributes(t *testing.T) {
|
||||
s := &Smart{
|
||||
Path: "smartctl",
|
||||
Attributes: false,
|
||||
}
|
||||
// overwriting exec commands with mock commands
|
||||
execCommand = fakeExecCommand
|
||||
var acc testutil.Accumulator
|
||||
|
||||
err := s.Gather(&acc)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 5, acc.NFields(), "Wrong number of fields gathered")
|
||||
acc.AssertDoesNotContainMeasurement(t, "smart_attribute")
|
||||
|
||||
// tags = map[string]string{}
|
||||
|
||||
var testsAda0Device = []struct {
|
||||
fields map[string]interface{}
|
||||
tags map[string]string
|
||||
}{
|
||||
{
|
||||
map[string]interface{}{
|
||||
"exit_status": int(0),
|
||||
"health_ok": bool(true),
|
||||
"read_error_rate": int64(0),
|
||||
"temp_c": int64(34),
|
||||
"udma_crc_errors": int64(0),
|
||||
},
|
||||
map[string]string{
|
||||
"device": "/dev/ada0",
|
||||
"model": "APPLE SSD SM256E",
|
||||
"serial_no": "S0X5NZBC422720",
|
||||
"wwn": "5002538043584d30",
|
||||
"enabled": "Enabled",
|
||||
"capacity": "251000193024",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testsAda0Device {
|
||||
acc.AssertContainsTaggedFields(t, "smart_device", test.fields, test.tags)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestExcludedDev(t *testing.T) {
|
||||
assert.Equal(t, true, excludedDev([]string{"/dev/pass6"}, "/dev/pass6 -d atacam"), "Should be excluded.")
|
||||
assert.Equal(t, false, excludedDev([]string{}, "/dev/pass6 -d atacam"), "Shouldn't be excluded.")
|
||||
assert.Equal(t, false, excludedDev([]string{"/dev/pass6"}, "/dev/pass1 -d atacam"), "Shouldn't be excluded.")
|
||||
|
||||
}
|
||||
|
||||
// fackeExecCommand is a helper function that mock
|
||||
// the exec.Command call (and call the test binary)
|
||||
func fakeExecCommand(command string, args ...string) *exec.Cmd {
|
||||
cs := []string{"-test.run=TestHelperProcess", "--", command}
|
||||
cs = append(cs, args...)
|
||||
cmd := exec.Command(os.Args[0], cs...)
|
||||
cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
|
||||
return cmd
|
||||
}
|
||||
|
||||
// TestHelperProcess isn't a real test. It's used to mock exec.Command
|
||||
// For example, if you run:
|
||||
// GO_WANT_HELPER_PROCESS=1 go test -test.run=TestHelperProcess -- --scan
|
||||
// it returns below mockScanData.
|
||||
func TestHelperProcess(t *testing.T) {
|
||||
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
|
||||
return
|
||||
}
|
||||
|
||||
args := os.Args
|
||||
|
||||
// Previous arguments are tests stuff, that looks like :
|
||||
// /tmp/go-build970079519/…/_test/integration.test -test.run=TestHelperProcess --
|
||||
cmd, arg1, args := args[3], args[4], args[5:]
|
||||
|
||||
if cmd == "smartctl" {
|
||||
if arg1 == "--scan" {
|
||||
fmt.Fprint(os.Stdout, mockScanData)
|
||||
}
|
||||
if arg1 == "--info" {
|
||||
fmt.Fprint(os.Stdout, mockInfoAttributeData)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprint(os.Stdout, "command not found")
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
Reference in New Issue
Block a user