173 lines
4.7 KiB
Go
173 lines
4.7 KiB
Go
package main
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// ContainerStats holds real-time stats for a single container
|
|
type ContainerStats struct {
|
|
ContainerID string `json:"container_id"`
|
|
ContainerName string `json:"container_name"`
|
|
HostID string `json:"host_id"`
|
|
CPUPercent float64 `json:"cpu_percent"`
|
|
MemoryUsage uint64 `json:"memory_usage"`
|
|
MemoryLimit uint64 `json:"memory_limit"`
|
|
MemoryPercent float64 `json:"memory_percent"`
|
|
NetworkRx uint64 `json:"network_rx"`
|
|
NetworkTx uint64 `json:"network_tx"`
|
|
DiskRead uint64 `json:"disk_read"`
|
|
DiskWrite uint64 `json:"disk_write"`
|
|
LastUpdate time.Time `json:"last_update"`
|
|
}
|
|
|
|
// HostStats holds aggregated stats for a host
|
|
type HostStats struct {
|
|
HostID string `json:"host_id"`
|
|
CPUPercent float64 `json:"cpu_percent"`
|
|
MemoryPercent float64 `json:"memory_percent"`
|
|
MemoryUsedBytes uint64 `json:"memory_used_bytes"`
|
|
MemoryLimitBytes uint64 `json:"memory_limit_bytes"`
|
|
NetworkRxBytes uint64 `json:"network_rx_bytes"`
|
|
NetworkTxBytes uint64 `json:"network_tx_bytes"`
|
|
ContainerCount int `json:"container_count"`
|
|
LastUpdate time.Time `json:"last_update"`
|
|
}
|
|
|
|
// StatsCache is a thread-safe cache for container and host stats
|
|
type StatsCache struct {
|
|
mu sync.RWMutex
|
|
containerStats map[string]*ContainerStats // key: composite key (hostID:containerID)
|
|
hostStats map[string]*HostStats // key: hostID
|
|
}
|
|
|
|
// NewStatsCache creates a new stats cache
|
|
func NewStatsCache() *StatsCache {
|
|
return &StatsCache{
|
|
containerStats: make(map[string]*ContainerStats),
|
|
hostStats: make(map[string]*HostStats),
|
|
}
|
|
}
|
|
|
|
// UpdateContainerStats updates stats for a container
|
|
func (c *StatsCache) UpdateContainerStats(stats *ContainerStats) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
|
|
stats.LastUpdate = time.Now()
|
|
// Use composite key to support containers with duplicate IDs on different hosts
|
|
compositeKey := stats.HostID + ":" + stats.ContainerID
|
|
c.containerStats[compositeKey] = stats
|
|
}
|
|
|
|
// GetContainerStats retrieves stats for a specific container
|
|
func (c *StatsCache) GetContainerStats(containerID, hostID string) (*ContainerStats, bool) {
|
|
c.mu.RLock()
|
|
defer c.mu.RUnlock()
|
|
|
|
compositeKey := hostID + ":" + containerID
|
|
stats, ok := c.containerStats[compositeKey]
|
|
return stats, ok
|
|
}
|
|
|
|
// GetAllContainerStats returns all container stats
|
|
func (c *StatsCache) GetAllContainerStats() map[string]*ContainerStats {
|
|
c.mu.RLock()
|
|
defer c.mu.RUnlock()
|
|
|
|
// Return a copy to avoid race conditions
|
|
result := make(map[string]*ContainerStats, len(c.containerStats))
|
|
for k, v := range c.containerStats {
|
|
statsCopy := *v
|
|
result[k] = &statsCopy
|
|
}
|
|
return result
|
|
}
|
|
|
|
// RemoveContainerStats removes stats for a container (when it stops)
|
|
func (c *StatsCache) RemoveContainerStats(containerID, hostID string) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
|
|
compositeKey := hostID + ":" + containerID
|
|
delete(c.containerStats, compositeKey)
|
|
}
|
|
|
|
// UpdateHostStats updates aggregated stats for a host
|
|
func (c *StatsCache) UpdateHostStats(stats *HostStats) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
|
|
stats.LastUpdate = time.Now()
|
|
c.hostStats[stats.HostID] = stats
|
|
}
|
|
|
|
// GetHostStats retrieves stats for a specific host
|
|
func (c *StatsCache) GetHostStats(hostID string) (*HostStats, bool) {
|
|
c.mu.RLock()
|
|
defer c.mu.RUnlock()
|
|
|
|
stats, ok := c.hostStats[hostID]
|
|
return stats, ok
|
|
}
|
|
|
|
// GetAllHostStats returns all host stats
|
|
func (c *StatsCache) GetAllHostStats() map[string]*HostStats {
|
|
c.mu.RLock()
|
|
defer c.mu.RUnlock()
|
|
|
|
// Return a copy to avoid race conditions
|
|
result := make(map[string]*HostStats, len(c.hostStats))
|
|
for k, v := range c.hostStats {
|
|
statsCopy := *v
|
|
result[k] = &statsCopy
|
|
}
|
|
return result
|
|
}
|
|
|
|
// RemoveHostStats removes all stats for a specific host
|
|
func (c *StatsCache) RemoveHostStats(hostID string) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
|
|
// Remove host stats
|
|
delete(c.hostStats, hostID)
|
|
|
|
// Remove all container stats for this host
|
|
for id, stats := range c.containerStats {
|
|
if stats.HostID == hostID {
|
|
delete(c.containerStats, id)
|
|
}
|
|
}
|
|
}
|
|
|
|
// CleanStaleStats removes stats older than maxAge
|
|
func (c *StatsCache) CleanStaleStats(maxAge time.Duration) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
|
|
now := time.Now()
|
|
|
|
// Clean container stats
|
|
for id, stats := range c.containerStats {
|
|
if now.Sub(stats.LastUpdate) > maxAge {
|
|
delete(c.containerStats, id)
|
|
}
|
|
}
|
|
|
|
// Clean host stats
|
|
for id, stats := range c.hostStats {
|
|
if now.Sub(stats.LastUpdate) > maxAge {
|
|
delete(c.hostStats, id)
|
|
}
|
|
}
|
|
}
|
|
|
|
// GetStats returns a summary of cache state
|
|
func (c *StatsCache) GetStats() (containerCount, hostCount int) {
|
|
c.mu.RLock()
|
|
defer c.mu.RUnlock()
|
|
|
|
return len(c.containerStats), len(c.hostStats)
|
|
}
|