open-nomad/scheduler/device.go

137 lines
3.4 KiB
Go
Raw Normal View History

2018-10-15 22:15:46 +00:00
package scheduler
import (
"fmt"
"math"
2018-10-15 22:15:46 +00:00
"github.com/hashicorp/nomad/nomad/structs"
)
2018-10-17 18:04:54 +00:00
// deviceAllocator is used to allocate devices to allocations. The allocator
// tracks availability as to not double allocate devices.
2018-10-15 22:15:46 +00:00
type deviceAllocator struct {
2018-10-17 21:26:25 +00:00
*structs.DeviceAccounter
2018-10-15 22:15:46 +00:00
2018-10-17 21:26:25 +00:00
ctx Context
2018-10-15 22:15:46 +00:00
}
2018-10-17 18:04:54 +00:00
// newDeviceAllocator returns a new device allocator. The node is used to
// populate the set of available devices based on what healthy device instances
// exist on the node.
2018-10-15 22:15:46 +00:00
func newDeviceAllocator(ctx Context, n *structs.Node) *deviceAllocator {
2018-10-17 21:26:25 +00:00
return &deviceAllocator{
ctx: ctx,
DeviceAccounter: structs.NewDeviceAccounter(n),
2018-10-15 22:15:46 +00:00
}
}
2018-10-17 18:04:54 +00:00
// AssignDevice takes a device request and returns an assignment as well as a
// score for the assignment. If no assignment could be made, an error is
// returned explaining why.
func (d *deviceAllocator) AssignDevice(ask *structs.RequestedDevice) (out *structs.AllocatedDeviceResource, score float64, err error) {
2018-10-15 22:15:46 +00:00
// Try to hot path
2018-10-17 21:26:25 +00:00
if len(d.Devices) == 0 {
2018-10-17 18:04:54 +00:00
return nil, 0.0, fmt.Errorf("no devices available")
2018-10-15 22:15:46 +00:00
}
if ask.Count == 0 {
2018-10-17 18:04:54 +00:00
return nil, 0.0, fmt.Errorf("invalid request of zero devices")
2018-10-15 22:15:46 +00:00
}
// Hold the current best offer
var offer *structs.AllocatedDeviceResource
2018-10-17 18:04:54 +00:00
var offerScore float64
2018-11-08 23:01:58 +00:00
var matchedWeights float64
2018-10-15 22:15:46 +00:00
// Determine the devices that are feasible based on availability and
// constraints
2018-10-17 21:26:25 +00:00
for id, devInst := range d.Devices {
2018-10-15 22:15:46 +00:00
// Check if we have enough unused instances to use this
assignable := uint64(0)
2018-10-17 21:26:25 +00:00
for _, v := range devInst.Instances {
2018-10-15 22:15:46 +00:00
if v == 0 {
assignable++
}
}
// This device doesn't have enough instances
if assignable < ask.Count {
continue
}
// Check if the device works
2018-10-17 21:26:25 +00:00
if !nodeDeviceMatches(d.ctx, devInst.Device, ask) {
2018-10-15 22:15:46 +00:00
continue
}
// Score the choice
var choiceScore float64
// Track the sum of matched affinity weights in a separate variable
// We return this if this device had the best score compared to other devices considered
var sumMatchedWeights float64
2018-10-17 18:04:54 +00:00
if l := len(ask.Affinities); l != 0 {
2018-10-31 20:57:43 +00:00
totalWeight := 0.0
2018-10-17 18:04:54 +00:00
for _, a := range ask.Affinities {
// Resolve the targets
2018-10-17 21:26:25 +00:00
lVal, ok := resolveDeviceTarget(a.LTarget, devInst.Device)
2018-10-17 18:04:54 +00:00
if !ok {
continue
}
2018-10-17 21:26:25 +00:00
rVal, ok := resolveDeviceTarget(a.RTarget, devInst.Device)
2018-10-17 18:04:54 +00:00
if !ok {
continue
}
totalWeight += math.Abs(a.Weight)
2018-10-31 20:57:43 +00:00
2018-10-17 18:04:54 +00:00
// Check if satisfied
if !checkAttributeAffinity(d.ctx, a.Operand, lVal, rVal) {
continue
}
choiceScore += a.Weight
sumMatchedWeights += a.Weight
2018-10-17 18:04:54 +00:00
}
// normalize
2018-10-31 20:57:43 +00:00
choiceScore /= totalWeight
2018-10-15 22:15:46 +00:00
}
2018-10-17 18:04:54 +00:00
// Only use the device if it is a higher score than we have already seen
if offer != nil && choiceScore < offerScore {
2018-10-15 22:15:46 +00:00
continue
}
// Set the new highest score
2018-10-17 18:04:54 +00:00
offerScore = choiceScore
2018-10-15 22:15:46 +00:00
// Set the new sum of matching affinity weights
matchedWeights = sumMatchedWeights
2018-10-15 22:15:46 +00:00
// Build the choice
offer = &structs.AllocatedDeviceResource{
Vendor: id.Vendor,
Type: id.Type,
Name: id.Name,
DeviceIDs: make([]string, 0, ask.Count),
}
assigned := uint64(0)
2018-10-17 21:26:25 +00:00
for id, v := range devInst.Instances {
2018-10-15 22:15:46 +00:00
if v == 0 && assigned < ask.Count {
assigned++
offer.DeviceIDs = append(offer.DeviceIDs, id)
if assigned == ask.Count {
break
}
}
}
}
2018-10-17 18:04:54 +00:00
// Failed to find a match
2018-10-15 22:15:46 +00:00
if offer == nil {
2018-10-17 18:04:54 +00:00
return nil, 0.0, fmt.Errorf("no devices match request")
2018-10-15 22:15:46 +00:00
}
2018-11-08 23:01:58 +00:00
return offer, matchedWeights, nil
2018-10-15 22:15:46 +00:00
}