191 lines
4.9 KiB
Go
191 lines
4.9 KiB
Go
// +build linux
|
|
|
|
package dmcache
|
|
|
|
import (
|
|
"os/exec"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"errors"
|
|
|
|
"github.com/influxdata/telegraf"
|
|
)
|
|
|
|
const metricName = "dmcache"
|
|
|
|
type cacheStatus struct {
|
|
device string
|
|
length int64
|
|
target string
|
|
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
|
|
}
|
|
|
|
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], ":")
|
|
status.length, err = strconv.ParseInt(values[2], 10, 64)
|
|
if err != nil {
|
|
return cacheStatus{}, err
|
|
}
|
|
status.target = values[3]
|
|
status.metadataBlocksize, err = strconv.ParseInt(values[4], 10, 64)
|
|
if err != nil {
|
|
return cacheStatus{}, err
|
|
}
|
|
metadata := strings.Split(values[5], "/")
|
|
if len(metadata) != 2 {
|
|
return cacheStatus{}, parseError
|
|
}
|
|
status.metadataUsed, err = strconv.ParseInt(metadata[0], 10, 64)
|
|
if err != nil {
|
|
return cacheStatus{}, err
|
|
}
|
|
status.metadataTotal, err = strconv.ParseInt(metadata[1], 10, 64)
|
|
if err != nil {
|
|
return cacheStatus{}, err
|
|
}
|
|
status.cacheBlocksize, err = strconv.ParseInt(values[6], 10, 64)
|
|
if err != nil {
|
|
return cacheStatus{}, err
|
|
}
|
|
cache := strings.Split(values[7], "/")
|
|
if len(cache) != 2 {
|
|
return cacheStatus{}, parseError
|
|
}
|
|
status.cacheUsed, err = strconv.ParseInt(cache[0], 10, 64)
|
|
if err != nil {
|
|
return cacheStatus{}, err
|
|
}
|
|
status.cacheTotal, err = strconv.ParseInt(cache[1], 10, 64)
|
|
if err != nil {
|
|
return cacheStatus{}, err
|
|
}
|
|
status.readHits, err = strconv.ParseInt(values[8], 10, 64)
|
|
if err != nil {
|
|
return cacheStatus{}, err
|
|
}
|
|
status.readMisses, err = strconv.ParseInt(values[9], 10, 64)
|
|
if err != nil {
|
|
return cacheStatus{}, err
|
|
}
|
|
status.writeHits, err = strconv.ParseInt(values[10], 10, 64)
|
|
if err != nil {
|
|
return cacheStatus{}, err
|
|
}
|
|
status.writeMisses, err = strconv.ParseInt(values[11], 10, 64)
|
|
if err != nil {
|
|
return cacheStatus{}, err
|
|
}
|
|
status.demotions, err = strconv.ParseInt(values[12], 10, 64)
|
|
if err != nil {
|
|
return cacheStatus{}, err
|
|
}
|
|
status.promotions, err = strconv.ParseInt(values[13], 10, 64)
|
|
if err != nil {
|
|
return cacheStatus{}, err
|
|
}
|
|
status.dirty, err = strconv.ParseInt(values[14], 10, 64)
|
|
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
|
|
}
|