2019-08-14 22:34:49 +02:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2019-08-14 23:04:32 +02:00
|
|
|
// NewSMARTctl is smartctl constructor
|
2019-08-14 22:34:49 +02:00
|
|
|
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
|
2019-08-15 23:01:16 +02:00
|
|
|
func (smart *SMARTctl) Collect() {
|
|
|
|
smart.mineExitStatus()
|
2019-08-14 22:34:49 +02:00
|
|
|
smart.mineDevice()
|
|
|
|
smart.mineCapacity()
|
|
|
|
smart.mineInterfaceSpeed()
|
|
|
|
smart.mineDeviceAttribute()
|
|
|
|
smart.minePowerOnSeconds()
|
|
|
|
smart.mineRotationRate()
|
|
|
|
smart.mineTemperatures()
|
2019-08-15 23:01:16 +02:00
|
|
|
smart.minePowerCycleCount()
|
2019-08-14 22:34:49 +02:00
|
|
|
}
|
|
|
|
|
2019-08-15 23:01:16 +02:00
|
|
|
func (smart *SMARTctl) mineExitStatus() {
|
2019-08-14 22:34:49 +02:00
|
|
|
smart.ch <- prometheus.MustNewConstMetric(
|
2019-08-15 23:01:16 +02:00
|
|
|
metricDeviceExitStatus,
|
2019-08-14 22:34:49 +02:00
|
|
|
prometheus.GaugeValue,
|
2019-08-15 23:01:16 +02:00
|
|
|
smart.json.Get("smartctl.exit_status").Float(),
|
|
|
|
smart.device.device,
|
|
|
|
smart.device.family,
|
|
|
|
smart.device.model,
|
|
|
|
smart.device.serial,
|
2019-08-14 22:34:49 +02:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-08-15 23:01:16 +02:00
|
|
|
func (smart *SMARTctl) mineDevice() {
|
2019-08-14 22:34:49 +02:00
|
|
|
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(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-08-15 23:01:16 +02:00
|
|
|
func (smart *SMARTctl) mineCapacity() {
|
2019-08-14 22:34:49 +02:00
|
|
|
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,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-15 23:01:16 +02:00
|
|
|
func (smart *SMARTctl) mineInterfaceSpeed() {
|
2019-08-14 22:34:49 +02:00
|
|
|
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,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-15 23:01:16 +02:00
|
|
|
func (smart *SMARTctl) mineDeviceAttribute() {
|
2019-08-14 22:34:49 +02:00
|
|
|
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,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-15 23:01:16 +02:00
|
|
|
func (smart *SMARTctl) minePowerOnSeconds() {
|
2019-08-14 22:34:49 +02:00
|
|
|
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,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-08-15 23:01:16 +02:00
|
|
|
func (smart *SMARTctl) mineRotationRate() {
|
2019-08-14 22:34:49 +02:00
|
|
|
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,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-15 23:01:16 +02:00
|
|
|
func (smart *SMARTctl) mineTemperatures() {
|
2019-08-14 22:34:49 +02:00
|
|
|
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
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-15 23:01:16 +02:00
|
|
|
func (smart *SMARTctl) minePowerCycleCount() {
|
2019-08-14 22:34:49 +02:00
|
|
|
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,
|
|
|
|
)
|
|
|
|
}
|