mirror of
https://github.com/kemko/nomad.git
synced 2026-01-01 16:05:42 +03:00
196 lines
5.6 KiB
Go
196 lines
5.6 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package fingerprint
|
|
|
|
import (
|
|
"fmt"
|
|
"runtime"
|
|
"strconv"
|
|
|
|
"github.com/hashicorp/go-hclog"
|
|
"github.com/hashicorp/nomad/client/lib/idset"
|
|
"github.com/hashicorp/nomad/client/lib/numalib"
|
|
"github.com/hashicorp/nomad/client/lib/numalib/hw"
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
|
"github.com/klauspost/cpuid/v2"
|
|
)
|
|
|
|
// CPUFingerprint is used to fingerprint the CPU
|
|
type CPUFingerprint struct {
|
|
StaticFingerprinter
|
|
logger hclog.Logger
|
|
|
|
// builds this topology
|
|
top *numalib.Topology
|
|
|
|
// sets the built topology for this resources response
|
|
nodeResources *structs.NodeResources
|
|
}
|
|
|
|
// NewCPUFingerprint is used to create a CPU fingerprint.
|
|
func NewCPUFingerprint(logger hclog.Logger) Fingerprint {
|
|
return &CPUFingerprint{
|
|
logger: logger.Named("cpu"),
|
|
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(response)
|
|
|
|
f.setTotalCompute(response)
|
|
|
|
f.setNUMA(response)
|
|
|
|
f.setResponseResources(response)
|
|
|
|
// indicate we successfully detected the system cpu / memory configuration
|
|
response.Detected = true
|
|
|
|
// pass the topology back up to the client
|
|
response.UpdateInitialResult = func(ir *InitialResult) {
|
|
ir.Topology = f.top
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (*CPUFingerprint) reservedCompute(request *FingerprintRequest) structs.NodeReservedCpuResources {
|
|
switch {
|
|
case request.Config.Node == nil:
|
|
return structs.NodeReservedCpuResources{}
|
|
case request.Config.Node.ReservedResources == nil:
|
|
return structs.NodeReservedCpuResources{}
|
|
default:
|
|
return request.Config.Node.ReservedResources.Cpu
|
|
}
|
|
}
|
|
|
|
func (f *CPUFingerprint) initialize(request *FingerprintRequest) {
|
|
var (
|
|
reservableCores *idset.Set[hw.CoreID]
|
|
totalCompute = request.Config.CpuCompute
|
|
reservedCompute = f.reservedCompute(request)
|
|
reservedCores = idset.From[hw.CoreID](reservedCompute.ReservedCpuCores)
|
|
)
|
|
|
|
if rc := request.Config.ReservableCores; rc != nil {
|
|
reservableCores = idset.From[hw.CoreID](rc)
|
|
}
|
|
|
|
f.top = numalib.Scan(append(
|
|
numalib.PlatformScanners(request.Config.CpuDisableDmidecode),
|
|
&numalib.ConfigScanner{
|
|
ReservableCores: reservableCores,
|
|
ReservedCores: reservedCores,
|
|
TotalCompute: hw.MHz(totalCompute),
|
|
ReservedCompute: hw.MHz(reservedCompute.CpuShares),
|
|
},
|
|
))
|
|
}
|
|
|
|
func (f *CPUFingerprint) setModelName(response *FingerprintResponse) {
|
|
if model := cpuid.CPU.BrandName; model != "" {
|
|
response.AddAttribute("cpu.modelname", model)
|
|
f.logger.Debug("detected CPU model", "name", model)
|
|
}
|
|
}
|
|
|
|
func (*CPUFingerprint) frequency(mhz hw.MHz) string {
|
|
return strconv.FormatUint(uint64(mhz), 10)
|
|
}
|
|
|
|
func (f *CPUFingerprint) setFrequency(response *FingerprintResponse) {
|
|
performance, efficiency := f.top.CoreSpeeds()
|
|
switch {
|
|
case efficiency > 0:
|
|
response.AddAttribute("cpu.frequency.efficiency", f.frequency(efficiency))
|
|
response.AddAttribute("cpu.frequency.performance", f.frequency(performance))
|
|
f.logger.Debug("detected CPU efficiency core speed", "mhz", efficiency)
|
|
f.logger.Debug("detected CPU performance core speed", "mhz", performance)
|
|
case performance > 0:
|
|
response.AddAttribute("cpu.frequency", f.frequency(performance))
|
|
f.logger.Debug("detected CPU frequency", "mhz", performance)
|
|
}
|
|
}
|
|
|
|
func (*CPUFingerprint) cores(count int) string {
|
|
return strconv.Itoa(count)
|
|
}
|
|
|
|
func (*CPUFingerprint) nodes(count int) string {
|
|
return strconv.Itoa(count)
|
|
}
|
|
|
|
func (f *CPUFingerprint) setCoreCount(response *FingerprintResponse) {
|
|
total := f.top.NumCores()
|
|
performance := f.top.NumPCores()
|
|
efficiency := f.top.NumECores()
|
|
switch {
|
|
case efficiency > 0:
|
|
response.AddAttribute("cpu.numcores.efficiency", f.cores(efficiency))
|
|
response.AddAttribute("cpu.numcores.performance", f.cores(performance))
|
|
response.AddAttribute("cpu.numcores", f.cores(total))
|
|
f.logger.Debug("detected CPU efficiency core count", "cores", efficiency)
|
|
f.logger.Debug("detected CPU performance core count", "cores", performance)
|
|
f.logger.Debug("detected CPU core count", "cores", total)
|
|
default:
|
|
response.AddAttribute("cpu.numcores", f.cores(total))
|
|
f.logger.Debug("detected CPU core count", "cores", total)
|
|
}
|
|
}
|
|
|
|
func (f *CPUFingerprint) setReservableCores(response *FingerprintResponse) {
|
|
switch runtime.GOOS {
|
|
case "linux":
|
|
// topology has already reduced to the intersection of usable cores
|
|
usable := f.top.UsableCores()
|
|
response.AddAttribute("cpu.reservablecores", f.cores(usable.Size()))
|
|
default:
|
|
response.AddAttribute("cpu.reservablecores", "0")
|
|
}
|
|
}
|
|
|
|
func (f *CPUFingerprint) setTotalCompute(response *FingerprintResponse) {
|
|
totalCompute := f.top.TotalCompute()
|
|
usableCompute := f.top.UsableCompute()
|
|
|
|
if totalCompute == 0 {
|
|
f.logger.Error("cpu.totalcompute is zero, CPU fingerprinting likely failed", "cpu.totalcompute", totalCompute)
|
|
}
|
|
|
|
response.AddAttribute("cpu.totalcompute", f.frequency(totalCompute))
|
|
response.AddAttribute("cpu.usablecompute", f.frequency(usableCompute))
|
|
}
|
|
|
|
func (f *CPUFingerprint) setNUMA(response *FingerprintResponse) {
|
|
if !f.top.SupportsNUMA() {
|
|
return
|
|
}
|
|
|
|
nodes := f.top.GetNodes()
|
|
response.AddAttribute("numa.node.count", f.nodes(nodes.Size()))
|
|
|
|
nodes.ForEach(func(id hw.NodeID) error {
|
|
key := fmt.Sprintf("numa.node%d.cores", id)
|
|
cores := f.top.NodeCores(id)
|
|
response.AddAttribute(key, cores.String())
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func (f *CPUFingerprint) setResponseResources(response *FingerprintResponse) {
|
|
f.nodeResources.Processors = structs.NewNodeProcessorResources(f.top)
|
|
f.nodeResources.Compatibility()
|
|
response.NodeResources = f.nodeResources
|
|
}
|