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 }