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