github.com/maier/nomad@v0.4.1-0.20161110003312-a9e3d0b8549d/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 and
    32  // returns the latest terminal allocations
    33  func FilterTerminalAllocs(allocs []*Allocation) ([]*Allocation, map[string]*Allocation) {
    34  	terminalAllocsByName := make(map[string]*Allocation)
    35  	n := len(allocs)
    36  	for i := 0; i < n; i++ {
    37  		if allocs[i].TerminalStatus() {
    38  
    39  			// Add the allocation to the terminal allocs map if it's not already
    40  			// added or has a higher create index than the one which is
    41  			// currently present.
    42  			alloc, ok := terminalAllocsByName[allocs[i].Name]
    43  			if !ok || alloc.CreateIndex < allocs[i].CreateIndex {
    44  				terminalAllocsByName[allocs[i].Name] = allocs[i]
    45  			}
    46  
    47  			// Remove the allocation
    48  			allocs[i], allocs[n-1] = allocs[n-1], nil
    49  			i--
    50  			n--
    51  		}
    52  	}
    53  	return allocs[:n], terminalAllocsByName
    54  }
    55  
    56  // AllocsFit checks if a given set of allocations will fit on a node.
    57  // The netIdx can optionally be provided if its already been computed.
    58  // If the netIdx is provided, it is assumed that the client has already
    59  // ensured there are no collisions.
    60  func AllocsFit(node *Node, allocs []*Allocation, netIdx *NetworkIndex) (bool, string, *Resources, error) {
    61  	// Compute the utilization from zero
    62  	used := new(Resources)
    63  
    64  	// Add the reserved resources of the node
    65  	if node.Reserved != nil {
    66  		if err := used.Add(node.Reserved); err != nil {
    67  			return false, "", nil, err
    68  		}
    69  	}
    70  
    71  	// For each alloc, add the resources
    72  	for _, alloc := range allocs {
    73  		if alloc.Resources != nil {
    74  			if err := used.Add(alloc.Resources); err != nil {
    75  				return false, "", nil, err
    76  			}
    77  		} else if alloc.TaskResources != nil {
    78  
    79  			// Adding the shared resource asks for the allocation to the used
    80  			// resources
    81  			if err := used.Add(alloc.SharedResources); err != nil {
    82  				return false, "", nil, err
    83  			}
    84  			// Allocations within the plan have the combined resources stripped
    85  			// to save space, so sum up the individual task resources.
    86  			for _, taskResource := range alloc.TaskResources {
    87  				if err := used.Add(taskResource); err != nil {
    88  					return false, "", nil, err
    89  				}
    90  			}
    91  		} else {
    92  			return false, "", nil, fmt.Errorf("allocation %q has no resources set", alloc.ID)
    93  		}
    94  	}
    95  
    96  	// Check that the node resources are a super set of those
    97  	// that are being allocated
    98  	if superset, dimension := node.Resources.Superset(used); !superset {
    99  		return false, dimension, used, nil
   100  	}
   101  
   102  	// Create the network index if missing
   103  	if netIdx == nil {
   104  		netIdx = NewNetworkIndex()
   105  		defer netIdx.Release()
   106  		if netIdx.SetNode(node) || netIdx.AddAllocs(allocs) {
   107  			return false, "reserved port collision", used, nil
   108  		}
   109  	}
   110  
   111  	// Check if the network is overcommitted
   112  	if netIdx.Overcommitted() {
   113  		return false, "bandwidth exceeded", used, nil
   114  	}
   115  
   116  	// Allocations fit!
   117  	return true, "", used, nil
   118  }
   119  
   120  // ScoreFit is used to score the fit based on the Google work published here:
   121  // http://www.columbia.edu/~cs2035/courses/ieor4405.S13/datacenter_scheduling.ppt
   122  // This is equivalent to their BestFit v3
   123  func ScoreFit(node *Node, util *Resources) float64 {
   124  	// Determine the node availability
   125  	nodeCpu := float64(node.Resources.CPU)
   126  	if node.Reserved != nil {
   127  		nodeCpu -= float64(node.Reserved.CPU)
   128  	}
   129  	nodeMem := float64(node.Resources.MemoryMB)
   130  	if node.Reserved != nil {
   131  		nodeMem -= float64(node.Reserved.MemoryMB)
   132  	}
   133  
   134  	// Compute the free percentage
   135  	freePctCpu := 1 - (float64(util.CPU) / nodeCpu)
   136  	freePctRam := 1 - (float64(util.MemoryMB) / nodeMem)
   137  
   138  	// Total will be "maximized" the smaller the value is.
   139  	// At 100% utilization, the total is 2, while at 0% util it is 20.
   140  	total := math.Pow(10, freePctCpu) + math.Pow(10, freePctRam)
   141  
   142  	// Invert so that the "maximized" total represents a high-value
   143  	// score. Because the floor is 20, we simply use that as an anchor.
   144  	// This means at a perfect fit, we return 18 as the score.
   145  	score := 20.0 - total
   146  
   147  	// Bound the score, just in case
   148  	// If the score is over 18, that means we've overfit the node.
   149  	if score > 18.0 {
   150  		score = 18.0
   151  	} else if score < 0 {
   152  		score = 0
   153  	}
   154  	return score
   155  }
   156  
   157  // GenerateUUID is used to generate a random UUID
   158  func GenerateUUID() string {
   159  	buf := make([]byte, 16)
   160  	if _, err := crand.Read(buf); err != nil {
   161  		panic(fmt.Errorf("failed to read random bytes: %v", err))
   162  	}
   163  
   164  	return fmt.Sprintf("%08x-%04x-%04x-%04x-%12x",
   165  		buf[0:4],
   166  		buf[4:6],
   167  		buf[6:8],
   168  		buf[8:10],
   169  		buf[10:16])
   170  }
   171  
   172  // Helpers for copying generic structures.
   173  func CopyMapStringString(m map[string]string) map[string]string {
   174  	l := len(m)
   175  	if l == 0 {
   176  		return nil
   177  	}
   178  
   179  	c := make(map[string]string, l)
   180  	for k, v := range m {
   181  		c[k] = v
   182  	}
   183  	return c
   184  }
   185  
   186  func CopyMapStringInt(m map[string]int) map[string]int {
   187  	l := len(m)
   188  	if l == 0 {
   189  		return nil
   190  	}
   191  
   192  	c := make(map[string]int, l)
   193  	for k, v := range m {
   194  		c[k] = v
   195  	}
   196  	return c
   197  }
   198  
   199  func CopyMapStringFloat64(m map[string]float64) map[string]float64 {
   200  	l := len(m)
   201  	if l == 0 {
   202  		return nil
   203  	}
   204  
   205  	c := make(map[string]float64, l)
   206  	for k, v := range m {
   207  		c[k] = v
   208  	}
   209  	return c
   210  }
   211  
   212  func CopySliceString(s []string) []string {
   213  	l := len(s)
   214  	if l == 0 {
   215  		return nil
   216  	}
   217  
   218  	c := make([]string, l)
   219  	for i, v := range s {
   220  		c[i] = v
   221  	}
   222  	return c
   223  }
   224  
   225  func CopySliceInt(s []int) []int {
   226  	l := len(s)
   227  	if l == 0 {
   228  		return nil
   229  	}
   230  
   231  	c := make([]int, l)
   232  	for i, v := range s {
   233  		c[i] = v
   234  	}
   235  	return c
   236  }
   237  
   238  func CopySliceConstraints(s []*Constraint) []*Constraint {
   239  	l := len(s)
   240  	if l == 0 {
   241  		return nil
   242  	}
   243  
   244  	c := make([]*Constraint, l)
   245  	for i, v := range s {
   246  		c[i] = v.Copy()
   247  	}
   248  	return c
   249  }
   250  
   251  // SliceStringIsSubset returns whether the smaller set of strings is a subset of
   252  // the larger. If the smaller slice is not a subset, the offending elements are
   253  // returned.
   254  func SliceStringIsSubset(larger, smaller []string) (bool, []string) {
   255  	largerSet := make(map[string]struct{}, len(larger))
   256  	for _, l := range larger {
   257  		largerSet[l] = struct{}{}
   258  	}
   259  
   260  	subset := true
   261  	var offending []string
   262  	for _, s := range smaller {
   263  		if _, ok := largerSet[s]; !ok {
   264  			subset = false
   265  			offending = append(offending, s)
   266  		}
   267  	}
   268  
   269  	return subset, offending
   270  }
   271  
   272  // VaultPoliciesSet takes the structure returned by VaultPolicies and returns
   273  // the set of required policies
   274  func VaultPoliciesSet(policies map[string]map[string]*Vault) []string {
   275  	set := make(map[string]struct{})
   276  
   277  	for _, tgp := range policies {
   278  		for _, tp := range tgp {
   279  			for _, p := range tp.Policies {
   280  				set[p] = struct{}{}
   281  			}
   282  		}
   283  	}
   284  
   285  	flattened := make([]string, 0, len(set))
   286  	for p := range set {
   287  		flattened = append(flattened, p)
   288  	}
   289  	return flattened
   290  }
   291  
   292  // MapStringStringSliceValueSet returns the set of values in a map[string][]string
   293  func MapStringStringSliceValueSet(m map[string][]string) []string {
   294  	set := make(map[string]struct{})
   295  	for _, slice := range m {
   296  		for _, v := range slice {
   297  			set[v] = struct{}{}
   298  		}
   299  	}
   300  
   301  	flat := make([]string, 0, len(set))
   302  	for k := range set {
   303  		flat = append(flat, k)
   304  	}
   305  	return flat
   306  }