github.com/ranjib/nomad@v0.1.1-0.20160225204057-97751b02f70b/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  		defer netIdx.Release()
    76  		if netIdx.SetNode(node) || netIdx.AddAllocs(allocs) {
    77  			return false, "reserved port collision", used, nil
    78  		}
    79  	}
    80  
    81  	// Check if the network is overcommitted
    82  	if netIdx.Overcommitted() {
    83  		return false, "bandwidth exceeded", used, nil
    84  	}
    85  
    86  	// Allocations fit!
    87  	return true, "", used, nil
    88  }
    89  
    90  // ScoreFit is used to score the fit based on the Google work published here:
    91  // http://www.columbia.edu/~cs2035/courses/ieor4405.S13/datacenter_scheduling.ppt
    92  // This is equivalent to their BestFit v3
    93  func ScoreFit(node *Node, util *Resources) float64 {
    94  	// Determine the node availability
    95  	nodeCpu := float64(node.Resources.CPU)
    96  	if node.Reserved != nil {
    97  		nodeCpu -= float64(node.Reserved.CPU)
    98  	}
    99  	nodeMem := float64(node.Resources.MemoryMB)
   100  	if node.Reserved != nil {
   101  		nodeMem -= float64(node.Reserved.MemoryMB)
   102  	}
   103  
   104  	// Compute the free percentage
   105  	freePctCpu := 1 - (float64(util.CPU) / nodeCpu)
   106  	freePctRam := 1 - (float64(util.MemoryMB) / nodeMem)
   107  
   108  	// Total will be "maximized" the smaller the value is.
   109  	// At 100% utilization, the total is 2, while at 0% util it is 20.
   110  	total := math.Pow(10, freePctCpu) + math.Pow(10, freePctRam)
   111  
   112  	// Invert so that the "maximized" total represents a high-value
   113  	// score. Because the floor is 20, we simply use that as an anchor.
   114  	// This means at a perfect fit, we return 18 as the score.
   115  	score := 20.0 - total
   116  
   117  	// Bound the score, just in case
   118  	// If the score is over 18, that means we've overfit the node.
   119  	if score > 18.0 {
   120  		score = 18.0
   121  	} else if score < 0 {
   122  		score = 0
   123  	}
   124  	return score
   125  }
   126  
   127  // GenerateUUID is used to generate a random UUID
   128  func GenerateUUID() string {
   129  	buf := make([]byte, 16)
   130  	if _, err := crand.Read(buf); err != nil {
   131  		panic(fmt.Errorf("failed to read random bytes: %v", err))
   132  	}
   133  
   134  	return fmt.Sprintf("%08x-%04x-%04x-%04x-%12x",
   135  		buf[0:4],
   136  		buf[4:6],
   137  		buf[6:8],
   138  		buf[8:10],
   139  		buf[10:16])
   140  }
   141  
   142  // Helpers for copying generic structures.
   143  func CopyMapStringString(m map[string]string) map[string]string {
   144  	l := len(m)
   145  	if l == 0 {
   146  		return nil
   147  	}
   148  
   149  	c := make(map[string]string, l)
   150  	for k, v := range m {
   151  		c[k] = v
   152  	}
   153  	return c
   154  }
   155  
   156  func CopyMapStringInt(m map[string]int) map[string]int {
   157  	l := len(m)
   158  	if l == 0 {
   159  		return nil
   160  	}
   161  
   162  	c := make(map[string]int, l)
   163  	for k, v := range m {
   164  		c[k] = v
   165  	}
   166  	return c
   167  }
   168  
   169  func CopyMapStringFloat64(m map[string]float64) map[string]float64 {
   170  	l := len(m)
   171  	if l == 0 {
   172  		return nil
   173  	}
   174  
   175  	c := make(map[string]float64, l)
   176  	for k, v := range m {
   177  		c[k] = v
   178  	}
   179  	return c
   180  }
   181  
   182  func CopySliceString(s []string) []string {
   183  	l := len(s)
   184  	if l == 0 {
   185  		return nil
   186  	}
   187  
   188  	c := make([]string, l)
   189  	for i, v := range s {
   190  		c[i] = v
   191  	}
   192  	return c
   193  }
   194  
   195  func CopySliceConstraints(s []*Constraint) []*Constraint {
   196  	l := len(s)
   197  	if l == 0 {
   198  		return nil
   199  	}
   200  
   201  	c := make([]*Constraint, l)
   202  	for i, v := range s {
   203  		c[i] = v.Copy()
   204  	}
   205  	return c
   206  }