First commit

This commit is contained in:
Горлов Максим 2019-08-14 23:34:49 +03:00
parent f1e1e3fe23
commit 9e6e240e85
11 changed files with 744 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
vendor
bin
*.json

42
Makefile Normal file
View File

@ -0,0 +1,42 @@
GOPATH=$(shell pwd)/vendor:$(shell pwd)
GOBIN=$(shell pwd)/bin
GOFILES=$(wildcard *.go)
GONAME=$(shell basename "$(PWD)")
PID=/tmp/go-$(GONAME).pid
build: get
@echo "Building $(GOFILES) to ./bin"
@GOPATH=$(GOPATH) GOBIN=$(GOBIN) go build -o bin/$(GONAME) $(GOFILES)
get:
@GOPATH=$(GOPATH) GOBIN=$(GOBIN) go get .
install:
@GOPATH=$(GOPATH) GOBIN=$(GOBIN) go install $(GOFILES)
run: build
@GOPATH=$(GOPATH) GOBIN=$(GOBIN) go run $(GOFILES) --config=$(shell pwd)/smartctl_exporter.yaml --debug --verbose
run-sudo: build
sudo bin/$(GONAME) --config=$(shell pwd)/smartctl_exporter.yaml --debug --verbose
watch:
@$(MAKE) restart &
@fswatch -o . -e 'bin/.*' | xargs -n1 -I{} make restart
restart: clear stop clean build start
start: build
@echo "Starting bin/$(GONAME)"
@./bin/$(GONAME) & echo $$! > $(PID)
stop:
@echo "Stopping bin/$(GONAME) if it's running"
@-kill `[[ -f $(PID) ]] && cat $(PID)` 2>/dev/null || true
clear:
@clear
clean:
@echo "Cleaning"
@GOPATH=$(GOPATH) GOBIN=$(GOBIN) go clean

View File

@ -1,2 +1,76 @@
# smartctl_exporter
Export smartctl statistics to prometheus
## Need more?
**If you need additional metrics - contact me :)**
**Create a feature request, describe the metric that you would like to have and attach exported from smartctl json file**
# Configuration
## Command line options
* `--config=/path/to/file.yaml`: Path to configuration file, defaulr `/etc/smartctl_exporter.yaml`
* `--verbose`: verbosed log, default no
* `--debug`: Debug logging, default no
* `--version`: Show version and exit
## Configuration file
Example content:
```
smartctl_exporter:
bind_to: "[::1]:9631"
url_path: "/metrics"
fake_json: no
smartctl_location: /usr/sbin/smartctl
devices:
- /dev/sda
- /dev/sdb
- /dev/sdc
- /dev/sdd
- /dev/sde
- /dev/sdf
```
`fake_json` used for debugging.
# Example metrics
```
# HELP smartctl_device Device info
# TYPE smartctl_device gauge
smartctl_device{ata_additional_product_id="HC7020E0",ata_version="ATA8-ACS, ATA/ATAPI-7 T13/1532D revision 4a",device="/dev/sda",firmware_version="1.03",interface="sat",model_family="Plextor M3/M5/M6 Series SSDs",model_name="PLEXTOR PX-128M6S",protocol="ATA",sata_version="SATA 3.1",serial_number="P02448109994"} 1.0
smartctl_device{ata_additional_product_id="unknown",ata_version="ACS-2 (minor revision not indicated)",device="/dev/sdc",firmware_version="82.00A82",interface="sat",model_family="Western Digital Red",model_name="WDC WD20EFRX-68EUZN0",protocol="ATA",sata_version="SATA 3.0",serial_number="WD-WCC4M4VX3C69"} 1.0
# HELP smartctl_device_attribute Device attributes
# TYPE smartctl_device_attribute gauge
smartctl_device_attribute{device="/dev/sda",flags="-O----",id="9",model_family="Plextor M3/M5/M6 Series SSDs",model_name="PLEXTOR PX-128M6S",name="Power_On_Hours",serial_number="P02448109994",value_type="raw"} 2952.0
smartctl_device_attribute{device="/dev/sda",flags="-O----",id="9",model_family="Plextor M3/M5/M6 Series SSDs",model_name="PLEXTOR PX-128M6S",name="Power_On_Hours",serial_number="P02448109994",value_type="thresh"} 0.0
smartctl_device_attribute{device="/dev/sda",flags="-O----",id="9",model_family="Plextor M3/M5/M6 Series SSDs",model_name="PLEXTOR PX-128M6S",name="Power_On_Hours",serial_number="P02448109994",value_type="value"} 100.0
smartctl_device_attribute{device="/dev/sda",flags="-O----",id="9",model_family="Plextor M3/M5/M6 Series SSDs",model_name="PLEXTOR PX-128M6S",name="Power_On_Hours",serial_number="P02448109994",value_type="worst"} 100.0
# HELP smartctl_device_block_size Device block size
# TYPE smartctl_device_block_size gauge
smartctl_device_block_size{blocks_type="logical",device="/dev/sda",model_family="Plextor M3/M5/M6 Series SSDs",model_name="PLEXTOR PX-128M6S",serial_number="P02448109994"} 512.0
smartctl_device_block_size{blocks_type="logical",device="/dev/sdb",model_family="SandForce Driven SSDs",model_name="OCZ-VERTEX3",serial_number="A20Y8011312000361"} 512.0
smartctl_device_block_size{blocks_type="logical",device="/dev/sdc",model_family="Western Digital Red",model_name="WDC WD20EFRX-68EUZN0",serial_number="WD-WCC4M4VX3C69"} 512.0
smartctl_device_block_size{blocks_type="logical",device="/dev/sdd",model_family="Western Digital Red",model_name="WDC WD20EFRX-68EUZN0",serial_number="WD-WCC4M6DCPPC7"} 512.0
# HELP smartctl_device_capacity_blocks Device capacity in blocks
# TYPE smartctl_device_capacity_blocks gauge
smartctl_device_capacity_blocks{device="/dev/sda",model_family="Plextor M3/M5/M6 Series SSDs",model_name="PLEXTOR PX-128M6S",serial_number="P02448109994"} 2.5006968e+08
smartctl_device_capacity_blocks{device="/dev/sdb",model_family="SandForce Driven SSDs",model_name="OCZ-VERTEX3",serial_number="A20Y8011312000361"} 2.34441648e+08
smartctl_device_capacity_blocks{device="/dev/sdc",model_family="Western Digital Red",model_name="WDC WD20EFRX-68EUZN0",serial_number="WD-WCC4M4VX3C69"} 3.907029168e+09
# HELP smartctl_device_capacity_bytes Device capacity in bytes
# TYPE smartctl_device_capacity_bytes gauge
smartctl_device_capacity_bytes{device="/dev/sda",model_family="Plextor M3/M5/M6 Series SSDs",model_name="PLEXTOR PX-128M6S",serial_number="P02448109994"} 1.2803567616e+11
smartctl_device_capacity_bytes{device="/dev/sdb",model_family="SandForce Driven SSDs",model_name="OCZ-VERTEX3",serial_number="A20Y8011312000361"} 1.20034123776e+11
smartctl_device_capacity_bytes{device="/dev/sdc",model_family="Western Digital Red",model_name="WDC WD20EFRX-68EUZN0",serial_number="WD-WCC4M4VX3C69"} 2.000398934016e+12
# HELP smartctl_device_interface_speed Device interface speed, bits per second
# TYPE smartctl_device_interface_speed gauge
smartctl_device_interface_speed{device="/dev/sda",model_family="Plextor M3/M5/M6 Series SSDs",model_name="PLEXTOR PX-128M6S",serial_number="P02448109994",speed_type="current"} 6e+09
smartctl_device_interface_speed{device="/dev/sda",model_family="Plextor M3/M5/M6 Series SSDs",model_name="PLEXTOR PX-128M6S",serial_number="P02448109994",speed_type="max"} 6e+09
smartctl_device_interface_speed{device="/dev/sdb",model_family="SandForce Driven SSDs",model_name="OCZ-VERTEX3",serial_number="A20Y8011312000361",speed_type="current"} 6e+09
# HELP smartctl_device_power_on_seconds Device power on seconds
# TYPE smartctl_device_power_on_seconds counter
smartctl_device_power_on_seconds{device="/dev/sda",model_family="Plextor M3/M5/M6 Series SSDs",model_name="PLEXTOR PX-128M6S",serial_number="P02448109994"} 1.06272e+07
smartctl_device_power_on_seconds{device="/dev/sdb",model_family="SandForce Driven SSDs",model_name="OCZ-VERTEX3",serial_number="A20Y8011312000361"} 1.0568592e+08
smartctl_device_power_on_seconds{device="/dev/sdc",model_family="Western Digital Red",model_name="WDC WD20EFRX-68EUZN0",serial_number="WD-WCC4M4VX3C69"} 6.68232e+07
# HELP smartctl_device_temperature Device temperature celsius
# TYPE smartctl_device_temperature gauge
smartctl_device_temperature{device="/dev/sdb",model_family="SandForce Driven SSDs",model_name="OCZ-VERTEX3",serial_number="A20Y8011312000361",temperature_type="current"} 30.0
smartctl_device_temperature{device="/dev/sdb",model_family="SandForce Driven SSDs",model_name="OCZ-VERTEX3",serial_number="A20Y8011312000361",temperature_type="lifetime_max"} 30.0
smartctl_device_temperature{device="/dev/sdb",model_family="SandForce Driven SSDs",model_name="OCZ-VERTEX3",serial_number="A20Y8011312000361",temperature_type="lifetime_min"} 30.0
```

23
gjsonext.go Normal file
View File

@ -0,0 +1,23 @@
package main
import (
"github.com/tidwall/gjson"
)
// GetStringIfExists returns json value or default
func GetStringIfExists(json gjson.Result, key string, def string) string {
value := json.Get(key)
if value.Exists() {
return value.String()
}
return def
}
// GetFloatIfExists returns json value or default
func GetFloatIfExists(json gjson.Result, key string, def float64) float64 {
value := json.Get(key)
if value.Exists() {
return value.Float()
}
return def
}

55
logging.go Normal file
View File

@ -0,0 +1,55 @@
package main
import (
"fmt"
)
// Logger is logger
type Logger struct {
verbose bool
debug bool
}
func newLogger(verbose bool, debug bool) Logger {
logger := Logger{verbose: verbose, debug: debug}
return logger
}
// Msg formatted message
func (i Logger) print(prefix string, format string, values ...interface{}) {
fmt.Printf(fmt.Sprintf("[%s] %s\n", prefix, format), values...)
}
// Info log message
func (i Logger) Info(format string, values ...interface{}) {
i.print("Info", format, values...)
}
// Warning log message
func (i Logger) Warning(format string, values ...interface{}) {
i.print("Warning", format, values...)
}
// Error log message
func (i Logger) Error(format string, values ...interface{}) {
i.print("Error", format, values...)
}
// Panic log message
func (i Logger) Panic(format string, values ...interface{}) {
i.print("Panic", format, values...)
}
// Verbose log message
func (i Logger) Verbose(format string, values ...interface{}) {
if i.verbose {
i.print("Verbose", format, values...)
}
}
// Debug log message
func (i Logger) Debug(format string, values ...interface{}) {
if i.debug {
i.print("Debug", format, values...)
}
}

54
main.go Normal file
View File

@ -0,0 +1,54 @@
package main
import (
"log"
"net/http"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
var (
options Options
logger Logger
)
// SMARTctlManagerCollector implements the Collector interface.
type SMARTctlManagerCollector struct {
}
// Describe sends the super-set of all possible descriptors of metrics
func (i SMARTctlManagerCollector) Describe(ch chan<- *prometheus.Desc) {
prometheus.DescribeByCollect(i, ch)
}
// Collect is called by the Prometheus registry when collecting metrics.
func (i SMARTctlManagerCollector) Collect(ch chan<- prometheus.Metric) {
for _, device := range options.SMARTctl.Devices {
json, err := readData(device)
if err != nil {
logger.Error("Failed parsing SMARTctl data: %s", err)
} else {
smart := NewSMARTctl(json, ch)
smart.Collect()
}
}
}
func init() {
options = loadOptions()
}
func main() {
reg := prometheus.NewPedanticRegistry()
reg.MustRegister(
prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{}),
prometheus.NewGoCollector(),
)
prometheus.WrapRegistererWithPrefix("", reg).MustRegister(SMARTctlManagerCollector{})
logger.Info("Starting on %s%s", options.SMARTctl.BindTo, options.SMARTctl.URLPath)
http.Handle(options.SMARTctl.URLPath, promhttp.HandlerFor(reg, promhttp.HandlerOpts{}))
log.Fatal(http.ListenAndServe(options.SMARTctl.BindTo, nil))
}

142
metrics.go Normal file
View File

@ -0,0 +1,142 @@
package main
import (
"github.com/prometheus/client_golang/prometheus"
)
var (
metricSmartctlVersion = prometheus.NewDesc(
"smartctl_version",
"smartctl version",
[]string{
"json_format_version",
"smartctl_version",
"svn_revision",
"build_info",
},
nil,
)
metricDeviceModel = prometheus.NewDesc(
"smartctl_device",
"Device info",
[]string{
"device",
"interface",
"protocol",
"model_family",
"model_name",
"serial_number",
"ata_additional_product_id",
"firmware_version",
"ata_version",
"sata_version",
},
nil,
)
metricDeviceCapacityBlocks = prometheus.NewDesc(
"smartctl_device_capacity_blocks",
"Device capacity in blocks",
[]string{
"device",
"model_family",
"model_name",
"serial_number",
},
nil,
)
metricDeviceCapacityBytes = prometheus.NewDesc(
"smartctl_device_capacity_bytes",
"Device capacity in bytes",
[]string{
"device",
"model_family",
"model_name",
"serial_number",
},
nil,
)
metricDeviceBlockSize = prometheus.NewDesc(
"smartctl_device_block_size",
"Device block size",
[]string{
"device",
"model_family",
"model_name",
"serial_number",
"blocks_type",
},
nil,
)
metricDeviceInterfaceSpeed = prometheus.NewDesc(
"smartctl_device_interface_speed",
"Device interface speed, bits per second",
[]string{
"device",
"model_family",
"model_name",
"serial_number",
"speed_type",
},
nil,
)
metricDeviceAttribute = prometheus.NewDesc(
"smartctl_device_attribute",
"Device attributes",
[]string{
"device",
"model_family",
"model_name",
"serial_number",
"name",
"flags",
"value_type",
"id",
},
nil,
)
metricDevicePowerOnSeconds = prometheus.NewDesc(
"smartctl_device_power_on_seconds",
"Device power on seconds",
[]string{
"device",
"model_family",
"model_name",
"serial_number",
},
nil,
)
metricDeviceRotationRate = prometheus.NewDesc(
"smartctl_device_rotation_rate",
"Device rotation rate",
[]string{
"device",
"model_family",
"model_name",
"serial_number",
},
nil,
)
metricDeviceTemperature = prometheus.NewDesc(
"smartctl_device_temperature",
"Device temperature celsius",
[]string{
"device",
"model_family",
"model_name",
"serial_number",
"temperature_type",
},
nil,
)
metricDevicePowerCycleCount = prometheus.NewDesc(
"smartctl_device_power_cycle_count",
"Device power cycle count",
[]string{
"device",
"model_family",
"model_name",
"serial_number",
},
nil,
)
)

66
options.go Normal file
View File

@ -0,0 +1,66 @@
package main
import (
"flag"
"fmt"
"io/ioutil"
"os"
yaml "gopkg.in/yaml.v2"
)
var (
exporterVersion = "0.5"
)
// SMARTOptions is a inner representation of a options
type SMARTOptions struct {
BindTo string `yaml:"bind_to"`
URLPath string `yaml:"url_path"`
FakeJSON bool `yaml:"fake_json"`
SMARTctlLocation string `yaml:"smartctl_location"`
Devices []string `yaml:"devices"`
}
// Options is a representation of a options
type Options struct {
SMARTctl SMARTOptions `yaml:"smartctl_exporter"`
}
// Parse options from yaml config file
func loadOptions() Options {
configFile := flag.String("config", "/etc/smartctl_exporter.yaml", "Path to smartctl_exporter config file")
verbose := flag.Bool("verbose", false, "Verbose log output")
debug := flag.Bool("debug", false, "Debug log output")
version := flag.Bool("version", false, "Show application version and exit")
flag.Parse()
if *version {
fmt.Printf("smartctl_exporter version: %s\n", exporterVersion)
os.Exit(0)
}
logger = newLogger(*verbose, *debug)
logger.Verbose("Read options from %s\n", *configFile)
yamlFile, err := ioutil.ReadFile(*configFile)
if err != nil {
logger.Panic("Failed read %s: %s", configFile, err)
}
opts := Options{
SMARTOptions{
BindTo: "9631",
URLPath: "/metrics",
FakeJSON: false,
SMARTctlLocation: "/usr/sbin/smartctl",
Devices: []string{},
},
}
if yaml.Unmarshal(yamlFile, &opts) != nil {
logger.Panic("Failed parse %s: %s", configFile, err)
}
logger.Debug("Parsed options: %s", opts)
return opts
}

50
readjson.go Normal file
View File

@ -0,0 +1,50 @@
package main
import (
"errors"
"fmt"
"io/ioutil"
"os/exec"
"strings"
"github.com/tidwall/gjson"
)
// Parse json to gjson object
func parseJSON(data string) (gjson.Result, error) {
if !gjson.Valid(data) {
return gjson.Parse("{}"), errors.New("Invalid JSON")
}
return gjson.Parse(data), nil
}
// Reading fake smartctl json
func readFakeSMARTctl(device string) (gjson.Result, error) {
splitted := strings.Split(device, "/")
filename := fmt.Sprintf("%s.json", splitted[len(splitted)-1])
logger.Verbose("Read fake S.M.A.R.T. data from json: %s", filename)
jsonFile, err := ioutil.ReadFile(filename)
if err != nil {
logger.Error("Fake S.M.A.R.T. data reading error: %s", err)
return parseJSON("{}")
}
return parseJSON(string(jsonFile))
}
// Get json from smartctl and parse it
func readSMARTctl(device string) (gjson.Result, error) {
logger.Debug("Collecting S.M.A.R.T. counters, device: %s...", device)
out, err := exec.Command(options.SMARTctl.SMARTctlLocation, "--json", "--xall", device).Output()
if err != nil {
logger.Error("S.M.A.R.T. output reading error: %s", err)
}
return parseJSON(string(out))
}
// Select json source and parse
func readData(device string) (gjson.Result, error) {
if options.SMARTctl.FakeJSON {
return readFakeSMARTctl(device)
}
return readSMARTctl(device)
}

223
smartctl.go Normal file
View File

@ -0,0 +1,223 @@
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 NFTables 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.mineVersion()
smart.mineDevice()
smart.mineCapacity()
smart.mineInterfaceSpeed()
smart.mineDeviceAttribute()
smart.minePowerOnSeconds()
smart.mineRotationRate()
smart.mineTemperatures()
}
func (smart SMARTctl) mineVersion() {
jsonVersion := smart.json.Get("json_format_version").Array()
smartctlJSON := smart.json.Get("smartctl")
smartctlVersion := smartctlJSON.Get("version").Array()
smart.ch <- prometheus.MustNewConstMetric(
metricSmartctlVersion,
prometheus.GaugeValue,
1,
fmt.Sprintf("%d.%d", jsonVersion[0].Int(), jsonVersion[1].Int()),
fmt.Sprintf("%d.%d", smartctlVersion[0].Int(), smartctlVersion[1].Int()),
smartctlJSON.Get("svn_revision").String(),
smartctlJSON.Get("build_info").String(),
)
}
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,
)
}

12
smartctl_exporter.yaml Normal file
View File

@ -0,0 +1,12 @@
smartctl_exporter:
bind_to: "[::1]:9631"
url_path: "/metrics"
fake_json: no
smartctl_location: /usr/sbin/smartctl
devices:
- /dev/sda
- /dev/sdb
- /dev/sdc
- /dev/sdd
- /dev/sde
- /dev/sdf