0415e905af
build-and-release / release (push) Successful in 2m34s
Adds GPUInfo struct and readGPUsFromSysfs parsing DRM card entries (/sys/class/drm/card*). Supports: - AMD GPUs (amdgpu driver): VRAM totals/utilization from sysfs files - NVIDIA GPUs: enrichment via nvidia-smi query - Intel/other: basic PCI vendor/device/driver identification Includes full test coverage for AMD enrichment, i915 fallback, missing sysfs dir, and non-GPU DRM entry filtering.
81 lines
3.1 KiB
Go
81 lines
3.1 KiB
Go
package system
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/danielgtaylor/huma/v2"
|
|
)
|
|
|
|
// SystemInfoBody is the dashboard overview: OS identity plus live CPU, memory,
|
|
// disk, load, network, GPU, and temperature readings. Every section is best-effort —
|
|
// a source that's unavailable (e.g. no thermal zones in a VM) yields a zero
|
|
// value or empty list rather than failing the whole call.
|
|
type SystemInfoBody struct {
|
|
OS OSInfo `json:"os" doc:"OS and kernel identity"`
|
|
CPU CPUInfo `json:"cpu" doc:"Processor model and core count"`
|
|
Memory MemoryInfo `json:"memory" doc:"RAM and swap usage in bytes"`
|
|
Load LoadInfo `json:"load" doc:"Load averages (1/5/15 min)"`
|
|
UptimeSec int64 `json:"uptime_seconds" example:"12490" doc:"Seconds since boot"`
|
|
BootTime string `json:"boot_time" example:"2026-06-19T12:08:00Z" doc:"Boot time (RFC3339, UTC)"`
|
|
Disks []DiskInfo `json:"disks" doc:"Mounted block-device filesystems"`
|
|
NetworkInterfaces []NetInterface `json:"network_interfaces" doc:"Network interfaces and their addresses"`
|
|
Temperatures []Temperature `json:"temperatures" doc:"Thermal sensor readings in Celsius"`
|
|
GPUs []GPUInfo `json:"gpus" doc:"Graphics processors detected via DRM sysfs"`
|
|
}
|
|
|
|
type GetInfoOutput struct{ Body SystemInfoBody }
|
|
|
|
func registerInfo(api huma.API, sampler *Sampler) {
|
|
huma.Register(api, huma.Operation{
|
|
OperationID: "system-get-info",
|
|
Method: "GET",
|
|
Path: "/api/system/info",
|
|
Summary: "Get system information",
|
|
Description: "Returns an overview for a dashboard: OS/kernel identity, CPU, " +
|
|
"memory and swap, mounted disks, load averages, uptime, network " +
|
|
"interfaces, temperatures, and GPU information. All values come from cheap " +
|
|
"local reads (/proc, /sys, syscalls) with no D-Bus dependency; each " +
|
|
"section is best-effort.",
|
|
Tags: []string{tagSystem},
|
|
Metadata: op("read"),
|
|
Errors: readErrors,
|
|
}, func(ctx context.Context, _ *struct{}) (*GetInfoOutput, error) {
|
|
uptime, boot := uptimeAndBoot()
|
|
return &GetInfoOutput{Body: SystemInfoBody{
|
|
OS: osInfo(),
|
|
CPU: cpuInfo(),
|
|
Memory: memInfo(),
|
|
Load: loadInfo(sampler),
|
|
UptimeSec: uptime,
|
|
BootTime: boot.Format(time.RFC3339),
|
|
Disks: diskInfo(),
|
|
NetworkInterfaces: netInfo(),
|
|
Temperatures: tempInfo(),
|
|
GPUs: gpuInfo(),
|
|
}}, nil
|
|
})
|
|
}
|
|
|
|
// uptimeAndBoot reads /proc/uptime (seconds since boot) and derives boot time.
|
|
// On any read error it returns zero values rather than failing the request.
|
|
func uptimeAndBoot() (int64, time.Time) {
|
|
data, err := os.ReadFile("/proc/uptime")
|
|
if err != nil {
|
|
return 0, time.Time{}
|
|
}
|
|
fields := strings.Fields(string(data))
|
|
if len(fields) == 0 {
|
|
return 0, time.Time{}
|
|
}
|
|
secs, err := strconv.ParseFloat(fields[0], 64)
|
|
if err != nil {
|
|
return 0, time.Time{}
|
|
}
|
|
boot := time.Now().Add(-time.Duration(secs * float64(time.Second))).UTC()
|
|
return int64(secs), boot
|
|
}
|