Use include/exclude flags for ethtool filtering (#2165)

Use the same flag pattern as netdev to make filtering methods the same.
* Move SanitizeMetricName to helper.go

Signed-off-by: Ben Kochie <superq@gmail.com>
pull/2139/head^2
Ben Kochie 2021-10-11 15:12:25 +02:00 committed by GitHub
parent 68a6c78c0d
commit f61be48d94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 55 additions and 53 deletions

View File

@ -39,11 +39,11 @@ import (
) )
var ( var (
ethtoolIgnoredDevices = kingpin.Flag("collector.ethtool.ignored-devices", "Regexp of net devices to ignore for ethtool collector.").Default("^$").String() ethtoolDeviceInclude = kingpin.Flag("collector.ethtool.device-include", "Regexp of ethtool devices to include (mutually exclusive to device-exclude).").String()
ethtoolDeviceExclude = kingpin.Flag("collector.ethtool.device-exclude", "Regexp of ethtool devices to exclude (mutually exclusive to device-include).").String()
ethtoolIncludedMetrics = kingpin.Flag("collector.ethtool.metrics-include", "Regexp of ethtool stats to include.").Default(".*").String() ethtoolIncludedMetrics = kingpin.Flag("collector.ethtool.metrics-include", "Regexp of ethtool stats to include.").Default(".*").String()
metricNameRegex = regexp.MustCompile(`_*[^0-9A-Za-z_]+_*`) ethtoolReceivedRegex = regexp.MustCompile(`(^|_)rx(_|$)`)
receivedRegex = regexp.MustCompile(`(^|_)rx(_|$)`) ethtoolTransmitRegex = regexp.MustCompile(`(^|_)tx(_|$)`)
transmittedRegex = regexp.MustCompile(`(^|_)tx(_|$)`)
) )
type Ethtool interface { type Ethtool interface {
@ -64,13 +64,13 @@ func (e *ethtoolLibrary) Stats(intf string) (map[string]uint64, error) {
} }
type ethtoolCollector struct { type ethtoolCollector struct {
fs sysfs.FS fs sysfs.FS
entries map[string]*prometheus.Desc entries map[string]*prometheus.Desc
ethtool Ethtool ethtool Ethtool
ignoredDevicesPattern *regexp.Regexp deviceFilter netDevFilter
infoDesc *prometheus.Desc infoDesc *prometheus.Desc
metricsPattern *regexp.Regexp metricsPattern *regexp.Regexp
logger log.Logger logger log.Logger
} }
// makeEthtoolCollector is the internal constructor for EthtoolCollector. // makeEthtoolCollector is the internal constructor for EthtoolCollector.
@ -89,11 +89,11 @@ func makeEthtoolCollector(logger log.Logger) (*ethtoolCollector, error) {
// Pre-populate some common ethtool metrics. // Pre-populate some common ethtool metrics.
return &ethtoolCollector{ return &ethtoolCollector{
fs: fs, fs: fs,
ethtool: &ethtoolLibrary{e}, ethtool: &ethtoolLibrary{e},
ignoredDevicesPattern: regexp.MustCompile(*ethtoolIgnoredDevices), deviceFilter: newNetDevFilter(*ethtoolDeviceExclude, *ethtoolDeviceInclude),
metricsPattern: regexp.MustCompile(*ethtoolIncludedMetrics), metricsPattern: regexp.MustCompile(*ethtoolIncludedMetrics),
logger: logger, logger: logger,
entries: map[string]*prometheus.Desc{ entries: map[string]*prometheus.Desc{
"rx_bytes": prometheus.NewDesc( "rx_bytes": prometheus.NewDesc(
prometheus.BuildFQName(namespace, "ethtool", "received_bytes_total"), prometheus.BuildFQName(namespace, "ethtool", "received_bytes_total"),
@ -143,26 +143,11 @@ func init() {
registerCollector("ethtool", defaultDisabled, NewEthtoolCollector) registerCollector("ethtool", defaultDisabled, NewEthtoolCollector)
} }
// Sanitize the given metric name by replacing invalid characters by underscores.
//
// OpenMetrics and the Prometheus exposition format require the metric name
// to consist only of alphanumericals and "_", ":" and they must not start
// with digits. Since colons in MetricFamily are reserved to signal that the
// MetricFamily is the result of a calculation or aggregation of a general
// purpose monitoring system, colons will be replaced as well.
//
// Note: If not subsequently prepending a namespace and/or subsystem (e.g.,
// with prometheus.BuildFQName), the caller must ensure that the supplied
// metricName does not begin with a digit.
func SanitizeMetricName(metricName string) string {
return metricNameRegex.ReplaceAllString(metricName, "_")
}
// Generate the fully-qualified metric name for the ethool metric. // Generate the fully-qualified metric name for the ethool metric.
func buildEthtoolFQName(metric string) string { func buildEthtoolFQName(metric string) string {
metricName := strings.TrimLeft(strings.ToLower(SanitizeMetricName(metric)), "_") metricName := strings.TrimLeft(strings.ToLower(SanitizeMetricName(metric)), "_")
metricName = receivedRegex.ReplaceAllString(metricName, "${1}received${2}") metricName = ethtoolReceivedRegex.ReplaceAllString(metricName, "${1}received${2}")
metricName = transmittedRegex.ReplaceAllString(metricName, "${1}transmitted${2}") metricName = ethtoolTransmitRegex.ReplaceAllString(metricName, "${1}transmitted${2}")
return prometheus.BuildFQName(namespace, "ethtool", metricName) return prometheus.BuildFQName(namespace, "ethtool", metricName)
} }
@ -189,7 +174,7 @@ func (c *ethtoolCollector) Update(ch chan<- prometheus.Metric) error {
var stats map[string]uint64 var stats map[string]uint64
var err error var err error
if c.ignoredDevicesPattern.MatchString(device) { if c.deviceFilter.ignored(device) {
continue continue
} }

View File

@ -121,24 +121,6 @@ func NewEthtoolTestCollector(logger log.Logger) (Collector, error) {
return collector, nil return collector, nil
} }
func TestSanitizeMetricName(t *testing.T) {
testcases := map[string]string{
"": "",
"rx_errors": "rx_errors",
"Queue[0] AllocFails": "Queue_0_AllocFails",
"Tx LPI entry count": "Tx_LPI_entry_count",
"port.VF_admin_queue_requests": "port_VF_admin_queue_requests",
"[3]: tx_bytes": "_3_tx_bytes",
}
for metricName, expected := range testcases {
got := SanitizeMetricName(metricName)
if expected != got {
t.Errorf("Expected '%s' but got '%s'", expected, got)
}
}
}
func TestBuildEthtoolFQName(t *testing.T) { func TestBuildEthtoolFQName(t *testing.T) {
testcases := map[string]string{ testcases := map[string]string{
"rx_errors": "node_ethtool_received_errors", "rx_errors": "node_ethtool_received_errors",
@ -174,7 +156,6 @@ func TestEthtoolCollector(t *testing.T) {
prometheus.NewDesc("node_ethtool_transmitted_packets_total", "Network interface packets sent", []string{"device"}, nil).String(), prometheus.NewDesc("node_ethtool_transmitted_packets_total", "Network interface packets sent", []string{"device"}, nil).String(),
} }
*ethtoolIgnoredDevices = "^$"
*sysPath = "fixtures/sys" *sysPath = "fixtures/sys"
collector, err := NewEthtoolTestCollector(log.NewNopLogger()) collector, err := NewEthtoolTestCollector(log.NewNopLogger())

View File

@ -16,6 +16,7 @@ package collector
import ( import (
"bytes" "bytes"
"io/ioutil" "io/ioutil"
"regexp"
"strconv" "strconv"
"strings" "strings"
) )
@ -44,3 +45,20 @@ func bytesToString(byteArray []byte) string {
} }
return string(byteArray[:n]) return string(byteArray[:n])
} }
var metricNameRegex = regexp.MustCompile(`_*[^0-9A-Za-z_]+_*`)
// Sanitize the given metric name by replacing invalid characters by underscores.
//
// OpenMetrics and the Prometheus exposition format require the metric name
// to consist only of alphanumericals and "_", ":" and they must not start
// with digits. Since colons in MetricFamily are reserved to signal that the
// MetricFamily is the result of a calculation or aggregation of a general
// purpose monitoring system, colons will be replaced as well.
//
// Note: If not subsequently prepending a namespace and/or subsystem (e.g.,
// with prometheus.BuildFQName), the caller must ensure that the supplied
// metricName does not begin with a digit.
func SanitizeMetricName(metricName string) string {
return metricNameRegex.ReplaceAllString(metricName, "_")
}

View File

@ -61,3 +61,21 @@ func TestBytesToString(t *testing.T) {
} }
} }
} }
func TestSanitizeMetricName(t *testing.T) {
testcases := map[string]string{
"": "",
"rx_errors": "rx_errors",
"Queue[0] AllocFails": "Queue_0_AllocFails",
"Tx LPI entry count": "Tx_LPI_entry_count",
"port.VF_admin_queue_requests": "port_VF_admin_queue_requests",
"[3]: tx_bytes": "_3_tx_bytes",
}
for metricName, expected := range testcases {
got := SanitizeMetricName(metricName)
if expected != got {
t.Errorf("Expected '%s' but got '%s'", expected, got)
}
}
}