From b24e03b5974cb6741ca90df9629e837f751f8413 Mon Sep 17 00:00:00 2001 From: James Maidment Date: Mon, 29 Oct 2018 18:57:39 -0400 Subject: [PATCH] Add wireless input plugin (#3847) --- plugins/inputs/all/all.go | 1 + plugins/inputs/wireless/README.md | 38 +++++ plugins/inputs/wireless/wireless.go | 3 + plugins/inputs/wireless/wireless_linux.go | 165 ++++++++++++++++++++++ plugins/inputs/wireless/wireless_test.go | 52 +++++++ 5 files changed, 259 insertions(+) create mode 100644 plugins/inputs/wireless/README.md create mode 100644 plugins/inputs/wireless/wireless.go create mode 100644 plugins/inputs/wireless/wireless_linux.go create mode 100644 plugins/inputs/wireless/wireless_test.go diff --git a/plugins/inputs/all/all.go b/plugins/inputs/all/all.go index ad193224a..d45013bf3 100644 --- a/plugins/inputs/all/all.go +++ b/plugins/inputs/all/all.go @@ -135,6 +135,7 @@ import ( _ "github.com/influxdata/telegraf/plugins/inputs/webhooks" _ "github.com/influxdata/telegraf/plugins/inputs/win_perf_counters" _ "github.com/influxdata/telegraf/plugins/inputs/win_services" + _ "github.com/influxdata/telegraf/plugins/inputs/wireless" _ "github.com/influxdata/telegraf/plugins/inputs/x509_cert" _ "github.com/influxdata/telegraf/plugins/inputs/zfs" _ "github.com/influxdata/telegraf/plugins/inputs/zipkin" diff --git a/plugins/inputs/wireless/README.md b/plugins/inputs/wireless/README.md new file mode 100644 index 000000000..6be7bd383 --- /dev/null +++ b/plugins/inputs/wireless/README.md @@ -0,0 +1,38 @@ +# Wireless Input Plugin + +The wireless plugin gathers metrics about wireless link quality by reading the `/proc/net/wireless` file. This plugin currently supports linux only. + +### Configuration: + +```toml +# Monitor wifi signal strength and quality +[[inputs.wireless]] + ## Sets 'proc' directory path + ## If not specified, then default is /proc + # host_proc = "/proc" +``` + +### Metrics: + +- metric + - tags: + - interface (wireless interface) + - fields: + - status (int64, gauge) - Its current state. This is a device dependent information + - link (int64, percentage, gauge) - general quality of the reception + - level (int64, dBm, gauge) - signal strength at the receiver + - noise (int64, dBm, gauge) - silence level (no packet) at the receiver + - nwid (int64, packets, counter) - number of discarded packets due to invalid network id + - crypt (int64, packets, counter) - number of packet unable to decrypt + - frag (int64, packets, counter) - fragmented packets + - retry (int64, packets, counter) - cumulative retry counts + - misc (int64, packets, counter) - dropped for un-specified reason + - missed_beacon (int64, packets, counter) - missed beacon packets + +### Example Output: + +This section shows example output in Line Protocol format. + +``` +wireless,host=example.localdomain,interface=wlan0 misc=0i,frag=0i,link=60i,level=-50i,noise=-256i,nwid=0i,crypt=0i,retry=1525i,missed_beacon=0i,status=0i 1519843022000000000 +``` diff --git a/plugins/inputs/wireless/wireless.go b/plugins/inputs/wireless/wireless.go new file mode 100644 index 000000000..a992e2efe --- /dev/null +++ b/plugins/inputs/wireless/wireless.go @@ -0,0 +1,3 @@ +// +build !linux + +package wireless diff --git a/plugins/inputs/wireless/wireless_linux.go b/plugins/inputs/wireless/wireless_linux.go new file mode 100644 index 000000000..ed5dff27f --- /dev/null +++ b/plugins/inputs/wireless/wireless_linux.go @@ -0,0 +1,165 @@ +// +build linux + +package wireless + +import ( + "bytes" + "io/ioutil" + "log" + "os" + "path" + "strconv" + "strings" + + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/plugins/inputs" +) + +// default host proc path +const defaultHostProc = "/proc" + +// env host proc variable name +const envProc = "HOST_PROC" + +// length of wireless interface fields +const interfaceFieldLength = 10 + +var newLineByte = []byte("\n") + +type wirelessInterface struct { + Interface string + Status int64 + Link int64 + Level int64 + Noise int64 + Nwid int64 + Crypt int64 + Frag int64 + Retry int64 + Misc int64 + Beacon int64 +} + +// Wireless is used to store configuration values. +type Wireless struct { + HostProc string `toml:"host_proc"` +} + +var sampleConfig = ` + ## Sets 'proc' directory path + ## If not specified, then default is /proc + # host_proc = "/proc" +` + +// Description returns information about the plugin. +func (w *Wireless) Description() string { + return "Monitor wifi signal strength and quality" +} + +// SampleConfig displays configuration instructions. +func (w *Wireless) SampleConfig() string { + return sampleConfig +} + +// Gather collects the wireless information. +func (w *Wireless) Gather(acc telegraf.Accumulator) error { + // load proc path, get default value if config value and env variable are empty + w.loadPath() + + wirelessPath := path.Join(w.HostProc, "net", "wireless") + table, err := ioutil.ReadFile(wirelessPath) + if err != nil { + return err + } + + interfaces, err := loadWirelessTable(table) + if err != nil { + return err + } + for _, w := range interfaces { + tags := map[string]string{ + "interface": w.Interface, + } + fieldsG := map[string]interface{}{ + "status": w.Status, + "link": w.Link, + "level": w.Level, + "noise": w.Noise, + } + fieldsC := map[string]interface{}{ + "nwid": w.Nwid, + "crypt": w.Crypt, + "frag": w.Frag, + "retry": w.Retry, + "misc": w.Misc, + "beacon": w.Beacon, + } + acc.AddGauge("wireless", fieldsG, tags) + acc.AddCounter("wireless", fieldsC, tags) + } + + return nil +} + +func loadWirelessTable(table []byte) ([]*wirelessInterface, error) { + var w []*wirelessInterface + lines := bytes.Split(table, newLineByte) + + // iterate over interfaces + for i := 2; i < len(lines); i = i + 1 { + if len(lines[i]) == 0 { + continue + } + values := make([]int64, 0, interfaceFieldLength) + fields := strings.Fields(string(lines[i])) + for j := 1; j < len(fields); j = j + 1 { + v, err := strconv.ParseInt(strings.Trim(fields[j], "."), 10, 64) + if err != nil { + return nil, err + } + values = append(values, v) + } + if len(values) != interfaceFieldLength { + log.Printf("E! [input.wireless] invalid length of interface values") + continue + } + w = append(w, &wirelessInterface{ + Interface: strings.Trim(fields[0], ":"), + Status: values[0], + Link: values[1], + Level: values[2], + Noise: values[3], + Nwid: values[4], + Crypt: values[5], + Frag: values[6], + Retry: values[7], + Misc: values[8], + Beacon: values[9], + }) + } + return w, nil +} + +// loadPath can be used to read path firstly from config +// if it is empty then try read from env variable +func (w *Wireless) loadPath() { + if w.HostProc == "" { + w.HostProc = proc(envProc, defaultHostProc) + } +} + +// proc can be used to read file paths from env +func proc(env, path string) string { + // try to read full file path + if p := os.Getenv(env); p != "" { + return p + } + // return default path + return path +} + +func init() { + inputs.Add("wireless", func() telegraf.Input { + return &Wireless{} + }) +} diff --git a/plugins/inputs/wireless/wireless_test.go b/plugins/inputs/wireless/wireless_test.go new file mode 100644 index 000000000..f2ca1fc21 --- /dev/null +++ b/plugins/inputs/wireless/wireless_test.go @@ -0,0 +1,52 @@ +// +build linux + +package wireless + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +var testInput = []byte(`Inter-| sta-| Quality | Discarded packets | Missed | WE + face | tus | link level noise | nwid crypt frag retry misc | beacon | 22 + wlan0: 0000 60. -50. -256 0 0 0 1525 0 0 + wlan1: 0000 70. -39. -256 0 0 0 12096 191188 0`) + +func TestLoadWirelessTable(t *testing.T) { + expectedMetrics := []*wirelessInterface{ + &wirelessInterface{ + Interface: "wlan0", + Status: int64(0000), + Link: int64(60), + Level: int64(-50), + Noise: int64(-256), + Nwid: int64(0), + Crypt: int64(0), + Frag: int64(0), + Retry: int64(1525), + Misc: int64(0), + Beacon: int64(0), + }, + &wirelessInterface{ + Interface: "wlan1", + Status: int64(0000), + Link: int64(70), + Level: int64(-39), + Noise: int64(-256), + Nwid: int64(0), + Crypt: int64(0), + Frag: int64(0), + Retry: int64(12096), + Misc: int64(191188), + Beacon: int64(0), + }, + } + metrics, err := loadWirelessTable(testInput) + if err != nil { + t.Fatal(err) + } + + as := assert.New(t) + as.Equal(metrics, expectedMetrics) +}