github.com/hhrutter/nomad@v0.6.0-rc2.0.20170723054333-80c4b03f0705/client/stats/host.go (about) 1 package stats 2 3 import ( 4 "log" 5 "math" 6 "runtime" 7 "sync" 8 "time" 9 10 "github.com/shirou/gopsutil/cpu" 11 "github.com/shirou/gopsutil/disk" 12 "github.com/shirou/gopsutil/host" 13 "github.com/shirou/gopsutil/mem" 14 15 shelpers "github.com/hashicorp/nomad/helper/stats" 16 ) 17 18 // HostStats represents resource usage stats of the host running a Nomad client 19 type HostStats struct { 20 Memory *MemoryStats 21 CPU []*CPUStats 22 DiskStats []*DiskStats 23 AllocDirStats *DiskStats 24 Uptime uint64 25 Timestamp int64 26 CPUTicksConsumed float64 27 } 28 29 // MemoryStats represnts stats related to virtual memory usage 30 type MemoryStats struct { 31 Total uint64 32 Available uint64 33 Used uint64 34 Free uint64 35 } 36 37 // CPUStats represents stats related to cpu usage 38 type CPUStats struct { 39 CPU string 40 User float64 41 System float64 42 Idle float64 43 Total float64 44 } 45 46 // DiskStats represents stats related to disk usage 47 type DiskStats struct { 48 Device string 49 Mountpoint string 50 Size uint64 51 Used uint64 52 Available uint64 53 UsedPercent float64 54 InodesUsedPercent float64 55 } 56 57 // NodeStatsCollector is an interface which is used for the puproses of mocking 58 // the HostStatsCollector in the tests 59 type NodeStatsCollector interface { 60 Collect() error 61 Stats() *HostStats 62 } 63 64 // HostStatsCollector collects host resource usage stats 65 type HostStatsCollector struct { 66 clkSpeed float64 67 numCores int 68 statsCalculator map[string]*HostCpuStatsCalculator 69 logger *log.Logger 70 hostStats *HostStats 71 hostStatsLock sync.RWMutex 72 allocDir string 73 } 74 75 // NewHostStatsCollector returns a HostStatsCollector. The allocDir is passed in 76 // so that we can present the disk related statistics for the mountpoint where 77 // the allocation directory lives 78 func NewHostStatsCollector(logger *log.Logger, allocDir string) *HostStatsCollector { 79 numCores := runtime.NumCPU() 80 statsCalculator := make(map[string]*HostCpuStatsCalculator) 81 collector := &HostStatsCollector{ 82 statsCalculator: statsCalculator, 83 numCores: numCores, 84 logger: logger, 85 allocDir: allocDir, 86 } 87 return collector 88 } 89 90 // Collect collects stats related to resource usage of a host 91 func (h *HostStatsCollector) Collect() error { 92 hs := &HostStats{Timestamp: time.Now().UTC().UnixNano()} 93 memStats, err := mem.VirtualMemory() 94 if err != nil { 95 return err 96 } 97 hs.Memory = &MemoryStats{ 98 Total: memStats.Total, 99 Available: memStats.Available, 100 Used: memStats.Used, 101 Free: memStats.Free, 102 } 103 104 ticksConsumed := 0.0 105 cpuStats, err := cpu.Times(true) 106 if err != nil { 107 return err 108 } 109 cs := make([]*CPUStats, len(cpuStats)) 110 for idx, cpuStat := range cpuStats { 111 percentCalculator, ok := h.statsCalculator[cpuStat.CPU] 112 if !ok { 113 percentCalculator = NewHostCpuStatsCalculator() 114 h.statsCalculator[cpuStat.CPU] = percentCalculator 115 } 116 idle, user, system, total := percentCalculator.Calculate(cpuStat) 117 cs[idx] = &CPUStats{ 118 CPU: cpuStat.CPU, 119 User: user, 120 System: system, 121 Idle: idle, 122 Total: total, 123 } 124 ticksConsumed += (total / 100) * (shelpers.TotalTicksAvailable() / float64(len(cpuStats))) 125 } 126 hs.CPU = cs 127 hs.CPUTicksConsumed = ticksConsumed 128 129 partitions, err := disk.Partitions(false) 130 if err != nil { 131 return err 132 } 133 var diskStats []*DiskStats 134 for _, partition := range partitions { 135 usage, err := disk.Usage(partition.Mountpoint) 136 if err != nil { 137 h.logger.Printf("[WARN] client: error fetching host disk usage stats for %v: %v", partition.Mountpoint, err) 138 continue 139 } 140 ds := h.toDiskStats(usage, &partition) 141 diskStats = append(diskStats, ds) 142 } 143 hs.DiskStats = diskStats 144 145 // Getting the disk stats for the allocation directory 146 usage, err := disk.Usage(h.allocDir) 147 if err != nil { 148 return err 149 } 150 hs.AllocDirStats = h.toDiskStats(usage, nil) 151 152 uptime, err := host.Uptime() 153 if err != nil { 154 return err 155 } 156 hs.Uptime = uptime 157 158 h.hostStatsLock.Lock() 159 h.hostStats = hs 160 h.hostStatsLock.Unlock() 161 return nil 162 } 163 164 // Stats returns the host stats that has been collected 165 func (h *HostStatsCollector) Stats() *HostStats { 166 h.hostStatsLock.RLock() 167 defer h.hostStatsLock.RUnlock() 168 return h.hostStats 169 } 170 171 // toDiskStats merges UsageStat and PartitionStat to create a DiskStat 172 func (h *HostStatsCollector) toDiskStats(usage *disk.UsageStat, partitionStat *disk.PartitionStat) *DiskStats { 173 if usage == nil { 174 return nil 175 } 176 ds := DiskStats{ 177 Size: usage.Total, 178 Used: usage.Used, 179 Available: usage.Free, 180 UsedPercent: usage.UsedPercent, 181 InodesUsedPercent: usage.InodesUsedPercent, 182 } 183 if math.IsNaN(ds.UsedPercent) { 184 ds.UsedPercent = 0.0 185 } 186 if math.IsNaN(ds.InodesUsedPercent) { 187 ds.InodesUsedPercent = 0.0 188 } 189 190 if partitionStat != nil { 191 ds.Device = partitionStat.Device 192 ds.Mountpoint = partitionStat.Mountpoint 193 } 194 195 return &ds 196 } 197 198 // HostCpuStatsCalculator calculates cpu usage percentages 199 type HostCpuStatsCalculator struct { 200 prevIdle float64 201 prevUser float64 202 prevSystem float64 203 prevBusy float64 204 prevTotal float64 205 } 206 207 // NewHostCpuStatsCalculator returns a HostCpuStatsCalculator 208 func NewHostCpuStatsCalculator() *HostCpuStatsCalculator { 209 return &HostCpuStatsCalculator{} 210 } 211 212 // Calculate calculates the current cpu usage percentages 213 func (h *HostCpuStatsCalculator) Calculate(times cpu.TimesStat) (idle float64, user float64, system float64, total float64) { 214 currentIdle := times.Idle 215 currentUser := times.User 216 currentSystem := times.System 217 currentTotal := times.Total() 218 219 deltaTotal := currentTotal - h.prevTotal 220 idle = ((currentIdle - h.prevIdle) / deltaTotal) * 100 221 user = ((currentUser - h.prevUser) / deltaTotal) * 100 222 system = ((currentSystem - h.prevSystem) / deltaTotal) * 100 223 224 currentBusy := times.User + times.System + times.Nice + times.Iowait + times.Irq + 225 times.Softirq + times.Steal + times.Guest + times.GuestNice + times.Stolen 226 227 total = ((currentBusy - h.prevBusy) / deltaTotal) * 100 228 229 h.prevIdle = currentIdle 230 h.prevUser = currentUser 231 h.prevSystem = currentSystem 232 h.prevTotal = currentTotal 233 h.prevBusy = currentBusy 234 235 return 236 }