smartctl_exporter/smartctl.go

221 lines
5.5 KiB
Go

package main
import (
"fmt"
"strings"
"github.com/prometheus/client_golang/prometheus"
"github.com/tidwall/gjson"
)
// SMARTDevice - short info about device
type SMARTDevice struct {
device string
serial string
family string
model string
}
// SMARTctl object
type SMARTctl struct {
ch chan<- prometheus.Metric
json gjson.Result
device SMARTDevice
}
// NewSMARTctl is smartctl constructor
func NewSMARTctl(json gjson.Result, ch chan<- prometheus.Metric) SMARTctl {
smart := SMARTctl{}
smart.ch = ch
smart.json = json
smart.device = SMARTDevice{
device: strings.TrimSpace(smart.json.Get("device.name").String()),
serial: strings.TrimSpace(smart.json.Get("serial_number").String()),
family: strings.TrimSpace(smart.json.Get("model_family").String()),
model: strings.TrimSpace(smart.json.Get("model_name").String()),
}
logger.Verbose("Collecting metrics from %s: %s, %s", smart.device.device, smart.device.family, smart.device.model)
return smart
}
// Collect metrics
func (smart *SMARTctl) Collect() {
smart.mineExitStatus()
smart.mineDevice()
smart.mineCapacity()
smart.mineInterfaceSpeed()
smart.mineDeviceAttribute()
smart.minePowerOnSeconds()
smart.mineRotationRate()
smart.mineTemperatures()
smart.minePowerCycleCount()
}
func (smart *SMARTctl) mineExitStatus() {
smart.ch <- prometheus.MustNewConstMetric(
metricDeviceExitStatus,
prometheus.GaugeValue,
smart.json.Get("smartctl.exit_status").Float(),
smart.device.device,
smart.device.family,
smart.device.model,
smart.device.serial,
)
}
func (smart *SMARTctl) mineDevice() {
device := smart.json.Get("device")
smart.ch <- prometheus.MustNewConstMetric(
metricDeviceModel,
prometheus.GaugeValue,
1,
smart.device.device,
device.Get("type").String(),
device.Get("protocol").String(),
smart.device.family,
smart.device.model,
smart.device.serial,
GetStringIfExists(smart.json, "ata_additional_product_id", "unknown"),
smart.json.Get("firmware_version").String(),
smart.json.Get("ata_version.string").String(),
smart.json.Get("sata_version.string").String(),
)
}
func (smart *SMARTctl) mineCapacity() {
capacity := smart.json.Get("user_capacity")
smart.ch <- prometheus.MustNewConstMetric(
metricDeviceCapacityBlocks,
prometheus.GaugeValue,
capacity.Get("blocks").Float(),
smart.device.device,
smart.device.family,
smart.device.model,
smart.device.serial,
)
smart.ch <- prometheus.MustNewConstMetric(
metricDeviceCapacityBytes,
prometheus.GaugeValue,
capacity.Get("bytes").Float(),
smart.device.device,
smart.device.family,
smart.device.model,
smart.device.serial,
)
for _, blockType := range []string{"logical", "physical"} {
smart.ch <- prometheus.MustNewConstMetric(
metricDeviceBlockSize,
prometheus.GaugeValue,
smart.json.Get(fmt.Sprintf("%s_block_size", blockType)).Float(),
smart.device.device,
smart.device.family,
smart.device.model,
smart.device.serial,
blockType,
)
}
}
func (smart *SMARTctl) mineInterfaceSpeed() {
iSpeed := smart.json.Get("interface_speed")
for _, speedType := range []string{"max", "current"} {
tSpeed := iSpeed.Get(speedType)
smart.ch <- prometheus.MustNewConstMetric(
metricDeviceInterfaceSpeed,
prometheus.GaugeValue,
tSpeed.Get("units_per_second").Float()*tSpeed.Get("bits_per_unit").Float(),
smart.device.device,
smart.device.family,
smart.device.model,
smart.device.serial,
speedType,
)
}
}
func (smart *SMARTctl) mineDeviceAttribute() {
for _, attribute := range smart.json.Get("ata_smart_attributes.table").Array() {
name := strings.TrimSpace(attribute.Get("name").String())
flags := strings.TrimSpace(attribute.Get("flags.string").String())
id := attribute.Get("id").String()
for key, path := range map[string]string{
"value": "value",
"worst": "worst",
"thresh": "thresh",
"raw": "raw.value",
} {
smart.ch <- prometheus.MustNewConstMetric(
metricDeviceAttribute,
prometheus.GaugeValue,
attribute.Get(path).Float(),
smart.device.device,
smart.device.family,
smart.device.model,
smart.device.serial,
name,
flags,
key,
id,
)
}
}
}
func (smart *SMARTctl) minePowerOnSeconds() {
pot := smart.json.Get("power_on_time")
smart.ch <- prometheus.MustNewConstMetric(
metricDevicePowerOnSeconds,
prometheus.CounterValue,
GetFloatIfExists(pot, "hours", 0)*60*60+GetFloatIfExists(pot, "minutes", 0)*60,
smart.device.device,
smart.device.family,
smart.device.model,
smart.device.serial,
)
}
func (smart *SMARTctl) mineRotationRate() {
rRate := GetFloatIfExists(smart.json, "rotation_rate", 0)
if rRate > 0 {
smart.ch <- prometheus.MustNewConstMetric(
metricDeviceRotationRate,
prometheus.GaugeValue,
rRate,
smart.device.device,
smart.device.family,
smart.device.model,
smart.device.serial,
)
}
}
func (smart *SMARTctl) mineTemperatures() {
temperatures := smart.json.Get("temperature")
if temperatures.Exists() {
temperatures.ForEach(func(key, value gjson.Result) bool {
smart.ch <- prometheus.MustNewConstMetric(
metricDeviceTemperature,
prometheus.GaugeValue,
value.Float(),
smart.device.device,
smart.device.family,
smart.device.model,
smart.device.serial,
key.String(),
)
return true
})
}
}
func (smart *SMARTctl) minePowerCycleCount() {
smart.ch <- prometheus.MustNewConstMetric(
metricDevicePowerCycleCount,
prometheus.CounterValue,
smart.json.Get("power_cycle_count").Float(),
smart.device.device,
smart.device.family,
smart.device.model,
smart.device.serial,
)
}