github.com/Cloud-Foundations/Dominator@v0.3.4/cmd/vm-control/migrateVm.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "net" 6 "time" 7 8 hyperclient "github.com/Cloud-Foundations/Dominator/hypervisor/client" 9 "github.com/Cloud-Foundations/Dominator/lib/errors" 10 "github.com/Cloud-Foundations/Dominator/lib/log" 11 "github.com/Cloud-Foundations/Dominator/lib/srpc" 12 hyper_proto "github.com/Cloud-Foundations/Dominator/proto/hypervisor" 13 ) 14 15 func migrateVmSubcommand(args []string, logger log.DebugLogger) error { 16 if err := migrateVm(args[0], logger); err != nil { 17 return fmt.Errorf("error migrating VM: %s", err) 18 } 19 return nil 20 } 21 22 func discardAccessToken(hypervisor *srpc.Client, ipAddr net.IP) { 23 request := hyper_proto.DiscardVmAccessTokenRequest{IpAddress: ipAddr} 24 var reply hyper_proto.DiscardVmAccessTokenResponse 25 hypervisor.RequestReply("Hypervisor.DiscardVmAccessToken", request, &reply) 26 } 27 28 func getVmAccessTokenClient(hypervisor *srpc.Client, 29 ipAddr net.IP) ([]byte, error) { 30 request := hyper_proto.GetVmAccessTokenRequest{ipAddr, time.Hour * 24} 31 var reply hyper_proto.GetVmAccessTokenResponse 32 err := hypervisor.RequestReply("Hypervisor.GetVmAccessToken", request, 33 &reply) 34 if err != nil { 35 return nil, err 36 } 37 if err := errors.New(reply.Error); err != nil { 38 return nil, err 39 } 40 return reply.Token, nil 41 } 42 43 func migrateVm(vmHostname string, logger log.DebugLogger) error { 44 if vmIP, hypervisor, err := searchVmAndHypervisor(vmHostname); err != nil { 45 return err 46 } else { 47 return migrateVmFromHypervisor(hypervisor, vmIP, logger) 48 } 49 } 50 51 func migrateVmFromHypervisor(sourceHypervisorAddress string, vmIP net.IP, 52 logger log.DebugLogger) error { 53 sourceHypervisor, err := dialHypervisor(sourceHypervisorAddress) 54 if err != nil { 55 return err 56 } 57 defer sourceHypervisor.Close() 58 vmInfo, err := hyperclient.GetVmInfo(sourceHypervisor, vmIP) 59 if err != nil { 60 return err 61 } else if vmInfo.State == hyper_proto.StateMigrating { 62 return errors.New("VM is migrating") 63 } 64 accessToken, err := getVmAccessTokenClient(sourceHypervisor, vmIP) 65 if err != nil { 66 return err 67 } 68 defer discardAccessToken(sourceHypervisor, vmIP) 69 destHypervisorAddress, err := getHypervisorAddress(vmInfo) 70 if err != nil { 71 return err 72 } 73 destHypervisor, err := dialHypervisor(destHypervisorAddress) 74 if err != nil { 75 return err 76 } 77 defer destHypervisor.Close() 78 logger.Debugf(0, "migrating VM to %s\n", destHypervisorAddress) 79 conn, err := destHypervisor.Call("Hypervisor.MigrateVm") 80 if err != nil { 81 return err 82 } 83 defer conn.Close() 84 request := hyper_proto.MigrateVmRequest{ 85 AccessToken: accessToken, 86 IpAddress: vmIP, 87 SkipMemoryCheck: *skipMemoryCheck, 88 SourceHypervisor: sourceHypervisorAddress, 89 } 90 if err := conn.Encode(request); err != nil { 91 return err 92 } 93 if err := conn.Flush(); err != nil { 94 return err 95 } 96 for { 97 var reply hyper_proto.MigrateVmResponse 98 if err := conn.Decode(&reply); err != nil { 99 return err 100 } 101 if reply.Error != "" { 102 return errors.New(reply.Error) 103 } 104 if reply.ProgressMessage != "" { 105 logger.Debugln(0, reply.ProgressMessage) 106 } 107 if reply.RequestCommit { 108 if err := requestCommit(conn); err != nil { 109 return err 110 } 111 } 112 if reply.Final { 113 break 114 } 115 } 116 return nil 117 } 118 119 func requestCommit(conn *srpc.Conn) error { 120 userResponse, err := askForInputChoice("Commit VM", 121 []string{"commit", "abandon"}) 122 if err != nil { 123 return err 124 } 125 var response hyper_proto.MigrateVmResponseResponse 126 switch userResponse { 127 case "abandon": 128 case "commit": 129 response.Commit = true 130 default: 131 return fmt.Errorf("invalid response: %s", userResponse) 132 } 133 if err := conn.Encode(response); err != nil { 134 return err 135 } 136 return conn.Flush() 137 }