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 }