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**
* 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
* [CHANGE]
* [CHANGE] Add `--collector.netdev.device-whitelist`. #1279
* [FEATURE]
* [ENHANCEMENT]
* [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"
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{}
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 {
if ifa.ifa_addr.sa_family == C.AF_LINK {
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)
continue
}

View file

@ -17,6 +17,7 @@
package collector
import (
"errors"
"fmt"
"regexp"
"strconv"
@ -26,12 +27,14 @@ import (
)
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 {
subsystem string
ignoredDevicesPattern *regexp.Regexp
acceptDevicesPattern *regexp.Regexp
metricDescs map[string]*prometheus.Desc
}
@ -41,16 +44,30 @@ func init() {
// NewNetDevCollector returns a new Collector exposing network device stats.
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{
subsystem: "network",
ignoredDevicesPattern: pattern,
ignoredDevicesPattern: ignorePattern,
acceptDevicesPattern: acceptPattern,
metricDescs: map[string]*prometheus.Desc{},
}, nil
}
func (c *netDevCollector) Update(ch chan<- prometheus.Metric) error {
netDev, err := getNetDevStats(c.ignoredDevicesPattern)
netDev, err := getNetDevStats(c.ignoredDevicesPattern, c.acceptDevicesPattern)
if err != nil {
return fmt.Errorf("couldn't get netstats: %s", err)
}

View file

@ -27,7 +27,7 @@ import (
"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{}
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)
continue
}
if accept != nil && !accept.MatchString(iface.Name) {
log.Debugf("Ignoring device: %s", iface.Name)
continue
}
devStats := map[string]string{}
devStats["receive_packets"] = strconv.FormatUint(ifaceData.Data.Ipackets, 10)

View file

@ -31,17 +31,17 @@ var (
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"))
if err != nil {
return nil, err
}
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.Scan() // skip first header
scanner.Scan()
@ -64,7 +64,11 @@ func parseNetDevStats(r io.Reader, ignore *regexp.Regexp) (map[string]map[string
}
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)
continue
}

View file

@ -19,14 +19,14 @@ import (
"testing"
)
func TestNetDevStats(t *testing.T) {
func TestNetDevStatsIgnore(t *testing.T) {
file, err := os.Open("fixtures/proc/net/dev")
if err != nil {
t.Fatal(err)
}
defer file.Close()
netStats, err := parseNetDevStats(file, regexp.MustCompile("^veth"))
netStats, err := parseNetDevStats(file, regexp.MustCompile("^veth"), nil)
if err != nil {
t.Fatal(err)
}
@ -59,3 +59,23 @@ func TestNetDevStats(t *testing.T) {
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"
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{}
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 {
if ifa.ifa_addr.sa_family == C.AF_LINK {
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)
continue
}