Skip to content
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/LICENSE_OF_DEPENDENCIES.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,8 @@ following works:
- github.com/jackc/pgx [MIT License](https://github.com/jackc/pgx/blob/master/LICENSE)
- github.com/jackc/puddle [MIT License](https://github.com/jackc/puddle/blob/master/LICENSE)
- github.com/jaegertracing/jaeger [Apache License 2.0](https://github.com/jaegertracing/jaeger/blob/master/LICENSE)
- github.com/jaypipes/ghw [Apache License 2.0](https://github.com/jaypipes/ghw/blob/main/COPYING)
- github.com/jaypipes/pcidb [Apache License 2.0](https://github.com/jaypipes/pcidb/blob/main/LICENSE)
- github.com/jcmturner/aescts [Apache License 2.0](https://github.com/jcmturner/aescts/blob/master/LICENSE)
- github.com/jcmturner/dnsutils [Apache License 2.0](https://github.com/jcmturner/dnsutils/blob/master/LICENSE)
- github.com/jcmturner/gofork [BSD 3-Clause "New" or "Revised" License](https://github.com/jcmturner/gofork/blob/master/LICENSE)
Expand Down
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ require (
github.com/intel/powertelemetry v1.0.2
github.com/jackc/pgio v1.0.0
github.com/jackc/pgx/v5 v5.9.2
github.com/jaypipes/ghw v0.24.0
github.com/jedib0t/go-pretty/v6 v6.7.10
github.com/jeremywohl/flatten/v2 v2.0.0-20211013061545-07e4a09fb8e4
github.com/jmespath/go-jmespath v0.4.0
Expand Down Expand Up @@ -439,6 +440,7 @@ require (
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/jaegertracing/jaeger v1.47.0 // indirect
github.com/jaypipes/pcidb v1.1.1 // indirect
github.com/jcmturner/aescts/v2 v2.0.0 // indirect
github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect
github.com/jcmturner/gofork v1.7.6 // indirect
Expand Down Expand Up @@ -596,7 +598,7 @@ require (
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
honnef.co/go/tools v0.2.2 // indirect
howett.net/plist v0.0.0-20181124034731-591f970eefbb // indirect
howett.net/plist v1.0.2-0.20250314012144-ee69052608d9 // indirect
k8s.io/klog/v2 v2.140.0 // indirect
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect
k8s.io/utils v0.0.0-20260319190234-28399d86e0b5 // indirect
Expand Down
7 changes: 6 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1692,6 +1692,10 @@ github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/jaegertracing/jaeger v1.47.0 h1:XXxTMO+GxX930gxKWsg90rFr6RswkCRIW0AgWFnTYsg=
github.com/jaegertracing/jaeger v1.47.0/go.mod h1:mHU/OHFML51CijQql4+rLfgPOcIb9MhxOMn+RKQwrJc=
github.com/jaypipes/ghw v0.24.0 h1:6RBrJzvHvZ0t+hSvqPmOd5b21C4fMsyiyFzWljEj8Wg=
github.com/jaypipes/ghw v0.24.0/go.mod h1:Qk3UjdH8Xu/OiVyb/eDJqnDsUc+awHU75y23ErZU33s=
github.com/jaypipes/pcidb v1.1.1 h1:QmPhpsbmmnCwZmHeYAATxEaoRuiMAJusKYkUncMC0ro=
github.com/jaypipes/pcidb v1.1.1/go.mod h1:x27LT2krrUgjf875KxQXKB0Ha/YXLdZRVmw6hH0G7g8=
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
Expand Down Expand Up @@ -3460,8 +3464,9 @@ honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=
honnef.co/go/tools v0.2.2 h1:MNh1AVMyVX23VUHE2O27jm6lNj3vjO5DexS4A1xvnzk=
honnef.co/go/tools v0.2.2/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=
howett.net/plist v0.0.0-20181124034731-591f970eefbb h1:jhnBjNi9UFpfpl8YZhA9CrOqpnJdvzuiHsl/dnxl11M=
howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
howett.net/plist v1.0.2-0.20250314012144-ee69052608d9 h1:eeH1AIcPvSc0Z25ThsYF+Xoqbn0CI/YnXVYoTLFdGQw=
howett.net/plist v1.0.2-0.20250314012144-ee69052608d9/go.mod h1:fyFX5Hj5tP1Mpk8obqA9MZgXT416Q5711SDT7dQLTLk=
k8s.io/api v0.36.0 h1:SgqDhZzHdOtMk40xVSvCXkP9ME0H05hPM3p9AB1kL80=
k8s.io/api v0.36.0/go.mod h1:m1LVrGPNYax5NBHdO+QuAedXyuzTt4RryI/qnmNvs34=
k8s.io/apimachinery v0.36.0 h1:jZyPzhd5Z+3h9vJLt0z9XdzW9VzNzWAUw+P1xZ9PXtQ=
Expand Down
59 changes: 59 additions & 0 deletions plugins/inputs/system/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ plugin ordering. See [CONFIGURATION.md][CONFIGURATION.md] for more details.
## uptime - system uptime
## legacy_uptime - legacy layout of system uptime; see README for details
## os - operating system release and uname information
## dmi - BIOS, baseboard, chassis and product information from DMI/SMBIOS
# include = ["load", "users", "legacy_cpus", "legacy_uptime"]

## How long to cache the result of the "os" group between gathers.
Expand Down Expand Up @@ -68,6 +69,14 @@ FreeBSD/OpenBSD/Solaris) the `platform`, `platform_family`, `platform_version`
and `kernel_version` fields may be empty. Results are cached between gathers,
see `os_cache_ttl` above.

The `dmi` group exposes BIOS, baseboard, chassis and product information from
DMI/SMBIOS. On Linux the data is read from `/sys/class/dmi/id/` and does not
require root access for most fields; serial numbers and asset tags are
generally restricted by the kernel. On Windows the data is read via WMI.
macOS, BSD and Solaris are not supported and the `dmi` value is ignored
there. The data is read once during plugin initialization since DMI/SMBIOS
does not change at runtime.

## Metrics

The `include` option controls which measurements and fields are gathered.
Expand Down Expand Up @@ -108,6 +117,38 @@ may be empty on platforms where gopsutil cannot determine them.
| `platform_version` | string | Platform / distribution version (e.g. `26.04`) |
| `kernel_version` | string | Kernel release as returned by `uname -r` (e.g. `7.0.0-7-generic`) |

### `system_dmi`

Emitted only when `dmi` is included. All fields are reported as strings
with the values returned by the underlying source: an empty string when
the field is not exposed by the system, or `unknown` when it is restricted
by the kernel (typical for serial numbers, asset tags and the product
UUID on Linux without root).

| Field | Type | Description |
|----------------------------|--------|----------------------------------------------------------------------|
| `bios_vendor` | string | BIOS vendor (e.g. `Dell Inc.`) |
| `bios_version` | string | BIOS version (e.g. `2.18.0`) |
| `bios_date` | string | BIOS release date (e.g. `04/12/2024`) |
| `board_vendor` | string | Baseboard / motherboard vendor |
| `board_product` | string | Baseboard product name (e.g. `0X3D66`) |
| `board_version` | string | Baseboard version |
| `board_serial` | string | Baseboard serial number (kernel-restricted on Linux) |
| `board_asset_tag` | string | Baseboard asset tag (kernel-restricted on Linux) |
| `chassis_vendor` | string | Chassis vendor |
| `chassis_type` | string | Chassis type code as defined by SMBIOS DSP0134 (e.g. `3`, `10`) |
| `chassis_type_description` | string | Human-readable chassis type description (e.g. `Desktop`, `Notebook`) |
| `chassis_version` | string | Chassis version |
| `chassis_serial` | string | Chassis serial number (kernel-restricted on Linux) |
| `chassis_asset_tag` | string | Chassis asset tag (kernel-restricted on Linux) |
| `product_vendor` | string | System product vendor (e.g. `Dell Inc.`) |
| `product_name` | string | System product name (e.g. `PowerEdge R750`) |
| `product_family` | string | System product family |
| `product_version` | string | System product version |
| `product_serial` | string | System product serial number (kernel-restricted on Linux) |
| `product_sku` | string | System product SKU |
| `product_uuid` | string | System product UUID (kernel-restricted on Linux) |

## Example Output

### Default configuration
Expand Down Expand Up @@ -137,3 +178,21 @@ With `include = ["os"]`, a separate `system_os` measurement is emitted:
```text
system_os,host=worker-01 os="linux",arch="x86_64",platform="ubuntu",platform_family="debian",platform_version="26.04",kernel_version="7.0.0-7-generic" 1748000000000000000
```

### DMI information

With `include = ["dmi"]`, a separate `system_dmi` measurement is emitted.
When telegraf has access to all DMI fields (e.g. running as root or with
`CAP_SYS_ADMIN` on Linux), the metric carries the full information:

```text
system_dmi,host=worker-01 bios_vendor="Dell Inc.",bios_version="2.18.0",bios_date="04/12/2024",board_vendor="Dell Inc.",board_product="0X3D66",board_version="A00",board_serial="CN747503AB0123",board_asset_tag="",chassis_vendor="Dell Inc.",chassis_type="23",chassis_type_description="Rack mount chassis",chassis_version="",chassis_serial="7XK4P03",chassis_asset_tag="",product_vendor="Dell Inc.",product_name="PowerEdge R750",product_family="PowerEdge",product_version="",product_serial="7XK4P03",product_sku="SKU=NotProvided;ModelName=PowerEdge R750",product_uuid="4c4c4544-0058-4b10-8034-b3c04f503033" 1748000000000000000
```

When telegraf runs without privileges to read kernel-restricted DMI fields,
those fields are reported as `unknown` instead. This is the typical case
when telegraf runs as a regular user on Linux:

```text
system_dmi,host=worker-01 bios_vendor="Dell Inc.",bios_version="2.18.0",bios_date="04/12/2024",board_vendor="Dell Inc.",board_product="0X3D66",board_version="A00",board_serial="unknown",board_asset_tag="",chassis_vendor="Dell Inc.",chassis_type="23",chassis_type_description="Rack mount chassis",chassis_version="",chassis_serial="unknown",chassis_asset_tag="",product_vendor="Dell Inc.",product_name="PowerEdge R750",product_family="PowerEdge",product_version="",product_serial="unknown",product_sku="SKU=NotProvided;ModelName=PowerEdge R750",product_uuid="unknown" 1748000000000000000
```
5 changes: 5 additions & 0 deletions plugins/inputs/system/dmi_other.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//go:build !linux && !windows

package system

const dmiSupported = false
5 changes: 5 additions & 0 deletions plugins/inputs/system/dmi_supported.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//go:build linux || windows

package system

const dmiSupported = true
1 change: 1 addition & 0 deletions plugins/inputs/system/sample.conf
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
## uptime - system uptime
## legacy_uptime - legacy layout of system uptime; see README for details
## os - operating system release and uname information
## dmi - BIOS, baseboard, chassis and product information from DMI/SMBIOS
# include = ["load", "users", "legacy_cpus", "legacy_uptime"]

## How long to cache the result of the "os" group between gathers.
Expand Down
81 changes: 80 additions & 1 deletion plugins/inputs/system/system.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,18 @@ import (
"fmt"
"os"
"runtime"
"slices"
"strings"
"time"

"github.com/jaypipes/ghw"
"github.com/shirou/gopsutil/v4/cpu"
"github.com/shirou/gopsutil/v4/host"
"github.com/shirou/gopsutil/v4/load"

"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/config"
"github.com/influxdata/telegraf/metric"
"github.com/influxdata/telegraf/plugins/inputs"
)

Expand All @@ -31,6 +34,7 @@ type System struct {

osCache map[string]interface{}
osCachedAt time.Time
dmiMetric telegraf.Metric
}

func (*System) SampleConfig() string {
Expand All @@ -51,7 +55,7 @@ func (s *System) Init() error {
continue
}
switch incl {
case "load", "users", "cpus", "uptime", "os":
case "load", "users", "cpus", "uptime", "os", "dmi":
case "legacy_cpus":
if userSupplied {
config.PrintOptionValueDeprecationNotice(
Expand Down Expand Up @@ -93,6 +97,11 @@ func (s *System) Init() error {
return errors.New(`"uptime" and "legacy_uptime" are mutually exclusive`)
}

if enabled["dmi"] && !dmiSupported {
s.Log.Warn("'dmi' is not supported on this platform, ignoring")
s.Include = slices.DeleteFunc(s.Include, func(v string) bool { return v == "dmi" })
}

return nil
}

Expand All @@ -115,6 +124,16 @@ func (s *System) Gather(acc telegraf.Accumulator) error {
if len(s.osCache) > 0 {
acc.AddFields("system_os", s.osCache, nil, now)
}
case "dmi":
if s.dmiMetric != nil {
s.dmiMetric.SetTime(now)
acc.AddMetric(s.dmiMetric)
} else if m, err := gatherDMI(now); err != nil {
acc.AddError(fmt.Errorf("gathering DMI information failed: %w", err))
} else {
s.dmiMetric = m
acc.AddMetric(m)
}
case "load":
loadavg, err := load.Avg()
if err != nil {
Expand Down Expand Up @@ -214,6 +233,66 @@ func gatherOS() (map[string]interface{}, error) {
}, nil
}

// gatherDMI reads BIOS, baseboard, chassis and product DMI/SMBIOS information.
func gatherDMI(now time.Time) (telegraf.Metric, error) {
ctx := ghw.ContextFromEnv()
ctx = ghw.WithDisableWarnings()(ctx)
ctx = ghw.WithDisableTools()(ctx)

fields := make(map[string]interface{}, 21)

bios, err := ghw.BIOS(ctx)
if err != nil && !strings.Contains(err.Error(), "not implemented") {
return nil, fmt.Errorf("reading BIOS information: %w", err)
}
if bios != nil {
fields["bios_vendor"] = bios.Vendor
fields["bios_version"] = bios.Version
fields["bios_date"] = bios.Date
}

bb, err := ghw.Baseboard(ctx)
if err != nil && !strings.Contains(err.Error(), "not implemented") {
return nil, fmt.Errorf("reading baseboard information: %w", err)
}
if bb != nil {
fields["board_vendor"] = bb.Vendor
fields["board_product"] = bb.Product
fields["board_version"] = bb.Version
fields["board_serial"] = bb.SerialNumber
fields["board_asset_tag"] = bb.AssetTag
}

ch, err := ghw.Chassis(ctx)
if err != nil && !strings.Contains(err.Error(), "not implemented") {
return nil, fmt.Errorf("reading chassis information: %w", err)
}
if ch != nil {
fields["chassis_vendor"] = ch.Vendor
fields["chassis_type"] = ch.Type
fields["chassis_type_description"] = ch.TypeDescription
fields["chassis_version"] = ch.Version
fields["chassis_serial"] = ch.SerialNumber
fields["chassis_asset_tag"] = ch.AssetTag
}

prod, err := ghw.Product(ctx)
if err != nil && !strings.Contains(err.Error(), "not implemented") {
return nil, fmt.Errorf("reading product information: %w", err)
}
if prod != nil {
fields["product_vendor"] = prod.Vendor
fields["product_name"] = prod.Name
fields["product_family"] = prod.Family
fields["product_version"] = prod.Version
fields["product_serial"] = prod.SerialNumber
fields["product_sku"] = prod.SKU
fields["product_uuid"] = prod.UUID
}

return metric.New("system_dmi", nil, fields, now), nil
}

func findUniqueUsers(userStats []host.UserStat) int {
uniqueUsers := make(map[string]bool)
for _, userstat := range userStats {
Expand Down
56 changes: 54 additions & 2 deletions plugins/inputs/system/system_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ func TestInitAllValidOptions(t *testing.T) {
name string
include []string
}{
{"new", []string{"load", "users", "cpus", "uptime", "os"}},
{"legacy", []string{"load", "users", "legacy_cpus", "legacy_uptime", "os"}},
{"new", []string{"load", "users", "cpus", "uptime", "os", "dmi"}},
{"legacy", []string{"load", "users", "legacy_cpus", "legacy_uptime", "os", "dmi"}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down Expand Up @@ -357,3 +357,55 @@ func TestGatherOSValues(t *testing.T) {
require.True(t, ok)
require.NotEmpty(t, kernelVersion)
}

func TestGatherDMIValues(t *testing.T) {
if runtime.GOOS != "linux" {
t.Skip("Skipping test on non-Linux setups...")
}

chroot, err := filepath.Abs(filepath.Join("testdata", "dmi"))
require.NoError(t, err)
t.Setenv("GHW_CHROOT", chroot)

s := &System{
Include: []string{"dmi"},
Log: &testutil.Logger{},
}
require.NoError(t, s.Init())

var acc testutil.Accumulator
require.NoError(t, s.Gather(&acc))

expected := []telegraf.Metric{
metric.New(
"system_dmi",
map[string]string{},
map[string]interface{}{
"bios_vendor": "Telegraf BIOS, Inc.",
"bios_version": "1.2.3",
"bios_date": "01/01/2026",
"board_vendor": "Telegraf Boards Co.",
"board_product": "TG-BOARD-X1",
"board_version": "A1",
"board_serial": "BS-1234",
"board_asset_tag": "ASSET-A",
"chassis_vendor": "Telegraf Chassis Ltd.",
"chassis_type": "3",
"chassis_type_description": "Desktop",
"chassis_version": "v0",
"chassis_serial": "CS-5678",
"chassis_asset_tag": "ASSET-C",
"product_vendor": "Telegraf Systems",
"product_name": "TG-Server-9000",
"product_family": "TG-Server",
"product_version": "v9",
"product_serial": "PS-9999",
"product_sku": "SKU-XYZ",
"product_uuid": "00000000-0000-0000-0000-000000000001",
},
time.Unix(0, 0),
),
}

testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
01/01/2026
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Telegraf BIOS, Inc.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1.2.3
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ASSET-A
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
TG-BOARD-X1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
BS-1234
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Telegraf Boards Co.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
A1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ASSET-C
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CS-5678
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Telegraf Chassis Ltd.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v0
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
TG-Server
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
TG-Server-9000
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
PS-9999
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
SKU-XYZ
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
00000000-0000-0000-0000-000000000001
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v9
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Telegraf Systems
Loading