169 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			169 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
| 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 int    `xml:"total"`
 | |
| 			InUse int    `xml:"inuse"`
 | |
| 		} `xml:"contexts>context"`
 | |
| 		Summary struct {
 | |
| 			TotalUse    int
 | |
| 			InUse       int
 | |
| 			BlockSize   int
 | |
| 			ContextSize int
 | |
| 			Lost        int
 | |
| 		} `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
 | |
| }
 |