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
Prev Previous commit
Next Next commit
Support gauge & counters
  • Loading branch information
jamesmaidment committed Apr 17, 2018
commit f6f4301973afcbd4fd8f27e736954b2ed95ddd9c
23 changes: 10 additions & 13 deletions plugins/inputs/wireless/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ The wireless plugin gathers metrics about wireless link quality by reading the `
## file paths for proc files. If empty default paths will be used:
## /proc/net/wireless
proc_net_wireless = "/proc/net/wireless"

## dump metrics with 0 values too
dump_zeros = false
```

### Metrics:
Expand All @@ -21,16 +18,16 @@ The wireless plugin gathers metrics about wireless link quality by reading the `
- tags:
- interface (wireless interface)
- fields:
- status (int64) - Its current state. This is a device dependent information
- link (int64, percentage) - general quality of the reception
- level (int64, dBm) - signal strength at the receiver
- noise (int64, dBm) - silence level (no packet) at the receiver
- nwid (int64, packets) - number of discarded packets due to invalid network id
- crypt (int64, packets) - number of packet unable to decrypt
- frag (int64, packets) - fragmented packets
- retry (int64, packets) - cumulative retry counts
- misc (int64, packets) - dropped for un-specified reason
- missed_beacon (int64, packets) - missed beacon packets
- 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:

Expand Down
133 changes: 71 additions & 62 deletions plugins/inputs/wireless/wireless.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,6 @@ import (
"github.com/influxdata/telegraf/plugins/inputs"
)

var (
newLineByte = []byte("\n")
wirelessHeaders = map[int]string{
0: "interface",
1: "status",
2: "link",
3: "level",
4: "noise",
5: "nwid",
6: "crypt",
7: "frag",
8: "retry",
9: "misc",
10: "missed_beacon",
}
)

// default file paths
const (
NET_WIRELESS = "/net/wireless"
Expand All @@ -42,22 +25,36 @@ const (
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"`
DumpZeros bool `toml:"dump_zeros"`
}

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

## dump metrics with 0 values too
dump_zeros = false
`

// Desciption returns information about the plugin.
// Description returns information about the plugin.
func (w *Wireless) Description() string {
return "Monitor wifi signal strength and quality"
}
Expand All @@ -72,70 +69,82 @@ func (w *Wireless) Gather(acc telegraf.Accumulator) error {
// load paths, get from env if config values are empty
w.loadPaths()

wireless, err := ioutil.ReadFile(w.ProcNetWireless)
table, err := ioutil.ReadFile(w.ProcNetWireless)
if err != nil {
return err
}

// collect wireless data
err = w.gatherWireless(wireless, acc)
interfaces, err := loadWirelessTable(table)
if err != nil {
return err
}

return nil
}

func (w *Wireless) gatherWireless(data []byte, acc telegraf.Accumulator) error {
metrics, tags, err := loadWirelessTable(data, w.DumpZeros)
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)
}
acc.AddFields("wireless", metrics, tags)

return 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)
}
}
func loadWirelessTable(table []byte) ([]*wirelessInterface, error) {
var w []*wirelessInterface

func loadWirelessTable(table []byte, dumpZeros bool) (map[string]interface{}, map[string]string, error) {
entries := map[string]interface{}{}
tags := map[string]string{}
// split the lines by newline
lines := bytes.Split(table, newLineByte)
var value int64
var err error
// iterate over intefaces
for i := 2; i < len(lines); i = i + 1 {
if len(lines[i]) == 0 {
continue
}
fields := strings.Fields(string(lines[i]))
for j := 0; j < len(fields); j = j + 1 {
// parse interface
if j == 0 {
tags[wirelessHeaders[j]] = strings.Trim(fields[j], ":")
continue
}
// parse value
value, err = strconv.ParseInt(strings.Trim(fields[j], "."), 10, 64)
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 {
continue
return nil, err
}
// value is zero
if value == 0 && dumpZeros {
continue
}
// the value is not zero, so parse it
entries[wirelessHeaders[j]] = value
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)
}
return entries, tags, nil
}

// proc can be used to read file paths from env
Expand Down
54 changes: 31 additions & 23 deletions plugins/inputs/wireless/wireless_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,35 +10,43 @@ import (

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`)
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 := map[string]interface{}{
"status": int64(0),
"link": int64(60),
"level": int64(-50),
"noise": int64(-256),
"nwid": int64(0),
"crypt": int64(0),
"frag": int64(0),
"retry": int64(1525),
"misc": int64(0),
"missed_beacon": int64(0),
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),
},
}
expectedTags := map[string]string{
"interface": "wlan0",
}

metrics, tags, err := loadWirelessTable(testInput, false)
metrics, err := loadWirelessTable(testInput)
if err != nil {
t.Fatal(err)
}

as := assert.New(t)
for k := range metrics {
as.Equal(metrics[k], expectedMetrics[k])
}
for k := range tags {
as.Equal(tags[k], expectedTags[k])
}
as.Equal(metrics, expectedMetrics)
}