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 }