telegraf/plugins/inputs/dmcache/dmcache_linux.go

191 lines
4.9 KiB
Go
Raw Permalink Normal View History

2017-04-07 22:39:43 +00:00
// +build linux
package dmcache
import (
"os/exec"
"strconv"
"strings"
"errors"
"github.com/influxdata/telegraf"
)
const metricName = "dmcache"
type cacheStatus struct {
device string
2017-09-25 22:49:36 +00:00
length int64
2017-04-07 22:39:43 +00:00
target string
2017-09-25 22:49:36 +00:00
metadataBlocksize int64
metadataUsed int64
metadataTotal int64
cacheBlocksize int64
cacheUsed int64
cacheTotal int64
readHits int64
readMisses int64
writeHits int64
writeMisses int64
demotions int64
promotions int64
dirty int64
2017-04-07 22:39:43 +00:00
}
func (c *DMCache) Gather(acc telegraf.Accumulator) error {
outputLines, err := c.getCurrentStatus()
if err != nil {
return err
}
totalStatus := cacheStatus{}
for _, s := range outputLines {
status, err := parseDMSetupStatus(s)
if err != nil {
return err
}
if c.PerDevice {
tags := map[string]string{"device": status.device}
acc.AddFields(metricName, toFields(status), tags)
}
aggregateStats(&totalStatus, status)
}
acc.AddFields(metricName, toFields(totalStatus), map[string]string{"device": "all"})
return nil
}
func parseDMSetupStatus(line string) (cacheStatus, error) {
var err error
parseError := errors.New("Output from dmsetup could not be parsed")
status := cacheStatus{}
values := strings.Fields(line)
if len(values) < 15 {
return cacheStatus{}, parseError
}
status.device = strings.TrimRight(values[0], ":")
2017-09-25 22:49:36 +00:00
status.length, err = strconv.ParseInt(values[2], 10, 64)
2017-04-07 22:39:43 +00:00
if err != nil {
return cacheStatus{}, err
}
status.target = values[3]
2017-09-25 22:49:36 +00:00
status.metadataBlocksize, err = strconv.ParseInt(values[4], 10, 64)
2017-04-07 22:39:43 +00:00
if err != nil {
return cacheStatus{}, err
}
metadata := strings.Split(values[5], "/")
if len(metadata) != 2 {
return cacheStatus{}, parseError
}
2017-09-25 22:49:36 +00:00
status.metadataUsed, err = strconv.ParseInt(metadata[0], 10, 64)
2017-04-07 22:39:43 +00:00
if err != nil {
return cacheStatus{}, err
}
2017-09-25 22:49:36 +00:00
status.metadataTotal, err = strconv.ParseInt(metadata[1], 10, 64)
2017-04-07 22:39:43 +00:00
if err != nil {
return cacheStatus{}, err
}
2017-09-25 22:49:36 +00:00
status.cacheBlocksize, err = strconv.ParseInt(values[6], 10, 64)
2017-04-07 22:39:43 +00:00
if err != nil {
return cacheStatus{}, err
}
cache := strings.Split(values[7], "/")
if len(cache) != 2 {
return cacheStatus{}, parseError
}
2017-09-25 22:49:36 +00:00
status.cacheUsed, err = strconv.ParseInt(cache[0], 10, 64)
2017-04-07 22:39:43 +00:00
if err != nil {
return cacheStatus{}, err
}
2017-09-25 22:49:36 +00:00
status.cacheTotal, err = strconv.ParseInt(cache[1], 10, 64)
2017-04-07 22:39:43 +00:00
if err != nil {
return cacheStatus{}, err
}
2017-09-25 22:49:36 +00:00
status.readHits, err = strconv.ParseInt(values[8], 10, 64)
2017-04-07 22:39:43 +00:00
if err != nil {
return cacheStatus{}, err
}
2017-09-25 22:49:36 +00:00
status.readMisses, err = strconv.ParseInt(values[9], 10, 64)
2017-04-07 22:39:43 +00:00
if err != nil {
return cacheStatus{}, err
}
2017-09-25 22:49:36 +00:00
status.writeHits, err = strconv.ParseInt(values[10], 10, 64)
2017-04-07 22:39:43 +00:00
if err != nil {
return cacheStatus{}, err
}
2017-09-25 22:49:36 +00:00
status.writeMisses, err = strconv.ParseInt(values[11], 10, 64)
2017-04-07 22:39:43 +00:00
if err != nil {
return cacheStatus{}, err
}
2017-09-25 22:49:36 +00:00
status.demotions, err = strconv.ParseInt(values[12], 10, 64)
2017-04-07 22:39:43 +00:00
if err != nil {
return cacheStatus{}, err
}
2017-09-25 22:49:36 +00:00
status.promotions, err = strconv.ParseInt(values[13], 10, 64)
2017-04-07 22:39:43 +00:00
if err != nil {
return cacheStatus{}, err
}
2017-09-25 22:49:36 +00:00
status.dirty, err = strconv.ParseInt(values[14], 10, 64)
2017-04-07 22:39:43 +00:00
if err != nil {
return cacheStatus{}, err
}
return status, nil
}
func aggregateStats(totalStatus *cacheStatus, status cacheStatus) {
totalStatus.length += status.length
totalStatus.metadataBlocksize += status.metadataBlocksize
totalStatus.metadataUsed += status.metadataUsed
totalStatus.metadataTotal += status.metadataTotal
totalStatus.cacheBlocksize += status.cacheBlocksize
totalStatus.cacheUsed += status.cacheUsed
totalStatus.cacheTotal += status.cacheTotal
totalStatus.readHits += status.readHits
totalStatus.readMisses += status.readMisses
totalStatus.writeHits += status.writeHits
totalStatus.writeMisses += status.writeMisses
totalStatus.demotions += status.demotions
totalStatus.promotions += status.promotions
totalStatus.dirty += status.dirty
}
func toFields(status cacheStatus) map[string]interface{} {
fields := make(map[string]interface{})
fields["length"] = status.length
fields["metadata_blocksize"] = status.metadataBlocksize
fields["metadata_used"] = status.metadataUsed
fields["metadata_total"] = status.metadataTotal
fields["cache_blocksize"] = status.cacheBlocksize
fields["cache_used"] = status.cacheUsed
fields["cache_total"] = status.cacheTotal
fields["read_hits"] = status.readHits
fields["read_misses"] = status.readMisses
fields["write_hits"] = status.writeHits
fields["write_misses"] = status.writeMisses
fields["demotions"] = status.demotions
fields["promotions"] = status.promotions
fields["dirty"] = status.dirty
return fields
}
func dmSetupStatus() ([]string, error) {
out, err := exec.Command("/bin/sh", "-c", "sudo /sbin/dmsetup status --target cache").Output()
if err != nil {
return nil, err
}
if string(out) == "No devices found\n" {
return []string{}, nil
}
outString := strings.TrimRight(string(out), "\n")
status := strings.Split(outString, "\n")
return status, nil
}