feat: add SystemdVirtualization type

Signed-off-by: IbraAoad <Ibrahim.Awwad@canonical.com>
This commit is contained in:
IbraAoad 2025-02-18 11:12:25 +02:00 committed by IbraAoad
parent 8107c3f6c4
commit 0982234bfc
No known key found for this signature in database
2 changed files with 94 additions and 2 deletions

View file

@ -74,6 +74,7 @@ type systemdCollector struct {
socketCurrentConnectionsDesc *prometheus.Desc
socketRefusedConnectionsDesc *prometheus.Desc
systemdVersionDesc *prometheus.Desc
virtualizationDesc *prometheus.Desc
// Use regexps for more flexibility than device_filter.go allows
systemdUnitIncludePattern *regexp.Regexp
systemdUnitExcludePattern *regexp.Regexp
@ -82,6 +83,10 @@ type systemdCollector struct {
var unitStatesName = []string{"active", "activating", "deactivating", "inactive", "failed"}
var getManagerPropertyFunc = func(conn *dbus.Conn, name string) (string, error) {
return conn.GetManagerProperty(name)
}
func init() {
registerCollector("systemd", defaultDisabled, NewSystemdCollector)
}
@ -132,6 +137,9 @@ func NewSystemdCollector(logger *slog.Logger) (Collector, error) {
systemdVersionDesc := prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "version"),
"Detected systemd version", []string{"version"}, nil)
virtualizationDesc := prometheus.NewDesc(
prometheus.BuildFQName(namespace, subsystem, "virtualization"),
"Detected virtualization technology", []string{"type"}, nil)
if *oldSystemdUnitExclude != "" {
if !systemdUnitExcludeSet {
@ -167,6 +175,7 @@ func NewSystemdCollector(logger *slog.Logger) (Collector, error) {
socketCurrentConnectionsDesc: socketCurrentConnectionsDesc,
socketRefusedConnectionsDesc: socketRefusedConnectionsDesc,
systemdVersionDesc: systemdVersionDesc,
virtualizationDesc: virtualizationDesc,
systemdUnitIncludePattern: systemdUnitIncludePattern,
systemdUnitExcludePattern: systemdUnitExcludePattern,
logger: logger,
@ -194,6 +203,14 @@ func (c *systemdCollector) Update(ch chan<- prometheus.Metric) error {
systemdVersionFull,
)
systemdVirtualization := c.getSystemdVirtualization(conn)
ch <- prometheus.MustNewConstMetric(
c.virtualizationDesc,
prometheus.GaugeValue,
1.0,
systemdVirtualization,
)
allUnits, err := c.getAllUnits(conn)
if err != nil {
return fmt.Errorf("couldn't get units: %w", err)
@ -421,7 +438,7 @@ func (c *systemdCollector) collectSummaryMetrics(ch chan<- prometheus.Metric, su
}
func (c *systemdCollector) collectSystemState(conn *dbus.Conn, ch chan<- prometheus.Metric) error {
systemState, err := conn.GetManagerProperty("SystemState")
systemState, err := getManagerPropertyFunc(conn, "SystemState")
if err != nil {
return fmt.Errorf("couldn't get system state: %w", err)
}
@ -490,7 +507,7 @@ func filterUnits(units []unit, includePattern, excludePattern *regexp.Regexp, lo
}
func (c *systemdCollector) getSystemdVersion(conn *dbus.Conn) (float64, string) {
version, err := conn.GetManagerProperty("Version")
version, err := getManagerPropertyFunc(conn, "Version")
if err != nil {
c.logger.Debug("Unable to get systemd version property, defaulting to 0")
return 0, ""
@ -505,3 +522,19 @@ func (c *systemdCollector) getSystemdVersion(conn *dbus.Conn) (float64, string)
}
return v, version
}
func (c *systemdCollector) getSystemdVirtualization(conn *dbus.Conn) string {
virt, err := getManagerPropertyFunc(conn, "Virtualization")
if err != nil {
c.logger.Debug("Could not get Virtualization property", "err", err)
return "unknown"
}
virtStr := strings.Trim(virt, `"`)
if virtStr == "" {
// If no virtualization type is returned, assume it's bare metal.
return "none"
}
return virtStr
}

View file

@ -17,6 +17,7 @@
package collector
import (
"errors"
"io"
"log/slog"
"regexp"
@ -137,3 +138,61 @@ func testSummaryHelper(t *testing.T, state string, actual float64, expected floa
t.Errorf("Summary mode didn't count %s jobs correctly. Actual: %f, expected: %f", state, actual, expected)
}
}
func Test_systemdCollector_getSystemdVirtualization(t *testing.T) {
type fields struct {
logger *slog.Logger
}
type args struct {
conn *dbus.Conn
}
tests := []struct {
name string
fields fields
args args
mock func(conn *dbus.Conn, name string) (string, error)
want string
}{
{
name: "Error",
fields: fields{logger: slog.New(slog.NewTextHandler(io.Discard, nil))},
args: args{conn: &dbus.Conn{}},
mock: func(conn *dbus.Conn, name string) (string, error) {
return "", errors.New("test error")
},
want: "unknown",
},
{
name: "Empty",
fields: fields{logger: slog.New(slog.NewTextHandler(io.Discard, nil))},
args: args{conn: &dbus.Conn{}},
mock: func(conn *dbus.Conn, name string) (string, error) {
return `""`, nil
},
want: "none",
},
{
name: "Valid",
fields: fields{logger: slog.New(slog.NewTextHandler(io.Discard, nil))},
args: args{conn: &dbus.Conn{}},
mock: func(conn *dbus.Conn, name string) (string, error) {
return `"kvm"`, nil
},
want: "kvm",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
origFunc := getManagerPropertyFunc
defer func() { getManagerPropertyFunc = origFunc }()
getManagerPropertyFunc = tt.mock
c := &systemdCollector{
logger: tt.fields.logger,
}
if got := c.getSystemdVirtualization(tt.args.conn); got != tt.want {
t.Errorf("systemdCollector.getSystemdVirtualization() = %v, want %v", got, tt.want)
}
})
}
}