forked from hashicorp/nomad
/
cpu.go
151 lines (124 loc) · 4.72 KB
/
cpu.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package fingerprint
import (
"fmt"
"strconv"
"github.com/hernad/nomad/lib/cpuset"
"github.com/hashicorp/go-hclog"
"github.com/hernad/nomad/client/stats"
shelpers "github.com/hernad/nomad/helper/stats"
"github.com/hernad/nomad/nomad/structs"
)
const (
// defaultCPUTicks is the default amount of CPU resources assumed to be
// available if the CPU performance data is unable to be detected. This is
// common on EC2 instances, where the env_aws fingerprinter will follow up,
// setting an accurate value.
defaultCPUTicks = 1000 // 1 core * 1 GHz
)
// CPUFingerprint is used to fingerprint the CPU
type CPUFingerprint struct {
StaticFingerprinter
logger hclog.Logger
// accumulates result in these resource structs
resources *structs.Resources
nodeResources *structs.NodeResources
}
// NewCPUFingerprint is used to create a CPU fingerprint
func NewCPUFingerprint(logger hclog.Logger) Fingerprint {
return &CPUFingerprint{
logger: logger.Named("cpu"),
resources: new(structs.Resources), // COMPAT (to be removed after 0.10)
nodeResources: new(structs.NodeResources),
}
}
func (f *CPUFingerprint) Fingerprint(request *FingerprintRequest, response *FingerprintResponse) error {
f.initialize(request)
f.setModelName(response)
f.setFrequency(response)
f.setCoreCount(response)
f.setReservableCores(request, response)
f.setTotalCompute(request, response)
f.setResponseResources(response)
response.Detected = true
return nil
}
func (f *CPUFingerprint) initialize(request *FingerprintRequest) {
if err := stats.Init(uint64(request.Config.CpuCompute)); err != nil {
f.logger.Warn("failed initializing stats collector", "error", err)
}
}
func (f *CPUFingerprint) setModelName(response *FingerprintResponse) {
if modelName := stats.CPUModelName(); modelName != "" {
response.AddAttribute("cpu.modelname", modelName)
f.logger.Debug("detected CPU model", "name", modelName)
}
}
func (*CPUFingerprint) frequency(mhz uint64) string {
return fmt.Sprintf("%.0f", float64(mhz))
}
func (f *CPUFingerprint) setFrequency(response *FingerprintResponse) {
power, efficiency := stats.CPUMHzPerCore()
switch {
case efficiency > 0:
response.AddAttribute("cpu.frequency.efficiency", f.frequency(efficiency))
response.AddAttribute("cpu.frequency.power", f.frequency(power))
f.logger.Debug("detected CPU efficiency core speed", "mhz", efficiency)
f.logger.Debug("detected CPU power core speed", "mhz", power)
case power > 0:
response.AddAttribute("cpu.frequency", f.frequency(power))
f.logger.Debug("detected CPU frequency", "mhz", power)
}
}
func (*CPUFingerprint) cores(count int) string {
return strconv.Itoa(count)
}
func (f *CPUFingerprint) setCoreCount(response *FingerprintResponse) {
power, efficiency := stats.CPUNumCores()
switch {
case efficiency > 0:
response.AddAttribute("cpu.numcores.efficiency", f.cores(efficiency))
response.AddAttribute("cpu.numcores.power", f.cores(power))
f.logger.Debug("detected CPU efficiency core count", "cores", efficiency)
f.logger.Debug("detected CPU power core count", "cores", power)
case power > 0:
response.AddAttribute("cpu.numcores", f.cores(power))
f.logger.Debug("detected CPU core count", power)
}
f.nodeResources.Cpu.TotalCpuCores = uint16(power + efficiency)
}
func (f *CPUFingerprint) setReservableCores(request *FingerprintRequest, response *FingerprintResponse) {
reservable := request.Config.ReservableCores
if len(reservable) > 0 {
f.logger.Debug("reservable cores set by config", "cpuset", reservable)
} else {
cgroupParent := request.Config.CgroupParent
if reservable = f.deriveReservableCores(cgroupParent); reservable != nil {
if request.Node.ReservedResources != nil {
forNode := request.Node.ReservedResources.Cpu.ReservedCpuCores
reservable = cpuset.New(reservable...).Difference(cpuset.New(forNode...)).ToSlice()
f.logger.Debug("client configuration reserves these cores for node", "cores", forNode)
}
f.logger.Debug("set of reservable cores available for tasks", "cores", reservable)
}
}
response.AddAttribute("cpu.reservablecores", strconv.Itoa(len(reservable)))
f.nodeResources.Cpu.ReservableCpuCores = reservable
}
func (f *CPUFingerprint) setTotalCompute(request *FingerprintRequest, response *FingerprintResponse) {
var ticks uint64
switch {
case shelpers.CpuTotalTicks() > 0:
ticks = shelpers.CpuTotalTicks()
default:
ticks = defaultCPUTicks
}
response.AddAttribute("cpu.totalcompute", fmt.Sprintf("%d", ticks))
f.resources.CPU = int(ticks)
f.nodeResources.Cpu.CpuShares = int64(ticks)
}
func (f *CPUFingerprint) setResponseResources(response *FingerprintResponse) {
response.Resources = f.resources
response.NodeResources = f.nodeResources
}