github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/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 destHypervisorAddress, err := getHypervisorAddress() 54 if err != nil { 55 return err 56 } 57 sourceHypervisor, err := dialHypervisor(sourceHypervisorAddress) 58 if err != nil { 59 return err 60 } 61 defer sourceHypervisor.Close() 62 vmInfo, err := hyperclient.GetVmInfo(sourceHypervisor, vmIP) 63 if err != nil { 64 return err 65 } else if vmInfo.State == hyper_proto.StateMigrating { 66 return errors.New("VM is migrating") 67 } 68 accessToken, err := getVmAccessTokenClient(sourceHypervisor, vmIP) 69 if err != nil { 70 return err 71 } 72 defer discardAccessToken(sourceHypervisor, vmIP) 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 SourceHypervisor: sourceHypervisorAddress, 88 } 89 if err := conn.Encode(request); err != nil { 90 return err 91 } 92 if err := conn.Flush(); err != nil { 93 return err 94 } 95 for { 96 var reply hyper_proto.MigrateVmResponse 97 if err := conn.Decode(&reply); err != nil { 98 return err 99 } 100 if reply.Error != "" { 101 return errors.New(reply.Error) 102 } 103 if reply.ProgressMessage != "" { 104 logger.Debugln(0, reply.ProgressMessage) 105 } 106 if reply.RequestCommit { 107 if err := requestCommit(conn); err != nil { 108 return err 109 } 110 } 111 if reply.Final { 112 break 113 } 114 } 115 return nil 116 } 117 118 func requestCommit(conn *srpc.Conn) error { 119 userResponse, err := askForInputChoice("Commit VM", 120 []string{"commit", "abandon"}) 121 if err != nil { 122 return err 123 } 124 var response hyper_proto.MigrateVmResponseResponse 125 switch userResponse { 126 case "abandon": 127 case "commit": 128 response.Commit = true 129 default: 130 return fmt.Errorf("invalid response: %s", userResponse) 131 } 132 if err := conn.Encode(response); err != nil { 133 return err 134 } 135 return conn.Flush() 136 }