github.com/Cloud-Foundations/Dominator@v0.3.4/hypervisor/manager/addressPool.go (about) 1 package manager 2 3 import ( 4 "errors" 5 "fmt" 6 "net" 7 "os" 8 "path" 9 10 "github.com/Cloud-Foundations/Dominator/lib/fsutil" 11 "github.com/Cloud-Foundations/Dominator/lib/json" 12 "github.com/Cloud-Foundations/Dominator/lib/srpc" 13 proto "github.com/Cloud-Foundations/Dominator/proto/hypervisor" 14 ) 15 16 func ipIsUnspecified(ipAddr net.IP) bool { 17 if len(ipAddr) < 1 { 18 return true 19 } 20 return ipAddr.IsUnspecified() 21 } 22 23 func removeAddresses(addresses []proto.Address, 24 ipsToRemove, macsToRemove map[string]struct{}, message string) ( 25 []proto.Address, error) { 26 newAddresses := make([]proto.Address, 0) 27 for _, address := range addresses { 28 keep := true 29 if len(address.IpAddress) > 0 { 30 ipAddr := address.IpAddress.String() 31 if _, ok := ipsToRemove[ipAddr]; ok { 32 delete(ipsToRemove, ipAddr) 33 delete(macsToRemove, address.MacAddress) 34 keep = false 35 } 36 } 37 if _, ok := macsToRemove[address.MacAddress]; ok { 38 delete(macsToRemove, address.MacAddress) 39 keep = false 40 } 41 if keep { 42 newAddresses = append(newAddresses, address) 43 } 44 } 45 if len(ipsToRemove) > 0 { 46 return nil, fmt.Errorf("IPs: %v %s", ipsToRemove, message) 47 } 48 if len(macsToRemove) > 0 { 49 return nil, fmt.Errorf("MACs: %v %s", macsToRemove, message) 50 } 51 return newAddresses, nil 52 } 53 54 func (m *Manager) addAddressesToPool(addresses []proto.Address) error { 55 for index := range addresses { 56 addresses[index].Shrink() 57 if _, err := net.ParseMAC(addresses[index].MacAddress); err != nil { 58 return err 59 } 60 } 61 existingIpAddresses := make(map[string]struct{}) 62 existingMacAddresses := make(map[string]struct{}) 63 m.mutex.Lock() 64 defer m.mutex.Unlock() 65 for _, address := range m.addressPool.Registered { 66 if address.IpAddress != nil { 67 existingIpAddresses[address.IpAddress.String()] = struct{}{} 68 } 69 existingMacAddresses[address.MacAddress] = struct{}{} 70 } 71 for ipAddress, vm := range m.vms { 72 existingIpAddresses[ipAddress] = struct{}{} 73 existingMacAddresses[vm.Address.MacAddress] = struct{}{} 74 } 75 for _, address := range addresses { 76 ipAddr := address.IpAddress 77 if ipAddr != nil { 78 if m.getMatchingSubnet(ipAddr) == "" { 79 return fmt.Errorf("no subnet matching: %s", address.IpAddress) 80 } 81 if _, ok := existingIpAddresses[ipAddr.String()]; ok { 82 return fmt.Errorf("duplicate IP address: %s", address.IpAddress) 83 } 84 } 85 if _, ok := existingMacAddresses[address.MacAddress]; ok { 86 return fmt.Errorf("duplicate MAC address: %s", address.MacAddress) 87 } 88 } 89 m.Logger.Debugf(0, "adding %d addresses to pool\n", len(addresses)) 90 m.addressPool.Free = append(m.addressPool.Free, addresses...) 91 m.addressPool.Registered = append(m.addressPool.Registered, addresses...) 92 return m.writeAddressPoolWithLock(m.addressPool, true) 93 } 94 95 func (m *Manager) computeNumFreeAddressesMap(addressPool addressPoolType) ( 96 map[string]uint, error) { 97 numFreeAddresses := make(map[string]uint, len(m.subnets)) 98 for subnetId := range m.subnets { 99 if subnetId == "" || subnetId == "hypervisor" { 100 continue 101 } 102 numFreeAddresses[subnetId] = 0 103 } 104 for _, address := range addressPool.Free { 105 if len(address.IpAddress) < 1 { 106 continue 107 } 108 if subnetId := m.getMatchingSubnet(address.IpAddress); subnetId == "" { 109 return nil, 110 fmt.Errorf("no matching subnet for: %s\n", address.IpAddress) 111 } else if subnetId != "hypervisor" { 112 numFreeAddresses[subnetId]++ 113 } 114 } 115 return numFreeAddresses, nil 116 } 117 118 func (m *Manager) loadAddressPool() error { 119 var addressPool addressPoolType 120 err := json.ReadFromFile(path.Join(m.StateDir, "address-pool.json"), 121 &addressPool) 122 if err != nil && !os.IsNotExist(err) { 123 return err 124 } 125 for index := range addressPool.Free { 126 addressPool.Free[index].Shrink() 127 } 128 for index := range addressPool.Registered { 129 addressPool.Registered[index].Shrink() 130 } 131 m.addressPool = addressPool 132 return nil 133 } 134 135 func (m *Manager) getFreeAddress(ipAddr net.IP, subnetId string, 136 authInfo *srpc.AuthInformation) (proto.Address, string, error) { 137 m.mutex.Lock() 138 defer m.mutex.Unlock() 139 if len(m.addressPool.Free) < 1 { 140 return proto.Address{}, "", errors.New("no free addresses in pool") 141 } 142 if subnet, err := m.getSubnetAndAuth(subnetId, authInfo); err != nil { 143 return proto.Address{}, "", err 144 } else { 145 subnetMask := net.IPMask(subnet.IpMask) 146 subnetAddr := subnet.IpGateway.Mask(subnetMask) 147 foundPos := -1 148 for index, address := range m.addressPool.Free { 149 if !ipIsUnspecified(ipAddr) && !ipAddr.Equal(address.IpAddress) { 150 continue 151 } 152 if address.IpAddress.Mask(subnetMask).Equal(subnetAddr) { 153 foundPos = index 154 break 155 } 156 } 157 if foundPos < 0 { 158 if ipIsUnspecified(ipAddr) { 159 return proto.Address{}, "", 160 fmt.Errorf("no free address in subnet: %s", subnetId) 161 } else { 162 return proto.Address{}, "", 163 fmt.Errorf("address: %s not found in free pool", ipAddr) 164 } 165 } 166 addressPool := addressPoolType{ 167 Free: make([]proto.Address, 0, len(m.addressPool.Free)-1), 168 Registered: m.addressPool.Registered, 169 } 170 for index, address := range m.addressPool.Free { 171 if index == foundPos { 172 continue 173 } 174 addressPool.Free = append(addressPool.Free, address) 175 } 176 if err := m.writeAddressPoolWithLock(addressPool, false); err != nil { 177 return proto.Address{}, "", err 178 } 179 address := m.addressPool.Free[foundPos] 180 m.addressPool = addressPool 181 return address, subnet.Id, nil 182 } 183 } 184 185 func (m *Manager) listAvailableAddresses() []proto.Address { 186 m.mutex.Lock() 187 defer m.mutex.Unlock() 188 addresses := make([]proto.Address, 0, len(m.addressPool.Free)) 189 for _, address := range m.addressPool.Free { 190 addresses = append(addresses, address) 191 } 192 return addresses 193 } 194 195 func (m *Manager) listRegisteredAddresses() []proto.Address { 196 m.mutex.Lock() 197 defer m.mutex.Unlock() 198 addresses := make([]proto.Address, 0, len(m.addressPool.Registered)) 199 for _, address := range m.addressPool.Registered { 200 addresses = append(addresses, address) 201 } 202 return addresses 203 } 204 205 func (m *Manager) registerAddress(address proto.Address) error { 206 m.mutex.Lock() 207 defer m.mutex.Unlock() 208 m.addressPool.Registered = append(m.addressPool.Registered, address) 209 if err := m.writeAddressPoolWithLock(m.addressPool, true); err != nil { 210 return err 211 } 212 return nil 213 } 214 215 func (m *Manager) releaseAddressInPool(address proto.Address) error { 216 m.mutex.Lock() 217 defer m.mutex.Unlock() 218 return m.releaseAddressInPoolWithLock(address) 219 } 220 221 func (m *Manager) releaseAddressInPoolWithLock(address proto.Address) error { 222 m.addressPool.Free = append(m.addressPool.Free, address) 223 return m.writeAddressPoolWithLock(m.addressPool, false) 224 } 225 226 func (m *Manager) removeAddressesFromPool(addresses []proto.Address) error { 227 ipsToRemoveFree := make(map[string]struct{}, len(addresses)) 228 ipsToRemoveRegistered := make(map[string]struct{}, len(addresses)) 229 macsToRemoveFree := make(map[string]struct{}, len(addresses)) 230 macsToRemoveRegistered := make(map[string]struct{}, len(addresses)) 231 for _, address := range addresses { 232 if len(address.IpAddress) > 0 { 233 ipsToRemoveFree[address.IpAddress.String()] = struct{}{} 234 ipsToRemoveRegistered[address.IpAddress.String()] = struct{}{} 235 } 236 if address.MacAddress != "" { 237 macsToRemoveFree[address.MacAddress] = struct{}{} 238 macsToRemoveRegistered[address.MacAddress] = struct{}{} 239 } 240 } 241 if len(ipsToRemoveFree) < 1 && len(macsToRemoveFree) < 1 { 242 return nil 243 } 244 m.mutex.Lock() 245 defer m.mutex.Unlock() 246 freeAddresses, err := removeAddresses(m.addressPool.Free, ipsToRemoveFree, 247 macsToRemoveFree, "not in free pool") 248 if err != nil { 249 return err 250 } 251 registeredAddresses, err := removeAddresses(m.addressPool.Registered, 252 ipsToRemoveRegistered, macsToRemoveRegistered, "not registered") 253 if err != nil { 254 return err 255 } 256 m.addressPool.Free = freeAddresses 257 m.addressPool.Registered = registeredAddresses 258 return m.writeAddressPoolWithLock(m.addressPool, true) 259 } 260 261 func (m *Manager) removeExcessAddressesFromPool(maxFree map[string]uint) error { 262 freeCount := make(map[string]uint) 263 macAddressesToRemove := make(map[string]struct{}) 264 m.mutex.Lock() 265 defer m.mutex.Unlock() 266 // TODO(rgooch): Should precompute a map to avoid this N*M loop. 267 for _, address := range m.addressPool.Free { 268 subnetId := m.getMatchingSubnet(address.IpAddress) 269 freeCount[subnetId]++ 270 if maxFree, ok := maxFree[subnetId]; ok { 271 if freeCount[subnetId] > maxFree { 272 macAddressesToRemove[address.MacAddress] = struct{}{} 273 } 274 } 275 if maxFree, ok := maxFree[""]; ok { 276 if freeCount[subnetId] > maxFree { 277 macAddressesToRemove[address.MacAddress] = struct{}{} 278 } 279 } 280 } 281 if len(macAddressesToRemove) < 1 { 282 return nil 283 } 284 newPool := addressPoolType{} 285 for _, address := range m.addressPool.Free { 286 if _, ok := macAddressesToRemove[address.MacAddress]; !ok { 287 newPool.Free = append(newPool.Free, address) 288 } 289 } 290 for _, address := range m.addressPool.Registered { 291 if _, ok := macAddressesToRemove[address.MacAddress]; !ok { 292 newPool.Registered = append(newPool.Registered, address) 293 } 294 } 295 m.Logger.Debugf(0, 296 "removing %d addresses from pool, leaving %d with %d free\n", 297 len(macAddressesToRemove), len(newPool.Registered), len(newPool.Free)) 298 if err := m.writeAddressPoolWithLock(newPool, true); err != nil { 299 return err 300 } 301 m.addressPool = newPool 302 return nil 303 } 304 305 func (m *Manager) unregisterAddress(address proto.Address, lock bool) error { 306 found := false 307 if lock { 308 m.mutex.Lock() 309 defer m.mutex.Unlock() 310 } 311 addresses := make([]proto.Address, 0, len(m.addressPool.Registered)-1) 312 for _, addr := range m.addressPool.Registered { 313 if address.Equal(&addr) { 314 found = true 315 } else { 316 addresses = append(addresses, addr) 317 } 318 } 319 if !found { 320 return fmt.Errorf("%v not registered", address) 321 } 322 m.addressPool.Registered = addresses 323 return m.writeAddressPoolWithLock(m.addressPool, true) 324 } 325 326 func (m *Manager) writeAddressPoolWithLock(addressPool addressPoolType, 327 sendAll bool) error { 328 // TODO(rgooch): Should precompute the numFreeAddresses map. 329 numFreeAddresses, err := m.computeNumFreeAddressesMap(addressPool) 330 if err != nil { 331 return err 332 } 333 err = json.WriteToFile(path.Join(m.StateDir, "address-pool.json"), 334 fsutil.PublicFilePerms, " ", addressPool) 335 if err != nil { 336 return err 337 } 338 update := proto.Update{NumFreeAddresses: numFreeAddresses} 339 if sendAll { 340 update.HaveAddressPool = true 341 update.AddressPool = addressPool.Registered 342 } 343 m.sendUpdate(update) 344 return nil 345 }