open-nomad/nomad/structs/funcs.go

151 lines
3.7 KiB
Go
Raw Normal View History

2015-08-05 00:19:05 +00:00
package structs
2015-09-07 22:08:50 +00:00
import (
crand "crypto/rand"
"fmt"
"math"
)
2015-08-13 18:54:59 +00:00
2015-08-05 00:19:05 +00:00
// RemoveAllocs is used to remove any allocs with the given IDs
// from the list of allocations
func RemoveAllocs(alloc []*Allocation, remove []*Allocation) []*Allocation {
2015-08-05 00:19:05 +00:00
// Convert remove into a set
removeSet := make(map[string]struct{})
for _, remove := range remove {
removeSet[remove.ID] = struct{}{}
2015-08-05 00:19:05 +00:00
}
n := len(alloc)
for i := 0; i < n; i++ {
if _, ok := removeSet[alloc[i].ID]; ok {
alloc[i], alloc[n-1] = alloc[n-1], nil
i--
n--
}
}
alloc = alloc[:n]
return alloc
}
2015-08-05 00:28:19 +00:00
2015-08-23 01:27:51 +00:00
// FilterTerminalAllocs filters out all allocations in a terminal state
func FilterTerminalAllocs(allocs []*Allocation) []*Allocation {
n := len(allocs)
for i := 0; i < n; i++ {
if allocs[i].TerminalStatus() {
allocs[i], allocs[n-1] = allocs[n-1], nil
i--
n--
}
}
return allocs[:n]
}
2015-08-05 00:28:19 +00:00
// PortsOvercommited checks if any ports are over-committed.
// This does not handle CIDR subsets, and computes for the entire
// CIDR block currently.
func PortsOvercommited(r *Resources) bool {
for _, net := range r.Networks {
ports := make(map[int]struct{})
for _, port := range net.ReservedPorts {
if _, ok := ports[port]; ok {
return true
}
ports[port] = struct{}{}
}
}
return false
}
2015-08-05 00:48:24 +00:00
// AllocsFit checks if a given set of allocations will fit on a node
2015-08-13 18:54:59 +00:00
func AllocsFit(node *Node, allocs []*Allocation) (bool, *Resources, error) {
2015-08-05 00:48:24 +00:00
// Compute the utilization from zero
used := new(Resources)
for _, net := range node.Resources.Networks {
used.Networks = append(used.Networks, &NetworkResource{
Public: net.Public,
CIDR: net.CIDR,
})
}
// Add the reserved resources of the node
if node.Reserved != nil {
if err := used.Add(node.Reserved); err != nil {
2015-08-13 18:54:59 +00:00
return false, nil, err
2015-08-05 00:48:24 +00:00
}
}
// For each alloc, add the resources
for _, alloc := range allocs {
if err := used.Add(alloc.Resources); err != nil {
2015-08-13 18:54:59 +00:00
return false, nil, err
2015-08-05 00:48:24 +00:00
}
}
// Check that the node resources are a super set of those
// that are being allocated
if !node.Resources.Superset(used) {
2015-08-13 18:54:59 +00:00
return false, used, nil
2015-08-05 00:48:24 +00:00
}
// Ensure ports are not over commited
if PortsOvercommited(used) {
2015-08-13 18:54:59 +00:00
return false, used, nil
2015-08-05 00:48:24 +00:00
}
// Allocations fit!
2015-08-13 18:54:59 +00:00
return true, used, nil
}
// ScoreFit is used to score the fit based on the Google work published here:
// http://www.columbia.edu/~cs2035/courses/ieor4405.S13/datacenter_scheduling.ppt
// This is equivalent to their BestFit v3
func ScoreFit(node *Node, util *Resources) float64 {
// Determine the node availability
nodeCpu := node.Resources.CPU
if node.Reserved != nil {
nodeCpu -= node.Reserved.CPU
}
nodeMem := float64(node.Resources.MemoryMB)
if node.Reserved != nil {
nodeMem -= float64(node.Reserved.MemoryMB)
}
// Compute the free percentage
freePctCpu := 1 - (util.CPU / nodeCpu)
freePctRam := 1 - (float64(util.MemoryMB) / nodeMem)
// Total will be "maximized" the smaller the value is.
// At 100% utilization, the total is 2, while at 0% util it is 20.
total := math.Pow(10, freePctCpu) + math.Pow(10, freePctRam)
// Invert so that the "maximized" total represents a high-value
// score. Because the floor is 20, we simply use that as an anchor.
// This means at a perfect fit, we return 18 as the score.
score := 20.0 - total
// Bound the score, just in case
// If the score is over 18, that means we've overfit the node.
if score > 18.0 {
score = 18.0
} else if score < 0 {
score = 0
}
return score
2015-08-05 00:48:24 +00:00
}
2015-09-07 22:08:50 +00:00
// GenerateUUID is used to generate a random UUID
func GenerateUUID() string {
buf := make([]byte, 16)
if _, err := crand.Read(buf); err != nil {
panic(fmt.Errorf("failed to read random bytes: %v", err))
}
return fmt.Sprintf("%08x-%04x-%04x-%04x-%12x",
buf[0:4],
buf[4:6],
buf[6:8],
buf[8:10],
buf[10:16])
}