diff options
Diffstat (limited to '')
15 files changed, 2627 insertions, 0 deletions
diff --git a/src/go/collectors/go.d.plugin/modules/systemdunits/README.md b/src/go/collectors/go.d.plugin/modules/systemdunits/README.md new file mode 120000 index 000000000..68dd433bf --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/systemdunits/README.md @@ -0,0 +1 @@ +integrations/systemd_units.md
\ No newline at end of file diff --git a/src/go/collectors/go.d.plugin/modules/systemdunits/charts.go b/src/go/collectors/go.d.plugin/modules/systemdunits/charts.go new file mode 100644 index 000000000..18d8838fb --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/systemdunits/charts.go @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +//go:build linux +// +build linux + +package systemdunits + +import ( + "fmt" + "path/filepath" + "strings" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" + + "golang.org/x/text/cases" + "golang.org/x/text/language" +) + +const ( + prioUnitState = module.Priority + iota + prioUnitFileState +) + +func (s *SystemdUnits) addUnitCharts(name, typ string) { + chart := module.Chart{ + ID: "unit_%s_%s_state", + Title: "%s Unit State", + Units: "state", + Fam: "%s units", + Ctx: "systemd.%s_unit_state", + Priority: prioUnitState, + Labels: []module.Label{ + {Key: "unit_name", Value: name}, + }, + Dims: module.Dims{ + {ID: "unit_%s_%s_state_%s", Name: unitStateActive}, + {ID: "unit_%s_%s_state_%s", Name: unitStateInactive}, + {ID: "unit_%s_%s_state_%s", Name: unitStateActivating}, + {ID: "unit_%s_%s_state_%s", Name: unitStateDeactivating}, + {ID: "unit_%s_%s_state_%s", Name: unitStateFailed}, + }, + } + + chart.ID = fmt.Sprintf(chart.ID, name, typ) + chart.Title = fmt.Sprintf(chart.Title, cases.Title(language.English, cases.Compact).String(typ)) + chart.Fam = fmt.Sprintf(chart.Fam, typ) + chart.Ctx = fmt.Sprintf(chart.Ctx, typ) + + for _, d := range chart.Dims { + d.ID = fmt.Sprintf(d.ID, name, typ, d.Name) + } + + if err := s.Charts().Add(&chart); err != nil { + s.Warning(err) + } +} + +func (s *SystemdUnits) removeUnitCharts(name, typ string) { + px := fmt.Sprintf("unit_%s_%s_", name, typ) + s.removeCharts(px) +} + +func (s *SystemdUnits) addUnitFileCharts(unitPath string) { + _, unitName := filepath.Split(unitPath) + unitType := strings.TrimPrefix(filepath.Ext(unitPath), ".") + + chart := module.Chart{ + ID: "unit_file_%s_state", + Title: "Unit File State", + Units: "state", + Fam: "unit files", + Ctx: "systemd.unit_file_state", + Type: module.Line, + Priority: prioUnitFileState, + Labels: []module.Label{ + {Key: "unit_file_name", Value: unitName}, + {Key: "unit_file_type", Value: unitType}, + }, + Dims: module.Dims{ + {ID: "unit_file_%s_state_enabled", Name: "enabled"}, + {ID: "unit_file_%s_state_enabled-runtime", Name: "enabled-runtime"}, + {ID: "unit_file_%s_state_linked", Name: "linked"}, + {ID: "unit_file_%s_state_linked-runtime", Name: "linked-runtime"}, + {ID: "unit_file_%s_state_alias", Name: "alias"}, + {ID: "unit_file_%s_state_masked", Name: "masked"}, + {ID: "unit_file_%s_state_masked-runtime", Name: "masked-runtime"}, + {ID: "unit_file_%s_state_static", Name: "static"}, + {ID: "unit_file_%s_state_disabled", Name: "disabled"}, + {ID: "unit_file_%s_state_indirect", Name: "indirect"}, + {ID: "unit_file_%s_state_generated", Name: "generated"}, + {ID: "unit_file_%s_state_transient", Name: "transient"}, + {ID: "unit_file_%s_state_bad", Name: "bad"}, + }, + } + + chart.ID = fmt.Sprintf(chart.ID, strings.ReplaceAll(unitPath, ".", "_")) + for _, dim := range chart.Dims { + dim.ID = fmt.Sprintf(dim.ID, unitPath) + } + + if err := s.Charts().Add(&chart); err != nil { + s.Warning(err) + } +} + +func (s *SystemdUnits) removeUnitFileCharts(unitPath string) { + px := fmt.Sprintf("unit_file_%s_", strings.ReplaceAll(unitPath, ".", "_")) + s.removeCharts(px) +} + +func (s *SystemdUnits) removeCharts(prefix string) { + for _, chart := range *s.Charts() { + if strings.HasPrefix(chart.ID, prefix) { + chart.MarkRemove() + chart.MarkNotCreated() + } + } +} diff --git a/src/go/collectors/go.d.plugin/modules/systemdunits/client.go b/src/go/collectors/go.d.plugin/modules/systemdunits/client.go new file mode 100644 index 000000000..e6363d132 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/systemdunits/client.go @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +//go:build linux +// +build linux + +package systemdunits + +import ( + "context" + + "github.com/coreos/go-systemd/v22/dbus" +) + +type systemdClient interface { + connect() (systemdConnection, error) +} +type systemdConnection interface { + Close() + GetManagerProperty(string) (string, error) + GetUnitPropertyContext(ctx context.Context, unit string, propertyName string) (*dbus.Property, error) + ListUnitsContext(ctx context.Context) ([]dbus.UnitStatus, error) + ListUnitsByPatternsContext(ctx context.Context, states []string, patterns []string) ([]dbus.UnitStatus, error) + ListUnitFilesByPatternsContext(ctx context.Context, states []string, patterns []string) ([]dbus.UnitFile, error) +} + +type systemdDBusClient struct{} + +func (systemdDBusClient) connect() (systemdConnection, error) { + return dbus.NewWithContext(context.Background()) +} + +func newSystemdDBusClient() *systemdDBusClient { + return &systemdDBusClient{} +} diff --git a/src/go/collectors/go.d.plugin/modules/systemdunits/collect.go b/src/go/collectors/go.d.plugin/modules/systemdunits/collect.go new file mode 100644 index 000000000..0d61c9998 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/systemdunits/collect.go @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +//go:build linux +// +build linux + +package systemdunits + +import ( + "fmt" + "regexp" + "strconv" +) + +func (s *SystemdUnits) collect() (map[string]int64, error) { + conn, err := s.getConnection() + if err != nil { + return nil, err + } + + if s.systemdVersion == 0 { + ver, err := s.getSystemdVersion(conn) + if err != nil { + s.closeConnection() + return nil, err + } + s.systemdVersion = ver + } + + mx := make(map[string]int64) + + if err := s.collectUnits(mx, conn); err != nil { + s.closeConnection() + return nil, err + } + + if s.CollectUnitFiles && len(s.IncludeUnitFiles) > 0 { + if err := s.collectUnitFiles(mx, conn); err != nil { + s.closeConnection() + return mx, err + } + } + + return mx, nil +} + +func (s *SystemdUnits) getConnection() (systemdConnection, error) { + if s.conn == nil { + conn, err := s.client.connect() + if err != nil { + return nil, fmt.Errorf("error on creating a connection: %v", err) + } + s.conn = conn + } + return s.conn, nil +} + +func (s *SystemdUnits) closeConnection() { + if s.conn != nil { + s.conn.Close() + s.conn = nil + } +} + +var reVersion = regexp.MustCompile(`[0-9][0-9][0-9]`) + +const versionProperty = "Version" + +func (s *SystemdUnits) getSystemdVersion(conn systemdConnection) (int, error) { + s.Debugf("calling function 'GetManagerProperty'") + version, err := conn.GetManagerProperty(versionProperty) + if err != nil { + return 0, fmt.Errorf("error on getting '%s' manager property: %v", versionProperty, err) + } + + s.Debugf("systemd version: %s", version) + + major := reVersion.FindString(version) + if major == "" { + return 0, fmt.Errorf("couldn't parse systemd version string '%s'", version) + } + + ver, err := strconv.Atoi(major) + if err != nil { + return 0, fmt.Errorf("couldn't parse systemd version string '%s': %v", version, err) + } + + return ver, nil +} diff --git a/src/go/collectors/go.d.plugin/modules/systemdunits/collect_unit_files.go b/src/go/collectors/go.d.plugin/modules/systemdunits/collect_unit_files.go new file mode 100644 index 000000000..eff2d6ecb --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/systemdunits/collect_unit_files.go @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +//go:build linux +// +build linux + +package systemdunits + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/coreos/go-systemd/v22/dbus" +) + +// https://github.com/systemd/systemd/blob/3d320785c4bbba74459096b07e85a79c4f0cdffb/src/shared/install.c#L3785 +// see "is-enabled" in https://www.man7.org/linux/man-pages/man1/systemctl.1.html +var unitFileStates = []string{ + "enabled", + "enabled-runtime", + "linked", + "linked-runtime", + "alias", + "masked", + "masked-runtime", + "static", + "disabled", + "indirect", + "generated", + "transient", + "bad", +} + +func (s *SystemdUnits) collectUnitFiles(mx map[string]int64, conn systemdConnection) error { + if s.systemdVersion < 230 { + return nil + } + + if now := time.Now(); now.After(s.lastListUnitFilesTime.Add(s.CollectUnitFilesEvery.Duration())) { + unitFiles, err := s.getUnitFilesByPatterns(conn) + if err != nil { + return err + } + s.lastListUnitFilesTime = now + s.cachedUnitFiles = unitFiles + } + + seen := make(map[string]bool) + + for _, unitFile := range s.cachedUnitFiles { + seen[unitFile.Path] = true + + if !s.seenUnitFiles[unitFile.Path] { + s.seenUnitFiles[unitFile.Path] = true + s.addUnitFileCharts(unitFile.Path) + } + + px := fmt.Sprintf("unit_file_%s_state_", unitFile.Path) + for _, st := range unitFileStates { + mx[px+st] = 0 + } + mx[px+strings.ToLower(unitFile.Type)] = 1 + } + + for k := range s.seenUnitFiles { + if !seen[k] { + delete(s.seenUnitFiles, k) + s.removeUnitFileCharts(k) + } + } + + return nil +} + +func (s *SystemdUnits) getUnitFilesByPatterns(conn systemdConnection) ([]dbus.UnitFile, error) { + ctx, cancel := context.WithTimeout(context.Background(), s.Timeout.Duration()) + defer cancel() + + s.Debugf("calling function 'ListUnitFilesByPatterns'") + + unitFiles, err := conn.ListUnitFilesByPatternsContext(ctx, nil, s.IncludeUnitFiles) + if err != nil { + return nil, fmt.Errorf("error on ListUnitFilesByPatterns: %v", err) + } + + for i := range unitFiles { + unitFiles[i].Path = cleanUnitName(unitFiles[i].Path) + } + + s.Debugf("got %d unit files", len(unitFiles)) + + return unitFiles, nil +} diff --git a/src/go/collectors/go.d.plugin/modules/systemdunits/collect_units.go b/src/go/collectors/go.d.plugin/modules/systemdunits/collect_units.go new file mode 100644 index 000000000..0cf97af03 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/systemdunits/collect_units.go @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +//go:build linux +// +build linux + +package systemdunits + +import ( + "context" + "fmt" + "strconv" + "strings" + + "github.com/coreos/go-systemd/v22/dbus" +) + +const transientProperty = "Transient" + +const ( + // https://www.freedesktop.org/software/systemd/man/systemd.html + unitStateActive = "active" + unitStateInactive = "inactive" + unitStateActivating = "activating" + unitStateDeactivating = "deactivating" + unitStateFailed = "failed" +) + +var unitStates = []string{ + unitStateActive, + unitStateActivating, + unitStateFailed, + unitStateInactive, + unitStateDeactivating, +} + +func (s *SystemdUnits) collectUnits(mx map[string]int64, conn systemdConnection) error { + var units []dbus.UnitStatus + var err error + + if s.systemdVersion >= 230 { + // https://github.com/systemd/systemd/pull/3142 + units, err = s.getLoadedUnitsByPatterns(conn) + } else { + units, err = s.getLoadedUnits(conn) + } + if err != nil { + return err + } + + seen := make(map[string]bool) + + for _, unit := range units { + name, typ, ok := extractUnitNameType(unit.Name) + if !ok { + continue + } + + seen[unit.Name] = true + + if s.SkipTransient { + if _, ok := s.unitTransient[unit.Name]; !ok { + prop, err := s.getUnitTransientProperty(conn, unit.Name) + if err != nil { + return err + } + prop = strings.Trim(prop, "\"") + s.unitTransient[unit.Name] = prop == "true" + } + if s.unitTransient[unit.Name] { + continue + } + } + + if !s.seenUnits[unit.Name] { + s.seenUnits[unit.Name] = true + s.addUnitCharts(name, typ) + } + + for _, s := range unitStates { + mx[fmt.Sprintf("unit_%s_%s_state_%s", name, typ, s)] = 0 + } + mx[fmt.Sprintf("unit_%s_%s_state_%s", name, typ, unit.ActiveState)] = 1 + } + + for k := range s.seenUnits { + if !seen[k] { + delete(s.seenUnits, k) + if name, typ, ok := extractUnitNameType(k); ok { + s.removeUnitCharts(name, typ) + } + } + } + + for k := range s.unitTransient { + if !seen[k] { + delete(s.unitTransient, k) + } + } + + return nil +} + +func (s *SystemdUnits) getLoadedUnits(conn systemdConnection) ([]dbus.UnitStatus, error) { + ctx, cancel := context.WithTimeout(context.Background(), s.Timeout.Duration()) + defer cancel() + + s.Debugf("calling function 'ListUnits'") + units, err := conn.ListUnitsContext(ctx) + if err != nil { + return nil, fmt.Errorf("error on ListUnits: %v", err) + } + + for i := range units { + units[i].Name = cleanUnitName(units[i].Name) + } + + loaded := units[:0] + for _, unit := range units { + if unit.LoadState == "loaded" && s.unitSr.MatchString(unit.Name) { + loaded = append(loaded, unit) + } + } + + s.Debugf("got total/loaded %d/%d units", len(units), len(loaded)) + + return loaded, nil +} + +func (s *SystemdUnits) getLoadedUnitsByPatterns(conn systemdConnection) ([]dbus.UnitStatus, error) { + ctx, cancel := context.WithTimeout(context.Background(), s.Timeout.Duration()) + defer cancel() + + s.Debugf("calling function 'ListUnitsByPatterns'") + + units, err := conn.ListUnitsByPatternsContext(ctx, unitStates, s.Include) + if err != nil { + return nil, fmt.Errorf("error on ListUnitsByPatterns: %v", err) + } + + for i := range units { + units[i].Name = cleanUnitName(units[i].Name) + } + + loaded := units[:0] + for _, unit := range units { + if unit.LoadState == "loaded" { + loaded = append(loaded, unit) + } + } + s.Debugf("got total/loaded %d/%d units", len(units), len(loaded)) + + return loaded, nil +} + +func (s *SystemdUnits) getUnitTransientProperty(conn systemdConnection, unit string) (string, error) { + ctx, cancel := context.WithTimeout(context.Background(), s.Timeout.Duration()) + defer cancel() + + s.Debugf("calling function 'GetUnitProperty' for unit '%s'", unit) + + prop, err := conn.GetUnitPropertyContext(ctx, unit, transientProperty) + if err != nil { + return "", fmt.Errorf("error on GetUnitProperty: %v", err) + } + + return prop.Value.String(), nil +} + +func extractUnitNameType(name string) (string, string, bool) { + idx := strings.LastIndexByte(name, '.') + if idx <= 0 { + return "", "", false + } + return name[:idx], name[idx+1:], true +} + +func cleanUnitName(name string) string { + // dev-disk-by\x2duuid-DE44\x2dCEE0.device => dev-disk-by-uuid-DE44-CEE0.device + if strings.IndexByte(name, '\\') == -1 { + return name + } + v, err := strconv.Unquote("\"" + name + "\"") + if err != nil { + return name + } + return v +} diff --git a/src/go/collectors/go.d.plugin/modules/systemdunits/config_schema.json b/src/go/collectors/go.d.plugin/modules/systemdunits/config_schema.json new file mode 100644 index 000000000..016e984ce --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/systemdunits/config_schema.json @@ -0,0 +1,122 @@ +{ + "jsonSchema": { + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Systemdunits collector configuration.", + "type": "object", + "properties": { + "update_every": { + "title": "Update every", + "description": "Data collection interval, measured in seconds.", + "type": "integer", + "minimum": 1, + "default": 10 + }, + "timeout": { + "title": "Timeout", + "description": "The timeout, in seconds, for connecting and querying systemd's D-Bus endpoint.", + "type": "number", + "minimum": 0.5, + "default": 2 + }, + "skip_transient": { + "title": "Skip transient units", + "description": "If set, skip data collection for systemd transient units.", + "type": "boolean", + "default": false + }, + "include": { + "title": "Include", + "description": "Configuration for monitoring specific systemd units. Include systemd units whose names match any of the specified [patterns](https://golang.org/pkg/path/filepath/#Match).", + "type": [ + "array", + "null" + ], + "uniqueItems": true, + "minItems": 1, + "items": { + "title": "Unit pattern", + "type": "string" + }, + "default": [ + "*.service" + ] + }, + "collect_unit_files": { + "title": "Collect unit files", + "description": "If set, collect the state of installed unit files. **Enabling this may increase system overhead**, particularly if the pattern matches a large number of unit files.", + "type": "boolean", + "default": false + }, + "collect_unit_files_every": { + "title": "Unit files polling interval", + "description": "Interval for querying systemd about unit files and their enablement state, measured in seconds. Data is cached for this interval to reduce system overhead.", + "type": "number", + "minimum": 1, + "default": 300 + }, + "include_unit_files": { + "title": "Include unit files", + "description": "Configuration for monitoring specific systemd unit files. Include systemd unit files whose names match any of the specified [patterns](https://golang.org/pkg/path/filepath/#Match).", + "type": [ + "array", + "null" + ], + "uniqueItems": true, + "minItems": 1, + "items": { + "title": "Unit file name pattern", + "type": "string" + }, + "default": [ + "*.service" + ] + } + }, + "required": [ + "include" + ], + "additionalProperties": false, + "patternProperties": { + "^name$": {} + } + }, + "uiSchema": { + "uiOptions": { + "fullPage": true + }, + "ui:flavour": "tabs", + "ui:options": { + "tabs": [ + { + "title": "Base", + "fields": [ + "update_every", + "timeout", + "skip_transient", + "include" + ] + }, + { + "title": "Unit Files", + "fields": [ + "collect_unit_files", + "collect_unit_files_every", + "include_unit_files" + ] + } + ] + }, + "timeout": { + "ui:help": "Accepts decimals for precise control (e.g., type 1.5 for 1.5 seconds)." + }, + "skip_transient": { + "ui:help": "A systemd transient unit is a temporary unit created on-the-fly, typically used for ad-hoc tasks or testing purposes. They are created using the `systemd-run` command, which allows you to specify unit properties directly on the command line." + }, + "include": { + "ui:listFlavour": "list" + }, + "include_unit_files": { + "ui:listFlavour": "list" + } + } +} diff --git a/src/go/collectors/go.d.plugin/modules/systemdunits/doc.go b/src/go/collectors/go.d.plugin/modules/systemdunits/doc.go new file mode 100644 index 000000000..8bb45fab9 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/systemdunits/doc.go @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +// Package systemdunits is a systemd units states collector +package systemdunits diff --git a/src/go/collectors/go.d.plugin/modules/systemdunits/init.go b/src/go/collectors/go.d.plugin/modules/systemdunits/init.go new file mode 100644 index 000000000..ea3d21d37 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/systemdunits/init.go @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +//go:build linux +// +build linux + +package systemdunits + +import ( + "errors" + "strings" + + "github.com/netdata/netdata/go/go.d.plugin/pkg/matcher" +) + +func (s *SystemdUnits) validateConfig() error { + if len(s.Include) == 0 { + return errors.New("'include' option not set") + } + return nil +} + +func (s *SystemdUnits) initUnitSelector() (matcher.Matcher, error) { + if len(s.Include) == 0 { + return matcher.TRUE(), nil + } + + expr := strings.Join(s.Include, " ") + return matcher.NewSimplePatternsMatcher(expr) +} diff --git a/src/go/collectors/go.d.plugin/modules/systemdunits/integrations/systemd_units.md b/src/go/collectors/go.d.plugin/modules/systemdunits/integrations/systemd_units.md new file mode 100644 index 000000000..431f084ba --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/systemdunits/integrations/systemd_units.md @@ -0,0 +1,289 @@ +<!--startmeta +custom_edit_url: "https://github.com/netdata/netdata/edit/master/src/go/collectors/go.d.plugin/modules/systemdunits/README.md" +meta_yaml: "https://github.com/netdata/netdata/edit/master/src/go/collectors/go.d.plugin/modules/systemdunits/metadata.yaml" +sidebar_label: "Systemd Units" +learn_status: "Published" +learn_rel_path: "Collecting Metrics/Systemd" +most_popular: False +message: "DO NOT EDIT THIS FILE DIRECTLY, IT IS GENERATED BY THE COLLECTOR'S metadata.yaml FILE" +endmeta--> + +# Systemd Units + + +<img src="https://netdata.cloud/img/systemd.svg" width="150"/> + + +Plugin: go.d.plugin +Module: systemdunits + +<img src="https://img.shields.io/badge/maintained%20by-Netdata-%2300ab44" /> + +## Overview + +This collector monitors the state of Systemd units and unit files. + + + + +This collector is supported on all platforms. + +This collector supports collecting metrics from multiple instances of this integration, including remote instances. + + +### Default Behavior + +#### Auto-Detection + +This integration doesn't support auto-detection. + +#### Limits + +The default configuration for this integration does not impose any limits on data collection. + +#### Performance Impact + +The default configuration for this integration is not expected to impose a significant performance impact on the system. + + +## Metrics + +Metrics grouped by *scope*. + +The scope defines the instance that the metric belongs to. An instance is uniquely identified by a set of labels. + + + +### Per unit + +These metrics refer to the systemd unit. + +Labels: + +| Label | Description | +|:-----------|:----------------| +| unit_name | systemd unit name | + +Metrics: + +| Metric | Dimensions | Unit | +|:------|:----------|:----| +| systemd.service_unit_state | active, inactive, activating, deactivating, failed | state | +| systemd.socket_unit_state | active, inactive, activating, deactivating, failed | state | +| systemd.target_unit_state | active, inactive, activating, deactivating, failed | state | +| systemd.path_unit_state | active, inactive, activating, deactivating, failed | state | +| systemd.device_unit_state | active, inactive, activating, deactivating, failed | state | +| systemd.mount_unit_state | active, inactive, activating, deactivating, failed | state | +| systemd.automount_unit_state | active, inactive, activating, deactivating, failed | state | +| systemd.swap_unit_state | active, inactive, activating, deactivating, failed | state | +| systemd.timer_unit_state | active, inactive, activating, deactivating, failed | state | +| systemd.scope_unit_state | active, inactive, activating, deactivating, failed | state | +| systemd.slice_unit_state | active, inactive, activating, deactivating, failed | state | + +### Per unit file + +These metrics refer to the systemd unit file. + +Labels: + +| Label | Description | +|:-----------|:----------------| +| unit_file_name | systemd unit file name | +| unit_file_type | systemd unit file type | + +Metrics: + +| Metric | Dimensions | Unit | +|:------|:----------|:----| +| systemd.unit_file_state | enabled, enabled-runtime, linked, linked-runtime, alias, masked, masked-runtime, static, disabled, indirect, generated, transient, bad | state | + + + +## Alerts + + +The following alerts are available: + +| Alert name | On metric | Description | +|:------------|:----------|:------------| +| [ systemd_service_unit_failed_state ](https://github.com/netdata/netdata/blob/master/src/health/health.d/systemdunits.conf) | systemd.service_unit_state | systemd service unit in the failed state | +| [ systemd_socket_unit_failed_state ](https://github.com/netdata/netdata/blob/master/src/health/health.d/systemdunits.conf) | systemd.socket_unit_state | systemd socket unit in the failed state | +| [ systemd_target_unit_failed_state ](https://github.com/netdata/netdata/blob/master/src/health/health.d/systemdunits.conf) | systemd.target_unit_state | systemd target unit in the failed state | +| [ systemd_path_unit_failed_state ](https://github.com/netdata/netdata/blob/master/src/health/health.d/systemdunits.conf) | systemd.path_unit_state | systemd path unit in the failed state | +| [ systemd_device_unit_failed_state ](https://github.com/netdata/netdata/blob/master/src/health/health.d/systemdunits.conf) | systemd.device_unit_state | systemd device unit in the failed state | +| [ systemd_mount_unit_failed_state ](https://github.com/netdata/netdata/blob/master/src/health/health.d/systemdunits.conf) | systemd.mount_unit_state | systemd mount unit in the failed state | +| [ systemd_automount_unit_failed_state ](https://github.com/netdata/netdata/blob/master/src/health/health.d/systemdunits.conf) | systemd.automount_unit_state | systemd automount unit in the failed state | +| [ systemd_swap_unit_failed_state ](https://github.com/netdata/netdata/blob/master/src/health/health.d/systemdunits.conf) | systemd.swap_unit_state | systemd swap unit in the failed state | +| [ systemd_scope_unit_failed_state ](https://github.com/netdata/netdata/blob/master/src/health/health.d/systemdunits.conf) | systemd.scope_unit_state | systemd scope unit in the failed state | +| [ systemd_slice_unit_failed_state ](https://github.com/netdata/netdata/blob/master/src/health/health.d/systemdunits.conf) | systemd.slice_unit_state | systemd slice unit in the failed state | +| [ systemd_timer_unit_failed_state ](https://github.com/netdata/netdata/blob/master/src/health/health.d/systemdunits.conf) | systemd.timer_unit_state | systemd timer unit in the failed state | + + +## Setup + +### Prerequisites + +No action required. + +### Configuration + +#### File + +The configuration file name for this integration is `go.d/systemdunits.conf`. + + +You can edit the configuration file using the `edit-config` script from the +Netdata [config directory](/docs/netdata-agent/configuration/README.md#the-netdata-config-directory). + +```bash +cd /etc/netdata 2>/dev/null || cd /opt/netdata/etc/netdata +sudo ./edit-config go.d/systemdunits.conf +``` +#### Options + +The following options can be defined globally: update_every, autodetection_retry. + + +<details open><summary>Config options</summary> + +| Name | Description | Default | Required | +|:----|:-----------|:-------|:--------:| +| update_every | Data collection frequency. | 1 | no | +| autodetection_retry | Recheck interval in seconds. Zero means no recheck will be scheduled. | 0 | no | +| timeout | System bus requests timeout. | 1 | no | +| include | Systemd units selector. | *.service | no | +| skip_transient | If set, skip data collection for systemd transient units. | false | no | +| collect_unit_files | If set to true, collect the state of installed unit files. Enabling this may increase system overhead. | false | no | +| collect_unit_files_every | Interval for querying systemd about unit files and their enablement state, measured in seconds. Data is cached for this interval to reduce system overhead. | 300 | no | +| include_unit_files | Systemd unit files selector. | *.service | no | + +##### include + +Systemd units matching the selector will be monitored. + +- Logic: (pattern1 OR pattern2) +- Pattern syntax: [shell file name pattern](https://golang.org/pkg/path/filepath/#Match) +- Syntax: + +```yaml +includes: + - pattern1 + - pattern2 +``` + + +##### include_unit_files + +Systemd unit files matching the selector will be monitored. + +- Logic: (pattern1 OR pattern2) +- Pattern syntax: [shell file name pattern](https://golang.org/pkg/path/filepath/#Match) +- Syntax: + +```yaml +includes: + - pattern1 + - pattern2 +``` + + +</details> + +#### Examples + +##### Service units + +Collect state of all service type units. + +<details open><summary>Config</summary> + +```yaml +jobs: + - name: service + include: + - '*.service' + +``` +</details> + +##### One specific unit + +Collect state of one specific unit. + +<details open><summary>Config</summary> + +```yaml +jobs: + - name: my-specific-service + include: + - 'my-specific.service' + +``` +</details> + +##### All unit types + +Collect state of all units. + +<details open><summary>Config</summary> + +```yaml +jobs: + - name: my-specific-service-unit + include: + - '*' + +``` +</details> + +##### Multi-instance + +> **Note**: When you define multiple jobs, their names must be unique. + +Collect state of all service and socket type units. + + +<details open><summary>Config</summary> + +```yaml +jobs: + - name: service + include: + - '*.service' + + - name: socket + include: + - '*.socket' + +``` +</details> + + + +## Troubleshooting + +### Debug Mode + +To troubleshoot issues with the `systemdunits` collector, run the `go.d.plugin` with the debug option enabled. The output +should give you clues as to why the collector isn't working. + +- Navigate to the `plugins.d` directory, usually at `/usr/libexec/netdata/plugins.d/`. If that's not the case on + your system, open `netdata.conf` and look for the `plugins` setting under `[directories]`. + + ```bash + cd /usr/libexec/netdata/plugins.d/ + ``` + +- Switch to the `netdata` user. + + ```bash + sudo -u netdata -s + ``` + +- Run the `go.d.plugin` to debug the collector: + + ```bash + ./go.d.plugin -d -m systemdunits + ``` + + diff --git a/src/go/collectors/go.d.plugin/modules/systemdunits/metadata.yaml b/src/go/collectors/go.d.plugin/modules/systemdunits/metadata.yaml new file mode 100644 index 000000000..791e58400 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/systemdunits/metadata.yaml @@ -0,0 +1,344 @@ +plugin_name: go.d.plugin +modules: + - meta: + id: collector-go.d.plugin-systemdunits + plugin_name: go.d.plugin + module_name: systemdunits + monitored_instance: + name: Systemd Units + link: https://www.freedesktop.org/wiki/Software/systemd/ + icon_filename: systemd.svg + categories: + - data-collection.systemd + keywords: + - systemd + related_resources: + integrations: + list: [] + info_provided_to_referring_integrations: + description: "" + most_popular: false + overview: + data_collection: + metrics_description: | + This collector monitors the state of Systemd units and unit files. + method_description: "" + supported_platforms: + include: [] + exclude: [] + multi_instance: true + additional_permissions: + description: "" + default_behavior: + auto_detection: + description: "" + limits: + description: "" + performance_impact: + description: "" + setup: + prerequisites: + list: [] + configuration: + file: + name: go.d/systemdunits.conf + options: + description: | + The following options can be defined globally: update_every, autodetection_retry. + folding: + title: Config options + enabled: true + list: + - name: update_every + description: Data collection frequency. + default_value: 1 + required: false + - name: autodetection_retry + description: Recheck interval in seconds. Zero means no recheck will be scheduled. + default_value: 0 + required: false + - name: timeout + description: System bus requests timeout. + default_value: 1 + required: false + - name: include + description: Systemd units selector. + default_value: "*.service" + required: false + detailed_description: | + Systemd units matching the selector will be monitored. + + - Logic: (pattern1 OR pattern2) + - Pattern syntax: [shell file name pattern](https://golang.org/pkg/path/filepath/#Match) + - Syntax: + + ```yaml + includes: + - pattern1 + - pattern2 + ``` + - name: skip_transient + description: If set, skip data collection for systemd transient units. + default_value: "false" + required: false + - name: collect_unit_files + description: If set to true, collect the state of installed unit files. Enabling this may increase system overhead. + default_value: "false" + required: false + - name: collect_unit_files_every + description: Interval for querying systemd about unit files and their enablement state, measured in seconds. Data is cached for this interval to reduce system overhead. + default_value: 300 + required: false + - name: include_unit_files + description: Systemd unit files selector. + default_value: "*.service" + required: false + detailed_description: | + Systemd unit files matching the selector will be monitored. + + - Logic: (pattern1 OR pattern2) + - Pattern syntax: [shell file name pattern](https://golang.org/pkg/path/filepath/#Match) + - Syntax: + + ```yaml + includes: + - pattern1 + - pattern2 + ``` + examples: + folding: + title: Config + enabled: true + list: + - name: Service units + description: Collect state of all service type units. + config: | + jobs: + - name: service + include: + - '*.service' + - name: One specific unit + description: Collect state of one specific unit. + config: | + jobs: + - name: my-specific-service + include: + - 'my-specific.service' + - name: All unit types + description: Collect state of all units. + config: | + jobs: + - name: my-specific-service-unit + include: + - '*' + - name: Multi-instance + description: | + > **Note**: When you define multiple jobs, their names must be unique. + + Collect state of all service and socket type units. + config: | + jobs: + - name: service + include: + - '*.service' + + - name: socket + include: + - '*.socket' + troubleshooting: + problems: + list: [] + alerts: + - name: systemd_service_unit_failed_state + metric: systemd.service_unit_state + info: systemd service unit in the failed state + link: https://github.com/netdata/netdata/blob/master/src/health/health.d/systemdunits.conf + - name: systemd_socket_unit_failed_state + metric: systemd.socket_unit_state + info: systemd socket unit in the failed state + link: https://github.com/netdata/netdata/blob/master/src/health/health.d/systemdunits.conf + - name: systemd_target_unit_failed_state + metric: systemd.target_unit_state + info: systemd target unit in the failed state + link: https://github.com/netdata/netdata/blob/master/src/health/health.d/systemdunits.conf + - name: systemd_path_unit_failed_state + metric: systemd.path_unit_state + info: systemd path unit in the failed state + link: https://github.com/netdata/netdata/blob/master/src/health/health.d/systemdunits.conf + - name: systemd_device_unit_failed_state + metric: systemd.device_unit_state + info: systemd device unit in the failed state + link: https://github.com/netdata/netdata/blob/master/src/health/health.d/systemdunits.conf + - name: systemd_mount_unit_failed_state + metric: systemd.mount_unit_state + info: systemd mount unit in the failed state + link: https://github.com/netdata/netdata/blob/master/src/health/health.d/systemdunits.conf + - name: systemd_automount_unit_failed_state + metric: systemd.automount_unit_state + info: systemd automount unit in the failed state + link: https://github.com/netdata/netdata/blob/master/src/health/health.d/systemdunits.conf + - name: systemd_swap_unit_failed_state + metric: systemd.swap_unit_state + info: systemd swap unit in the failed state + link: https://github.com/netdata/netdata/blob/master/src/health/health.d/systemdunits.conf + - name: systemd_scope_unit_failed_state + metric: systemd.scope_unit_state + info: systemd scope unit in the failed state + link: https://github.com/netdata/netdata/blob/master/src/health/health.d/systemdunits.conf + - name: systemd_slice_unit_failed_state + metric: systemd.slice_unit_state + info: systemd slice unit in the failed state + link: https://github.com/netdata/netdata/blob/master/src/health/health.d/systemdunits.conf + - name: systemd_timer_unit_failed_state + metric: systemd.timer_unit_state + info: systemd timer unit in the failed state + link: https://github.com/netdata/netdata/blob/master/src/health/health.d/systemdunits.conf + metrics: + folding: + title: Metrics + enabled: false + description: "" + availability: [] + scopes: + - name: unit + description: These metrics refer to the systemd unit. + labels: + - name: unit_name + description: systemd unit name + metrics: + - name: systemd.service_unit_state + description: Service Unit State + unit: state + chart_type: line + dimensions: + - name: active + - name: inactive + - name: activating + - name: deactivating + - name: failed + - name: systemd.socket_unit_state + description: Socket Unit State + unit: state + chart_type: line + dimensions: + - name: active + - name: inactive + - name: activating + - name: deactivating + - name: failed + - name: systemd.target_unit_state + description: Target Unit State + unit: state + chart_type: line + dimensions: + - name: active + - name: inactive + - name: activating + - name: deactivating + - name: failed + - name: systemd.path_unit_state + description: Path Unit State + unit: state + chart_type: line + dimensions: + - name: active + - name: inactive + - name: activating + - name: deactivating + - name: failed + - name: systemd.device_unit_state + description: Device Unit State + unit: state + chart_type: line + dimensions: + - name: active + - name: inactive + - name: activating + - name: deactivating + - name: failed + - name: systemd.mount_unit_state + description: Mount Unit State + unit: state + chart_type: line + dimensions: + - name: active + - name: inactive + - name: activating + - name: deactivating + - name: failed + - name: systemd.automount_unit_state + description: Automount Unit State + unit: state + chart_type: line + dimensions: + - name: active + - name: inactive + - name: activating + - name: deactivating + - name: failed + - name: systemd.swap_unit_state + description: Swap Unit State + unit: state + chart_type: line + dimensions: + - name: active + - name: inactive + - name: activating + - name: deactivating + - name: failed + - name: systemd.timer_unit_state + description: Timer Unit State + unit: state + chart_type: line + dimensions: + - name: active + - name: inactive + - name: activating + - name: deactivating + - name: failed + - name: systemd.scope_unit_state + description: Scope Unit State + unit: state + chart_type: line + dimensions: + - name: active + - name: inactive + - name: activating + - name: deactivating + - name: failed + - name: systemd.slice_unit_state + description: Slice Unit State + unit: state + chart_type: line + dimensions: + - name: active + - name: inactive + - name: activating + - name: deactivating + - name: failed + - name: unit file + description: These metrics refer to the systemd unit file. + labels: + - name: unit_file_name + description: systemd unit file name + - name: unit_file_type + description: systemd unit file type + metrics: + - name: systemd.unit_file_state + description: Unit File State + unit: state + chart_type: line + dimensions: + - name: enabled + - name: enabled-runtime + - name: linked + - name: linked-runtime + - name: alias + - name: masked + - name: masked-runtime + - name: static + - name: disabled + - name: indirect + - name: generated + - name: transient + - name: bad diff --git a/src/go/collectors/go.d.plugin/modules/systemdunits/systemdunits.go b/src/go/collectors/go.d.plugin/modules/systemdunits/systemdunits.go new file mode 100644 index 000000000..367fa2a44 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/systemdunits/systemdunits.go @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +//go:build linux +// +build linux + +package systemdunits + +import ( + _ "embed" + "errors" + "time" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" + "github.com/netdata/netdata/go/go.d.plugin/pkg/matcher" + "github.com/netdata/netdata/go/go.d.plugin/pkg/web" + + "github.com/coreos/go-systemd/v22/dbus" +) + +//go:embed "config_schema.json" +var configSchema string + +func init() { + module.Register("systemdunits", module.Creator{ + JobConfigSchema: configSchema, + Defaults: module.Defaults{ + UpdateEvery: 10, // gathering systemd units can be a CPU intensive op + }, + Create: func() module.Module { return New() }, + Config: func() any { return &Config{} }, + }) +} + +func New() *SystemdUnits { + return &SystemdUnits{ + Config: Config{ + Timeout: web.Duration(time.Second * 2), + Include: []string{"*.service"}, + SkipTransient: false, + CollectUnitFiles: false, + IncludeUnitFiles: []string{"*.service"}, + CollectUnitFilesEvery: web.Duration(time.Minute * 5), + }, + charts: &module.Charts{}, + client: newSystemdDBusClient(), + seenUnits: make(map[string]bool), + unitTransient: make(map[string]bool), + seenUnitFiles: make(map[string]bool), + } +} + +type Config struct { + UpdateEvery int `yaml:"update_every,omitempty" json:"update_every"` + Timeout web.Duration `yaml:"timeout,omitempty" json:"timeout"` + Include []string `yaml:"include,omitempty" json:"include"` + SkipTransient bool `yaml:"skip_transient" json:"skip_transient"` + CollectUnitFiles bool `yaml:"collect_unit_files" json:"collect_unit_files"` + IncludeUnitFiles []string `yaml:"include_unit_files,omitempty" json:"include_unit_files"` + CollectUnitFilesEvery web.Duration `yaml:"collect_unit_files_every,omitempty" json:"collect_unit_files_every"` +} + +type SystemdUnits struct { + module.Base + Config `yaml:",inline" json:""` + + client systemdClient + conn systemdConnection + + systemdVersion int + + seenUnits map[string]bool + unitTransient map[string]bool + unitSr matcher.Matcher + + lastListUnitFilesTime time.Time + cachedUnitFiles []dbus.UnitFile + seenUnitFiles map[string]bool + + charts *module.Charts +} + +func (s *SystemdUnits) Configuration() any { + return s.Config +} + +func (s *SystemdUnits) Init() error { + if err := s.validateConfig(); err != nil { + s.Errorf("config validation: %v", err) + return err + } + + sr, err := s.initUnitSelector() + if err != nil { + s.Errorf("init unit selector: %v", err) + return err + } + s.unitSr = sr + + s.Debugf("timeout: %s", s.Timeout) + s.Debugf("units: patterns '%v'", s.Include) + s.Debugf("unit files: enabled '%v', every '%s', patterns: %v", + s.CollectUnitFiles, s.CollectUnitFilesEvery, s.IncludeUnitFiles) + + return nil +} + +func (s *SystemdUnits) Check() error { + mx, err := s.collect() + if err != nil { + s.Error(err) + return err + } + + if len(mx) == 0 { + return errors.New("no metrics collected") + } + + return nil +} + +func (s *SystemdUnits) Charts() *module.Charts { + return s.charts +} + +func (s *SystemdUnits) Collect() map[string]int64 { + mx, err := s.collect() + if err != nil { + s.Error(err) + } + + if len(mx) == 0 { + return nil + } + return mx +} + +func (s *SystemdUnits) Cleanup() { + s.closeConnection() +} diff --git a/src/go/collectors/go.d.plugin/modules/systemdunits/systemdunits_test.go b/src/go/collectors/go.d.plugin/modules/systemdunits/systemdunits_test.go new file mode 100644 index 000000000..89c0a92dd --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/systemdunits/systemdunits_test.go @@ -0,0 +1,1156 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +//go:build linux +// +build linux + +package systemdunits + +import ( + "context" + "errors" + "fmt" + "os" + "path/filepath" + "slices" + "strings" + "testing" + + "github.com/netdata/netdata/go/go.d.plugin/agent/module" + + "github.com/coreos/go-systemd/v22/dbus" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var ( + dataConfigJSON, _ = os.ReadFile("testdata/config.json") + dataConfigYAML, _ = os.ReadFile("testdata/config.yaml") +) + +func Test_testDataIsValid(t *testing.T) { + for name, data := range map[string][]byte{ + "dataConfigJSON": dataConfigJSON, + "dataConfigYAML": dataConfigYAML, + } { + require.NotNil(t, data, name) + } +} + +func TestSystemdUnits_ConfigurationSerialize(t *testing.T) { + module.TestConfigurationSerialize(t, &SystemdUnits{}, dataConfigJSON, dataConfigYAML) +} + +func TestSystemdUnits_Init(t *testing.T) { + tests := map[string]struct { + config Config + wantFail bool + }{ + "success on default config": { + config: New().Config, + }, + "success when 'include' option set": { + config: Config{ + Include: []string{"*"}, + }, + }, + "fails when 'include' option not set": { + wantFail: true, + config: Config{Include: []string{}}, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + systemd := New() + systemd.Config = test.config + + if test.wantFail { + assert.Error(t, systemd.Init()) + } else { + assert.NoError(t, systemd.Init()) + } + }) + } +} + +func TestSystemdUnits_Check(t *testing.T) { + tests := map[string]struct { + prepare func() *SystemdUnits + wantFail bool + }{ + "success on systemd v230+": { + prepare: func() *SystemdUnits { + systemd := New() + systemd.Include = []string{"*"} + systemd.client = prepareOKClient(230) + return systemd + }, + }, + "success on systemd v230-": { + prepare: func() *SystemdUnits { + systemd := New() + systemd.Include = []string{"*"} + systemd.client = prepareOKClient(220) + return systemd + }, + }, + "fails when all unites are filtered": { + wantFail: true, + prepare: func() *SystemdUnits { + systemd := New() + systemd.Include = []string{"*.not_exists"} + systemd.client = prepareOKClient(230) + return systemd + }, + }, + "fails on error on connect": { + wantFail: true, + prepare: func() *SystemdUnits { + systemd := New() + systemd.client = prepareClientErrOnConnect() + return systemd + }, + }, + "fails on error on get manager property": { + wantFail: true, + prepare: func() *SystemdUnits { + systemd := New() + systemd.client = prepareClientErrOnGetManagerProperty() + return systemd + }, + }, + "fails on error on list units": { + wantFail: true, + prepare: func() *SystemdUnits { + systemd := New() + systemd.client = prepareClientErrOnListUnits() + return systemd + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + systemd := test.prepare() + require.NoError(t, systemd.Init()) + + if test.wantFail { + assert.Error(t, systemd.Check()) + } else { + assert.NoError(t, systemd.Check()) + } + }) + } +} + +func TestSystemdUnits_Charts(t *testing.T) { + systemd := New() + require.NoError(t, systemd.Init()) + assert.NotNil(t, systemd.Charts()) +} + +func TestSystemdUnits_Cleanup(t *testing.T) { + systemd := New() + systemd.Include = []string{"*"} + client := prepareOKClient(230) + systemd.client = client + + require.NoError(t, systemd.Init()) + require.NotNil(t, systemd.Collect()) + conn := systemd.conn + systemd.Cleanup() + + assert.Nil(t, systemd.conn) + v, _ := conn.(*mockConn) + assert.True(t, v.closeCalled) +} + +func TestSystemdUnits_Collect(t *testing.T) { + tests := map[string]struct { + prepare func() *SystemdUnits + wantCollected map[string]int64 + }{ + "success v230+ on collecting all unit type": { + prepare: func() *SystemdUnits { + systemd := New() + systemd.Include = []string{"*"} + systemd.client = prepareOKClient(230) + return systemd + }, + wantCollected: map[string]int64{ + "unit_dbus_socket_state_activating": 0, + "unit_dbus_socket_state_active": 1, + "unit_dbus_socket_state_deactivating": 0, + "unit_dbus_socket_state_failed": 0, + "unit_dbus_socket_state_inactive": 0, + "unit_dev-disk-by-uuid-DE44-CEE0_device_state_activating": 0, + "unit_dev-disk-by-uuid-DE44-CEE0_device_state_active": 1, + "unit_dev-disk-by-uuid-DE44-CEE0_device_state_deactivating": 0, + "unit_dev-disk-by-uuid-DE44-CEE0_device_state_failed": 0, + "unit_dev-disk-by-uuid-DE44-CEE0_device_state_inactive": 0, + "unit_dev-nvme0n1_device_state_activating": 0, + "unit_dev-nvme0n1_device_state_active": 1, + "unit_dev-nvme0n1_device_state_deactivating": 0, + "unit_dev-nvme0n1_device_state_failed": 0, + "unit_dev-nvme0n1_device_state_inactive": 0, + "unit_docker_socket_state_activating": 0, + "unit_docker_socket_state_active": 1, + "unit_docker_socket_state_deactivating": 0, + "unit_docker_socket_state_failed": 0, + "unit_docker_socket_state_inactive": 0, + "unit_getty-pre_target_state_activating": 0, + "unit_getty-pre_target_state_active": 0, + "unit_getty-pre_target_state_deactivating": 0, + "unit_getty-pre_target_state_failed": 0, + "unit_getty-pre_target_state_inactive": 1, + "unit_init_scope_state_activating": 0, + "unit_init_scope_state_active": 1, + "unit_init_scope_state_deactivating": 0, + "unit_init_scope_state_failed": 0, + "unit_init_scope_state_inactive": 0, + "unit_logrotate_timer_state_activating": 0, + "unit_logrotate_timer_state_active": 1, + "unit_logrotate_timer_state_deactivating": 0, + "unit_logrotate_timer_state_failed": 0, + "unit_logrotate_timer_state_inactive": 0, + "unit_lvm2-lvmetad_socket_state_activating": 0, + "unit_lvm2-lvmetad_socket_state_active": 1, + "unit_lvm2-lvmetad_socket_state_deactivating": 0, + "unit_lvm2-lvmetad_socket_state_failed": 0, + "unit_lvm2-lvmetad_socket_state_inactive": 0, + "unit_lvm2-lvmpolld_socket_state_activating": 0, + "unit_lvm2-lvmpolld_socket_state_active": 1, + "unit_lvm2-lvmpolld_socket_state_deactivating": 0, + "unit_lvm2-lvmpolld_socket_state_failed": 0, + "unit_lvm2-lvmpolld_socket_state_inactive": 0, + "unit_man-db_timer_state_activating": 0, + "unit_man-db_timer_state_active": 1, + "unit_man-db_timer_state_deactivating": 0, + "unit_man-db_timer_state_failed": 0, + "unit_man-db_timer_state_inactive": 0, + "unit_org.cups.cupsd_path_state_activating": 0, + "unit_org.cups.cupsd_path_state_active": 1, + "unit_org.cups.cupsd_path_state_deactivating": 0, + "unit_org.cups.cupsd_path_state_failed": 0, + "unit_org.cups.cupsd_path_state_inactive": 0, + "unit_pamac-cleancache_timer_state_activating": 0, + "unit_pamac-cleancache_timer_state_active": 1, + "unit_pamac-cleancache_timer_state_deactivating": 0, + "unit_pamac-cleancache_timer_state_failed": 0, + "unit_pamac-cleancache_timer_state_inactive": 0, + "unit_pamac-mirrorlist_timer_state_activating": 0, + "unit_pamac-mirrorlist_timer_state_active": 1, + "unit_pamac-mirrorlist_timer_state_deactivating": 0, + "unit_pamac-mirrorlist_timer_state_failed": 0, + "unit_pamac-mirrorlist_timer_state_inactive": 0, + "unit_proc-sys-fs-binfmt_misc_automount_state_activating": 0, + "unit_proc-sys-fs-binfmt_misc_automount_state_active": 1, + "unit_proc-sys-fs-binfmt_misc_automount_state_deactivating": 0, + "unit_proc-sys-fs-binfmt_misc_automount_state_failed": 0, + "unit_proc-sys-fs-binfmt_misc_automount_state_inactive": 0, + "unit_remote-fs-pre_target_state_activating": 0, + "unit_remote-fs-pre_target_state_active": 0, + "unit_remote-fs-pre_target_state_deactivating": 0, + "unit_remote-fs-pre_target_state_failed": 0, + "unit_remote-fs-pre_target_state_inactive": 1, + "unit_rpc_pipefs_target_state_activating": 0, + "unit_rpc_pipefs_target_state_active": 0, + "unit_rpc_pipefs_target_state_deactivating": 0, + "unit_rpc_pipefs_target_state_failed": 0, + "unit_rpc_pipefs_target_state_inactive": 1, + "unit_run-user-1000-gvfs_mount_state_activating": 0, + "unit_run-user-1000-gvfs_mount_state_active": 1, + "unit_run-user-1000-gvfs_mount_state_deactivating": 0, + "unit_run-user-1000-gvfs_mount_state_failed": 0, + "unit_run-user-1000-gvfs_mount_state_inactive": 0, + "unit_run-user-1000_mount_state_activating": 0, + "unit_run-user-1000_mount_state_active": 1, + "unit_run-user-1000_mount_state_deactivating": 0, + "unit_run-user-1000_mount_state_failed": 0, + "unit_run-user-1000_mount_state_inactive": 0, + "unit_session-1_scope_state_activating": 0, + "unit_session-1_scope_state_active": 1, + "unit_session-1_scope_state_deactivating": 0, + "unit_session-1_scope_state_failed": 0, + "unit_session-1_scope_state_inactive": 0, + "unit_session-2_scope_state_activating": 0, + "unit_session-2_scope_state_active": 1, + "unit_session-2_scope_state_deactivating": 0, + "unit_session-2_scope_state_failed": 0, + "unit_session-2_scope_state_inactive": 0, + "unit_session-3_scope_state_activating": 0, + "unit_session-3_scope_state_active": 1, + "unit_session-3_scope_state_deactivating": 0, + "unit_session-3_scope_state_failed": 0, + "unit_session-3_scope_state_inactive": 0, + "unit_session-6_scope_state_activating": 0, + "unit_session-6_scope_state_active": 1, + "unit_session-6_scope_state_deactivating": 0, + "unit_session-6_scope_state_failed": 0, + "unit_session-6_scope_state_inactive": 0, + "unit_shadow_timer_state_activating": 0, + "unit_shadow_timer_state_active": 1, + "unit_shadow_timer_state_deactivating": 0, + "unit_shadow_timer_state_failed": 0, + "unit_shadow_timer_state_inactive": 0, + "unit_sound_target_state_activating": 0, + "unit_sound_target_state_active": 1, + "unit_sound_target_state_deactivating": 0, + "unit_sound_target_state_failed": 0, + "unit_sound_target_state_inactive": 0, + "unit_sys-devices-virtual-net-loopback1_device_state_activating": 0, + "unit_sys-devices-virtual-net-loopback1_device_state_active": 1, + "unit_sys-devices-virtual-net-loopback1_device_state_deactivating": 0, + "unit_sys-devices-virtual-net-loopback1_device_state_failed": 0, + "unit_sys-devices-virtual-net-loopback1_device_state_inactive": 0, + "unit_sys-module-fuse_device_state_activating": 0, + "unit_sys-module-fuse_device_state_active": 1, + "unit_sys-module-fuse_device_state_deactivating": 0, + "unit_sys-module-fuse_device_state_failed": 0, + "unit_sys-module-fuse_device_state_inactive": 0, + "unit_sysinit_target_state_activating": 0, + "unit_sysinit_target_state_active": 1, + "unit_sysinit_target_state_deactivating": 0, + "unit_sysinit_target_state_failed": 0, + "unit_sysinit_target_state_inactive": 0, + "unit_system-getty_slice_state_activating": 0, + "unit_system-getty_slice_state_active": 1, + "unit_system-getty_slice_state_deactivating": 0, + "unit_system-getty_slice_state_failed": 0, + "unit_system-getty_slice_state_inactive": 0, + "unit_system-netctl_slice_state_activating": 0, + "unit_system-netctl_slice_state_active": 1, + "unit_system-netctl_slice_state_deactivating": 0, + "unit_system-netctl_slice_state_failed": 0, + "unit_system-netctl_slice_state_inactive": 0, + "unit_system-systemd-fsck_slice_state_activating": 0, + "unit_system-systemd-fsck_slice_state_active": 1, + "unit_system-systemd-fsck_slice_state_deactivating": 0, + "unit_system-systemd-fsck_slice_state_failed": 0, + "unit_system-systemd-fsck_slice_state_inactive": 0, + "unit_system_slice_state_activating": 0, + "unit_system_slice_state_active": 1, + "unit_system_slice_state_deactivating": 0, + "unit_system_slice_state_failed": 0, + "unit_system_slice_state_inactive": 0, + "unit_systemd-ask-password-console_path_state_activating": 0, + "unit_systemd-ask-password-console_path_state_active": 1, + "unit_systemd-ask-password-console_path_state_deactivating": 0, + "unit_systemd-ask-password-console_path_state_failed": 0, + "unit_systemd-ask-password-console_path_state_inactive": 0, + "unit_systemd-ask-password-wall_path_state_activating": 0, + "unit_systemd-ask-password-wall_path_state_active": 1, + "unit_systemd-ask-password-wall_path_state_deactivating": 0, + "unit_systemd-ask-password-wall_path_state_failed": 0, + "unit_systemd-ask-password-wall_path_state_inactive": 0, + "unit_systemd-ask-password-wall_service_state_activating": 0, + "unit_systemd-ask-password-wall_service_state_active": 0, + "unit_systemd-ask-password-wall_service_state_deactivating": 0, + "unit_systemd-ask-password-wall_service_state_failed": 0, + "unit_systemd-ask-password-wall_service_state_inactive": 1, + "unit_systemd-fsck-root_service_state_activating": 0, + "unit_systemd-fsck-root_service_state_active": 0, + "unit_systemd-fsck-root_service_state_deactivating": 0, + "unit_systemd-fsck-root_service_state_failed": 0, + "unit_systemd-fsck-root_service_state_inactive": 1, + "unit_systemd-udevd-kernel_socket_state_activating": 0, + "unit_systemd-udevd-kernel_socket_state_active": 1, + "unit_systemd-udevd-kernel_socket_state_deactivating": 0, + "unit_systemd-udevd-kernel_socket_state_failed": 0, + "unit_systemd-udevd-kernel_socket_state_inactive": 0, + "unit_tmp_mount_state_activating": 0, + "unit_tmp_mount_state_active": 1, + "unit_tmp_mount_state_deactivating": 0, + "unit_tmp_mount_state_failed": 0, + "unit_tmp_mount_state_inactive": 0, + "unit_user-runtime-dir@1000_service_state_activating": 0, + "unit_user-runtime-dir@1000_service_state_active": 1, + "unit_user-runtime-dir@1000_service_state_deactivating": 0, + "unit_user-runtime-dir@1000_service_state_failed": 0, + "unit_user-runtime-dir@1000_service_state_inactive": 0, + "unit_user@1000_service_state_activating": 0, + "unit_user@1000_service_state_active": 1, + "unit_user@1000_service_state_deactivating": 0, + "unit_user@1000_service_state_failed": 0, + "unit_user@1000_service_state_inactive": 0, + "unit_user_slice_state_activating": 0, + "unit_user_slice_state_active": 1, + "unit_user_slice_state_deactivating": 0, + "unit_user_slice_state_failed": 0, + "unit_user_slice_state_inactive": 0, + "unit_var-lib-nfs-rpc_pipefs_mount_state_activating": 0, + "unit_var-lib-nfs-rpc_pipefs_mount_state_active": 0, + "unit_var-lib-nfs-rpc_pipefs_mount_state_deactivating": 0, + "unit_var-lib-nfs-rpc_pipefs_mount_state_failed": 0, + "unit_var-lib-nfs-rpc_pipefs_mount_state_inactive": 1, + }, + }, + "success v230+ on collecting all unit type with skip transient": { + prepare: func() *SystemdUnits { + systemd := New() + systemd.Include = []string{"*"} + systemd.SkipTransient = true + systemd.client = prepareOKClient(230) + return systemd + }, + wantCollected: map[string]int64{ + "unit_systemd-ask-password-wall_service_state_activating": 0, + "unit_systemd-ask-password-wall_service_state_active": 0, + "unit_systemd-ask-password-wall_service_state_deactivating": 0, + "unit_systemd-ask-password-wall_service_state_failed": 0, + "unit_systemd-ask-password-wall_service_state_inactive": 1, + "unit_systemd-fsck-root_service_state_activating": 0, + "unit_systemd-fsck-root_service_state_active": 0, + "unit_systemd-fsck-root_service_state_deactivating": 0, + "unit_systemd-fsck-root_service_state_failed": 0, + "unit_systemd-fsck-root_service_state_inactive": 1, + "unit_user-runtime-dir@1000_service_state_activating": 0, + "unit_user-runtime-dir@1000_service_state_active": 1, + "unit_user-runtime-dir@1000_service_state_deactivating": 0, + "unit_user-runtime-dir@1000_service_state_failed": 0, + "unit_user-runtime-dir@1000_service_state_inactive": 0, + "unit_user@1000_service_state_activating": 0, + "unit_user@1000_service_state_active": 1, + "unit_user@1000_service_state_deactivating": 0, + "unit_user@1000_service_state_failed": 0, + "unit_user@1000_service_state_inactive": 0, + }, + }, + "success v230- on collecting all unit types": { + prepare: func() *SystemdUnits { + systemd := New() + systemd.Include = []string{"*"} + systemd.client = prepareOKClient(220) + return systemd + }, + wantCollected: map[string]int64{ + "unit_dbus_socket_state_activating": 0, + "unit_dbus_socket_state_active": 1, + "unit_dbus_socket_state_deactivating": 0, + "unit_dbus_socket_state_failed": 0, + "unit_dbus_socket_state_inactive": 0, + "unit_dev-disk-by-uuid-DE44-CEE0_device_state_activating": 0, + "unit_dev-disk-by-uuid-DE44-CEE0_device_state_active": 1, + "unit_dev-disk-by-uuid-DE44-CEE0_device_state_deactivating": 0, + "unit_dev-disk-by-uuid-DE44-CEE0_device_state_failed": 0, + "unit_dev-disk-by-uuid-DE44-CEE0_device_state_inactive": 0, + "unit_dev-nvme0n1_device_state_activating": 0, + "unit_dev-nvme0n1_device_state_active": 1, + "unit_dev-nvme0n1_device_state_deactivating": 0, + "unit_dev-nvme0n1_device_state_failed": 0, + "unit_dev-nvme0n1_device_state_inactive": 0, + "unit_docker_socket_state_activating": 0, + "unit_docker_socket_state_active": 1, + "unit_docker_socket_state_deactivating": 0, + "unit_docker_socket_state_failed": 0, + "unit_docker_socket_state_inactive": 0, + "unit_getty-pre_target_state_activating": 0, + "unit_getty-pre_target_state_active": 0, + "unit_getty-pre_target_state_deactivating": 0, + "unit_getty-pre_target_state_failed": 0, + "unit_getty-pre_target_state_inactive": 1, + "unit_init_scope_state_activating": 0, + "unit_init_scope_state_active": 1, + "unit_init_scope_state_deactivating": 0, + "unit_init_scope_state_failed": 0, + "unit_init_scope_state_inactive": 0, + "unit_logrotate_timer_state_activating": 0, + "unit_logrotate_timer_state_active": 1, + "unit_logrotate_timer_state_deactivating": 0, + "unit_logrotate_timer_state_failed": 0, + "unit_logrotate_timer_state_inactive": 0, + "unit_lvm2-lvmetad_socket_state_activating": 0, + "unit_lvm2-lvmetad_socket_state_active": 1, + "unit_lvm2-lvmetad_socket_state_deactivating": 0, + "unit_lvm2-lvmetad_socket_state_failed": 0, + "unit_lvm2-lvmetad_socket_state_inactive": 0, + "unit_lvm2-lvmpolld_socket_state_activating": 0, + "unit_lvm2-lvmpolld_socket_state_active": 1, + "unit_lvm2-lvmpolld_socket_state_deactivating": 0, + "unit_lvm2-lvmpolld_socket_state_failed": 0, + "unit_lvm2-lvmpolld_socket_state_inactive": 0, + "unit_man-db_timer_state_activating": 0, + "unit_man-db_timer_state_active": 1, + "unit_man-db_timer_state_deactivating": 0, + "unit_man-db_timer_state_failed": 0, + "unit_man-db_timer_state_inactive": 0, + "unit_org.cups.cupsd_path_state_activating": 0, + "unit_org.cups.cupsd_path_state_active": 1, + "unit_org.cups.cupsd_path_state_deactivating": 0, + "unit_org.cups.cupsd_path_state_failed": 0, + "unit_org.cups.cupsd_path_state_inactive": 0, + "unit_pamac-cleancache_timer_state_activating": 0, + "unit_pamac-cleancache_timer_state_active": 1, + "unit_pamac-cleancache_timer_state_deactivating": 0, + "unit_pamac-cleancache_timer_state_failed": 0, + "unit_pamac-cleancache_timer_state_inactive": 0, + "unit_pamac-mirrorlist_timer_state_activating": 0, + "unit_pamac-mirrorlist_timer_state_active": 1, + "unit_pamac-mirrorlist_timer_state_deactivating": 0, + "unit_pamac-mirrorlist_timer_state_failed": 0, + "unit_pamac-mirrorlist_timer_state_inactive": 0, + "unit_proc-sys-fs-binfmt_misc_automount_state_activating": 0, + "unit_proc-sys-fs-binfmt_misc_automount_state_active": 1, + "unit_proc-sys-fs-binfmt_misc_automount_state_deactivating": 0, + "unit_proc-sys-fs-binfmt_misc_automount_state_failed": 0, + "unit_proc-sys-fs-binfmt_misc_automount_state_inactive": 0, + "unit_remote-fs-pre_target_state_activating": 0, + "unit_remote-fs-pre_target_state_active": 0, + "unit_remote-fs-pre_target_state_deactivating": 0, + "unit_remote-fs-pre_target_state_failed": 0, + "unit_remote-fs-pre_target_state_inactive": 1, + "unit_rpc_pipefs_target_state_activating": 0, + "unit_rpc_pipefs_target_state_active": 0, + "unit_rpc_pipefs_target_state_deactivating": 0, + "unit_rpc_pipefs_target_state_failed": 0, + "unit_rpc_pipefs_target_state_inactive": 1, + "unit_run-user-1000-gvfs_mount_state_activating": 0, + "unit_run-user-1000-gvfs_mount_state_active": 1, + "unit_run-user-1000-gvfs_mount_state_deactivating": 0, + "unit_run-user-1000-gvfs_mount_state_failed": 0, + "unit_run-user-1000-gvfs_mount_state_inactive": 0, + "unit_run-user-1000_mount_state_activating": 0, + "unit_run-user-1000_mount_state_active": 1, + "unit_run-user-1000_mount_state_deactivating": 0, + "unit_run-user-1000_mount_state_failed": 0, + "unit_run-user-1000_mount_state_inactive": 0, + "unit_session-1_scope_state_activating": 0, + "unit_session-1_scope_state_active": 1, + "unit_session-1_scope_state_deactivating": 0, + "unit_session-1_scope_state_failed": 0, + "unit_session-1_scope_state_inactive": 0, + "unit_session-2_scope_state_activating": 0, + "unit_session-2_scope_state_active": 1, + "unit_session-2_scope_state_deactivating": 0, + "unit_session-2_scope_state_failed": 0, + "unit_session-2_scope_state_inactive": 0, + "unit_session-3_scope_state_activating": 0, + "unit_session-3_scope_state_active": 1, + "unit_session-3_scope_state_deactivating": 0, + "unit_session-3_scope_state_failed": 0, + "unit_session-3_scope_state_inactive": 0, + "unit_session-6_scope_state_activating": 0, + "unit_session-6_scope_state_active": 1, + "unit_session-6_scope_state_deactivating": 0, + "unit_session-6_scope_state_failed": 0, + "unit_session-6_scope_state_inactive": 0, + "unit_shadow_timer_state_activating": 0, + "unit_shadow_timer_state_active": 1, + "unit_shadow_timer_state_deactivating": 0, + "unit_shadow_timer_state_failed": 0, + "unit_shadow_timer_state_inactive": 0, + "unit_sound_target_state_activating": 0, + "unit_sound_target_state_active": 1, + "unit_sound_target_state_deactivating": 0, + "unit_sound_target_state_failed": 0, + "unit_sound_target_state_inactive": 0, + "unit_sys-devices-virtual-net-loopback1_device_state_activating": 0, + "unit_sys-devices-virtual-net-loopback1_device_state_active": 1, + "unit_sys-devices-virtual-net-loopback1_device_state_deactivating": 0, + "unit_sys-devices-virtual-net-loopback1_device_state_failed": 0, + "unit_sys-devices-virtual-net-loopback1_device_state_inactive": 0, + "unit_sys-module-fuse_device_state_activating": 0, + "unit_sys-module-fuse_device_state_active": 1, + "unit_sys-module-fuse_device_state_deactivating": 0, + "unit_sys-module-fuse_device_state_failed": 0, + "unit_sys-module-fuse_device_state_inactive": 0, + "unit_sysinit_target_state_activating": 0, + "unit_sysinit_target_state_active": 1, + "unit_sysinit_target_state_deactivating": 0, + "unit_sysinit_target_state_failed": 0, + "unit_sysinit_target_state_inactive": 0, + "unit_system-getty_slice_state_activating": 0, + "unit_system-getty_slice_state_active": 1, + "unit_system-getty_slice_state_deactivating": 0, + "unit_system-getty_slice_state_failed": 0, + "unit_system-getty_slice_state_inactive": 0, + "unit_system-netctl_slice_state_activating": 0, + "unit_system-netctl_slice_state_active": 1, + "unit_system-netctl_slice_state_deactivating": 0, + "unit_system-netctl_slice_state_failed": 0, + "unit_system-netctl_slice_state_inactive": 0, + "unit_system-systemd-fsck_slice_state_activating": 0, + "unit_system-systemd-fsck_slice_state_active": 1, + "unit_system-systemd-fsck_slice_state_deactivating": 0, + "unit_system-systemd-fsck_slice_state_failed": 0, + "unit_system-systemd-fsck_slice_state_inactive": 0, + "unit_system_slice_state_activating": 0, + "unit_system_slice_state_active": 1, + "unit_system_slice_state_deactivating": 0, + "unit_system_slice_state_failed": 0, + "unit_system_slice_state_inactive": 0, + "unit_systemd-ask-password-console_path_state_activating": 0, + "unit_systemd-ask-password-console_path_state_active": 1, + "unit_systemd-ask-password-console_path_state_deactivating": 0, + "unit_systemd-ask-password-console_path_state_failed": 0, + "unit_systemd-ask-password-console_path_state_inactive": 0, + "unit_systemd-ask-password-wall_path_state_activating": 0, + "unit_systemd-ask-password-wall_path_state_active": 1, + "unit_systemd-ask-password-wall_path_state_deactivating": 0, + "unit_systemd-ask-password-wall_path_state_failed": 0, + "unit_systemd-ask-password-wall_path_state_inactive": 0, + "unit_systemd-ask-password-wall_service_state_activating": 0, + "unit_systemd-ask-password-wall_service_state_active": 0, + "unit_systemd-ask-password-wall_service_state_deactivating": 0, + "unit_systemd-ask-password-wall_service_state_failed": 0, + "unit_systemd-ask-password-wall_service_state_inactive": 1, + "unit_systemd-fsck-root_service_state_activating": 0, + "unit_systemd-fsck-root_service_state_active": 0, + "unit_systemd-fsck-root_service_state_deactivating": 0, + "unit_systemd-fsck-root_service_state_failed": 0, + "unit_systemd-fsck-root_service_state_inactive": 1, + "unit_systemd-udevd-kernel_socket_state_activating": 0, + "unit_systemd-udevd-kernel_socket_state_active": 1, + "unit_systemd-udevd-kernel_socket_state_deactivating": 0, + "unit_systemd-udevd-kernel_socket_state_failed": 0, + "unit_systemd-udevd-kernel_socket_state_inactive": 0, + "unit_tmp_mount_state_activating": 0, + "unit_tmp_mount_state_active": 1, + "unit_tmp_mount_state_deactivating": 0, + "unit_tmp_mount_state_failed": 0, + "unit_tmp_mount_state_inactive": 0, + "unit_user-runtime-dir@1000_service_state_activating": 0, + "unit_user-runtime-dir@1000_service_state_active": 1, + "unit_user-runtime-dir@1000_service_state_deactivating": 0, + "unit_user-runtime-dir@1000_service_state_failed": 0, + "unit_user-runtime-dir@1000_service_state_inactive": 0, + "unit_user@1000_service_state_activating": 0, + "unit_user@1000_service_state_active": 1, + "unit_user@1000_service_state_deactivating": 0, + "unit_user@1000_service_state_failed": 0, + "unit_user@1000_service_state_inactive": 0, + "unit_user_slice_state_activating": 0, + "unit_user_slice_state_active": 1, + "unit_user_slice_state_deactivating": 0, + "unit_user_slice_state_failed": 0, + "unit_user_slice_state_inactive": 0, + "unit_var-lib-nfs-rpc_pipefs_mount_state_activating": 0, + "unit_var-lib-nfs-rpc_pipefs_mount_state_active": 0, + "unit_var-lib-nfs-rpc_pipefs_mount_state_deactivating": 0, + "unit_var-lib-nfs-rpc_pipefs_mount_state_failed": 0, + "unit_var-lib-nfs-rpc_pipefs_mount_state_inactive": 1, + }, + }, + "success v230+ on collecting only 'service' units": { + prepare: func() *SystemdUnits { + systemd := New() + systemd.Include = []string{"*.service"} + systemd.client = prepareOKClient(230) + return systemd + }, + wantCollected: map[string]int64{ + "unit_systemd-ask-password-wall_service_state_activating": 0, + "unit_systemd-ask-password-wall_service_state_active": 0, + "unit_systemd-ask-password-wall_service_state_deactivating": 0, + "unit_systemd-ask-password-wall_service_state_failed": 0, + "unit_systemd-ask-password-wall_service_state_inactive": 1, + "unit_systemd-fsck-root_service_state_activating": 0, + "unit_systemd-fsck-root_service_state_active": 0, + "unit_systemd-fsck-root_service_state_deactivating": 0, + "unit_systemd-fsck-root_service_state_failed": 0, + "unit_systemd-fsck-root_service_state_inactive": 1, + "unit_user-runtime-dir@1000_service_state_activating": 0, + "unit_user-runtime-dir@1000_service_state_active": 1, + "unit_user-runtime-dir@1000_service_state_deactivating": 0, + "unit_user-runtime-dir@1000_service_state_failed": 0, + "unit_user-runtime-dir@1000_service_state_inactive": 0, + "unit_user@1000_service_state_activating": 0, + "unit_user@1000_service_state_active": 1, + "unit_user@1000_service_state_deactivating": 0, + "unit_user@1000_service_state_failed": 0, + "unit_user@1000_service_state_inactive": 0, + }, + }, + "success v230- on collecting only 'service' units": { + prepare: func() *SystemdUnits { + systemd := New() + systemd.Include = []string{"*.service"} + systemd.client = prepareOKClient(220) + return systemd + }, + wantCollected: map[string]int64{ + "unit_systemd-ask-password-wall_service_state_activating": 0, + "unit_systemd-ask-password-wall_service_state_active": 0, + "unit_systemd-ask-password-wall_service_state_deactivating": 0, + "unit_systemd-ask-password-wall_service_state_failed": 0, + "unit_systemd-ask-password-wall_service_state_inactive": 1, + "unit_systemd-fsck-root_service_state_activating": 0, + "unit_systemd-fsck-root_service_state_active": 0, + "unit_systemd-fsck-root_service_state_deactivating": 0, + "unit_systemd-fsck-root_service_state_failed": 0, + "unit_systemd-fsck-root_service_state_inactive": 1, + "unit_user-runtime-dir@1000_service_state_activating": 0, + "unit_user-runtime-dir@1000_service_state_active": 1, + "unit_user-runtime-dir@1000_service_state_deactivating": 0, + "unit_user-runtime-dir@1000_service_state_failed": 0, + "unit_user-runtime-dir@1000_service_state_inactive": 0, + "unit_user@1000_service_state_activating": 0, + "unit_user@1000_service_state_active": 1, + "unit_user@1000_service_state_deactivating": 0, + "unit_user@1000_service_state_failed": 0, + "unit_user@1000_service_state_inactive": 0, + }, + }, + "success v230+ on collecting only 'service' units and files": { + prepare: func() *SystemdUnits { + systemd := New() + systemd.Include = []string{"*.service"} + systemd.IncludeUnitFiles = []string{"*.service", "*.slice"} + systemd.CollectUnitFiles = true + systemd.client = prepareOKClient(230) + return systemd + }, + wantCollected: map[string]int64{ + "unit_file_/lib/systemd/system/machine.slice_state_alias": 0, + "unit_file_/lib/systemd/system/machine.slice_state_bad": 0, + "unit_file_/lib/systemd/system/machine.slice_state_disabled": 0, + "unit_file_/lib/systemd/system/machine.slice_state_enabled": 0, + "unit_file_/lib/systemd/system/machine.slice_state_enabled-runtime": 0, + "unit_file_/lib/systemd/system/machine.slice_state_generated": 0, + "unit_file_/lib/systemd/system/machine.slice_state_indirect": 0, + "unit_file_/lib/systemd/system/machine.slice_state_linked": 0, + "unit_file_/lib/systemd/system/machine.slice_state_linked-runtime": 0, + "unit_file_/lib/systemd/system/machine.slice_state_masked": 0, + "unit_file_/lib/systemd/system/machine.slice_state_masked-runtime": 0, + "unit_file_/lib/systemd/system/machine.slice_state_static": 1, + "unit_file_/lib/systemd/system/machine.slice_state_transient": 0, + "unit_file_/lib/systemd/system/system-systemd-cryptsetup.slice_state_alias": 0, + "unit_file_/lib/systemd/system/system-systemd-cryptsetup.slice_state_bad": 0, + "unit_file_/lib/systemd/system/system-systemd-cryptsetup.slice_state_disabled": 0, + "unit_file_/lib/systemd/system/system-systemd-cryptsetup.slice_state_enabled": 0, + "unit_file_/lib/systemd/system/system-systemd-cryptsetup.slice_state_enabled-runtime": 0, + "unit_file_/lib/systemd/system/system-systemd-cryptsetup.slice_state_generated": 0, + "unit_file_/lib/systemd/system/system-systemd-cryptsetup.slice_state_indirect": 0, + "unit_file_/lib/systemd/system/system-systemd-cryptsetup.slice_state_linked": 0, + "unit_file_/lib/systemd/system/system-systemd-cryptsetup.slice_state_linked-runtime": 0, + "unit_file_/lib/systemd/system/system-systemd-cryptsetup.slice_state_masked": 0, + "unit_file_/lib/systemd/system/system-systemd-cryptsetup.slice_state_masked-runtime": 0, + "unit_file_/lib/systemd/system/system-systemd-cryptsetup.slice_state_static": 1, + "unit_file_/lib/systemd/system/system-systemd-cryptsetup.slice_state_transient": 0, + "unit_file_/lib/systemd/system/user.slice_state_alias": 0, + "unit_file_/lib/systemd/system/user.slice_state_bad": 0, + "unit_file_/lib/systemd/system/user.slice_state_disabled": 0, + "unit_file_/lib/systemd/system/user.slice_state_enabled": 0, + "unit_file_/lib/systemd/system/user.slice_state_enabled-runtime": 0, + "unit_file_/lib/systemd/system/user.slice_state_generated": 0, + "unit_file_/lib/systemd/system/user.slice_state_indirect": 0, + "unit_file_/lib/systemd/system/user.slice_state_linked": 0, + "unit_file_/lib/systemd/system/user.slice_state_linked-runtime": 0, + "unit_file_/lib/systemd/system/user.slice_state_masked": 0, + "unit_file_/lib/systemd/system/user.slice_state_masked-runtime": 0, + "unit_file_/lib/systemd/system/user.slice_state_static": 1, + "unit_file_/lib/systemd/system/user.slice_state_transient": 0, + "unit_file_/lib/systemd/system/uuidd.service_state_alias": 0, + "unit_file_/lib/systemd/system/uuidd.service_state_bad": 0, + "unit_file_/lib/systemd/system/uuidd.service_state_disabled": 0, + "unit_file_/lib/systemd/system/uuidd.service_state_enabled": 0, + "unit_file_/lib/systemd/system/uuidd.service_state_enabled-runtime": 0, + "unit_file_/lib/systemd/system/uuidd.service_state_generated": 0, + "unit_file_/lib/systemd/system/uuidd.service_state_indirect": 1, + "unit_file_/lib/systemd/system/uuidd.service_state_linked": 0, + "unit_file_/lib/systemd/system/uuidd.service_state_linked-runtime": 0, + "unit_file_/lib/systemd/system/uuidd.service_state_masked": 0, + "unit_file_/lib/systemd/system/uuidd.service_state_masked-runtime": 0, + "unit_file_/lib/systemd/system/uuidd.service_state_static": 0, + "unit_file_/lib/systemd/system/uuidd.service_state_transient": 0, + "unit_file_/lib/systemd/system/x11-common.service_state_alias": 0, + "unit_file_/lib/systemd/system/x11-common.service_state_bad": 0, + "unit_file_/lib/systemd/system/x11-common.service_state_disabled": 0, + "unit_file_/lib/systemd/system/x11-common.service_state_enabled": 0, + "unit_file_/lib/systemd/system/x11-common.service_state_enabled-runtime": 0, + "unit_file_/lib/systemd/system/x11-common.service_state_generated": 0, + "unit_file_/lib/systemd/system/x11-common.service_state_indirect": 0, + "unit_file_/lib/systemd/system/x11-common.service_state_linked": 0, + "unit_file_/lib/systemd/system/x11-common.service_state_linked-runtime": 0, + "unit_file_/lib/systemd/system/x11-common.service_state_masked": 1, + "unit_file_/lib/systemd/system/x11-common.service_state_masked-runtime": 0, + "unit_file_/lib/systemd/system/x11-common.service_state_static": 0, + "unit_file_/lib/systemd/system/x11-common.service_state_transient": 0, + "unit_file_/run/systemd/generator.late/monit.service_state_alias": 0, + "unit_file_/run/systemd/generator.late/monit.service_state_bad": 0, + "unit_file_/run/systemd/generator.late/monit.service_state_disabled": 0, + "unit_file_/run/systemd/generator.late/monit.service_state_enabled": 0, + "unit_file_/run/systemd/generator.late/monit.service_state_enabled-runtime": 0, + "unit_file_/run/systemd/generator.late/monit.service_state_generated": 1, + "unit_file_/run/systemd/generator.late/monit.service_state_indirect": 0, + "unit_file_/run/systemd/generator.late/monit.service_state_linked": 0, + "unit_file_/run/systemd/generator.late/monit.service_state_linked-runtime": 0, + "unit_file_/run/systemd/generator.late/monit.service_state_masked": 0, + "unit_file_/run/systemd/generator.late/monit.service_state_masked-runtime": 0, + "unit_file_/run/systemd/generator.late/monit.service_state_static": 0, + "unit_file_/run/systemd/generator.late/monit.service_state_transient": 0, + "unit_file_/run/systemd/generator.late/sendmail.service_state_alias": 0, + "unit_file_/run/systemd/generator.late/sendmail.service_state_bad": 0, + "unit_file_/run/systemd/generator.late/sendmail.service_state_disabled": 0, + "unit_file_/run/systemd/generator.late/sendmail.service_state_enabled": 0, + "unit_file_/run/systemd/generator.late/sendmail.service_state_enabled-runtime": 0, + "unit_file_/run/systemd/generator.late/sendmail.service_state_generated": 1, + "unit_file_/run/systemd/generator.late/sendmail.service_state_indirect": 0, + "unit_file_/run/systemd/generator.late/sendmail.service_state_linked": 0, + "unit_file_/run/systemd/generator.late/sendmail.service_state_linked-runtime": 0, + "unit_file_/run/systemd/generator.late/sendmail.service_state_masked": 0, + "unit_file_/run/systemd/generator.late/sendmail.service_state_masked-runtime": 0, + "unit_file_/run/systemd/generator.late/sendmail.service_state_static": 0, + "unit_file_/run/systemd/generator.late/sendmail.service_state_transient": 0, + "unit_systemd-ask-password-wall_service_state_activating": 0, + "unit_systemd-ask-password-wall_service_state_active": 0, + "unit_systemd-ask-password-wall_service_state_deactivating": 0, + "unit_systemd-ask-password-wall_service_state_failed": 0, + "unit_systemd-ask-password-wall_service_state_inactive": 1, + "unit_systemd-fsck-root_service_state_activating": 0, + "unit_systemd-fsck-root_service_state_active": 0, + "unit_systemd-fsck-root_service_state_deactivating": 0, + "unit_systemd-fsck-root_service_state_failed": 0, + "unit_systemd-fsck-root_service_state_inactive": 1, + "unit_user-runtime-dir@1000_service_state_activating": 0, + "unit_user-runtime-dir@1000_service_state_active": 1, + "unit_user-runtime-dir@1000_service_state_deactivating": 0, + "unit_user-runtime-dir@1000_service_state_failed": 0, + "unit_user-runtime-dir@1000_service_state_inactive": 0, + "unit_user@1000_service_state_activating": 0, + "unit_user@1000_service_state_active": 1, + "unit_user@1000_service_state_deactivating": 0, + "unit_user@1000_service_state_failed": 0, + "unit_user@1000_service_state_inactive": 0, + }, + }, + "fails when all unites are filtered": { + prepare: func() *SystemdUnits { + systemd := New() + systemd.Include = []string{"*.not_exists"} + systemd.client = prepareOKClient(230) + return systemd + }, + wantCollected: nil, + }, + "fails on error on connect": { + prepare: func() *SystemdUnits { + systemd := New() + systemd.client = prepareClientErrOnConnect() + return systemd + }, + wantCollected: nil, + }, + "fails on error on get manager property": { + prepare: func() *SystemdUnits { + systemd := New() + systemd.client = prepareClientErrOnGetManagerProperty() + return systemd + }, + wantCollected: nil, + }, + "fails on error on list units": { + prepare: func() *SystemdUnits { + systemd := New() + systemd.client = prepareClientErrOnListUnits() + return systemd + }, + wantCollected: nil, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + systemd := test.prepare() + require.NoError(t, systemd.Init()) + + var mx map[string]int64 + + for i := 0; i < 10; i++ { + mx = systemd.Collect() + } + + assert.Equal(t, test.wantCollected, mx) + if len(test.wantCollected) > 0 { + ensureCollectedHasAllChartsDimsVarsIDs(t, systemd, mx) + } + }) + } +} + +func TestSystemdUnits_connectionReuse(t *testing.T) { + systemd := New() + systemd.Include = []string{"*"} + client := prepareOKClient(230) + systemd.client = client + require.NoError(t, systemd.Init()) + + var collected map[string]int64 + for i := 0; i < 10; i++ { + collected = systemd.Collect() + } + + assert.NotEmpty(t, collected) + assert.Equal(t, 1, client.connectCalls) +} + +func ensureCollectedHasAllChartsDimsVarsIDs(t *testing.T, sd *SystemdUnits, collected map[string]int64) { + for _, chart := range *sd.Charts() { + if chart.Obsolete { + continue + } + for _, dim := range chart.Dims { + _, ok := collected[dim.ID] + assert.Truef(t, ok, "collected metrics has no data for dim '%s' chart '%s'", dim.ID, chart.ID) + } + for _, v := range chart.Vars { + _, ok := collected[v.ID] + assert.Truef(t, ok, "collected metrics has no data for var '%s' chart '%s'", v.ID, chart.ID) + } + } +} + +func prepareOKClient(ver int) *mockClient { + return &mockClient{ + conn: &mockConn{ + version: ver, + units: mockSystemdUnits, + unitFiles: mockSystemdUnitFiles, + }, + } +} + +func prepareClientErrOnConnect() *mockClient { + return &mockClient{ + errOnConnect: true, + } +} + +func prepareClientErrOnGetManagerProperty() *mockClient { + return &mockClient{ + conn: &mockConn{ + version: 230, + errOnGetManagerProperty: true, + units: mockSystemdUnits, + }, + } +} + +func prepareClientErrOnListUnits() *mockClient { + return &mockClient{ + conn: &mockConn{ + version: 230, + errOnListUnits: true, + units: mockSystemdUnits, + }, + } +} + +type mockClient struct { + conn systemdConnection + connectCalls int + errOnConnect bool +} + +func (m *mockClient) connect() (systemdConnection, error) { + m.connectCalls++ + if m.errOnConnect { + return nil, errors.New("mock 'connect' error") + } + return m.conn, nil +} + +type mockConn struct { + version int + errOnGetManagerProperty bool + + units []dbus.UnitStatus + errOnListUnits bool + + unitFiles []dbus.UnitFile + errOnListUnitFiles bool + + closeCalled bool +} + +func (m *mockConn) Close() { + m.closeCalled = true +} + +func (m *mockConn) GetManagerProperty(prop string) (string, error) { + if m.errOnGetManagerProperty { + return "", errors.New("'GetManagerProperty' call error") + } + if prop != versionProperty { + return "", fmt.Errorf("'GetManagerProperty' unkown property: %s", prop) + } + + return fmt.Sprintf("%d.6-1-manjaro", m.version), nil +} + +func (m *mockConn) GetUnitPropertyContext(_ context.Context, unit string, propertyName string) (*dbus.Property, error) { + if propertyName != transientProperty { + return nil, fmt.Errorf("'GetUnitProperty' unkown property name: %s", propertyName) + } + + var prop dbus.Property + + if strings.HasSuffix(unit, ".service") { + prop = dbus.PropDescription("false") + } else { + prop = dbus.PropDescription("true") + } + + prop.Name = propertyName + + return &prop, nil +} + +func (m *mockConn) ListUnitsContext(_ context.Context) ([]dbus.UnitStatus, error) { + if m.errOnListUnits { + return nil, errors.New("'ListUnits' call error") + } + if m.version >= 230 { + return nil, errors.New("'ListUnits' unsupported function error") + } + + return append([]dbus.UnitStatus{}, m.units...), nil +} + +func (m *mockConn) ListUnitsByPatternsContext(_ context.Context, _ []string, patterns []string) ([]dbus.UnitStatus, error) { + if m.errOnListUnits { + return nil, errors.New("'ListUnitsByPatterns' call error") + } + if m.version < 230 { + return nil, errors.New("'ListUnitsByPatterns' unsupported function error") + } + + if len(m.units) == 0 { + return nil, nil + } + + units := append([]dbus.UnitStatus{}, m.units...) + + units = slices.DeleteFunc(units, func(u dbus.UnitStatus) bool { + name := cleanUnitName(u.Name) + for _, p := range patterns { + if ok, _ := filepath.Match(p, name); ok { + return false + } + } + return true + }) + + return units, nil +} + +func (m *mockConn) ListUnitFilesByPatternsContext(_ context.Context, _ []string, patterns []string) ([]dbus.UnitFile, error) { + if m.errOnListUnitFiles { + return nil, errors.New("'ListUnitFilesByPatternsContex' call error") + } + if m.version < 230 { + return nil, errors.New("'ListUnitFilesByPatternsContex' unsupported function error") + } + + if len(m.unitFiles) == 0 { + return nil, nil + } + + unitFiles := append([]dbus.UnitFile{}, m.unitFiles...) + + unitFiles = slices.DeleteFunc(unitFiles, func(file dbus.UnitFile) bool { + _, name := filepath.Split(file.Path) + for _, p := range patterns { + if ok, _ := filepath.Match(p, name); ok { + return false + } + } + return true + }) + + return unitFiles, nil +} + +var mockSystemdUnits = []dbus.UnitStatus{ + {Name: `proc-sys-fs-binfmt_misc.automount`, LoadState: "loaded", ActiveState: "active"}, + {Name: `dev-nvme0n1.device`, LoadState: "loaded", ActiveState: "active"}, + {Name: `sys-devices-virtual-net-loopback1.device`, LoadState: "loaded", ActiveState: "active"}, + {Name: `sys-module-fuse.device`, LoadState: "loaded", ActiveState: "active"}, + {Name: `dev-disk-by\x2duuid-DE44\x2dCEE0.device`, LoadState: "loaded", ActiveState: "active"}, + + {Name: `var-lib-nfs-rpc_pipefs.mount`, LoadState: "loaded", ActiveState: "inactive"}, + {Name: `var.mount`, LoadState: "not-found", ActiveState: "inactive"}, + {Name: `run-user-1000.mount`, LoadState: "loaded", ActiveState: "active"}, + {Name: `tmp.mount`, LoadState: "loaded", ActiveState: "active"}, + {Name: `run-user-1000-gvfs.mount`, LoadState: "loaded", ActiveState: "active"}, + + {Name: `org.cups.cupsd.path`, LoadState: "loaded", ActiveState: "active"}, + {Name: `systemd-ask-password-wall.path`, LoadState: "loaded", ActiveState: "active"}, + {Name: `systemd-ask-password-console.path`, LoadState: "loaded", ActiveState: "active"}, + + {Name: `init.scope`, LoadState: "loaded", ActiveState: "active"}, + {Name: `session-3.scope`, LoadState: "loaded", ActiveState: "active"}, + {Name: `session-6.scope`, LoadState: "loaded", ActiveState: "active"}, + {Name: `session-1.scope`, LoadState: "loaded", ActiveState: "active"}, + {Name: `session-2.scope`, LoadState: "loaded", ActiveState: "active"}, + + {Name: `systemd-fsck-root.service`, LoadState: "loaded", ActiveState: "inactive"}, + {Name: `httpd.service`, LoadState: "not-found", ActiveState: "inactive"}, + {Name: `user-runtime-dir@1000.service`, LoadState: "loaded", ActiveState: "active"}, + {Name: `systemd-ask-password-wall.service`, LoadState: "loaded", ActiveState: "inactive"}, + {Name: `user@1000.service`, LoadState: "loaded", ActiveState: "active"}, + + {Name: `user.slice`, LoadState: "loaded", ActiveState: "active"}, + {Name: `system-getty.slice`, LoadState: "loaded", ActiveState: "active"}, + {Name: `system-netctl.slice`, LoadState: "loaded", ActiveState: "active"}, + {Name: `system.slice`, LoadState: "loaded", ActiveState: "active"}, + {Name: `system-systemd\x2dfsck.slice`, LoadState: "loaded", ActiveState: "active"}, + + {Name: `lvm2-lvmpolld.socket`, LoadState: "loaded", ActiveState: "active"}, + {Name: `docker.socket`, LoadState: "loaded", ActiveState: "active"}, + {Name: `systemd-udevd-kernel.socket`, LoadState: "loaded", ActiveState: "active"}, + {Name: `dbus.socket`, LoadState: "loaded", ActiveState: "active"}, + {Name: `lvm2-lvmetad.socket`, LoadState: "loaded", ActiveState: "active"}, + + {Name: `getty-pre.target`, LoadState: "loaded", ActiveState: "inactive"}, + {Name: `rpc_pipefs.target`, LoadState: "loaded", ActiveState: "inactive"}, + {Name: `remote-fs-pre.target`, LoadState: "loaded", ActiveState: "inactive"}, + {Name: `sysinit.target`, LoadState: "loaded", ActiveState: "active"}, + {Name: `sound.target`, LoadState: "loaded", ActiveState: "active"}, + + {Name: `man-db.timer`, LoadState: "loaded", ActiveState: "active"}, + {Name: `pamac-mirrorlist.timer`, LoadState: "loaded", ActiveState: "active"}, + {Name: `pamac-cleancache.timer`, LoadState: "loaded", ActiveState: "active"}, + {Name: `shadow.timer`, LoadState: "loaded", ActiveState: "active"}, + {Name: `logrotate.timer`, LoadState: "loaded", ActiveState: "active"}, +} + +var mockSystemdUnitFiles = []dbus.UnitFile{ + {Path: "/lib/systemd/system/systemd-tmpfiles-clean.timer", Type: "static"}, + {Path: "/lib/systemd/system/sysstat-summary.timer", Type: "disabled"}, + {Path: "/lib/systemd/system/sysstat-collect.timer", Type: "disabled"}, + {Path: "/lib/systemd/system/pg_dump@.timer", Type: "disabled"}, + + {Path: "/lib/systemd/system/veritysetup.target", Type: "static"}, + {Path: "/lib/systemd/system/veritysetup-pre.target", Type: "static"}, + {Path: "/lib/systemd/system/usb-gadget.target", Type: "static"}, + {Path: "/lib/systemd/system/umount.target", Type: "static"}, + + {Path: "/lib/systemd/system/syslog.socket", Type: "static"}, + {Path: "/lib/systemd/system/ssh.socket", Type: "disabled"}, + {Path: "/lib/systemd/system/docker.socket", Type: "enabled"}, + {Path: "/lib/systemd/system/dbus.socket", Type: "static"}, + + {Path: "/lib/systemd/system/user.slice", Type: "static"}, + {Path: "/lib/systemd/system/system-systemd\x2dcryptsetup.slice", Type: "static"}, + {Path: "/lib/systemd/system/machine.slice", Type: "static"}, + + {Path: "/run/systemd/generator.late/sendmail.service", Type: "generated"}, + {Path: "/run/systemd/generator.late/monit.service", Type: "generated"}, + {Path: "/lib/systemd/system/x11-common.service", Type: "masked"}, + {Path: "/lib/systemd/system/uuidd.service", Type: "indirect"}, + + {Path: "/run/systemd/transient/session-144.scope", Type: "transient"}, + {Path: "/run/systemd/transient/session-139.scope", Type: "transient"}, + {Path: "/run/systemd/transient/session-132.scope", Type: "transient"}, + + {Path: "/lib/systemd/system/systemd-ask-password-wall.path", Type: "static"}, + {Path: "/lib/systemd/system/systemd-ask-password-console.path", Type: "static"}, + {Path: "/lib/systemd/system/postfix-resolvconf.path", Type: "disabled"}, + {Path: "/lib/systemd/system/ntpsec-systemd-netif.path", Type: "enabled"}, + + {Path: "/run/systemd/generator/media-cdrom0.mount", Type: "generated"}, + {Path: "/run/systemd/generator/boot.mount", Type: "generated"}, + {Path: "/run/systemd/generator/-.mount", Type: "generated"}, + {Path: "/lib/systemd/system/sys-kernel-tracing.mount", Type: "static"}, +} diff --git a/src/go/collectors/go.d.plugin/modules/systemdunits/testdata/config.json b/src/go/collectors/go.d.plugin/modules/systemdunits/testdata/config.json new file mode 100644 index 000000000..1ab5b47ea --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/systemdunits/testdata/config.json @@ -0,0 +1,13 @@ +{ + "update_every": 123, + "timeout": 123.123, + "include": [ + "ok" + ], + "skip_transient": true, + "collect_unit_files": true, + "collect_unit_files_every": 123.123, + "include_unit_files": [ + "ok" + ] +} diff --git a/src/go/collectors/go.d.plugin/modules/systemdunits/testdata/config.yaml b/src/go/collectors/go.d.plugin/modules/systemdunits/testdata/config.yaml new file mode 100644 index 000000000..d1894aea1 --- /dev/null +++ b/src/go/collectors/go.d.plugin/modules/systemdunits/testdata/config.yaml @@ -0,0 +1,9 @@ +update_every: 123 +timeout: 123.123 +include: + - ok +skip_transient: true +collect_unit_files: true +collect_unit_files_every: 123.123 +include_unit_files: + - ok |