github.com/taylorchu/nomad@v0.5.3-rc1.0.20170407200202-db11e7dd7b55/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  func CopySliceConstraints(s []*Constraint) []*Constraint {
   173  	l := len(s)
   174  	if l == 0 {
   175  		return nil
   176  	}
   177  
   178  	c := make([]*Constraint, l)
   179  	for i, v := range s {
   180  		c[i] = v.Copy()
   181  	}
   182  	return c
   183  }
   184  
   185  // VaultPoliciesSet takes the structure returned by VaultPolicies and returns
   186  // the set of required policies
   187  func VaultPoliciesSet(policies map[string]map[string]*Vault) []string {
   188  	set := make(map[string]struct{})
   189  
   190  	for _, tgp := range policies {
   191  		for _, tp := range tgp {
   192  			for _, p := range tp.Policies {
   193  				set[p] = struct{}{}
   194  			}
   195  		}
   196  	}
   197  
   198  	flattened := make([]string, 0, len(set))
   199  	for p := range set {
   200  		flattened = append(flattened, p)
   201  	}
   202  	return flattened
   203  }