go.d systemdunits add "skip_transient" (#17725)
Ilya Mashchenko committed
May 21, 2024 at 22:28 UTC
e54b6164dd9f9c897e99ed17f802f014275f7578
8 files changed
+109
-2
src/go/collectors/go.d.plugin/modules/systemdunits/client.go
+1
@@ -17,6 +17,7 @@ type systemdClient interface {
17
type systemdConnection interface {
18
Close()
19
GetManagerProperty(string) (string, error)
20
+ GetUnitPropertyContext(ctx context.Context, unit string, propertyName string) (*dbus.Property, error)
21
ListUnitsContext(ctx context.Context) ([]dbus.UnitStatus, error)
22
ListUnitsByPatternsContext(ctx context.Context, states []string, patterns []string) ([]dbus.UnitStatus, error)
23
ListUnitFilesByPatternsContext(ctx context.Context, states []string, patterns []string) ([]dbus.UnitFile, error)
src/go/collectors/go.d.plugin/modules/systemdunits/collect_units.go
+36
@@ -14,6 +14,8 @@ import (
14
"github.com/coreos/go-systemd/v22/dbus"
15
)
16
17
+const transientProperty = "Transient"
18
+
19
const (
20
// https://www.freedesktop.org/software/systemd/man/systemd.html
21
unitStateActive = "active"
@@ -55,6 +57,20 @@ func (s *SystemdUnits) collectUnits(mx map[string]int64, conn systemdConnection)
57
58
seen[unit.Name] = true
59
60
+ if s.SkipTransient {
61
+ if _, ok := s.unitTransient[unit.Name]; !ok {
62
+ prop, err := s.getUnitTransientProperty(conn, unit.Name)
63
+ if err != nil {
64
+ return err
65
+ }
66
+ prop = strings.Trim(prop, "\"")
67
+ s.unitTransient[unit.Name] = prop == "true"
68
+ }
69
+ if s.unitTransient[unit.Name] {
70
+ continue
71
+ }
72
+ }
73
+
74
if !s.seenUnits[unit.Name] {
75
s.seenUnits[unit.Name] = true
76
s.addUnitCharts(name, typ)
@@ -75,6 +91,12 @@ func (s *SystemdUnits) collectUnits(mx map[string]int64, conn systemdConnection)
91
}
92
}
93
94
+ for k := range s.unitTransient {
95
+ if !seen[k] {
96
+ delete(s.unitTransient, k)
97
+ }
98
+ }
99
+
100
return nil
101
}
102
@@ -130,6 +152,20 @@ func (s *SystemdUnits) getLoadedUnitsByPatterns(conn systemdConnection) ([]dbus.
152
return loaded, nil
153
}
154
155
+func (s *SystemdUnits) getUnitTransientProperty(conn systemdConnection, unit string) (string, error) {
156
+ ctx, cancel := context.WithTimeout(context.Background(), s.Timeout.Duration())
157
+ defer cancel()
158
+
159
+ s.Debugf("calling function 'GetUnitProperty' for unit '%s'", unit)
160
+
161
+ prop, err := conn.GetUnitPropertyContext(ctx, unit, transientProperty)
162
+ if err != nil {
163
+ return "", fmt.Errorf("error on GetUnitProperty: %v", err)
164
+ }
165
+
166
+ return prop.Value.String(), nil
167
+}
168
+
169
func extractUnitNameType(name string) (string, string, bool) {
170
idx := strings.LastIndexByte(name, '.')
171
if idx <= 0 {
src/go/collectors/go.d.plugin/modules/systemdunits/config_schema.json
+10
@@ -18,6 +18,12 @@
18
"minimum": 0.5,
19
"default": 2
20
},
21
+ "skip_transient": {
22
+ "title": "Skip transient units",
23
+ "description": "If set, skip data collection for systemd transient units.",
24
+ "type": "boolean",
25
+ "default": false
26
+ },
27
"include": {
28
"title": "Include",
29
"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).",
@@ -86,6 +92,7 @@
92
"fields": [
93
"update_every",
94
"timeout",
95
+ "skip_transient",
96
"include"
97
]
98
},
@@ -102,6 +109,9 @@
109
"timeout": {
110
"ui:help": "Accepts decimals for precise control (e.g., type 1.5 for 1.5 seconds)."
111
},
112
+ "skip_transient": {
113
+ "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."
114
+ },
115
"include": {
116
"ui:listFlavour": "list"
117
},
src/go/collectors/go.d.plugin/modules/systemdunits/metadata.yaml
+4
@@ -77,6 +77,10 @@ modules:
77
- pattern1
78
- pattern2
79
```
80
+ - name: skip_transient
81
+ description: If set, skip data collection for systemd transient units.
82
+ default_value: "false"
83
+ required: false
84
- name: collect_unit_files
85
description: If set to true, collect the state of installed unit files. Enabling this may increase system overhead.
86
default_value: "false"
src/go/collectors/go.d.plugin/modules/systemdunits/systemdunits.go
+6
-2
@@ -36,6 +36,7 @@ func New() *SystemdUnits {
36
Config: Config{
37
Timeout: web.Duration(time.Second * 2),
38
Include: []string{"*.service"},
39
+ SkipTransient: false,
40
CollectUnitFiles: false,
41
IncludeUnitFiles: []string{"*.service"},
42
CollectUnitFilesEvery: web.Duration(time.Minute * 5),
@@ -43,6 +44,7 @@ func New() *SystemdUnits {
44
charts: &module.Charts{},
45
client: newSystemdDBusClient(),
46
seenUnits: make(map[string]bool),
47
+ unitTransient: make(map[string]bool),
48
seenUnitFiles: make(map[string]bool),
49
}
50
}
@@ -51,6 +53,7 @@ type Config struct {
53
UpdateEvery int `yaml:"update_every,omitempty" json:"update_every"`
54
Timeout web.Duration `yaml:"timeout,omitempty" json:"timeout"`
55
Include []string `yaml:"include,omitempty" json:"include"`
56
+ SkipTransient bool `yaml:"skip_transient" json:"skip_transient"`
57
CollectUnitFiles bool `yaml:"collect_unit_files,omitempty" json:"collect_unit_files"`
58
IncludeUnitFiles []string `yaml:"include_unit_files,omitempty" json:"include_unit_files"`
59
CollectUnitFilesEvery web.Duration `yaml:"collect_unit_files_every,omitempty" json:"collect_unit_files_every"`
@@ -65,8 +68,9 @@ type SystemdUnits struct {
68
69
systemdVersion int
70
68
- seenUnits map[string]bool
69
- unitSr matcher.Matcher
71
+ seenUnits map[string]bool
72
+ unitTransient map[string]bool
73
+ unitSr matcher.Matcher
74
75
lastListUnitFilesTime time.Time
76
cachedUnitFiles []dbus.UnitFile
src/go/collectors/go.d.plugin/modules/systemdunits/systemdunits_test.go
+50
@@ -12,6 +12,7 @@ import (
12
"os"
13
"path/filepath"
14
"slices"
15
+ "strings"
16
"testing"
17
18
"github.com/netdata/netdata/go/go.d.plugin/agent/module"
@@ -384,6 +385,37 @@ func TestSystemdUnits_Collect(t *testing.T) {
385
"unit_var-lib-nfs-rpc_pipefs_mount_state_inactive": 1,
386
},
387
},
388
+ "success v230+ on collecting all unit type with skip transient": {
389
+ prepare: func() *SystemdUnits {
390
+ systemd := New()
391
+ systemd.Include = []string{"*"}
392
+ systemd.SkipTransient = true
393
+ systemd.client = prepareOKClient(230)
394
+ return systemd
395
+ },
396
+ wantCollected: map[string]int64{
397
+ "unit_systemd-ask-password-wall_service_state_activating": 0,
398
+ "unit_systemd-ask-password-wall_service_state_active": 0,
399
+ "unit_systemd-ask-password-wall_service_state_deactivating": 0,
400
+ "unit_systemd-ask-password-wall_service_state_failed": 0,
401
+ "unit_systemd-ask-password-wall_service_state_inactive": 1,
402
+ "unit_systemd-fsck-root_service_state_activating": 0,
403
+ "unit_systemd-fsck-root_service_state_active": 0,
404
+ "unit_systemd-fsck-root_service_state_deactivating": 0,
405
+ "unit_systemd-fsck-root_service_state_failed": 0,
406
+ "unit_systemd-fsck-root_service_state_inactive": 1,
407
+ "unit_user-runtime-dir@1000_service_state_activating": 0,
408
+ "unit_user-runtime-dir@1000_service_state_active": 1,
409
+ "unit_user-runtime-dir@1000_service_state_deactivating": 0,
410
+ "unit_user-runtime-dir@1000_service_state_failed": 0,
411
+ "unit_user-runtime-dir@1000_service_state_inactive": 0,
412
+ "unit_user@1000_service_state_activating": 0,
413
+ "unit_user@1000_service_state_active": 1,
414
+ "unit_user@1000_service_state_deactivating": 0,
415
+ "unit_user@1000_service_state_failed": 0,
416
+ "unit_user@1000_service_state_inactive": 0,
417
+ },
418
+ },
419
"success v230- on collecting all unit types": {
420
prepare: func() *SystemdUnits {
421
systemd := New()
@@ -946,6 +978,24 @@ func (m *mockConn) GetManagerProperty(prop string) (string, error) {
978
return fmt.Sprintf("%d.6-1-manjaro", m.version), nil
979
}
980
981
+func (m *mockConn) GetUnitPropertyContext(_ context.Context, unit string, propertyName string) (*dbus.Property, error) {
982
+ if propertyName != transientProperty {
983
+ return nil, fmt.Errorf("'GetUnitProperty' unkown property name: %s", propertyName)
984
+ }
985
+
986
+ var prop dbus.Property
987
+
988
+ if strings.HasSuffix(unit, ".service") {
989
+ prop = dbus.PropDescription("false")
990
+ } else {
991
+ prop = dbus.PropDescription("true")
992
+ }
993
+
994
+ prop.Name = propertyName
995
+
996
+ return &prop, nil
997
+}
998
+
999
func (m *mockConn) ListUnitsContext(_ context.Context) ([]dbus.UnitStatus, error) {
1000
if m.errOnListUnits {
1001
return nil, errors.New("'ListUnits' call error")
src/go/collectors/go.d.plugin/modules/systemdunits/testdata/config.json
+1
@@ -4,6 +4,7 @@
4
"include": [
5
"ok"
6
],
7
+ "skip_transient": true,
8
"collect_unit_files": true,
9
"collect_unit_files_every": 123.123,
10
"include_unit_files": [
src/go/collectors/go.d.plugin/modules/systemdunits/testdata/config.yaml
+1
@@ -2,6 +2,7 @@ update_every: 123
2
timeout: 123.123
3
include:
4
- ok
5
+skip_transient: true
6
collect_unit_files: true
7
collect_unit_files_every: 123.123
8
include_unit_files: