Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Wireless Input plugin #3847

Merged
merged 13 commits into from
Oct 29, 2018
1 change: 1 addition & 0 deletions plugins/inputs/all/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,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/zfs"
_ "github.com/influxdata/telegraf/plugins/inputs/zipkin"
_ "github.com/influxdata/telegraf/plugins/inputs/zookeeper"
Expand Down
38 changes: 38 additions & 0 deletions plugins/inputs/wireless/README.md
Original file line number Diff line number Diff line change
@@ -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]]
## file paths for proc files. If empty default paths will be used:
## /proc/net/wireless
proc_net_wireless = "/proc/net/wireless"
```

### 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
```
168 changes: 168 additions & 0 deletions plugins/inputs/wireless/wireless.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
// +build linux
jamesmaidment marked this conversation as resolved.
Show resolved Hide resolved

package wireless

import (
"bytes"
"io/ioutil"
"os"
"strconv"
"strings"

"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/plugins/inputs"
)

// default file paths
const (
NET_WIRELESS = "/net/wireless"
NET_PROC = "/proc"
)

// env variable names
const (
ENV_WIRELESS = "PROC_NET_WIRELESS"
ENV_ROOT = "PROC_ROOT"
)

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 {
ProcNetWireless string `toml:"proc_net_wireless"`
}

var sampleConfig = `
## file paths for proc files. If empty default paths will be used:
## /proc/net/wireless
proc_net_wireless = "/proc/net/wireless"
jamesmaidment marked this conversation as resolved.
Show resolved Hide resolved
`

// 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 paths, get from env if config values are empty
w.loadPaths()

table, err := ioutil.ReadFile(w.ProcNetWireless)
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

// split the lines by newline
lines := bytes.Split(table, newLineByte)
// iterate over intefaces
for i := 2; i < len(lines); i = i + 1 {
if len(lines[i]) == 0 {
continue
}
fields := strings.Fields(string(lines[i]))
var values []int64
for i := 1; i < len(fields); i = i + 1 {
jamesmaidment marked this conversation as resolved.
Show resolved Hide resolved
v, err := strconv.ParseInt(strings.Trim(fields[i], "."), 10, 64)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to strings.Trim because strings.Fields handles this.

if err != nil {
return nil, err
}
values = append(values, v)
}
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
}

// loadPaths can be used to read paths firstly from config
// if it is empty then try read from env variables
func (w *Wireless) loadPaths() {
if w.ProcNetWireless == "" {
w.ProcNetWireless = proc(ENV_WIRELESS, NET_WIRELESS)
}
}

// 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
}
// try to read root path, or use default root path
root := os.Getenv(ENV_ROOT)
if root == "" {
root = NET_PROC
}
return root + path
}

func init() {
inputs.Add("wireless", func() telegraf.Input {
return &Wireless{}
})
}
3 changes: 3 additions & 0 deletions plugins/inputs/wireless/wireless_notlinux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// +build !linux

package wireless
52 changes: 52 additions & 0 deletions plugins/inputs/wireless/wireless_test.go
Original file line number Diff line number Diff line change
@@ -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)
}