[Feature] Expand system info bar to include memory, disk, CPU, and OS details (#952)

* collect OS info

* Fix systeminfo

* Fix it

* optimize it

* Add disk info

* add ethernet info

* add ethernet

* remove speed from ethernet

* add cpu info

* chore cleanup data

* chore fix podman

* restruct systeminfo

* use short cpu name

* debug memory

* collect and show memory

* remove os from the table

* truncate nic name

* chore: shorter names in json

* collect memory info

* add debug

* undo memory

* revert package.json

* fix conflicts

* fix conflixts

* Fix MacOs os family

* add ISP data for remote systems

* reorder the system page bar information

* remove OS from the system table

* Update with main

* Fix vulcheck

* Fix systembar

* fix system bar

* fix vulcheck

* update struct with static info

* Adjust collection method to upon agent connection
This commit is contained in:
Sven van Ginkel
2025-12-13 22:11:31 +01:00
committed by GitHub
parent 35329abcbd
commit d71714cbba
11 changed files with 504 additions and 62 deletions

View File

@@ -36,6 +36,7 @@ type System struct {
manager *SystemManager // Manager that this system belongs to
client *ssh.Client // SSH client for fetching data
data *system.CombinedData // system data from agent
staticInfo *system.StaticInfo // cached static system info, fetched once per connection
ctx context.Context // Context for stopping the updater
cancel context.CancelFunc // Stops and removes system from updater
WsConn *ws.WsConn // Handler for agent WebSocket connection
@@ -114,8 +115,22 @@ func (sys *System) update() error {
sys.handlePaused()
return nil
}
data, err := sys.fetchDataFromAgent(common.DataRequestOptions{CacheTimeMs: uint16(interval)})
// Determine which cache time to use based on whether we need static info
cacheTimeMs := uint16(interval)
if sys.staticInfo == nil {
// Request with a cache time that signals the agent to include static info
// We use 60001ms (just above the standard interval) since uint16 max is 65535
cacheTimeMs = 60_001
}
data, err := sys.fetchDataFromAgent(common.DataRequestOptions{CacheTimeMs: cacheTimeMs})
if err == nil {
// If we received static info, cache it
if data.StaticInfo != nil {
sys.staticInfo = data.StaticInfo
sys.manager.hub.Logger().Debug("Cached static system info", "system", sys.Id)
}
_, err = sys.createRecords(data)
}
return err
@@ -136,6 +151,11 @@ func (sys *System) handlePaused() {
// createRecords updates the system record and adds system_stats and container_stats records
func (sys *System) createRecords(data *system.CombinedData) (*core.Record, error) {
// Build complete info combining dynamic and static data
completeInfo := sys.buildCompleteInfo(data)
sys.manager.hub.Logger().Debug("Creating records - complete info", "info", completeInfo)
systemRecord, err := sys.getRecord()
if err != nil {
return nil, err
@@ -186,7 +206,7 @@ func (sys *System) createRecords(data *system.CombinedData) (*core.Record, error
// update system record (do this last because it triggers alerts and we need above records to be inserted first)
systemRecord.Set("status", up)
systemRecord.Set("info", data.Info)
systemRecord.Set("info", completeInfo)
if err := txApp.SaveNoValidate(systemRecord); err != nil {
return err
}
@@ -203,6 +223,70 @@ func (sys *System) createRecords(data *system.CombinedData) (*core.Record, error
return systemRecord, err
}
// buildCompleteInfo combines the dynamic Info with cached StaticInfo to create a complete system info structure
// This is needed because we've split the original Info structure for bandwidth optimization
func (sys *System) buildCompleteInfo(data *system.CombinedData) map[string]interface{} {
info := make(map[string]interface{})
// Add dynamic fields from data.Info
if data.Info.Uptime > 0 {
info["u"] = data.Info.Uptime
}
info["cpu"] = data.Info.Cpu
info["mp"] = data.Info.MemPct
info["dp"] = data.Info.DiskPct
info["b"] = data.Info.Bandwidth
info["bb"] = data.Info.BandwidthBytes
if data.Info.GpuPct > 0 {
info["g"] = data.Info.GpuPct
}
if data.Info.DashboardTemp > 0 {
info["dt"] = data.Info.DashboardTemp
}
if data.Info.LoadAvg1 > 0 || data.Info.LoadAvg5 > 0 || data.Info.LoadAvg15 > 0 {
info["l1"] = data.Info.LoadAvg1
info["l5"] = data.Info.LoadAvg5
info["l15"] = data.Info.LoadAvg15
info["la"] = data.Info.LoadAvg
}
if data.Info.ConnectionType > 0 {
info["ct"] = data.Info.ConnectionType
}
// Add static fields from cached staticInfo
if sys.staticInfo != nil {
info["h"] = sys.staticInfo.Hostname
if sys.staticInfo.KernelVersion != "" {
info["k"] = sys.staticInfo.KernelVersion
}
if sys.staticInfo.Threads > 0 {
info["t"] = sys.staticInfo.Threads
}
info["v"] = sys.staticInfo.AgentVersion
if sys.staticInfo.Podman {
info["p"] = true
}
info["os"] = sys.staticInfo.Os
if len(sys.staticInfo.Cpus) > 0 {
info["c"] = sys.staticInfo.Cpus
}
if len(sys.staticInfo.Memory) > 0 {
info["m"] = sys.staticInfo.Memory
}
if len(sys.staticInfo.Disks) > 0 {
info["d"] = sys.staticInfo.Disks
}
if len(sys.staticInfo.Networks) > 0 {
info["n"] = sys.staticInfo.Networks
}
if len(sys.staticInfo.Oses) > 0 {
info["o"] = sys.staticInfo.Oses
}
}
return info
}
func createSystemdStatsRecords(app core.App, data []*systemd.Service, systemId string) error {
if len(data) == 0 {
return nil
@@ -602,6 +686,7 @@ func (sys *System) closeSSHConnection() {
sys.client.Close()
sys.client = nil
}
sys.staticInfo = nil
}
// closeWebSocketConnection closes the WebSocket connection but keeps the system in the manager
@@ -611,6 +696,7 @@ func (sys *System) closeWebSocketConnection() {
if sys.WsConn != nil {
sys.WsConn.Close(nil)
}
sys.staticInfo = nil
}
// extractAgentVersion extracts the beszel version from SSH server version string