github.com/ranjib/nomad@v0.1.1-0.20160225204057-97751b02f70b/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 err := used.Add(alloc.Resources); err != nil { 62 return false, "", nil, err 63 } 64 } 65 66 // Check that the node resources are a super set of those 67 // that are being allocated 68 if superset, dimension := node.Resources.Superset(used); !superset { 69 return false, dimension, used, nil 70 } 71 72 // Create the network index if missing 73 if netIdx == nil { 74 netIdx = NewNetworkIndex() 75 defer netIdx.Release() 76 if netIdx.SetNode(node) || netIdx.AddAllocs(allocs) { 77 return false, "reserved port collision", used, nil 78 } 79 } 80 81 // Check if the network is overcommitted 82 if netIdx.Overcommitted() { 83 return false, "bandwidth exceeded", used, nil 84 } 85 86 // Allocations fit! 87 return true, "", used, nil 88 } 89 90 // ScoreFit is used to score the fit based on the Google work published here: 91 // http://www.columbia.edu/~cs2035/courses/ieor4405.S13/datacenter_scheduling.ppt 92 // This is equivalent to their BestFit v3 93 func ScoreFit(node *Node, util *Resources) float64 { 94 // Determine the node availability 95 nodeCpu := float64(node.Resources.CPU) 96 if node.Reserved != nil { 97 nodeCpu -= float64(node.Reserved.CPU) 98 } 99 nodeMem := float64(node.Resources.MemoryMB) 100 if node.Reserved != nil { 101 nodeMem -= float64(node.Reserved.MemoryMB) 102 } 103 104 // Compute the free percentage 105 freePctCpu := 1 - (float64(util.CPU) / nodeCpu) 106 freePctRam := 1 - (float64(util.MemoryMB) / nodeMem) 107 108 // Total will be "maximized" the smaller the value is. 109 // At 100% utilization, the total is 2, while at 0% util it is 20. 110 total := math.Pow(10, freePctCpu) + math.Pow(10, freePctRam) 111 112 // Invert so that the "maximized" total represents a high-value 113 // score. Because the floor is 20, we simply use that as an anchor. 114 // This means at a perfect fit, we return 18 as the score. 115 score := 20.0 - total 116 117 // Bound the score, just in case 118 // If the score is over 18, that means we've overfit the node. 119 if score > 18.0 { 120 score = 18.0 121 } else if score < 0 { 122 score = 0 123 } 124 return score 125 } 126 127 // GenerateUUID is used to generate a random UUID 128 func GenerateUUID() string { 129 buf := make([]byte, 16) 130 if _, err := crand.Read(buf); err != nil { 131 panic(fmt.Errorf("failed to read random bytes: %v", err)) 132 } 133 134 return fmt.Sprintf("%08x-%04x-%04x-%04x-%12x", 135 buf[0:4], 136 buf[4:6], 137 buf[6:8], 138 buf[8:10], 139 buf[10:16]) 140 } 141 142 // Helpers for copying generic structures. 143 func CopyMapStringString(m map[string]string) map[string]string { 144 l := len(m) 145 if l == 0 { 146 return nil 147 } 148 149 c := make(map[string]string, l) 150 for k, v := range m { 151 c[k] = v 152 } 153 return c 154 } 155 156 func CopyMapStringInt(m map[string]int) map[string]int { 157 l := len(m) 158 if l == 0 { 159 return nil 160 } 161 162 c := make(map[string]int, l) 163 for k, v := range m { 164 c[k] = v 165 } 166 return c 167 } 168 169 func CopyMapStringFloat64(m map[string]float64) map[string]float64 { 170 l := len(m) 171 if l == 0 { 172 return nil 173 } 174 175 c := make(map[string]float64, l) 176 for k, v := range m { 177 c[k] = v 178 } 179 return c 180 } 181 182 func CopySliceString(s []string) []string { 183 l := len(s) 184 if l == 0 { 185 return nil 186 } 187 188 c := make([]string, l) 189 for i, v := range s { 190 c[i] = v 191 } 192 return c 193 } 194 195 func CopySliceConstraints(s []*Constraint) []*Constraint { 196 l := len(s) 197 if l == 0 { 198 return nil 199 } 200 201 c := make([]*Constraint, l) 202 for i, v := range s { 203 c[i] = v.Copy() 204 } 205 return c 206 }