Dns query input plugin
This commit is contained in:
parent
a18f535f21
commit
636dc27ead
1
Godeps
1
Godeps
|
@ -50,3 +50,4 @@ gopkg.in/dancannon/gorethink.v1 6f088135ff288deb9d5546f4c71919207f891a70
|
|||
gopkg.in/fatih/pool.v2 cba550ebf9bce999a02e963296d4bc7a486cb715
|
||||
gopkg.in/mgo.v2 03c9f3ee4c14c8e51ee521a6a7d0425658dd6f64
|
||||
gopkg.in/yaml.v2 f7716cbe52baa25d2e9b0d0da546fcf909fc16b4
|
||||
github.com/miekg/dns e0d84d97e59bcb6561eae269c4e94d25b66822cb
|
|
@ -6,6 +6,7 @@ import (
|
|||
_ "github.com/influxdata/telegraf/plugins/inputs/bcache"
|
||||
_ "github.com/influxdata/telegraf/plugins/inputs/couchdb"
|
||||
_ "github.com/influxdata/telegraf/plugins/inputs/disque"
|
||||
_ "github.com/influxdata/telegraf/plugins/inputs/dns"
|
||||
_ "github.com/influxdata/telegraf/plugins/inputs/docker"
|
||||
_ "github.com/influxdata/telegraf/plugins/inputs/dovecot"
|
||||
_ "github.com/influxdata/telegraf/plugins/inputs/elasticsearch"
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
# DNS Input Plugin
|
||||
|
||||
The DNS plugin gathers dns query times in miliseconds - like [Dig](https://en.wikipedia.org/wiki/Dig_\(command\))
|
||||
|
||||
### Configuration:
|
||||
|
||||
```
|
||||
# Sample Config:
|
||||
[[inputs.dns]]
|
||||
### Domains or subdomains to query
|
||||
domains = ["mjasion.pl"] # required
|
||||
|
||||
### servers to query
|
||||
servers = ["8.8.8.8"] # required
|
||||
|
||||
### Query record type. Posible values: A, CNAME, MX, TXT, NS. Default is "A"
|
||||
recordType = "A" # optional
|
||||
|
||||
### Dns server port. 53 is default
|
||||
port = 53 # optional
|
||||
|
||||
### Query timeout in seconds. Default is 2 seconds
|
||||
timeout = 2 # optional
|
||||
```
|
||||
|
||||
For querying more than one record type make:
|
||||
|
||||
```
|
||||
[[inputs.dns]]
|
||||
domains = ["mjasion.pl"]
|
||||
servers = ["8.8.8.8", "8.8.4.4"]
|
||||
recordType = "A"
|
||||
|
||||
[[inputs.dns]]
|
||||
domains = ["mjasion.pl"]
|
||||
servers = ["8.8.8.8", "8.8.4.4"]
|
||||
recordType = "MX"
|
||||
```
|
||||
|
||||
### Tags:
|
||||
|
||||
- server
|
||||
- domain
|
||||
- recordType
|
||||
|
||||
### Example output:
|
||||
|
||||
```
|
||||
./telegraf -config telegraf.conf -test -input-filter dns -test
|
||||
> dns,domain=mjasion.pl,recordType=A,server=8.8.8.8 value=25.236181 1455452083165126877
|
||||
```
|
|
@ -0,0 +1,143 @@
|
|||
package dns
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/plugins/inputs"
|
||||
"github.com/miekg/dns"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Dns struct {
|
||||
// Domains or subdomains to query
|
||||
Domains []string
|
||||
|
||||
// Server to query
|
||||
Servers []string
|
||||
|
||||
// Record type
|
||||
RecordType string
|
||||
|
||||
// DNS server port number
|
||||
Port int
|
||||
|
||||
// Dns query timeout in seconds. 0 means no timeout
|
||||
Timeout int
|
||||
}
|
||||
|
||||
var sampleConfig = `
|
||||
### Domains or subdomains to query
|
||||
domains = ["mjasion.pl"] # required
|
||||
|
||||
### servers to query
|
||||
servers = ["8.8.8.8"] # required
|
||||
|
||||
### Query record type. Posible values: A, CNAME, MX, TXT, NS. Default is "A"
|
||||
recordType = "A" # optional
|
||||
|
||||
### Dns server port. 53 is default
|
||||
port = 53 # optional
|
||||
|
||||
### Query timeout in seconds. Default is 2 seconds
|
||||
timeout = 2 # optional
|
||||
`
|
||||
|
||||
func (d *Dns) SampleConfig() string {
|
||||
return sampleConfig
|
||||
}
|
||||
|
||||
func (d *Dns) Description() string {
|
||||
return "Query given DNS server and gives statistics"
|
||||
}
|
||||
func (d *Dns) Gather(acc telegraf.Accumulator) error {
|
||||
d.setDefaultValues()
|
||||
for _, domain := range d.Domains {
|
||||
for _, server := range d.Servers {
|
||||
dnsQueryTime, err := d.getDnsQueryTime(domain, server)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tags := map[string]string{
|
||||
"server": server,
|
||||
"domain": domain,
|
||||
"recordType": d.RecordType,
|
||||
}
|
||||
|
||||
acc.Add("dns", dnsQueryTime, tags)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Dns) setDefaultValues() {
|
||||
if len(d.RecordType) == 0 {
|
||||
d.RecordType = "A"
|
||||
}
|
||||
if d.Port == 0 {
|
||||
d.Port = 53
|
||||
}
|
||||
if d.Timeout == 0 {
|
||||
d.Timeout = 2
|
||||
}
|
||||
}
|
||||
|
||||
func (d *Dns) getDnsQueryTime(domain string, server string) (float64, error) {
|
||||
dnsQueryTime := float64(0)
|
||||
|
||||
c := new(dns.Client)
|
||||
c.ReadTimeout = time.Duration(d.Timeout) * time.Second
|
||||
|
||||
m := new(dns.Msg)
|
||||
recordType, err := d.parseRecordType()
|
||||
if err != nil {
|
||||
return dnsQueryTime, err
|
||||
}
|
||||
m.SetQuestion(dns.Fqdn(domain), recordType)
|
||||
m.RecursionDesired = true
|
||||
|
||||
start_time := time.Now()
|
||||
r, _, err := c.Exchange(m, net.JoinHostPort(server, strconv.Itoa(d.Port)))
|
||||
queryDuration := time.Since(start_time)
|
||||
|
||||
if err != nil {
|
||||
return dnsQueryTime, err
|
||||
}
|
||||
if r.Rcode != dns.RcodeSuccess {
|
||||
return dnsQueryTime, errors.New(fmt.Sprintf("Invalid answer name %s after %s query for %s\n", domain, d.RecordType, domain))
|
||||
}
|
||||
|
||||
dnsQueryTime = float64(queryDuration.Nanoseconds()) / 1e6
|
||||
return dnsQueryTime, nil
|
||||
}
|
||||
|
||||
func (d *Dns) parseRecordType() (uint16, error) {
|
||||
var recordType uint16
|
||||
var error error
|
||||
|
||||
switch d.RecordType {
|
||||
case "A":
|
||||
recordType = dns.TypeA
|
||||
case "CNAME":
|
||||
recordType = dns.TypeCNAME
|
||||
case "MX":
|
||||
recordType = dns.TypeMX
|
||||
case "NS":
|
||||
recordType = dns.TypeNS
|
||||
case "TXT":
|
||||
recordType = dns.TypeTXT
|
||||
default:
|
||||
error = errors.New(fmt.Sprintf("Record type %s not recognized", d.RecordType))
|
||||
}
|
||||
|
||||
return recordType, error
|
||||
}
|
||||
|
||||
func init() {
|
||||
inputs.Add("dns", func() telegraf.Input {
|
||||
return &Dns{}
|
||||
})
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
package dns
|
||||
|
||||
import (
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var servers = []string{"8.8.8.8"}
|
||||
var domains = []string{"mjasion.pl"}
|
||||
|
||||
func TestGathering(t *testing.T) {
|
||||
var dnsConfig = Dns{
|
||||
Servers: servers,
|
||||
Domains: domains,
|
||||
}
|
||||
var acc testutil.Accumulator
|
||||
|
||||
dnsConfig.Gather(&acc)
|
||||
metric, _ := acc.Get("dns")
|
||||
queryTime, _ := metric.Fields["value"].(float64)
|
||||
|
||||
assert.NotEqual(t, 0, queryTime)
|
||||
}
|
||||
|
||||
func TestGatheringMxRecord(t *testing.T) {
|
||||
var dnsConfig = Dns{
|
||||
Servers: servers,
|
||||
Domains: domains,
|
||||
}
|
||||
var acc testutil.Accumulator
|
||||
dnsConfig.RecordType = "MX"
|
||||
|
||||
dnsConfig.Gather(&acc)
|
||||
metric, _ := acc.Get("dns")
|
||||
queryTime, _ := metric.Fields["value"].(float64)
|
||||
|
||||
assert.NotEqual(t, 0, queryTime)
|
||||
}
|
||||
|
||||
func TestMetricContainsServerAndDomainAndRecordTypeTags(t *testing.T) {
|
||||
var dnsConfig = Dns{
|
||||
Servers: servers,
|
||||
Domains: domains,
|
||||
}
|
||||
var acc testutil.Accumulator
|
||||
tags := map[string]string{
|
||||
"server": "8.8.8.8",
|
||||
"domain": "mjasion.pl",
|
||||
"recordType": "A",
|
||||
}
|
||||
fields := map[string]interface{}{}
|
||||
|
||||
dnsConfig.Gather(&acc)
|
||||
metric, _ := acc.Get("dns")
|
||||
queryTime, _ := metric.Fields["value"].(float64)
|
||||
|
||||
fields["value"] = queryTime
|
||||
acc.AssertContainsTaggedFields(t, "dns", fields, tags)
|
||||
}
|
||||
|
||||
func TestGatheringTimeout(t *testing.T) {
|
||||
var dnsConfig = Dns{
|
||||
Servers: servers,
|
||||
Domains: domains,
|
||||
}
|
||||
var acc testutil.Accumulator
|
||||
dnsConfig.Port = 60054
|
||||
dnsConfig.Timeout = 1
|
||||
var err error
|
||||
|
||||
channel := make(chan error, 1)
|
||||
go func() {
|
||||
channel <- dnsConfig.Gather(&acc)
|
||||
}()
|
||||
select {
|
||||
case res := <-channel:
|
||||
err = res
|
||||
case <-time.After(time.Second * 2):
|
||||
err = nil
|
||||
}
|
||||
|
||||
assert.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "i/o timeout")
|
||||
}
|
||||
|
||||
func TestSettingDefaultValues(t *testing.T) {
|
||||
dnsConfig := Dns{}
|
||||
|
||||
dnsConfig.setDefaultValues()
|
||||
|
||||
assert.Equal(t, "A", dnsConfig.RecordType, "Default record type not equal 'A'")
|
||||
assert.Equal(t, 53, dnsConfig.Port, "Default port number not equal 53")
|
||||
assert.Equal(t, 2, dnsConfig.Timeout, "Default timeout not equal 2")
|
||||
}
|
||||
|
||||
func TestRecordTypeParser(t *testing.T) {
|
||||
var dnsConfig = Dns{}
|
||||
var recordType uint16
|
||||
var err error
|
||||
|
||||
dnsConfig.RecordType = "A"
|
||||
recordType, err = dnsConfig.parseRecordType()
|
||||
assert.Equal(t, dns.TypeA, recordType)
|
||||
|
||||
dnsConfig.RecordType = "CNAME"
|
||||
recordType, err = dnsConfig.parseRecordType()
|
||||
assert.Equal(t, dns.TypeCNAME, recordType)
|
||||
|
||||
dnsConfig.RecordType = "MX"
|
||||
recordType, err = dnsConfig.parseRecordType()
|
||||
assert.Equal(t, dns.TypeMX, recordType)
|
||||
|
||||
dnsConfig.RecordType = "NS"
|
||||
recordType, err = dnsConfig.parseRecordType()
|
||||
assert.Equal(t, dns.TypeNS, recordType)
|
||||
|
||||
dnsConfig.RecordType = "TXT"
|
||||
recordType, err = dnsConfig.parseRecordType()
|
||||
assert.Equal(t, dns.TypeTXT, recordType)
|
||||
|
||||
dnsConfig.RecordType = "nil"
|
||||
recordType, err = dnsConfig.parseRecordType()
|
||||
assert.Error(t, err)
|
||||
}
|
Loading…
Reference in New Issue