diff --git a/diskdrive.go b/diskdrive.go new file mode 100644 index 00000000..fa6749a7 --- /dev/null +++ b/diskdrive.go @@ -0,0 +1,203 @@ +//go:build windows +// +build windows + +package collector + +import ( + "errors" + "strings" + + "github.com/StackExchange/wmi" + "github.com/prometheus-community/windows_exporter/log" + "github.com/prometheus/client_golang/prometheus" +) + +func init() { + registerCollector("disk_drive", newDiskDriveInfoCollector) +} + +const ( + win32DiskQuery = "SELECT DeviceID, Model, Caption, Partitions, Size, Status, Availability FROM WIN32_DiskDrive" +) + +// A DiskDriveInfoCollector is a Prometheus collector for a few WMI metrics in Win32_DiskDrive +type DiskDriveInfoCollector struct { + DiskInfo *prometheus.Desc + Status *prometheus.Desc + Size *prometheus.Desc + Partitions *prometheus.Desc + Availability *prometheus.Desc +} + +func newDiskDriveInfoCollector() (Collector, error) { + const subsystem = "disk_drive" + + return &DiskDriveInfoCollector{ + DiskInfo: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, subsystem, "info"), + "General drive information", + []string{ + "device_id", + "model", + "caption", + }, + nil, + ), + + Status: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, subsystem, "status"), + "Status of the drive", + []string{ + "name", "status"}, + nil, + ), + + Size: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, subsystem, "size"), + "Size of the disk drive. It is calculated by multiplying the total number of cylinders, tracks in each cylinder, sectors in each track, and bytes in each sector.", + nil, + nil, + ), + + Partitions: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, subsystem, "partitions"), + "Number of partitions", + nil, + nil, + ), + + Availability: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, subsystem, "availability"), + "Availability Status", + []string{ + "name", "availability"}, + nil, + ), + }, nil +} + +type Win32_DiskDrive struct { + DeviceID string + Model string + Size uint64 + Caption string + Partitions uint32 + Status string + Availability uint16 +} + +var ( + allDiskStatus = []string{ + "OK", + "Error", + "Degraded", + "Unknown", + "Pred fail", + "Starting", + "Stopping", + "Service", + "Stressed", + "Nonrecover", + "No Contact", + "Lost Comm", + } + + availMap = map[int]string{ + + 1: "Other", + 2: "Unknown", + 3: "Running / Full Power", + 4: "Warning", + 5: "In Test", + 6: "Not Applicable", + 7: "Power Off", + 8: "Off line", + 9: "Off Duty", + 10: "Degraded", + 11: "Not Installed", + 12: "Install Error", + 13: "Power Save - Unknown", + 14: "Power Save - Low Power Mode", + 15: "Power Save - Standby", + 16: "Power Cycle", + 17: "Power Save - Warning", + 18: "Paused", + 19: "Not Ready", + 20: "Not Configured", + 21: "Quiesced", + } +) + +// Collect sends the metric values for each metric to the provided prometheus Metric channel. +func (c *DiskDriveInfoCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error { + if desc, err := c.collect(ch); err != nil { + log.Error("failed collecting disk_drive_info metrics:", desc, err) + return err + } + return nil +} + +func (c *DiskDriveInfoCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) { + var dst []Win32_DiskDrive + + if err := wmi.Query(win32DiskQuery, &dst); err != nil { + return nil, err + } + if len(dst) == 0 { + return nil, errors.New("WMI query returned empty result set") + } + + for _, processor := range dst { + ch <- prometheus.MustNewConstMetric( + c.DiskInfo, + prometheus.GaugeValue, + 1.0, + strings.TrimRight(processor.DeviceID, " "), + strings.TrimRight(processor.Model, " "), + strings.TrimRight(processor.Caption, " "), + ) + + for _, status := range allDiskStatus { + isCurrentState := 0.0 + if status == processor.Status { + isCurrentState = 1.0 + } + + ch <- prometheus.MustNewConstMetric( + c.Status, + prometheus.GaugeValue, + isCurrentState, + strings.TrimRight(processor.Model, " "), + status, + ) + } + + ch <- prometheus.MustNewConstMetric( + c.Size, + prometheus.CounterValue, + float64(dst[0].Size), + ) + + ch <- prometheus.MustNewConstMetric( + c.Partitions, + prometheus.CounterValue, + float64(dst[0].Partitions), + ) + + for availNum, val := range availMap { + isCurrentState := 0.0 + if availNum == int(processor.Availability) { + isCurrentState = 1.0 + } + ch <- prometheus.MustNewConstMetric( + c.Availability, + prometheus.GaugeValue, + isCurrentState, + strings.TrimRight(processor.Model, " "), + val, + ) + } + } + + return nil, nil +} diff --git a/diskdrive_test.go b/diskdrive_test.go new file mode 100644 index 00000000..2bd1466b --- /dev/null +++ b/diskdrive_test.go @@ -0,0 +1,17 @@ +package collector + +import ( + "testing" +) + +func BenchmarkDiskDriveCollector(b *testing.B) { + benchmarkCollector(b, "disk_drive", newDiskDriveInfoCollector) +} + +// goos: windows +// goarch: amd64 +// pkg: github.com/prometheus-community/windows_exporter/collector +// cpu: Intel(R) Core(TM) i7-7700 CPU @ 3.60GHz +// BenchmarkDiskDriveCollector-8 1 1334272800 ns/op 41182008 B/op 125688 allocs/op +// PASS +// ok github.com/prometheus-community/windows_exporter/collector 4.943s