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