Add --collector.netdev.device-whitelist flag (#1279)

* Add --collector.netdev.device-whitelist flag

Sometimes it is desired to monitor only one netdev. The golang regexp
does not support a negated regex, so the ignored-devices flag is too
cumbersome for this task.
This change introduces a new flag: accept-devices, which is mutually
exclusive to ignored-devices. This flag allows specifying ONLY the
netdev you'd like.

Signed-off-by: Noam Meltzer <noam@cynerio.co>
This commit is contained in:
Noam Meltzer 2019-05-31 18:55:50 +03:00 committed by Ben Kochie
parent fc02b5dfbc
commit 501ccf9fb4
7 changed files with 72 additions and 16 deletions

View file

@ -2,9 +2,12 @@
### **Breaking changes** ### **Breaking changes**
* The netdev collector CLI argument `--collector.netdev.ignored-devices` was renamed to `--collector.netdev.device-blacklist` in order to conform with the systemd collector. #1279
### Changes ### Changes
* [CHANGE] * [CHANGE] Add `--collector.netdev.device-whitelist`. #1279
* [FEATURE] * [FEATURE]
* [ENHANCEMENT] * [ENHANCEMENT]
* [BUGFIX] Fix incorrect sysctl call in BSD meminfo collector, resulting in broken swap metrics on FreeBSD #1345 * [BUGFIX] Fix incorrect sysctl call in BSD meminfo collector, resulting in broken swap metrics on FreeBSD #1345

View file

@ -34,7 +34,7 @@ import (
*/ */
import "C" import "C"
func getNetDevStats(ignore *regexp.Regexp) (map[string]map[string]string, error) { func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp) (map[string]map[string]string, error) {
netDev := map[string]map[string]string{} netDev := map[string]map[string]string{}
var ifap, ifa *C.struct_ifaddrs var ifap, ifa *C.struct_ifaddrs
@ -46,7 +46,11 @@ func getNetDevStats(ignore *regexp.Regexp) (map[string]map[string]string, error)
for ifa = ifap; ifa != nil; ifa = ifa.ifa_next { for ifa = ifap; ifa != nil; ifa = ifa.ifa_next {
if ifa.ifa_addr.sa_family == C.AF_LINK { if ifa.ifa_addr.sa_family == C.AF_LINK {
dev := C.GoString(ifa.ifa_name) dev := C.GoString(ifa.ifa_name)
if ignore.MatchString(dev) { if ignore != nil && ignore.MatchString(dev) {
log.Debugf("Ignoring device: %s", dev)
continue
}
if accept != nil && !accept.MatchString(dev) {
log.Debugf("Ignoring device: %s", dev) log.Debugf("Ignoring device: %s", dev)
continue continue
} }

View file

@ -17,6 +17,7 @@
package collector package collector
import ( import (
"errors"
"fmt" "fmt"
"regexp" "regexp"
"strconv" "strconv"
@ -26,12 +27,14 @@ import (
) )
var ( var (
netdevIgnoredDevices = kingpin.Flag("collector.netdev.ignored-devices", "Regexp of net devices to ignore for netdev collector.").Default("^$").String() netdevIgnoredDevices = kingpin.Flag("collector.netdev.device-blacklist", "Regexp of net devices to blacklist (mutually exclusive to device-whitelist).").String()
netdevAcceptDevices = kingpin.Flag("collector.netdev.device-whitelist", "Regexp of net devices to whitelist (mutually exclusive to device-blacklist).").String()
) )
type netDevCollector struct { type netDevCollector struct {
subsystem string subsystem string
ignoredDevicesPattern *regexp.Regexp ignoredDevicesPattern *regexp.Regexp
acceptDevicesPattern *regexp.Regexp
metricDescs map[string]*prometheus.Desc metricDescs map[string]*prometheus.Desc
} }
@ -41,16 +44,30 @@ func init() {
// NewNetDevCollector returns a new Collector exposing network device stats. // NewNetDevCollector returns a new Collector exposing network device stats.
func NewNetDevCollector() (Collector, error) { func NewNetDevCollector() (Collector, error) {
pattern := regexp.MustCompile(*netdevIgnoredDevices) if *netdevIgnoredDevices != "" && *netdevAcceptDevices != "" {
return nil, errors.New("device-blacklist & accept-devices are mutually exclusive")
}
var ignorePattern *regexp.Regexp = nil
if *netdevIgnoredDevices != "" {
ignorePattern = regexp.MustCompile(*netdevIgnoredDevices)
}
var acceptPattern *regexp.Regexp = nil
if *netdevAcceptDevices != "" {
acceptPattern = regexp.MustCompile(*netdevAcceptDevices)
}
return &netDevCollector{ return &netDevCollector{
subsystem: "network", subsystem: "network",
ignoredDevicesPattern: pattern, ignoredDevicesPattern: ignorePattern,
acceptDevicesPattern: acceptPattern,
metricDescs: map[string]*prometheus.Desc{}, metricDescs: map[string]*prometheus.Desc{},
}, nil }, nil
} }
func (c *netDevCollector) Update(ch chan<- prometheus.Metric) error { func (c *netDevCollector) Update(ch chan<- prometheus.Metric) error {
netDev, err := getNetDevStats(c.ignoredDevicesPattern) netDev, err := getNetDevStats(c.ignoredDevicesPattern, c.acceptDevicesPattern)
if err != nil { if err != nil {
return fmt.Errorf("couldn't get netstats: %s", err) return fmt.Errorf("couldn't get netstats: %s", err)
} }

View file

@ -27,7 +27,7 @@ import (
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
func getNetDevStats(ignore *regexp.Regexp) (map[string]map[string]string, error) { func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp) (map[string]map[string]string, error) {
netDev := map[string]map[string]string{} netDev := map[string]map[string]string{}
ifs, err := net.Interfaces() ifs, err := net.Interfaces()
@ -46,6 +46,10 @@ func getNetDevStats(ignore *regexp.Regexp) (map[string]map[string]string, error)
log.Debugf("Ignoring device: %s", iface.Name) log.Debugf("Ignoring device: %s", iface.Name)
continue continue
} }
if accept != nil && !accept.MatchString(iface.Name) {
log.Debugf("Ignoring device: %s", iface.Name)
continue
}
devStats := map[string]string{} devStats := map[string]string{}
devStats["receive_packets"] = strconv.FormatUint(ifaceData.Data.Ipackets, 10) devStats["receive_packets"] = strconv.FormatUint(ifaceData.Data.Ipackets, 10)

View file

@ -31,17 +31,17 @@ var (
procNetDevFieldSep = regexp.MustCompile(` +`) procNetDevFieldSep = regexp.MustCompile(` +`)
) )
func getNetDevStats(ignore *regexp.Regexp) (map[string]map[string]string, error) { func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp) (map[string]map[string]string, error) {
file, err := os.Open(procFilePath("net/dev")) file, err := os.Open(procFilePath("net/dev"))
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer file.Close() defer file.Close()
return parseNetDevStats(file, ignore) return parseNetDevStats(file, ignore, accept)
} }
func parseNetDevStats(r io.Reader, ignore *regexp.Regexp) (map[string]map[string]string, error) { func parseNetDevStats(r io.Reader, ignore *regexp.Regexp, accept *regexp.Regexp) (map[string]map[string]string, error) {
scanner := bufio.NewScanner(r) scanner := bufio.NewScanner(r)
scanner.Scan() // skip first header scanner.Scan() // skip first header
scanner.Scan() scanner.Scan()
@ -64,7 +64,11 @@ func parseNetDevStats(r io.Reader, ignore *regexp.Regexp) (map[string]map[string
} }
dev := parts[1] dev := parts[1]
if ignore.MatchString(dev) { if ignore != nil && ignore.MatchString(dev) {
log.Debugf("Ignoring device: %s", dev)
continue
}
if accept != nil && !accept.MatchString(dev) {
log.Debugf("Ignoring device: %s", dev) log.Debugf("Ignoring device: %s", dev)
continue continue
} }

View file

@ -19,14 +19,14 @@ import (
"testing" "testing"
) )
func TestNetDevStats(t *testing.T) { func TestNetDevStatsIgnore(t *testing.T) {
file, err := os.Open("fixtures/proc/net/dev") file, err := os.Open("fixtures/proc/net/dev")
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer file.Close() defer file.Close()
netStats, err := parseNetDevStats(file, regexp.MustCompile("^veth")) netStats, err := parseNetDevStats(file, regexp.MustCompile("^veth"), nil)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -59,3 +59,23 @@ func TestNetDevStats(t *testing.T) {
t.Error("want fixture interface 💩0 to exist, but it does not") t.Error("want fixture interface 💩0 to exist, but it does not")
} }
} }
func TestNetDevStatsAccept(t *testing.T) {
file, err := os.Open("fixtures/proc/net/dev")
if err != nil {
t.Fatal(err)
}
defer file.Close()
netStats, err := parseNetDevStats(file, nil, regexp.MustCompile("^💩0$"))
if err != nil {
t.Fatal(err)
}
if want, got := 1, len(netStats); want != got {
t.Errorf("want count of devices to be %d, got %d", want, got)
}
if want, got := "72", netStats["💩0"]["receive_multicast"]; want != got {
t.Error("want fixture interface 💩0 to exist, but it does not")
}
}

View file

@ -31,7 +31,7 @@ import (
*/ */
import "C" import "C"
func getNetDevStats(ignore *regexp.Regexp) (map[string]map[string]string, error) { func getNetDevStats(ignore *regexp.Regexp, accept *regexp.Regexp) (map[string]map[string]string, error) {
netDev := map[string]map[string]string{} netDev := map[string]map[string]string{}
var ifap, ifa *C.struct_ifaddrs var ifap, ifa *C.struct_ifaddrs
@ -43,7 +43,11 @@ func getNetDevStats(ignore *regexp.Regexp) (map[string]map[string]string, error)
for ifa = ifap; ifa != nil; ifa = ifa.ifa_next { for ifa = ifap; ifa != nil; ifa = ifa.ifa_next {
if ifa.ifa_addr.sa_family == C.AF_LINK { if ifa.ifa_addr.sa_family == C.AF_LINK {
dev := C.GoString(ifa.ifa_name) dev := C.GoString(ifa.ifa_name)
if ignore.MatchString(dev) { if ignore != nil && ignore.MatchString(dev) {
log.Debugf("Ignoring device: %s", dev)
continue
}
if accept != nil && !accept.MatchString(dev) {
log.Debugf("Ignoring device: %s", dev) log.Debugf("Ignoring device: %s", dev)
continue continue
} }