feat(inputs.systemd_units): Support user scoped units (#15458)

This commit is contained in:
Pawel Potrykus 2024-06-25 15:51:51 +02:00 committed by GitHub
parent 32a5b44d7b
commit 4a9eba6abd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 91 additions and 7 deletions

View File

@ -31,6 +31,10 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
## automount, swap, timer, path, slice and scope
# unittype = "service"
## Collect system or user scoped units
## ex: scope = "user"
# scope = "system"
## Collect also units not loaded by systemd, i.e. disabled or static units
## Enabling this feature might introduce significant load when used with
## unspecific patterns (such as '*') as systemd will need to load all
@ -69,6 +73,7 @@ These metrics are available in both modes:
- load (string, load state)
- active (string, active state)
- sub (string, sub state)
- user (string, username only for user scope)
- fields:
- load_code (int, see below)
- active_code (int, see below)
@ -198,15 +203,15 @@ were removed, tables are hex aligned to keep some space for future values
### Output in non-detailed mode
```text
systemd_units,host=host1.example.com,name=dbus.service,load=loaded,active=active,sub=running load_code=0i,active_code=0i,sub_code=0i 1533730725000000000
systemd_units,host=host1.example.com,name=networking.service,load=loaded,active=failed,sub=failed load_code=0i,active_code=3i,sub_code=12i 1533730725000000000
systemd_units,host=host1.example.com,name=ssh.service,load=loaded,active=active,sub=running load_code=0i,active_code=0i,sub_code=0i 1533730725000000000
systemd_units,host=host1.example.com,name=dbus.service,load=loaded,active=active,sub=running,user=telegraf load_code=0i,active_code=0i,sub_code=0i 1533730725000000000
systemd_units,host=host1.example.com,name=networking.service,load=loaded,active=failed,sub=failed,user=telegraf load_code=0i,active_code=3i,sub_code=12i 1533730725000000000
systemd_units,host=host1.example.com,name=ssh.service,load=loaded,active=active,sub=running,user=telegraf load_code=0i,active_code=0i,sub_code=0i 1533730725000000000
```
### Output in detailed mode
```text
systemd_units,active=active,host=host1.example.com,load=loaded,name=dbus.service,sub=running,preset=disabled,state=static active_code=0i,load_code=0i,mem_avail=6470856704i,mem_current=2691072i,mem_peak=3895296i,pid=481i,restarts=0i,status_errno=0i,sub_code=0i,swap_current=794624i,swap_peak=884736i 1533730725000000000
systemd_units,active=inactive,host=host1.example.com,load=not-found,name=networking.service,sub=dead active_code=2i,load_code=2i,pid=0i,restarts=0i,status_errno=0i,sub_code=1i 1533730725000000000
systemd_units,active=active,host=host1.example.com,load=loaded,name=pcscd.service,sub=running,preset=disabled,state=indirect active_code=0i,load_code=0i,mem_avail=6370541568i,mem_current=512000i,mem_peak=4399104i,pid=1673i,restarts=0i,status_errno=0i,sub_code=0i,swap_current=3149824i,swap_peak=3149824i 1533730725000000000
systemd_units,active=active,host=host1.example.com,load=loaded,name=dbus.service,sub=running,preset=disabled,state=static,user=telegraf active_code=0i,load_code=0i,mem_avail=6470856704i,mem_current=2691072i,mem_peak=3895296i,pid=481i,restarts=0i,status_errno=0i,sub_code=0i,swap_current=794624i,swap_peak=884736i 1533730725000000000
systemd_units,active=inactive,host=host1.example.com,load=not-found,name=networking.service,sub=dead,user=telegraf active_code=2i,load_code=2i,pid=0i,restarts=0i,status_errno=0i,sub_code=1i 1533730725000000000
systemd_units,active=active,host=host1.example.com,load=loaded,name=pcscd.service,sub=running,preset=disabled,state=indirect,user=telegraf active_code=0i,load_code=0i,mem_avail=6370541568i,mem_current=512000i,mem_peak=4399104i,pid=1673i,restarts=0i,status_errno=0i,sub_code=0i,swap_current=3149824i,swap_peak=3149824i 1533730725000000000
```

View File

@ -12,6 +12,10 @@
## automount, swap, timer, path, slice and scope
# unittype = "service"
## Collect system or user scoped units
## ex: scope = "user"
# scope = "system"
## Collect also units not loaded by systemd, i.e. disabled or static units
## Enabling this feature might introduce significant load when used with
## unspecific patterns (such as '*') as systemd will need to load all

View File

@ -17,6 +17,7 @@ var sampleConfig string
type SystemdUnits struct {
Pattern string `toml:"pattern"`
UnitType string `toml:"unittype"`
Scope string `toml:"scope"`
Details bool `toml:"details"`
CollectDisabled bool `toml:"collect_disabled_units"`
Timeout config.Duration `toml:"timeout"`

View File

@ -6,6 +6,7 @@ import (
"context"
"fmt"
"math"
"os/user"
"path"
"strings"
"time"
@ -131,6 +132,8 @@ type archParams struct {
pattern []string
filter filter.Filter
unitTypeDBus string
scope string
user string
}
func (s *SystemdUnits) Init() error {
@ -158,15 +161,38 @@ func (s *SystemdUnits) Init() error {
}
s.filter = f
switch s.Scope {
case "", "system":
s.scope = "system"
case "user":
u, err := user.Current()
if err != nil {
return fmt.Errorf("unable to determine user: %w", err)
}
s.scope = "user"
s.user = u.Username
default:
return fmt.Errorf("invalid 'scope' %q", s.Scope)
}
return nil
}
func (s *SystemdUnits) Start(telegraf.Accumulator) error {
ctx := context.Background()
client, err := dbus.NewSystemConnectionContext(ctx)
var client *dbus.Conn
var err error
if s.scope == "user" {
client, err = dbus.NewUserConnectionContext(ctx)
} else {
client, err = dbus.NewSystemConnectionContext(ctx)
}
if err != nil {
return err
}
s.client = client
return nil
@ -321,6 +347,9 @@ func (s *SystemdUnits) Gather(acc telegraf.Accumulator) error {
"active": state.ActiveState,
"sub": state.SubState,
}
if s.scope == "user" {
tags["user"] = s.user
}
fields := map[string]interface{}{
"load_code": load,

View File

@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"math"
"os/user"
"strings"
"testing"
"time"
@ -38,6 +39,50 @@ func TestDefaultPattern(t *testing.T) {
require.Equal(t, "*", plugin.Pattern)
}
func TestDefaultScope(t *testing.T) {
u, err := user.Current()
if err != nil {
return
}
tests := []struct {
name string
scope string
expectedScope string
expectedUser string
}{
{
name: "default scope",
scope: "",
expectedScope: "system",
expectedUser: "",
},
{
name: "system scope",
scope: "system",
expectedScope: "system",
expectedUser: "",
},
{
name: "user scope",
scope: "user",
expectedScope: "user",
expectedUser: u.Username,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
plugin := &SystemdUnits{
Scope: tt.scope,
}
require.NoError(t, plugin.Init())
require.Equal(t, tt.expectedScope, plugin.scope)
require.Equal(t, tt.expectedUser, plugin.user)
})
}
}
func TestListFiles(t *testing.T) {
tests := []struct {
name string