Merge pull request #2745 from hashicorp/b-2638-cpu_total_compute
Fix cpu_total_compute override
This commit is contained in:
commit
6b7d7a41f8
|
@ -31,39 +31,48 @@ func (f *CPUFingerprint) Fingerprint(cfg *config.Config, node *structs.Node) (bo
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := stats.Init(); err != nil {
|
if err := stats.Init(); err != nil {
|
||||||
err := fmt.Errorf("Unable to obtain CPU information: %v", err)
|
f.logger.Printf("[WARN] fingerprint.cpu: %v", err)
|
||||||
|
|
||||||
if cfg.CpuCompute != 0 {
|
|
||||||
f.logger.Printf("[DEBUG] fingerprint.cpu: %v. Using specified cpu compute %d", err, cfg.CpuCompute)
|
|
||||||
setResources(cfg.CpuCompute)
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
f.logger.Printf("[ERR] fingerprint.cpu: %v", err)
|
|
||||||
f.logger.Printf("[INFO] fingerprint.cpu: cpu compute may be set manually"+
|
|
||||||
" using the client config option %q on machines where cpu information"+
|
|
||||||
" can not be automatically detected.", "cpu_total_compute")
|
|
||||||
|
|
||||||
return false, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
modelName := stats.CPUModelName()
|
if cfg.CpuCompute != 0 {
|
||||||
if modelName != "" {
|
setResources(cfg.CpuCompute)
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if modelName := stats.CPUModelName(); modelName != "" {
|
||||||
node.Attributes["cpu.modelname"] = modelName
|
node.Attributes["cpu.modelname"] = modelName
|
||||||
}
|
}
|
||||||
|
|
||||||
mhz := stats.CPUMHzPerCore()
|
if mhz := stats.CPUMHzPerCore(); mhz > 0 {
|
||||||
node.Attributes["cpu.frequency"] = fmt.Sprintf("%.0f", mhz)
|
node.Attributes["cpu.frequency"] = fmt.Sprintf("%.0f", mhz)
|
||||||
f.logger.Printf("[DEBUG] fingerprint.cpu: frequency: %.0f MHz", mhz)
|
f.logger.Printf("[DEBUG] fingerprint.cpu: frequency: %.0f MHz", mhz)
|
||||||
|
}
|
||||||
|
|
||||||
numCores := stats.CPUNumCores()
|
if numCores := stats.CPUNumCores(); numCores > 0 {
|
||||||
node.Attributes["cpu.numcores"] = fmt.Sprintf("%d", numCores)
|
node.Attributes["cpu.numcores"] = fmt.Sprintf("%d", numCores)
|
||||||
f.logger.Printf("[DEBUG] fingerprint.cpu: core count: %d", numCores)
|
f.logger.Printf("[DEBUG] fingerprint.cpu: core count: %d", numCores)
|
||||||
|
}
|
||||||
|
|
||||||
tt := stats.TotalTicksAvailable()
|
tt := int(stats.TotalTicksAvailable())
|
||||||
|
if cfg.CpuCompute > 0 {
|
||||||
|
f.logger.Printf("[DEBUG] fingerprint.cpu: Using specified cpu compute %d", cfg.CpuCompute)
|
||||||
|
tt = cfg.CpuCompute
|
||||||
|
}
|
||||||
|
|
||||||
node.Attributes["cpu.totalcompute"] = fmt.Sprintf("%.0f", tt)
|
// Return an error if no cpu was detected or explicitly set as this
|
||||||
|
// node would be unable to receive any allocations.
|
||||||
|
if tt == 0 {
|
||||||
|
return false, fmt.Errorf("cannot detect cpu total compute. "+
|
||||||
|
"CPU compute must be set manually using the client config option %q",
|
||||||
|
"cpu_total_compute")
|
||||||
|
}
|
||||||
|
|
||||||
setResources(int(tt))
|
node.Attributes["cpu.totalcompute"] = fmt.Sprintf("%d", tt)
|
||||||
|
|
||||||
|
if node.Resources == nil {
|
||||||
|
node.Resources = &structs.Resources{}
|
||||||
|
}
|
||||||
|
|
||||||
|
node.Resources.CPU = tt
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,3 +40,39 @@ func TestCPUFingerprint(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestCPUFingerprint_OverrideCompute asserts that setting cpu_total_compute in
|
||||||
|
// the client config overrides the detected CPU freq (if any).
|
||||||
|
func TestCPUFingerprint_OverrideCompute(t *testing.T) {
|
||||||
|
f := NewCPUFingerprint(testLogger())
|
||||||
|
node := &structs.Node{
|
||||||
|
Attributes: make(map[string]string),
|
||||||
|
}
|
||||||
|
cfg := &config.Config{}
|
||||||
|
ok, err := f.Fingerprint(cfg, node)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("should apply")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get actual system CPU
|
||||||
|
origCPU := node.Resources.CPU
|
||||||
|
|
||||||
|
// Override it with a setting
|
||||||
|
cfg.CpuCompute = origCPU + 123
|
||||||
|
|
||||||
|
// Make sure the Fingerprinter applies the override
|
||||||
|
ok, err = f.Fingerprint(cfg, node)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %v", err)
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("should apply")
|
||||||
|
}
|
||||||
|
|
||||||
|
if node.Resources.CPU != cfg.CpuCompute {
|
||||||
|
t.Fatalf("expected override cpu of %d but found %d", cfg.CpuCompute, node.Resources.CPU)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"math"
|
"math"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
multierror "github.com/hashicorp/go-multierror"
|
||||||
"github.com/shirou/gopsutil/cpu"
|
"github.com/shirou/gopsutil/cpu"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,15 +21,15 @@ var (
|
||||||
|
|
||||||
func Init() error {
|
func Init() error {
|
||||||
onceLer.Do(func() {
|
onceLer.Do(func() {
|
||||||
if cpuNumCores, initErr = cpu.Counts(true); initErr != nil {
|
var merrs *multierror.Error
|
||||||
initErr = fmt.Errorf("Unable to determine the number of CPU cores available: %v", initErr)
|
var err error
|
||||||
return
|
if cpuNumCores, err = cpu.Counts(true); err != nil {
|
||||||
|
merrs = multierror.Append(merrs, fmt.Errorf("Unable to determine the number of CPU cores available: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
var cpuInfo []cpu.InfoStat
|
var cpuInfo []cpu.InfoStat
|
||||||
if cpuInfo, initErr = cpu.Info(); initErr != nil {
|
if cpuInfo, err = cpu.Info(); err != nil {
|
||||||
initErr = fmt.Errorf("Unable to obtain CPU information: %v", initErr)
|
merrs = multierror.Append(merrs, fmt.Errorf("Unable to obtain CPU information: %v", initErr))
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, cpu := range cpuInfo {
|
for _, cpu := range cpuInfo {
|
||||||
|
@ -41,6 +42,9 @@ func Init() error {
|
||||||
// node to fall into a unique computed node class
|
// node to fall into a unique computed node class
|
||||||
cpuMhzPerCore = math.Floor(cpuMhzPerCore)
|
cpuMhzPerCore = math.Floor(cpuMhzPerCore)
|
||||||
cpuTotalTicks = math.Floor(float64(cpuNumCores) * cpuMhzPerCore)
|
cpuTotalTicks = math.Floor(float64(cpuNumCores) * cpuMhzPerCore)
|
||||||
|
|
||||||
|
// Set any errors that occurred
|
||||||
|
initErr = merrs.ErrorOrNil()
|
||||||
})
|
})
|
||||||
return initErr
|
return initErr
|
||||||
}
|
}
|
||||||
|
@ -60,8 +64,7 @@ func CPUModelName() string {
|
||||||
return cpuModelName
|
return cpuModelName
|
||||||
}
|
}
|
||||||
|
|
||||||
// TotalTicksAvailable calculates the total frequency available across all
|
// TotalTicksAvailable calculates the total Mhz available across all cores
|
||||||
// cores
|
|
||||||
func TotalTicksAvailable() float64 {
|
func TotalTicksAvailable() float64 {
|
||||||
return cpuTotalTicks
|
return cpuTotalTicks
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,14 +114,20 @@ Below is a table documenting common node properties:
|
||||||
<td><tt>${attr.cpu.arch}</tt></td>
|
<td><tt>${attr.cpu.arch}</tt></td>
|
||||||
<td>CPU architecture of the client (e.g. <tt>amd64</tt>, <tt>386</tt>)</td>
|
<td>CPU architecture of the client (e.g. <tt>amd64</tt>, <tt>386</tt>)</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td><tt>${attr.consul.datacenter}</tt></td>
|
|
||||||
<td>The Consul datacenter of the client (if Consul is found)</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td><tt>${attr.cpu.numcores}</tt></td>
|
<td><tt>${attr.cpu.numcores}</tt></td>
|
||||||
<td>Number of CPU cores on the client</td>
|
<td>Number of CPU cores on the client</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><tt>${attr.cpu.totalcompute}</tt></td>
|
||||||
|
<td>
|
||||||
|
<tt>cpu.frequency × cpu.numcores</tt> but may be overridden by <tt>client.cpu_total_compute</tt>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><tt>${attr.consul.datacenter}</tt></td>
|
||||||
|
<td>The Consul datacenter of the client (if Consul is found)</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><tt>${attr.driver.<property>}</tt></td>
|
<td><tt>${attr.driver.<property>}</tt></td>
|
||||||
<td>See the [task drivers](/docs/drivers/index.html) for property documentation</td>
|
<td>See the [task drivers](/docs/drivers/index.html) for property documentation</td>
|
||||||
|
|
Loading…
Reference in New Issue