-
Notifications
You must be signed in to change notification settings - Fork 0
/
nsd.go
167 lines (136 loc) · 4.15 KB
/
nsd.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package nsd
import (
"bufio"
"bytes"
"fmt"
"net"
"os/exec"
"strconv"
"strings"
"time"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/filter"
"github.com/influxdata/telegraf/internal"
"github.com/influxdata/telegraf/plugins/inputs"
)
type runner func(cmdName string, Timeout internal.Duration, UseSudo bool, Server string, ConfigFile string) (*bytes.Buffer, error)
// NSD is used to store configuration values
type NSD struct {
Binary string
Timeout internal.Duration
UseSudo bool
Server string
ConfigFile string
filter filter.Filter
run runner
}
var defaultBinary = "/usr/sbin/nsd-control"
var defaultTimeout = internal.Duration{Duration: time.Second}
var sampleConfig = `
## Address of server to connect to, optionally ':port'. Defaults to the
## address in the nsd config file.
server = "127.0.0.1:8953"
## If running as a restricted user you can prepend sudo for additional access:
# use_sudo = false
## The default location of the nsd-control binary can be overridden with:
# binary = "/usr/sbin/nsd-control"
## The default location of the nsd config file can be overridden with:
# config_file = "/etc/nsd/nsd.conf"
## The default timeout of 1s can be overridden with:
# timeout = "1s"
`
// Description displays what this plugin is about
func (s *NSD) Description() string {
return "A plugin to collect stats from the NSD authoritative DNS name server"
}
// SampleConfig displays configuration instructions
func (s *NSD) SampleConfig() string {
return sampleConfig
}
// Shell out to nsd_stat and return the output
func nsdRunner(cmdName string, Timeout internal.Duration, UseSudo bool, Server string, ConfigFile string) (*bytes.Buffer, error) {
cmdArgs := []string{"stats_noreset"}
if Server != "" {
host, port, err := net.SplitHostPort(Server)
if err == nil {
Server = host + "@" + port
}
cmdArgs = append([]string{"-s", Server}, cmdArgs...)
}
if ConfigFile != "" {
cmdArgs = append([]string{"-c", ConfigFile}, cmdArgs...)
}
cmd := exec.Command(cmdName, cmdArgs...)
if UseSudo {
cmdArgs = append([]string{cmdName}, cmdArgs...)
cmd = exec.Command("sudo", cmdArgs...)
}
var out bytes.Buffer
cmd.Stdout = &out
err := internal.RunTimeout(cmd, Timeout.Duration)
if err != nil {
return &out, fmt.Errorf("error running nsd-control: %s (%s %v)", err, cmdName, cmdArgs)
}
return &out, nil
}
// Gather collects stats from nsd-control and adds them to the Accumulator
func (s *NSD) Gather(acc telegraf.Accumulator) error {
out, err := s.run(s.Binary, s.Timeout, s.UseSudo, s.Server, s.ConfigFile)
if err != nil {
return fmt.Errorf("error gathering metrics: %s", err)
}
// Process values
fields := make(map[string]interface{})
fieldsServers := make(map[string]map[string]interface{})
scanner := bufio.NewScanner(out)
for scanner.Scan() {
cols := strings.Split(scanner.Text(), "=")
// Check split correctness
if len(cols) != 2 {
continue
}
stat := cols[0]
value := cols[1]
fieldValue, err := strconv.ParseFloat(value, 64)
if err != nil {
acc.AddError(fmt.Errorf("Expected a numerical value for %s = %v",
stat, value))
continue
}
if strings.HasPrefix(stat, "server") {
statTokens := strings.Split(stat, ".")
if len(statTokens) > 1 {
serverId := strings.TrimPrefix(statTokens[0], "server")
if _, err := strconv.Atoi(serverId); err == nil {
serverTokens := statTokens[1:]
field := strings.Join(serverTokens[:], "_")
if fieldsServers[serverId] == nil {
fieldsServers[serverId] = make(map[string]interface{})
}
fieldsServers[serverId][field] = fieldValue
}
}
} else {
field := strings.Replace(stat, ".", "_", -1)
fields[field] = fieldValue
}
}
acc.AddFields("nsd", fields, nil)
for thisServerId, thisServerFields := range fieldsServers {
thisServerTag := map[string]string{"server": thisServerId}
acc.AddFields("nsd_servers", thisServerFields, thisServerTag)
}
return nil
}
func init() {
inputs.Add("nsd", func() telegraf.Input {
return &NSD{
run: nsdRunner,
Binary: defaultBinary,
Timeout: defaultTimeout,
UseSudo: false,
Server: "",
ConfigFile: "",
}
})
}