feat(ethtool): Possibility to skip gathering metrics for downed interfaces (#12087)
This commit is contained in:
parent
2ed413bdb0
commit
284edccf92
|
|
@ -14,6 +14,12 @@ on the network device and driver.
|
||||||
## List of interfaces to ignore when pulling metrics.
|
## List of interfaces to ignore when pulling metrics.
|
||||||
# interface_exclude = ["eth1"]
|
# interface_exclude = ["eth1"]
|
||||||
|
|
||||||
|
## Plugin behavior for downed interfaces
|
||||||
|
## Available choices:
|
||||||
|
## - expose: collect & report metrics for down interfaces
|
||||||
|
## - skip: ignore interfaces that are marked down
|
||||||
|
# down_interfaces = "expose"
|
||||||
|
|
||||||
## Some drivers declare statistics with extra whitespace, different spacing,
|
## Some drivers declare statistics with extra whitespace, different spacing,
|
||||||
## and mix cases. This list, when enabled, can be used to clean the keys.
|
## and mix cases. This list, when enabled, can be used to clean the keys.
|
||||||
## Here are the current possible normalizations:
|
## Here are the current possible normalizations:
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,14 @@ import (
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/filter"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed sample.conf
|
//go:embed sample.conf
|
||||||
var sampleConfig string
|
var sampleConfig string
|
||||||
|
|
||||||
|
var downInterfacesBehaviors = []string{"expose", "skip"}
|
||||||
|
|
||||||
type Command interface {
|
type Command interface {
|
||||||
Init() error
|
Init() error
|
||||||
DriverName(intf string) (string, error)
|
DriverName(intf string) (string, error)
|
||||||
|
|
@ -24,11 +27,16 @@ type Ethtool struct {
|
||||||
// This is the list of interface names to ignore
|
// This is the list of interface names to ignore
|
||||||
InterfaceExclude []string `toml:"interface_exclude"`
|
InterfaceExclude []string `toml:"interface_exclude"`
|
||||||
|
|
||||||
|
// Behavior regarding metrics for downed interfaces
|
||||||
|
DownInterfaces string `toml:" down_interfaces"`
|
||||||
|
|
||||||
// Normalization on the key names
|
// Normalization on the key names
|
||||||
NormalizeKeys []string `toml:"normalize_keys"`
|
NormalizeKeys []string `toml:"normalize_keys"`
|
||||||
|
|
||||||
Log telegraf.Logger `toml:"-"`
|
Log telegraf.Logger `toml:"-"`
|
||||||
|
|
||||||
|
interfaceFilter filter.Filter
|
||||||
|
|
||||||
// the ethtool command
|
// the ethtool command
|
||||||
command Command
|
command Command
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
package ethtool
|
package ethtool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -14,6 +15,7 @@ import (
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
"github.com/influxdata/telegraf/filter"
|
"github.com/influxdata/telegraf/filter"
|
||||||
|
"github.com/influxdata/telegraf/internal/choice"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -21,6 +23,24 @@ type CommandEthtool struct {
|
||||||
ethtool *ethtoolLib.Ethtool
|
ethtool *ethtoolLib.Ethtool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Ethtool) Init() error {
|
||||||
|
var err error
|
||||||
|
e.interfaceFilter, err = filter.NewIncludeExcludeFilter(e.InterfaceInclude, e.InterfaceExclude)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if e.DownInterfaces == "" {
|
||||||
|
e.DownInterfaces = "expose"
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = choice.Check(e.DownInterfaces, downInterfacesBehaviors); err != nil {
|
||||||
|
return fmt.Errorf("down_interfaces: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.command.Init()
|
||||||
|
}
|
||||||
|
|
||||||
func (e *Ethtool) Gather(acc telegraf.Accumulator) error {
|
func (e *Ethtool) Gather(acc telegraf.Accumulator) error {
|
||||||
// Get the list of interfaces
|
// Get the list of interfaces
|
||||||
interfaces, err := e.command.Interfaces()
|
interfaces, err := e.command.Interfaces()
|
||||||
|
|
@ -29,17 +49,11 @@ func (e *Ethtool) Gather(acc telegraf.Accumulator) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
interfaceFilter, err := filter.NewIncludeExcludeFilter(e.InterfaceInclude, e.InterfaceExclude)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// parallelize the ethtool call in event of many interfaces
|
// parallelize the ethtool call in event of many interfaces
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
for _, iface := range interfaces {
|
for _, iface := range interfaces {
|
||||||
// Check this isn't a loop back and that its matched by the filter
|
if e.interfaceEligibleForGather(iface) {
|
||||||
if (iface.Flags&net.FlagLoopback == 0) && interfaceFilter.Match(iface.Name) {
|
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|
||||||
go func(i net.Interface) {
|
go func(i net.Interface) {
|
||||||
|
|
@ -54,9 +68,18 @@ func (e *Ethtool) Gather(acc telegraf.Accumulator) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialise the Command Tool
|
func (e *Ethtool) interfaceEligibleForGather(iface net.Interface) bool {
|
||||||
func (e *Ethtool) Init() error {
|
// Don't gather if it is a loop back, or it isn't matched by the filter
|
||||||
return e.command.Init()
|
if isLoopback(iface) || !e.interfaceFilter.Match(iface.Name) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// For downed interfaces, gather only for "expose"
|
||||||
|
if !interfaceUp(iface) {
|
||||||
|
return e.DownInterfaces == "expose"
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gather the stats for the interface.
|
// Gather the stats for the interface.
|
||||||
|
|
@ -81,7 +104,7 @@ func (e *Ethtool) gatherEthtoolStats(iface net.Interface, acc telegraf.Accumulat
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fields[fieldInterfaceUp] = e.interfaceUp(iface)
|
fields[fieldInterfaceUp] = interfaceUp(iface)
|
||||||
for k, v := range stats {
|
for k, v := range stats {
|
||||||
fields[e.normalizeKey(k)] = v
|
fields[e.normalizeKey(k)] = v
|
||||||
}
|
}
|
||||||
|
|
@ -134,7 +157,11 @@ func inStringSlice(slice []string, value string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Ethtool) interfaceUp(iface net.Interface) bool {
|
func isLoopback(iface net.Interface) bool {
|
||||||
|
return (iface.Flags & net.FlagLoopback) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func interfaceUp(iface net.Interface) bool {
|
||||||
return (iface.Flags & net.FlagUp) != 0
|
return (iface.Flags & net.FlagUp) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -292,6 +292,7 @@ func setup() {
|
||||||
command = &Ethtool{
|
command = &Ethtool{
|
||||||
InterfaceInclude: []string{},
|
InterfaceInclude: []string{},
|
||||||
InterfaceExclude: []string{},
|
InterfaceExclude: []string{},
|
||||||
|
DownInterfaces: "expose",
|
||||||
command: c,
|
command: c,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -315,9 +316,12 @@ func toStringMapUint(in map[string]interface{}) map[string]uint64 {
|
||||||
|
|
||||||
func TestGather(t *testing.T) {
|
func TestGather(t *testing.T) {
|
||||||
setup()
|
setup()
|
||||||
var acc testutil.Accumulator
|
|
||||||
|
|
||||||
err := command.Gather(&acc)
|
err := command.Init()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
err = command.Gather(&acc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, acc.Metrics, 2)
|
require.Len(t, acc.Metrics, 2)
|
||||||
|
|
||||||
|
|
@ -342,11 +346,14 @@ func TestGather(t *testing.T) {
|
||||||
|
|
||||||
func TestGatherIncludeInterfaces(t *testing.T) {
|
func TestGatherIncludeInterfaces(t *testing.T) {
|
||||||
setup()
|
setup()
|
||||||
var acc testutil.Accumulator
|
|
||||||
|
|
||||||
command.InterfaceInclude = append(command.InterfaceInclude, "eth1")
|
command.InterfaceInclude = append(command.InterfaceInclude, "eth1")
|
||||||
|
|
||||||
err := command.Gather(&acc)
|
err := command.Init()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
err = command.Gather(&acc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, acc.Metrics, 1)
|
require.Len(t, acc.Metrics, 1)
|
||||||
|
|
||||||
|
|
@ -373,11 +380,14 @@ func TestGatherIncludeInterfaces(t *testing.T) {
|
||||||
|
|
||||||
func TestGatherIgnoreInterfaces(t *testing.T) {
|
func TestGatherIgnoreInterfaces(t *testing.T) {
|
||||||
setup()
|
setup()
|
||||||
var acc testutil.Accumulator
|
|
||||||
|
|
||||||
command.InterfaceExclude = append(command.InterfaceExclude, "eth1")
|
command.InterfaceExclude = append(command.InterfaceExclude, "eth1")
|
||||||
|
|
||||||
err := command.Gather(&acc)
|
err := command.Init()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
err = command.Gather(&acc)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, acc.Metrics, 1)
|
require.Len(t, acc.Metrics, 1)
|
||||||
|
|
||||||
|
|
@ -402,6 +412,30 @@ func TestGatherIgnoreInterfaces(t *testing.T) {
|
||||||
acc.AssertContainsTaggedFields(t, pluginName, expectedFieldsEth2, expectedTagsEth2)
|
acc.AssertContainsTaggedFields(t, pluginName, expectedFieldsEth2, expectedTagsEth2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSkipMetricsForInterfaceDown(t *testing.T) {
|
||||||
|
setup()
|
||||||
|
|
||||||
|
command.DownInterfaces = "skip"
|
||||||
|
|
||||||
|
err := command.Init()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
err = command.Gather(&acc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, acc.Metrics, 1)
|
||||||
|
|
||||||
|
expectedFieldsEth1 := toStringMapInterface(interfaceMap["eth1"].Stat)
|
||||||
|
expectedFieldsEth1["interface_up_counter"] = expectedFieldsEth1["interface_up"]
|
||||||
|
expectedFieldsEth1["interface_up"] = true
|
||||||
|
|
||||||
|
expectedTagsEth1 := map[string]string{
|
||||||
|
"interface": "eth1",
|
||||||
|
"driver": "driver1",
|
||||||
|
}
|
||||||
|
acc.AssertContainsTaggedFields(t, pluginName, expectedFieldsEth1, expectedTagsEth1)
|
||||||
|
}
|
||||||
|
|
||||||
type TestCase struct {
|
type TestCase struct {
|
||||||
normalization []string
|
normalization []string
|
||||||
stats map[string]interface{}
|
stats map[string]interface{}
|
||||||
|
|
@ -525,8 +559,11 @@ func TestNormalizedKeys(t *testing.T) {
|
||||||
command: cmd,
|
command: cmd,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err := command.Init()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
var acc testutil.Accumulator
|
var acc testutil.Accumulator
|
||||||
err := command.Gather(&acc)
|
err = command.Gather(&acc)
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Len(t, acc.Metrics, 1)
|
require.Len(t, acc.Metrics, 1)
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,12 @@
|
||||||
## List of interfaces to ignore when pulling metrics.
|
## List of interfaces to ignore when pulling metrics.
|
||||||
# interface_exclude = ["eth1"]
|
# interface_exclude = ["eth1"]
|
||||||
|
|
||||||
|
## Plugin behavior for downed interfaces
|
||||||
|
## Available choices:
|
||||||
|
## - expose: collect & report metrics for down interfaces
|
||||||
|
## - skip: ignore interfaces that are marked down
|
||||||
|
# down_interfaces = "expose"
|
||||||
|
|
||||||
## Some drivers declare statistics with extra whitespace, different spacing,
|
## Some drivers declare statistics with extra whitespace, different spacing,
|
||||||
## and mix cases. This list, when enabled, can be used to clean the keys.
|
## and mix cases. This list, when enabled, can be used to clean the keys.
|
||||||
## Here are the current possible normalizations:
|
## Here are the current possible normalizations:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue