Skip to content

Commit

Permalink
Ethtool Input Plugin (Issue #5864)
Browse files Browse the repository at this point in the history
Added new input plugin that will support pulling of data from
ethernet devices via ethtool
  • Loading branch information
philippreston committed May 16, 2019
1 parent 43c6d13 commit 5cb7ac9
Show file tree
Hide file tree
Showing 8 changed files with 722 additions and 0 deletions.
8 changes: 8 additions & 0 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -288,3 +288,7 @@
[[constraint]]
name = "github.com/google/go-github"
version = "24.0.1"

[[constraint]]
name = "github.com/safchain/ethtool"
revision = "42ed695e3de80b9d695f280295fd7994639f209d"
1 change: 1 addition & 0 deletions plugins/inputs/all/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import (
_ "github.com/influxdata/telegraf/plugins/inputs/docker"
_ "github.com/influxdata/telegraf/plugins/inputs/dovecot"
_ "github.com/influxdata/telegraf/plugins/inputs/elasticsearch"
_ "github.com/influxdata/telegraf/plugins/inputs/ethtool"
_ "github.com/influxdata/telegraf/plugins/inputs/exec"
_ "github.com/influxdata/telegraf/plugins/inputs/fail2ban"
_ "github.com/influxdata/telegraf/plugins/inputs/fibaro"
Expand Down
43 changes: 43 additions & 0 deletions plugins/inputs/ethtool/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Ethtool Input Plugin

The ethtool input plugin pulls ethernet device stats. Fields pulled will depend on the network device and driver



### Configuration:

```toml
# Returns ethtool statistics for given interfaces
[[inputs.ethtool]]
## List of interfaces to pull metrics for
# include_interfaces = ["eth0"]

## List of interfaces to ignore when pulling metrics.
# ignore_interfaces = ["lo0"]

## List of stat fields to include - default is all
# include_fields = [""]

## Add driver information as tag
driver_name_tag = true
```

Interfaces can be included or ignored using

- `include_interfaces`
- `ignore_interfaces`

Fields can be filtered using the `include_fields` array

The driver name can be dropped by setting `driver_name_tag` to false

### Metrics:

Metrics are dependant on the network device and drive

### Example Output:

```
1558004820000000000 sfc host1 sfc0 34503285 17460420127 2110321 2864308103 575875087 461432982 1936943999 1331 330765 0 83297760 5031166659864 0 19816863653 1916 5423 1 0 23335592573 5031166329099 0 0 21155071896 467 0 23335593904 0 0 0 0 118510 0 0 0 2097222917 5935186 37589985 19562047 1970004687 65731417 1337584 11020953 15 769847731426 0 0 104088 2111181859 4 2111077752 33185072 386225 367805 364416 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 95859 82516 61007 82432 1 1982 241268 321814 0 0 0 0 server
1558004820000000000 sfc host1 sfc1 17343701 8908686711 632191 1517683738 555287868 235073828 1198382648 4728 1157017 0 40065908 2827782326577 0 10760403171 0 1 0 0 12433085957 2827781169560 0 0 11465379144 3180 0 12433090685 0 0 0 0 0 0 0 0 927640905 5061453 23841543 16791171 1233044960 69992129 946623 7294533 3 514931291258 0 0 67467 1356972412 4 1356904938 31047711 367053 371436 389928 62 0 0 0 0 0 0 0 0 0 0 0 0 0 0 45662 75807 59790 61527 0 241 182379 240811 300 0 0 1975 server
```
61 changes: 61 additions & 0 deletions plugins/inputs/ethtool/ethtool.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package ethtool

import (
"sync"
)

type Command interface {
DriverName(intf string) (string, error)
InterfaceNames() ([]string, error)
Stats(intf string) (map[string]uint64, error)
}

type Ethtool struct {
// This is the list of interface names to include
IncludeInterfaces []string `toml:"include_interfaces"`

// This is the list of interface names to ignore
IgnoreInterfaces []string `toml:"ignore_interfaces"`

// Whether to include the driver name in the tag
DriverName bool `toml:"driver_name_tag"`

// Fields to include - all if empty
IncludeFields []string `toml:"include_fields"`

// the ethtool command
command Command

// Will parallelize the ethtool call in event of many interfaces
// so using this to sync
wg sync.WaitGroup
}

const (
pluginName = "ethtool"
tagInterface = "interface"
tagDriverName = "driver"

sampleConfig = `
## List of interfaces to pull metrics for
# include_interfaces = ["eth0"]
## List of interfaces to ignore when pulling metrics.
# ignore_interfaces = ["lo0"]
## List of stat fields to include - default is all
# include_fields = [""]
## Add driver information as tag
driver_name_tag = true
`
)

func (e *Ethtool) SampleConfig() string {
return sampleConfig
}

// Description returns a one-sentence description on the Input
func (e *Ethtool) Description() string {
return "Returns ethtool statistics for given interfaces"
}
168 changes: 168 additions & 0 deletions plugins/inputs/ethtool/ethtool_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
// +build linux

package ethtool

import (
"net"

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

type CommandEthtool struct {
ethtool *ethtool.Ethtool
}

func (e *Ethtool) Gather(acc telegraf.Accumulator) error {

// Get the list of interfaces
interfaces, err := e.command.InterfaceNames()
if err != nil {
acc.AddError(err)
return nil
}

for _, iface := range interfaces {

// Check if this is something we are configured to process
if e.shouldProcessInterface(iface) {
e.wg.Add(1)
go e.gatherEthtoolStats(iface, acc)
}
}

// Waiting for all the interfaces
e.wg.Wait()
return nil
}

// Decide whether or not to process the interface. If the interface list is empty or the interface name
// in the list - pass to the deny processing
func (e *Ethtool) shouldProcessInterface(iface string) bool {

// If there is an include filter - allow if in this list
if len(e.IncludeInterfaces) > 0 {
if contains(e.IncludeInterfaces, iface) {
return true
} else {
return false
}
}

// If there is an exclude filter - deny if in this list
if len(e.IgnoreInterfaces) > 0 {
if contains(e.IgnoreInterfaces, iface) {
return false
}
}

// By default include all interfaces
return true
}

// Decide whether a field should be included
func (e *Ethtool) shouldIncludeField(field string) bool {

if len(e.IncludeFields) > 0 {
if contains(e.IncludeFields, field) {
return true
} else {
return false
}

}

// By default include all fields
return true
}

func contains(s []string, value string) bool {
for _, item := range s {
if item == value {
return true
}
}
return false
}

// Gather the stats for the interface. Passing by value here as running in goroutine
func (e *Ethtool) gatherEthtoolStats(iface string, acc telegraf.Accumulator) {
defer e.wg.Done()

tags := make(map[string]string)
tags[tagInterface] = iface

// Optionally add driver name as a tag
if e.DriverName {

driverName, err := e.command.DriverName(iface)
if err != nil {
acc.AddError(err)
return
}

tags[tagDriverName] = driverName
}

fields := make(map[string]interface{})
stats, err := e.command.Stats(iface)
if err != nil {
acc.AddError(err)
return
}

// Apply the field filter
for k, v := range stats {
if e.shouldIncludeField(k) {
fields[k] = v
}
}

acc.AddFields(pluginName, fields, tags)
}

func NewCommandEthtool() *CommandEthtool {
e, _ := ethtool.NewEthtool()
return &CommandEthtool{e}
}

func (c *CommandEthtool) DriverName(intf string) (string, error) {
return c.ethtool.DriverName(intf)
}

func (c *CommandEthtool) Stats(intf string) (map[string]uint64, error) {
return c.ethtool.Stats(intf)
}

func (c *CommandEthtool) InterfaceNames() ([]string, error) {
// Get the list of interfaces
interfaces, err := net.Interfaces()
if err != nil {
return nil, err
}

interfaceNames := make([]string, len(interfaces))
for _, iface := range interfaces {
interfaceNames = append(interfaceNames, iface.Name)
}

return interfaceNames, nil
}

func init() {

// Create command handler
e := NewCommandEthtool()
s := Ethtool{
IncludeInterfaces: []string{},
IgnoreInterfaces: []string{},
IncludeFields: []string{},
DriverName: true,
command: e,
}

inputs.Add(pluginName, func() telegraf.Input {
return &s
})
}
21 changes: 21 additions & 0 deletions plugins/inputs/ethtool/ethtool_nonlinux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// +build !linux

package ethtool

import (
"log"

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

func (e *Ethtool) Gather(acc telegraf.Accumulator) error {
return nil
}

func init() {
inputs.Add(pluginName, func() telegraf.Input {
log.Print("W! [inputs.ethtool] Current platform is not supported")
return &Ethtool{}
})
}
Loading

0 comments on commit 5cb7ac9

Please sign in to comment.