systemd: check version for availability of properties (#1413)

The dbus property 'SystemState' and the timer property 'LastTriggerUSec'
were added in version 212 of systemd.
Check that the version of systemd is higher than 212 before attempting
to query these properties

f755e3b74b
dedabea4b3

Resolves issue #291

Signed-off-by: Paul Gier <pgier@redhat.com>
This commit is contained in:
Paul Gier 2019-09-04 09:27:25 -05:00 committed by Ben Kochie
parent d3478a207e
commit 8c3de12c22
2 changed files with 63 additions and 12 deletions

View File

@ -20,6 +20,7 @@
* [ENHANCEMENT] Include additional XFS runtime statistics. #1423 * [ENHANCEMENT] Include additional XFS runtime statistics. #1423
* [ENHANCEMENT] Report non-fatal collection errors in the exporter metric. #1439 * [ENHANCEMENT] Report non-fatal collection errors in the exporter metric. #1439
* [ENHANCEMENT] Expose IPVS firewall mark as a label #1455 * [ENHANCEMENT] Expose IPVS firewall mark as a label #1455
* [ENHANCEMENT] Add check for systemd version before attempting to query certain metrics. #1413
* [BUGFIX] Renamed label `state` to `name` on `node_systemd_service_restart_total`. #1393 * [BUGFIX] Renamed label `state` to `name` on `node_systemd_service_restart_total`. #1393
* [BUGFIX] Fix netdev nil reference on Darwin #1414 * [BUGFIX] Fix netdev nil reference on Darwin #1414
* [BUGFIX] Strip path.rootfs from mountpoint labels #1421 * [BUGFIX] Strip path.rootfs from mountpoint labels #1421

View File

@ -19,6 +19,7 @@ import (
"fmt" "fmt"
"math" "math"
"regexp" "regexp"
"strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
@ -29,6 +30,13 @@ import (
kingpin "gopkg.in/alecthomas/kingpin.v2" kingpin "gopkg.in/alecthomas/kingpin.v2"
) )
const (
// minSystemdVersionSystemState is the minimum SystemD version for availability of
// the 'SystemState' manager property and the timer property 'LastTriggerUSec'
// https://github.com/prometheus/node_exporter/issues/291
minSystemdVersionSystemState = 212
)
var ( var (
unitWhitelist = kingpin.Flag("collector.systemd.unit-whitelist", "Regexp of systemd units to whitelist. Units must both match whitelist and not match blacklist to be included.").Default(".+").String() unitWhitelist = kingpin.Flag("collector.systemd.unit-whitelist", "Regexp of systemd units to whitelist. Units must both match whitelist and not match blacklist to be included.").Default(".+").String()
unitBlacklist = kingpin.Flag("collector.systemd.unit-blacklist", "Regexp of systemd units to blacklist. Units must both match whitelist and not match blacklist to be included.").Default(".+\\.(automount|device|mount|scope|slice)").String() unitBlacklist = kingpin.Flag("collector.systemd.unit-blacklist", "Regexp of systemd units to blacklist. Units must both match whitelist and not match blacklist to be included.").Default(".+\\.(automount|device|mount|scope|slice)").String()
@ -50,6 +58,8 @@ type systemdCollector struct {
socketAcceptedConnectionsDesc *prometheus.Desc socketAcceptedConnectionsDesc *prometheus.Desc
socketCurrentConnectionsDesc *prometheus.Desc socketCurrentConnectionsDesc *prometheus.Desc
socketRefusedConnectionsDesc *prometheus.Desc socketRefusedConnectionsDesc *prometheus.Desc
systemdVersionDesc *prometheus.Desc
systemdVersion int
unitWhitelistPattern *regexp.Regexp unitWhitelistPattern *regexp.Regexp
unitBlacklistPattern *regexp.Regexp unitBlacklistPattern *regexp.Regexp
} }
@ -103,9 +113,18 @@ func NewSystemdCollector() (Collector, error) {
socketRefusedConnectionsDesc := prometheus.NewDesc( socketRefusedConnectionsDesc := prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "socket_refused_connections_total"), prometheus.BuildFQName(namespace, subsystem, "socket_refused_connections_total"),
"Total number of refused socket connections", []string{"name"}, nil) "Total number of refused socket connections", []string{"name"}, nil)
systemdVersionDesc := prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "version"),
"Detected systemd version", []string{}, nil)
unitWhitelistPattern := regexp.MustCompile(fmt.Sprintf("^(?:%s)$", *unitWhitelist)) unitWhitelistPattern := regexp.MustCompile(fmt.Sprintf("^(?:%s)$", *unitWhitelist))
unitBlacklistPattern := regexp.MustCompile(fmt.Sprintf("^(?:%s)$", *unitBlacklist)) unitBlacklistPattern := regexp.MustCompile(fmt.Sprintf("^(?:%s)$", *unitBlacklist))
systemdVersion := getSystemdVersion()
if systemdVersion < minSystemdVersionSystemState {
log.Warnf("Detected systemd version %v is lower than minimum %v", systemdVersion, minSystemdVersionSystemState)
log.Warn("Some systemd state and timer metrics will not be available")
}
return &systemdCollector{ return &systemdCollector{
unitDesc: unitDesc, unitDesc: unitDesc,
unitStartTimeDesc: unitStartTimeDesc, unitStartTimeDesc: unitStartTimeDesc,
@ -118,6 +137,8 @@ func NewSystemdCollector() (Collector, error) {
socketAcceptedConnectionsDesc: socketAcceptedConnectionsDesc, socketAcceptedConnectionsDesc: socketAcceptedConnectionsDesc,
socketCurrentConnectionsDesc: socketCurrentConnectionsDesc, socketCurrentConnectionsDesc: socketCurrentConnectionsDesc,
socketRefusedConnectionsDesc: socketRefusedConnectionsDesc, socketRefusedConnectionsDesc: socketRefusedConnectionsDesc,
systemdVersionDesc: systemdVersionDesc,
systemdVersion: systemdVersion,
unitWhitelistPattern: unitWhitelistPattern, unitWhitelistPattern: unitWhitelistPattern,
unitBlacklistPattern: unitBlacklistPattern, unitBlacklistPattern: unitBlacklistPattern,
}, nil }, nil
@ -127,7 +148,7 @@ func NewSystemdCollector() (Collector, error) {
// to reduce wait time for responses. // to reduce wait time for responses.
func (c *systemdCollector) Update(ch chan<- prometheus.Metric) error { func (c *systemdCollector) Update(ch chan<- prometheus.Metric) error {
begin := time.Now() begin := time.Now()
conn, err := c.newDbus() conn, err := newSystemdDbusConn()
if err != nil { if err != nil {
return fmt.Errorf("couldn't get dbus connection: %s", err) return fmt.Errorf("couldn't get dbus connection: %s", err)
} }
@ -179,6 +200,7 @@ func (c *systemdCollector) Update(ch chan<- prometheus.Metric) error {
}() }()
} }
if c.systemdVersion >= minSystemdVersionSystemState {
wg.Add(1) wg.Add(1)
go func() { go func() {
defer wg.Done() defer wg.Done()
@ -186,6 +208,7 @@ func (c *systemdCollector) Update(ch chan<- prometheus.Metric) error {
c.collectTimers(conn, ch, units) c.collectTimers(conn, ch, units)
log.Debugf("systemd collectTimers took %f", time.Since(begin).Seconds()) log.Debugf("systemd collectTimers took %f", time.Since(begin).Seconds())
}() }()
}
wg.Add(1) wg.Add(1)
go func() { go func() {
@ -195,9 +218,15 @@ func (c *systemdCollector) Update(ch chan<- prometheus.Metric) error {
log.Debugf("systemd collectSockets took %f", time.Since(begin).Seconds()) log.Debugf("systemd collectSockets took %f", time.Since(begin).Seconds())
}() }()
if c.systemdVersion >= minSystemdVersionSystemState {
begin = time.Now() begin = time.Now()
err = c.collectSystemState(conn, ch) err = c.collectSystemState(conn, ch)
log.Debugf("systemd collectSystemState took %f", time.Since(begin).Seconds()) log.Debugf("systemd collectSystemState took %f", time.Since(begin).Seconds())
}
ch <- prometheus.MustNewConstMetric(
c.systemdVersionDesc, prometheus.GaugeValue, float64(c.systemdVersion))
return err return err
} }
@ -369,7 +398,7 @@ func (c *systemdCollector) collectSystemState(conn *dbus.Conn, ch chan<- prometh
return nil return nil
} }
func (c *systemdCollector) newDbus() (*dbus.Conn, error) { func newSystemdDbusConn() (*dbus.Conn, error) {
if *systemdPrivate { if *systemdPrivate {
return dbus.NewSystemdConnection() return dbus.NewSystemdConnection()
} }
@ -424,3 +453,24 @@ func filterUnits(units []unit, whitelistPattern, blacklistPattern *regexp.Regexp
return filtered return filtered
} }
func getSystemdVersion() int {
conn, err := newSystemdDbusConn()
if err != nil {
log.Warnf("Unable to get systemd dbus connection, defaulting systemd version to 0: %s", err)
return 0
}
defer conn.Close()
version, err := conn.GetManagerProperty("Version")
if err != nil {
log.Warn("Unable to get systemd version property, defaulting to 0")
return 0
}
version = strings.Replace(version, "\"", "", 2)
v, err := strconv.Atoi(version)
if err != nil {
log.Warnf("Got invalid systemd version: %v", version)
return 0
}
return v
}