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  }