3e8baebfa5
- Fix "Helyi mentés" showing "–" after controller restart by synthesizing
LastBackup from snapshot history and LastDBDump from dump files on disk
- New monitoring page (/monitoring) with system info, metrics charts, and
container resource overview
- SQLite metrics store (modernc.org/sqlite, pure Go, no CGO) with 60s
collection interval and 30-day auto-prune
- REST API endpoints: /api/metrics/system, /api/metrics/containers/summary,
/api/metrics/containers/{name}, /api/metrics/sysinfo
- Chart.js 4.4.7 embedded locally for offline environments
- System info provider reads hostname, OS, kernel, CPU, uptime from /proc
- Docker compose updated with /etc/os-release host mount
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
111 lines
2.6 KiB
Go
111 lines
2.6 KiB
Go
//go:build linux
|
|
|
|
package metrics
|
|
|
|
import (
|
|
"bufio"
|
|
"fmt"
|
|
"os"
|
|
"runtime"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// GetStaticInfo reads host-level static system information.
|
|
// Reads from /proc and /etc (which reflect the host in Docker containers).
|
|
func GetStaticInfo() StaticSystemInfo {
|
|
info := StaticSystemInfo{}
|
|
|
|
// Hostname
|
|
info.Hostname, _ = os.Hostname()
|
|
|
|
// OS — try host mount first, fall back to container's
|
|
info.OS = readOSRelease("/host/etc/os-release")
|
|
if info.OS == "" {
|
|
info.OS = readOSRelease("/etc/os-release")
|
|
}
|
|
|
|
// Kernel version
|
|
if data, err := os.ReadFile("/proc/sys/kernel/osrelease"); err == nil {
|
|
info.Kernel = strings.TrimSpace(string(data))
|
|
}
|
|
|
|
// Architecture
|
|
if data, err := os.ReadFile("/proc/sys/kernel/arch"); err == nil {
|
|
info.Architecture = strings.TrimSpace(string(data))
|
|
}
|
|
if info.Architecture == "" {
|
|
// Fallback: use uname -m equivalent from /proc/cpuinfo or runtime
|
|
info.Architecture = runtime.GOARCH
|
|
// Try to get the actual host arch
|
|
if data, err := os.ReadFile("/proc/version"); err == nil {
|
|
v := string(data)
|
|
if strings.Contains(v, "x86_64") {
|
|
info.Architecture = "x86_64"
|
|
} else if strings.Contains(v, "aarch64") {
|
|
info.Architecture = "aarch64"
|
|
}
|
|
}
|
|
}
|
|
|
|
// CPU model and cores
|
|
info.CPUModel, info.CPUCores = readCPUInfo()
|
|
if info.CPUCores == 0 {
|
|
info.CPUCores = runtime.NumCPU()
|
|
}
|
|
|
|
// Uptime
|
|
if data, err := os.ReadFile("/proc/uptime"); err == nil {
|
|
var uptimeSec float64
|
|
if _, err := fmt.Sscanf(string(data), "%f", &uptimeSec); err == nil {
|
|
info.UptimeSeconds = int64(uptimeSec)
|
|
info.BootTime = time.Now().Add(-time.Duration(info.UptimeSeconds) * time.Second)
|
|
}
|
|
}
|
|
|
|
return info
|
|
}
|
|
|
|
// readOSRelease reads PRETTY_NAME from an os-release file.
|
|
func readOSRelease(path string) string {
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
defer f.Close()
|
|
|
|
scanner := bufio.NewScanner(f)
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
if strings.HasPrefix(line, "PRETTY_NAME=") {
|
|
val := strings.TrimPrefix(line, "PRETTY_NAME=")
|
|
val = strings.Trim(val, `"`)
|
|
return val
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// readCPUInfo reads the CPU model name and number of cores from /proc/cpuinfo.
|
|
func readCPUInfo() (model string, cores int) {
|
|
f, err := os.Open("/proc/cpuinfo")
|
|
if err != nil {
|
|
return "", 0
|
|
}
|
|
defer f.Close()
|
|
|
|
scanner := bufio.NewScanner(f)
|
|
for scanner.Scan() {
|
|
line := scanner.Text()
|
|
if strings.HasPrefix(line, "model name") {
|
|
if idx := strings.Index(line, ":"); idx >= 0 {
|
|
model = strings.TrimSpace(line[idx+1:])
|
|
}
|
|
}
|
|
if strings.HasPrefix(line, "processor") {
|
|
cores++
|
|
}
|
|
}
|
|
return model, cores
|
|
}
|