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  }