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/fatih/pool.v2 cba550ebf9bce999a02e963296d4bc7a486cb715
|
||||||
gopkg.in/mgo.v2 03c9f3ee4c14c8e51ee521a6a7d0425658dd6f64
|
gopkg.in/mgo.v2 03c9f3ee4c14c8e51ee521a6a7d0425658dd6f64
|
||||||
gopkg.in/yaml.v2 f7716cbe52baa25d2e9b0d0da546fcf909fc16b4
|
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/bcache"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/couchdb"
|
_ "github.com/influxdata/telegraf/plugins/inputs/couchdb"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/disque"
|
_ "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/docker"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/dovecot"
|
_ "github.com/influxdata/telegraf/plugins/inputs/dovecot"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/elasticsearch"
|
_ "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