github.com/kardianos/nomad@v0.1.3-0.20151022182107-b13df73ee850/nomad/structs/funcs.go (about)

     1  package structs
     2  
     3  import (
     4  	crand "crypto/rand"
     5  	"fmt"
     6  	"math"
     7  )
     8  
     9  // RemoveAllocs is used to remove any allocs with the given IDs
    10  // from the list of allocations
    11  func RemoveAllocs(alloc []*Allocation, remove []*Allocation) []*Allocation {
    12  	// Convert remove into a set
    13  	removeSet := make(map[string]struct{})
    14  	for _, remove := range remove {
    15  		removeSet[remove.ID] = struct{}{}
    16  	}
    17  
    18  	n := len(alloc)
    19  	for i := 0; i < n; i++ {
    20  		if _, ok := removeSet[alloc[i].ID]; ok {
    21  			alloc[i], alloc[n-1] = alloc[n-1], nil
    22  			i--
    23  			n--
    24  		}
    25  	}
    26  
    27  	alloc = alloc[:n]
    28  	return alloc
    29  }
    30  
    31  // FilterTerminalAllocs filters out all allocations in a terminal state
    32  func FilterTerminalAllocs(allocs []*Allocation) []*Allocation {
    33  	n := len(allocs)
    34  	for i := 0; i < n; i++ {
    35  		if allocs[i].TerminalStatus() {
    36  			allocs[i], allocs[n-1] = allocs[n-1], nil
    37  			i--
    38  			n--
    39  		}
    40  	}
    41  	return allocs[:n]
    42  }
    43  
    44  // AllocsFit checks if a given set of allocations will fit on a node.
    45  // The netIdx can optionally be provided if its already been computed.
    46  // If the netIdx is provided, it is assumed that the client has already
    47  // ensured there are no collisions.
    48  func AllocsFit(node *Node, allocs []*Allocation, netIdx *NetworkIndex) (bool, string, *Resources, error) {
    49  	// Compute the utilization from zero
    50  	used := new(Resources)
    51  
    52  	// Add the reserved resources of the node
    53  	if node.Reserved != nil {
    54  		if err := used.Add(node.Reserved); err != nil {
    55  			return false, "", nil, err
    56  		}
    57  	}
    58  
    59  	// For each alloc, add the resources
    60  	for _, alloc := range allocs {
    61  		if err := used.Add(alloc.Resources); err != nil {
    62  			return false, "", nil, err
    63  		}
    64  	}
    65  
    66  	// Check that the node resources are a super set of those
    67  	// that are being allocated
    68  	if superset, dimension := node.Resources.Superset(used); !superset {
    69  		return false, dimension, used, nil
    70  	}
    71  
    72  	// Create the network index if missing
    73  	if netIdx == nil {
    74  		netIdx = NewNetworkIndex()
    75  		if netIdx.SetNode(node) || netIdx.AddAllocs(allocs) {
    76  			return false, "reserved port collision", used, nil
    77  		}
    78  	}
    79  
    80  	// Check if the network is overcommitted
    81  	if netIdx.Overcommitted() {
    82  		return false, "bandwidth exceeded", used, nil
    83  	}
    84  
    85  	// Allocations fit!
    86  	return true, "", used, nil
    87  }
    88  
    89  // ScoreFit is used to score the fit based on the Google work published here:
    90  // http://www.columbia.edu/~cs2035/courses/ieor4405.S13/datacenter_scheduling.ppt
    91  // This is equivalent to their BestFit v3
    92  func ScoreFit(node *Node, util *Resources) float64 {
    93  	// Determine the node availability
    94  	nodeCpu := float64(node.Resources.CPU)
    95  	if node.Reserved != nil {
    96  		nodeCpu -= float64(node.Reserved.CPU)
    97  	}
    98  	nodeMem := float64(node.Resources.MemoryMB)
    99  	if node.Reserved != nil {
   100  		nodeMem -= float64(node.Reserved.MemoryMB)
   101  	}
   102  
   103  	// Compute the free percentage
   104  	freePctCpu := 1 - (float64(util.CPU) / nodeCpu)
   105  	freePctRam := 1 - (float64(util.MemoryMB) / nodeMem)
   106  
   107  	// Total will be "maximized" the smaller the value is.
   108  	// At 100% utilization, the total is 2, while at 0% util it is 20.
   109  	total := math.Pow(10, freePctCpu) + math.Pow(10, freePctRam)
   110  
   111  	// Invert so that the "maximized" total represents a high-value
   112  	// score. Because the floor is 20, we simply use that as an anchor.
   113  	// This means at a perfect fit, we return 18 as the score.
   114  	score := 20.0 - total
   115  
   116  	// Bound the score, just in case
   117  	// If the score is over 18, that means we've overfit the node.
   118  	if score > 18.0 {
   119  		score = 18.0
   120  	} else if score < 0 {
   121  		score = 0
   122  	}
   123  	return score
   124  }
   125  
   126  // GenerateUUID is used to generate a random UUID
   127  func GenerateUUID() string {
   128  	buf := make([]byte, 16)
   129  	if _, err := crand.Read(buf); err != nil {
   130  		panic(fmt.Errorf("failed to read random bytes: %v", err))
   131  	}
   132  
   133  	return fmt.Sprintf("%08x-%04x-%04x-%04x-%12x",
   134  		buf[0:4],
   135  		buf[4:6],
   136  		buf[6:8],
   137  		buf[8:10],
   138  		buf[10:16])
   139  }