github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/client/fingerprint/cpu.go (about) 1 package fingerprint 2 3 import ( 4 "fmt" 5 "strconv" 6 7 "github.com/hashicorp/nomad/lib/cpuset" 8 9 log "github.com/hashicorp/go-hclog" 10 "github.com/hashicorp/nomad/helper/stats" 11 "github.com/hashicorp/nomad/nomad/structs" 12 ) 13 14 const ( 15 // defaultCPUTicks is the default amount of CPU resources assumed to be 16 // available if the CPU performance data is unable to be detected. This is 17 // common on EC2 instances, where the env_aws fingerprinter will follow up, 18 // setting an accurate value. 19 defaultCPUTicks = 1000 // 1 core * 1 GHz 20 ) 21 22 // CPUFingerprint is used to fingerprint the CPU 23 type CPUFingerprint struct { 24 StaticFingerprinter 25 logger log.Logger 26 } 27 28 // NewCPUFingerprint is used to create a CPU fingerprint 29 func NewCPUFingerprint(logger log.Logger) Fingerprint { 30 f := &CPUFingerprint{logger: logger.Named("cpu")} 31 return f 32 } 33 34 func (f *CPUFingerprint) Fingerprint(req *FingerprintRequest, resp *FingerprintResponse) error { 35 cfg := req.Config 36 setResourcesCPU := func(totalCompute int, totalCores uint16, reservableCores []uint16) { 37 // COMPAT(0.10): Remove in 0.10 38 resp.Resources = &structs.Resources{ 39 CPU: totalCompute, 40 } 41 42 resp.NodeResources = &structs.NodeResources{ 43 Cpu: structs.NodeCpuResources{ 44 CpuShares: int64(totalCompute), 45 TotalCpuCores: totalCores, 46 ReservableCpuCores: reservableCores, 47 }, 48 } 49 } 50 51 if err := stats.Init(); err != nil { 52 f.logger.Warn("failed initializing stats collector", "error", err) 53 } 54 55 if modelName := stats.CPUModelName(); modelName != "" { 56 resp.AddAttribute("cpu.modelname", modelName) 57 } 58 59 if mhz := stats.CPUMHzPerCore(); mhz > 0 { 60 resp.AddAttribute("cpu.frequency", fmt.Sprintf("%.0f", mhz)) 61 f.logger.Debug("detected cpu frequency", "MHz", log.Fmt("%.0f", mhz)) 62 } 63 64 var numCores int 65 if numCores = stats.CPUNumCores(); numCores > 0 { 66 resp.AddAttribute("cpu.numcores", strconv.Itoa(numCores)) 67 f.logger.Debug("detected core count", "cores", numCores) 68 } 69 70 var reservableCores []uint16 71 if req.Config.ReservableCores != nil { 72 reservableCores = req.Config.ReservableCores 73 f.logger.Debug("reservable cores set by config", "cpuset", reservableCores) 74 } else { 75 if cores, err := f.deriveReservableCores(req); err != nil { 76 f.logger.Warn("failed to detect set of reservable cores", "error", err) 77 } else { 78 if req.Node.ReservedResources != nil { 79 reservableCores = cpuset.New(cores...).Difference(cpuset.New(req.Node.ReservedResources.Cpu.ReservedCpuCores...)).ToSlice() 80 } 81 f.logger.Debug("detected reservable cores", "cpuset", reservableCores) 82 } 83 } 84 resp.AddAttribute("cpu.reservablecores", strconv.Itoa(len(reservableCores))) 85 86 tt := int(stats.TotalTicksAvailable()) 87 if cfg.CpuCompute > 0 { 88 f.logger.Debug("using user specified cpu compute", "cpu_compute", cfg.CpuCompute) 89 tt = cfg.CpuCompute 90 } 91 92 // If we cannot detect the cpu total compute, fallback to a very low default 93 // value and log a message about configuring cpu_total_compute. This happens 94 // on Graviton instances where CPU information is unavailable. In that case, 95 // the env_aws fingerprinter updates the value with correct information. 96 if tt == 0 { 97 f.logger.Info("fallback to default cpu total compute, set client config option cpu_total_compute to override") 98 tt = defaultCPUTicks 99 } 100 101 resp.AddAttribute("cpu.totalcompute", fmt.Sprintf("%d", tt)) 102 setResourcesCPU(tt, uint16(numCores), reservableCores) 103 resp.Detected = true 104 105 return nil 106 }