telegraf/plugins/inputs/bind/xml_stats_v2.go

169 lines
4.5 KiB
Go
Raw Normal View History

2019-04-03 22:59:47 +00:00
package bind
import (
"encoding/xml"
"fmt"
"net"
"net/http"
"net/url"
"time"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/metric"
)
type v2Root struct {
XMLName xml.Name
Version string `xml:"version,attr"`
Statistics v2Statistics `xml:"bind>statistics"`
}
// Omitted branches: socketmgr, taskmgr
type v2Statistics struct {
Version string `xml:"version,attr"`
Views []struct {
// Omitted branches: zones
Name string `xml:"name"`
RdTypes []v2Counter `xml:"rdtype"`
ResStats []v2Counter `xml:"resstat"`
Caches []struct {
Name string `xml:"name,attr"`
RRSets []v2Counter `xml:"rrset"`
} `xml:"cache"`
} `xml:"views>view"`
Server struct {
OpCodes []v2Counter `xml:"requests>opcode"`
RdTypes []v2Counter `xml:"queries-in>rdtype"`
NSStats []v2Counter `xml:"nsstat"`
ZoneStats []v2Counter `xml:"zonestat"`
ResStats []v2Counter `xml:"resstat"`
SockStats []v2Counter `xml:"sockstat"`
} `xml:"server"`
Memory struct {
Contexts []struct {
// Omitted nodes: references, maxinuse, blocksize, pools, hiwater, lowater
Id string `xml:"id"`
Name string `xml:"name"`
Total int64 `xml:"total"`
InUse int64 `xml:"inuse"`
2019-04-03 22:59:47 +00:00
} `xml:"contexts>context"`
Summary struct {
TotalUse int64
InUse int64
BlockSize int64
ContextSize int64
Lost int64
2019-04-03 22:59:47 +00:00
} `xml:"summary"`
} `xml:"memory"`
}
// BIND statistics v2 counter struct used throughout
type v2Counter struct {
Name string `xml:"name"`
Value int `xml:"counter"`
}
// addXMLv2Counter adds a v2Counter array to a Telegraf Accumulator, with the specified tags
func addXMLv2Counter(acc telegraf.Accumulator, commonTags map[string]string, stats []v2Counter) {
grouper := metric.NewSeriesGrouper()
ts := time.Now()
for _, c := range stats {
tags := make(map[string]string)
// Create local copy of tags since maps are reference types
for k, v := range commonTags {
tags[k] = v
}
grouper.Add("bind_counter", tags, ts, c.Name, c.Value)
}
//Add grouped metrics
for _, metric := range grouper.Metrics() {
acc.AddMetric(metric)
}
}
// readStatsXMLv2 decodes a BIND9 XML statistics version 2 document. Unlike the XML v3 statistics
// format, the v2 format does not support broken-out subsets.
func (b *Bind) readStatsXMLv2(addr *url.URL, acc telegraf.Accumulator) error {
var stats v2Root
resp, err := client.Get(addr.String())
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("%s returned HTTP status: %s", addr, resp.Status)
}
if err := xml.NewDecoder(resp.Body).Decode(&stats); err != nil {
return fmt.Errorf("Unable to decode XML document: %s", err)
}
tags := map[string]string{"url": addr.Host}
host, port, _ := net.SplitHostPort(addr.Host)
tags["source"] = host
tags["port"] = port
// Opcodes
tags["type"] = "opcode"
addXMLv2Counter(acc, tags, stats.Statistics.Server.OpCodes)
// Query RDATA types
tags["type"] = "qtype"
addXMLv2Counter(acc, tags, stats.Statistics.Server.RdTypes)
// Nameserver stats
tags["type"] = "nsstat"
addXMLv2Counter(acc, tags, stats.Statistics.Server.NSStats)
// Zone stats
tags["type"] = "zonestat"
addXMLv2Counter(acc, tags, stats.Statistics.Server.ZoneStats)
// Socket statistics
tags["type"] = "sockstat"
addXMLv2Counter(acc, tags, stats.Statistics.Server.SockStats)
// Memory stats
fields := map[string]interface{}{
"total_use": stats.Statistics.Memory.Summary.TotalUse,
"in_use": stats.Statistics.Memory.Summary.InUse,
"block_size": stats.Statistics.Memory.Summary.BlockSize,
"context_size": stats.Statistics.Memory.Summary.ContextSize,
"lost": stats.Statistics.Memory.Summary.Lost,
}
acc.AddGauge("bind_memory", fields, map[string]string{"url": addr.Host, "source": host, "port": port})
// Detailed, per-context memory stats
if b.GatherMemoryContexts {
for _, c := range stats.Statistics.Memory.Contexts {
tags := map[string]string{"url": addr.Host, "id": c.Id, "name": c.Name, "source": host, "port": port}
fields := map[string]interface{}{"total": c.Total, "in_use": c.InUse}
acc.AddGauge("bind_memory_context", fields, tags)
}
}
// Detailed, per-view stats
if b.GatherViews {
for _, v := range stats.Statistics.Views {
tags := map[string]string{"url": addr.Host, "view": v.Name}
// Query RDATA types
tags["type"] = "qtype"
addXMLv2Counter(acc, tags, v.RdTypes)
// Resolver stats
tags["type"] = "resstats"
addXMLv2Counter(acc, tags, v.ResStats)
}
}
return nil
}