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 @@ -132,6 +132,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"
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]]
## 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
```
3 changes: 3 additions & 0 deletions plugins/inputs/wireless/wireless.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// +build !linux

package wireless
165 changes: 165 additions & 0 deletions plugins/inputs/wireless/wireless_linux.go
Original file line number Diff line number Diff line change
@@ -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{
jamesmaidment marked this conversation as resolved.
Show resolved Hide resolved
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{}
})
}
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)
}