From 4a4557e3713c4c2be085f7c6e4d941d21e8e7a35 Mon Sep 17 00:00:00 2001 From: Fred Cox Date: Wed, 21 Feb 2018 02:06:13 +0200 Subject: [PATCH] Add server option to unbound plugin (#3713) --- README.md | 2 +- plugins/inputs/unbound/README.md | 4 +++ plugins/inputs/unbound/unbound.go | 40 ++++++++++++++++++++++++-- plugins/inputs/unbound/unbound_test.go | 6 ++-- 4 files changed, 45 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 19a5afe7e..0ae1a9316 100644 --- a/README.md +++ b/README.md @@ -208,7 +208,7 @@ configuration options. * [teamspeak](./plugins/inputs/teamspeak) * [tomcat](./plugins/inputs/tomcat) * [twemproxy](./plugins/inputs/twemproxy) -* [unbound](./plugins/input/unbound) +* [unbound](./plugins/inputs/unbound) * [varnish](./plugins/inputs/varnish) * [zfs](./plugins/inputs/zfs) * [zookeeper](./plugins/inputs/zookeeper) diff --git a/plugins/inputs/unbound/README.md b/plugins/inputs/unbound/README.md index b3aff8078..b2cf3d799 100644 --- a/plugins/inputs/unbound/README.md +++ b/plugins/inputs/unbound/README.md @@ -18,6 +18,10 @@ This plugin gathers stats from [Unbound - a validating, recursive, and caching D ## Use the builtin fielddrop/fieldpass telegraf filters in order to keep only specific fields fieldpass = ["total_*", "num_*","time_up", "mem_*"] + + ## IP of server to connect to, read from unbound conf default, optionally ':port' + ## Will lookup IP if given a hostname + server = "127.0.0.1:8953" ``` ### Measurements & Fields: diff --git a/plugins/inputs/unbound/unbound.go b/plugins/inputs/unbound/unbound.go index 94a3323e5..6bed9ea75 100644 --- a/plugins/inputs/unbound/unbound.go +++ b/plugins/inputs/unbound/unbound.go @@ -3,7 +3,9 @@ package unbound import ( "bufio" "bytes" + "context" "fmt" + "net" "os/exec" "strconv" "strings" @@ -15,13 +17,14 @@ import ( "github.com/influxdata/telegraf/plugins/inputs" ) -type runner func(cmdName string, Timeout internal.Duration, UseSudo bool) (*bytes.Buffer, error) +type runner func(cmdName string, Timeout internal.Duration, UseSudo bool, Server string) (*bytes.Buffer, error) // Unbound is used to store configuration values type Unbound struct { Binary string Timeout internal.Duration UseSudo bool + Server string filter filter.Filter run runner @@ -42,6 +45,10 @@ var sampleConfig = ` ## Use the builtin fielddrop/fieldpass telegraf filters in order to keep/remove specific fields fieldpass = ["total_*", "num_*","time_up", "mem_*"] + + ## IP of server to connect to, read from unbound conf default, optionally ':port' + ## Will lookup IP if given a hostname + server = "127.0.0.1:8953" ` func (s *Unbound) Description() string { @@ -54,9 +61,35 @@ func (s *Unbound) SampleConfig() string { } // Shell out to unbound_stat and return the output -func unboundRunner(cmdName string, Timeout internal.Duration, UseSudo bool) (*bytes.Buffer, error) { +func unboundRunner(cmdName string, Timeout internal.Duration, UseSudo bool, Server string) (*bytes.Buffer, error) { cmdArgs := []string{"stats_noreset"} + if Server != "" { + host, port, err := net.SplitHostPort(Server) + if err != nil { // No port was specified + host = Server + port = "" + } + + // Unbound control requires an IP address, and we want to be nice to the user + resolver := net.Resolver{} + ctx, lookUpCancel := context.WithTimeout(context.Background(), Timeout.Duration) + defer lookUpCancel() + serverIps, err := resolver.LookupIPAddr(ctx, host) + if err != nil { + return nil, fmt.Errorf("error looking up ip for server: %s: %s", Server, err) + } + if len(serverIps) == 0 { + return nil, fmt.Errorf("error no ip for server: %s: %s", Server, err) + } + server := serverIps[0].IP.String() + if port != "" { + server = server + "@" + port + } + + cmdArgs = append(cmdArgs, "-s", server) + } + cmd := exec.Command(cmdName, cmdArgs...) if UseSudo { @@ -86,7 +119,7 @@ func (s *Unbound) Gather(acc telegraf.Accumulator) error { return err } - out, err := s.run(s.Binary, s.Timeout, s.UseSudo) + out, err := s.run(s.Binary, s.Timeout, s.UseSudo, s.Server) if err != nil { return fmt.Errorf("error gathering metrics: %s", err) } @@ -132,6 +165,7 @@ func init() { Binary: defaultBinary, Timeout: defaultTimeout, UseSudo: false, + Server: "", } }) } diff --git a/plugins/inputs/unbound/unbound_test.go b/plugins/inputs/unbound/unbound_test.go index b8e821089..b1e042307 100644 --- a/plugins/inputs/unbound/unbound_test.go +++ b/plugins/inputs/unbound/unbound_test.go @@ -12,8 +12,8 @@ import ( var TestTimeout = internal.Duration{Duration: time.Second} -func UnboundControl(output string, Timeout internal.Duration, useSudo bool) func(string, internal.Duration, bool) (*bytes.Buffer, error) { - return func(string, internal.Duration, bool) (*bytes.Buffer, error) { +func UnboundControl(output string, Timeout internal.Duration, useSudo bool, Server string) func(string, internal.Duration, bool, string) (*bytes.Buffer, error) { + return func(string, internal.Duration, bool, string) (*bytes.Buffer, error) { return bytes.NewBuffer([]byte(output)), nil } } @@ -21,7 +21,7 @@ func UnboundControl(output string, Timeout internal.Duration, useSudo bool) func func TestParseFullOutput(t *testing.T) { acc := &testutil.Accumulator{} v := &Unbound{ - run: UnboundControl(fullOutput, TestTimeout, true), + run: UnboundControl(fullOutput, TestTimeout, true, ""), } err := v.Gather(acc)