-
Notifications
You must be signed in to change notification settings - Fork 5.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Input plugin for running ntp queries
see #235
- Loading branch information
Showing
6 changed files
with
512 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
# ntpq Input Plugin | ||
|
||
Get standard NTP query metrics, requires ntpq executable. | ||
|
||
Below is the documentation of the various headers returned from the NTP query | ||
command when running `ntpq -p`. | ||
|
||
- remote – The remote peer or server being synced to. “LOCAL” is this local host | ||
(included in case there are no remote peers or servers available); | ||
- refid – Where or what the remote peer or server is itself synchronised to; | ||
- st (stratum) – The remote peer or server Stratum | ||
- t (type) – Type (u: unicast or manycast client, b: broadcast or multicast client, | ||
l: local reference clock, s: symmetric peer, A: manycast server, | ||
B: broadcast server, M: multicast server, see “Automatic Server Discovery“); | ||
- when – When last polled (seconds ago, “h” hours ago, or “d” days ago); | ||
- poll – Polling frequency: rfc5905 suggests this ranges in NTPv4 from 4 (16s) | ||
to 17 (36h) (log2 seconds), however observation suggests the actual displayed | ||
value is seconds for a much smaller range of 64 (26) to 1024 (210) seconds; | ||
- reach – An 8-bit left-shift shift register value recording polls (bit set = | ||
successful, bit reset = fail) displayed in octal; | ||
- delay – Round trip communication delay to the remote peer or server (milliseconds); | ||
- offset – Mean offset (phase) in the times reported between this local host and | ||
the remote peer or server (RMS, milliseconds); | ||
- jitter – Mean deviation (jitter) in the time reported for that remote peer or | ||
server (RMS of difference of multiple time samples, milliseconds); | ||
|
||
### Configuration: | ||
|
||
```toml | ||
# Get standard NTP query metrics, requires ntpq executable | ||
[[inputs.ntpq]] | ||
## If false, set the -n ntpq flag. Can reduce metric gather times. | ||
dns_lookup = true | ||
``` | ||
|
||
### Measurements & Fields: | ||
|
||
- ntpq | ||
- delay (float, milliseconds) | ||
- jitter (float, milliseconds) | ||
- offset (float, milliseconds) | ||
- poll (int, seconds) | ||
- reach (int) | ||
- when (int, seconds) | ||
|
||
### Tags: | ||
|
||
- All measurements have the following tags: | ||
- refid | ||
- remote | ||
- type | ||
- stratum | ||
|
||
### Example Output: | ||
|
||
``` | ||
$ telegraf -config ~/ws/telegraf.conf -input-filter ntpq -test | ||
* Plugin: ntpq, Collection 1 | ||
> ntpq,refid=.GPSs.,remote=*time.apple.com,stratum=1,type=u delay=91.797,jitter=3.735,offset=12.841,poll=64i,reach=377i,when=35i 1457960478909556134 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
// +build !windows | ||
|
||
package ntpq | ||
|
||
import ( | ||
"bufio" | ||
"bytes" | ||
"log" | ||
"os/exec" | ||
"strconv" | ||
"strings" | ||
|
||
"github.com/influxdata/telegraf" | ||
"github.com/influxdata/telegraf/plugins/inputs" | ||
) | ||
|
||
// Mapping of ntpq header names to tag keys | ||
var tagHeaders map[string]string = map[string]string{ | ||
"remote": "remote", | ||
"refid": "refid", | ||
"st": "stratum", | ||
"t": "type", | ||
} | ||
|
||
// Mapping of the ntpq tag key to the index in the command output | ||
var tagI map[string]int = map[string]int{ | ||
"remote": -1, | ||
"refid": -1, | ||
"stratum": -1, | ||
"type": -1, | ||
} | ||
|
||
// Mapping of float metrics to their index in the command output | ||
var floatI map[string]int = map[string]int{ | ||
"delay": -1, | ||
"offset": -1, | ||
"jitter": -1, | ||
} | ||
|
||
// Mapping of int metrics to their index in the command output | ||
var intI map[string]int = map[string]int{ | ||
"when": -1, | ||
"poll": -1, | ||
"reach": -1, | ||
} | ||
|
||
type NTPQ struct { | ||
runQ func() ([]byte, error) | ||
|
||
DNSLookup bool `toml:"dns_lookup"` | ||
} | ||
|
||
func (n *NTPQ) Description() string { | ||
return "Get standard NTP query metrics, requires ntpq executable." | ||
} | ||
|
||
func (n *NTPQ) SampleConfig() string { | ||
return ` | ||
## If false, set the -n ntpq flag. Can reduce metric gather times. | ||
dns_lookup = true | ||
` | ||
} | ||
|
||
func (n *NTPQ) Gather(acc telegraf.Accumulator) error { | ||
out, err := n.runQ() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
lineCounter := 0 | ||
scanner := bufio.NewScanner(bytes.NewReader(out)) | ||
for scanner.Scan() { | ||
fields := strings.Fields(scanner.Text()) | ||
if len(fields) < 2 { | ||
continue | ||
} | ||
|
||
// If lineCounter == 0, then this is the header line | ||
if lineCounter == 0 { | ||
for i, field := range fields { | ||
// Check if field is a tag: | ||
if tagKey, ok := tagHeaders[field]; ok { | ||
tagI[tagKey] = i | ||
continue | ||
} | ||
|
||
// check if field is a float metric: | ||
if _, ok := floatI[field]; ok { | ||
floatI[field] = i | ||
continue | ||
} | ||
|
||
// check if field is an int metric: | ||
if _, ok := intI[field]; ok { | ||
intI[field] = i | ||
continue | ||
} | ||
} | ||
} else { | ||
tags := make(map[string]string) | ||
mFields := make(map[string]interface{}) | ||
|
||
// Get tags from output | ||
for key, index := range tagI { | ||
if index == -1 { | ||
continue | ||
} | ||
tags[key] = fields[index] | ||
} | ||
|
||
// Get integer metrics from output | ||
for key, index := range intI { | ||
if index == -1 { | ||
continue | ||
} | ||
|
||
if key == "when" { | ||
when := fields[index] | ||
switch { | ||
case strings.HasSuffix(when, "h"): | ||
m, err := strconv.Atoi(strings.TrimSuffix(fields[index], "h")) | ||
if err != nil { | ||
log.Printf("ERROR ntpq: parsing int: %s", fields[index]) | ||
continue | ||
} | ||
// seconds in an hour | ||
mFields[key] = int64(m) * 360 | ||
continue | ||
case strings.HasSuffix(when, "d"): | ||
m, err := strconv.Atoi(strings.TrimSuffix(fields[index], "d")) | ||
if err != nil { | ||
log.Printf("ERROR ntpq: parsing int: %s", fields[index]) | ||
continue | ||
} | ||
// seconds in a day | ||
mFields[key] = int64(m) * 86400 | ||
continue | ||
} | ||
} | ||
|
||
m, err := strconv.Atoi(fields[index]) | ||
if err != nil { | ||
log.Printf("ERROR ntpq: parsing int: %s", fields[index]) | ||
continue | ||
} | ||
mFields[key] = int64(m) | ||
} | ||
|
||
// get float metrics from output | ||
for key, index := range floatI { | ||
if index == -1 { | ||
continue | ||
} | ||
|
||
m, err := strconv.ParseFloat(fields[index], 64) | ||
if err != nil { | ||
log.Printf("ERROR ntpq: parsing float: %s", fields[index]) | ||
} | ||
mFields[key] = m | ||
} | ||
|
||
acc.AddFields("ntpq", mFields, tags) | ||
} | ||
|
||
lineCounter++ | ||
} | ||
return nil | ||
} | ||
|
||
func (n *NTPQ) runq() ([]byte, error) { | ||
bin, err := exec.LookPath("ntpq") | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var cmd *exec.Cmd | ||
if n.DNSLookup { | ||
cmd = exec.Command(bin, "-p") | ||
} else { | ||
cmd = exec.Command(bin, "-p", "-n") | ||
} | ||
|
||
return cmd.Output() | ||
} | ||
|
||
func init() { | ||
inputs.Add("ntpq", func() telegraf.Input { | ||
n := &NTPQ{} | ||
n.runQ = n.runq | ||
return n | ||
}) | ||
} |
Oops, something went wrong.