github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/fleetmanager/hypervisors/moveIpAddresses.go (about) 1 package hypervisors 2 3 import ( 4 "fmt" 5 "net" 6 "time" 7 8 "github.com/Cloud-Foundations/Dominator/lib/constants" 9 "github.com/Cloud-Foundations/Dominator/lib/errors" 10 "github.com/Cloud-Foundations/Dominator/lib/net/util" 11 "github.com/Cloud-Foundations/Dominator/lib/srpc" 12 hyper_proto "github.com/Cloud-Foundations/Dominator/proto/hypervisor" 13 ) 14 15 func (m *Manager) addIp(hypervisorIpAddress, ip net.IP) error { 16 client, err := srpc.DialHTTP("tcp", 17 fmt.Sprintf("%s:%d", 18 hypervisorIpAddress, constants.HypervisorPortNumber), 19 time.Second*15) 20 if err != nil { 21 return err 22 } 23 defer client.Close() 24 request := hyper_proto.ChangeAddressPoolRequest{ 25 AddressesToAdd: []hyper_proto.Address{{ 26 IpAddress: ip, 27 MacAddress: fmt.Sprintf("52:54:%02x:%02x:%02x:%02x", 28 ip[0], ip[1], ip[2], ip[3]), 29 }}, 30 } 31 var reply hyper_proto.ChangeAddressPoolResponse 32 err = client.RequestReply("Hypervisor.ChangeAddressPool", request, &reply) 33 if err != nil { 34 return err 35 } 36 return errors.New(reply.Error) 37 } 38 39 func (m *Manager) getHealthyHypervisorAddr(hostname string) (net.IP, error) { 40 hypervisor, err := m.getLockedHypervisor(hostname, false) 41 if err != nil { 42 return nil, err 43 } 44 defer hypervisor.mutex.RUnlock() 45 if hypervisor.healthStatus == "marginal" || 46 hypervisor.healthStatus == "at risk" { 47 return nil, errors.New("cannot move IPs to unhealthy hypervisor") 48 } 49 if len(hypervisor.machine.HostIpAddress) < 1 { 50 return nil, fmt.Errorf("IP address for: %s not known", hostname) 51 } 52 return hypervisor.machine.HostIpAddress, nil 53 } 54 55 func (m *Manager) markIPsForMigration(ipAddresses []net.IP) error { 56 m.mutex.Lock() 57 defer m.mutex.Unlock() 58 if num := len(m.migratingIPs); num > 0 { 59 return fmt.Errorf("%d other migrations in progress: %v", 60 num, m.migratingIPs) 61 } 62 for _, ip := range ipAddresses { 63 if len(ip) > 0 { 64 m.migratingIPs[ip.String()] = struct{}{} 65 } 66 } 67 return nil 68 } 69 70 func (m *Manager) moveIpAddresses(hostname string, ipAddresses []net.IP) error { 71 if !*manageHypervisors { 72 return errors.New("this is a read-only Fleet Manager") 73 } 74 if len(ipAddresses) < 1 { 75 return nil 76 } 77 sourceHypervisorIPs := make([]net.IP, len(ipAddresses)) 78 for index, ip := range ipAddresses { 79 ip = util.ShrinkIP(ip) 80 ipAddresses[index] = ip 81 sourceHypervisorIp, err := m.storer.GetHypervisorForIp(ip) 82 if err != nil { 83 return err 84 } 85 sourceHypervisorIPs[index] = sourceHypervisorIp 86 } 87 hypervisorIpAddress, err := m.getHealthyHypervisorAddr(hostname) 88 if err != nil { 89 return err 90 } 91 if err := m.markIPsForMigration(ipAddresses); err != nil { 92 return err 93 } 94 defer m.unmarkIPsForMigration(ipAddresses) 95 // Move IPs. 96 for index, ip := range ipAddresses { 97 err := m.moveIpAddress(hypervisorIpAddress, sourceHypervisorIPs[index], 98 ip) 99 if err != nil { 100 return err 101 } 102 } 103 // Wait for IPs to have moved. 104 // TODO(rgooch): Change this to watch for the registration events. 105 stopTime := time.Now().Add(time.Second * 10) 106 for ; time.Until(stopTime) >= 0; time.Sleep(time.Millisecond * 10) { 107 allInPlace := true 108 for _, ip := range ipAddresses { 109 newHyperIp, err := m.storer.GetHypervisorForIp(ip) 110 if err != nil { 111 return err 112 } 113 if newHyperIp == nil || !newHyperIp.Equal(hypervisorIpAddress) { 114 allInPlace = false 115 break // Not yet registered with the destination Hypervisor. 116 } 117 } 118 if allInPlace { 119 return nil 120 } 121 } 122 return errors.New("timed out waiting for addresses to move") 123 } 124 125 func (m *Manager) moveIpAddress(destinationHypervisorIpAddress, 126 sourceHypervisorIpAddress, ipToMove net.IP) error { 127 if sourceHypervisorIpAddress != nil { 128 if sourceHypervisorIpAddress.Equal(destinationHypervisorIpAddress) { 129 return nil // IP address is already registered to dest Hypervisor. 130 } 131 err := m.removeIpAndWait(sourceHypervisorIpAddress, ipToMove) 132 if err != nil { 133 return err 134 } 135 } 136 return m.addIp(destinationHypervisorIpAddress, ipToMove) 137 } 138 139 func (m *Manager) removeIpAndWait(hypervisorIpAddress, ipToMove net.IP) error { 140 client, err := srpc.DialHTTP("tcp", 141 fmt.Sprintf("%s:%d", 142 hypervisorIpAddress, constants.HypervisorPortNumber), 143 time.Second*15) 144 if err != nil { 145 return err 146 } 147 defer client.Close() 148 request := hyper_proto.ChangeAddressPoolRequest{ 149 AddressesToRemove: []hyper_proto.Address{{IpAddress: ipToMove}}, 150 } 151 var reply hyper_proto.ChangeAddressPoolResponse 152 err = client.RequestReply("Hypervisor.ChangeAddressPool", request, &reply) 153 if err != nil { 154 return err 155 } 156 if err := errors.New(reply.Error); err != nil { 157 return fmt.Errorf("error unregistering %s from %s: %s", 158 ipToMove, hypervisorIpAddress, err) 159 } 160 // TODO(rgooch): Change this to watch for the deregistration event. 161 stopTime := time.Now().Add(time.Second * 10) 162 for ; time.Until(stopTime) >= 0; time.Sleep(time.Millisecond * 10) { 163 newHyperIp, err := m.storer.GetHypervisorForIp(ipToMove) 164 if err != nil { 165 return err 166 } 167 if newHyperIp == nil { 168 return nil // No longer registered with a Hypervisor. 169 } 170 } 171 return fmt.Errorf( 172 "timed out waiting for %s to become unregistered from %s", 173 ipToMove, hypervisorIpAddress) 174 } 175 176 func (m *Manager) unmarkIPsForMigration(ipAddresses []net.IP) { 177 m.mutex.Lock() 178 defer m.mutex.Unlock() 179 for _, ip := range ipAddresses { 180 if len(ip) > 0 { 181 delete(m.migratingIPs, ip.String()) 182 } 183 } 184 }