in engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java [2723:2925]
protected void migrate(final VMInstanceVO vm, final long srcHostId, final DeployDestination dest) throws ResourceUnavailableException, ConcurrentOperationException {
logger.info("Migrating {} to {}", vm, dest);
final long dstHostId = dest.getHost().getId();
final Host fromHost = _hostDao.findById(srcHostId);
if (fromHost == null) {
logger.info("Unable to find the host to migrate from: {}", srcHostId);
throw new CloudRuntimeException("Unable to find the host to migrate from: " + srcHostId);
}
if (fromHost.getClusterId() != dest.getCluster().getId() && vm.getHypervisorType() != HypervisorType.VMware) {
final List<VolumeVO> volumes = _volsDao.findCreatedByInstance(vm.getId());
for (final VolumeVO volume : volumes) {
if (!_storagePoolDao.findById(volume.getPoolId()).getScope().equals(ScopeType.ZONE)) {
logger.info("Source and destination host are not in same cluster and all volumes are not on zone wide primary store, unable to migrate to host: {}",
dest.getHost());
throw new CloudRuntimeException(String.format(
"Source and destination host are not in same cluster and all volumes are not on zone wide primary store, unable to migrate to host: %s",
dest.getHost()));
}
}
}
final VirtualMachineGuru vmGuru = getVmGuru(vm);
if (vm.getState() != State.Running) {
logger.debug("VM is not Running, unable to migrate the vm {}", vm);
throw new CloudRuntimeException("VM is not Running, unable to migrate the vm currently " + vm + " , current state: " + vm.getState().toString());
}
AlertManager.AlertType alertType = AlertManager.AlertType.ALERT_TYPE_USERVM_MIGRATE;
if (VirtualMachine.Type.DomainRouter.equals(vm.getType())) {
alertType = AlertManager.AlertType.ALERT_TYPE_DOMAIN_ROUTER_MIGRATE;
} else if (VirtualMachine.Type.ConsoleProxy.equals(vm.getType())) {
alertType = AlertManager.AlertType.ALERT_TYPE_CONSOLE_PROXY_MIGRATE;
}
final VirtualMachineProfile vmSrc = new VirtualMachineProfileImpl(vm);
vmSrc.setHost(fromHost);
for (final NicProfile nic : _networkMgr.getNicProfiles(vm)) {
vmSrc.addNic(nic);
}
final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm, null, _offeringDao.findById(vm.getId(), vm.getServiceOfferingId()), null, null);
profile.setHost(dest.getHost());
_networkMgr.prepareNicForMigration(profile, dest);
volumeMgr.prepareForMigration(profile, dest);
profile.setConfigDriveLabel(VmConfigDriveLabel.value());
updateOverCommitRatioForVmProfile(profile, dest.getHost().getClusterId());
final VirtualMachineTO to = toVmTO(profile);
final PrepareForMigrationCommand pfmc = new PrepareForMigrationCommand(to);
setVmNetworkDetails(vm, to);
ItWorkVO work = new ItWorkVO(UUID.randomUUID().toString(), _nodeId, State.Migrating, vm.getType(), vm.getId());
work.setStep(Step.Prepare);
work.setResourceType(ItWorkVO.ResourceType.Host);
work.setResourceId(dstHostId);
work = _workDao.persist(work);
Answer pfma = null;
try {
pfma = _agentMgr.send(dstHostId, pfmc);
if (pfma == null || !pfma.getResult()) {
final String details = pfma != null ? pfma.getDetails() : "null answer returned";
final String msg = "Unable to prepare for migration due to " + details;
pfma = null;
throw new AgentUnavailableException(msg, dstHostId);
}
} catch (final OperationTimedoutException e1) {
throw new AgentUnavailableException("Operation timed out", dstHostId);
} finally {
if (pfma == null) {
_networkMgr.rollbackNicForMigration(vmSrc, profile);
volumeMgr.release(vm.getId(), dstHostId);
work.setStep(Step.Done);
_workDao.update(work.getId(), work);
}
}
vm.setLastHostId(srcHostId);
_vmDao.resetVmPowerStateTracking(vm.getId());
try {
if (vm.getHostId() == null || vm.getHostId() != srcHostId || !changeState(vm, Event.MigrationRequested, dstHostId, work, Step.Migrating)) {
_networkMgr.rollbackNicForMigration(vmSrc, profile);
if (vm != null) {
volumeMgr.release(vm.getId(), dstHostId);
}
logger.info("Migration cancelled because state has changed: {}", vm);
throw new ConcurrentOperationException("Migration cancelled because state has changed: " + vm);
}
} catch (final NoTransitionException e1) {
_networkMgr.rollbackNicForMigration(vmSrc, profile);
volumeMgr.release(vm.getId(), dstHostId);
logger.info("Migration cancelled because {}", e1.getMessage());
throw new ConcurrentOperationException("Migration cancelled because " + e1.getMessage());
} catch (final CloudRuntimeException e2) {
_networkMgr.rollbackNicForMigration(vmSrc, profile);
volumeMgr.release(vm.getId(), dstHostId);
logger.info("Migration cancelled because {}", e2.getMessage());
work.setStep(Step.Done);
_workDao.update(work.getId(), work);
try {
stateTransitTo(vm, Event.OperationFailed, srcHostId);
} catch (final NoTransitionException e3) {
logger.warn(e3.getMessage());
}
throw new CloudRuntimeException("Migration cancelled because " + e2.getMessage());
}
boolean migrated = false;
Map<String, DpdkTO> dpdkInterfaceMapping = new HashMap<>();
try {
final MigrateCommand mc = buildMigrateCommand(vm, to, dest, pfma, dpdkInterfaceMapping);
try {
final Answer ma = _agentMgr.send(vm.getLastHostId(), mc);
if (ma == null || !ma.getResult()) {
final String details = ma != null ? ma.getDetails() : "null answer returned";
throw new CloudRuntimeException(details);
}
} catch (final OperationTimedoutException e) {
boolean success = false;
if (HypervisorType.KVM.equals(vm.getHypervisorType())) {
try {
final Answer answer = _agentMgr.send(vm.getHostId(), new CheckVirtualMachineCommand(vm.getInstanceName()));
if (answer != null && answer.getResult() && answer instanceof CheckVirtualMachineAnswer) {
final CheckVirtualMachineAnswer vmAnswer = (CheckVirtualMachineAnswer) answer;
if (VirtualMachine.PowerState.PowerOn.equals(vmAnswer.getState())) {
logger.info(String.format("Vm %s is found on destination host %s. Migration is successful", vm, vm.getHostId()));
success = true;
}
}
} catch (Exception ex) {
logger.error(String.format("Failed to get state of VM %s on destination host %s: %s", vm, vm.getHostId(), ex.getMessage()));
}
}
if (!success) {
if (e.isActive()) {
logger.warn("Active migration command so scheduling a restart for {}", vm, e);
_haMgr.scheduleRestart(vm, true);
throw new AgentUnavailableException("Operation timed out on migrating " + vm, dstHostId);
}
}
}
try {
if (!changeState(vm, VirtualMachine.Event.OperationSucceeded, dstHostId, work, Step.Started)) {
throw new ConcurrentOperationException("Unable to change the state for " + vm);
}
} catch (final NoTransitionException e1) {
throw new ConcurrentOperationException("Unable to change state due to " + e1.getMessage());
}
try {
if (!checkVmOnHost(vm, dstHostId)) {
logger.error("Unable to complete migration for {}", vm);
try {
_agentMgr.send(srcHostId, new Commands(cleanup(vm, dpdkInterfaceMapping)), null);
} catch (final AgentUnavailableException e) {
logger.error("AgentUnavailableException while cleanup on source host: {}", fromHost, e);
}
cleanup(vmGuru, new VirtualMachineProfileImpl(vm), work, Event.AgentReportStopped, true);
throw new CloudRuntimeException("Unable to complete migration for " + vm);
}
} catch (final OperationTimedoutException e) {
logger.warn("Error while checking the vm {} on host {}", vm, dest.getHost(), e);
}
migrated = true;
} finally {
if (!migrated) {
logger.info("Migration was unsuccessful. Cleaning up: {}", vm);
_networkMgr.rollbackNicForMigration(vmSrc, profile);
volumeMgr.release(vm.getId(), dstHostId);
_alertMgr.sendAlert(alertType, fromHost.getDataCenterId(), fromHost.getPodId(),
"Unable to migrate vm " + vm.getInstanceName() + " from host " + fromHost.getName() + " in zone " + dest.getDataCenter().getName() + " and pod " +
dest.getPod().getName(), "Migrate Command failed. Please check logs.");
try {
_agentMgr.send(dstHostId, new Commands(cleanup(vm, dpdkInterfaceMapping)), null);
} catch (final AgentUnavailableException ae) {
logger.warn("Looks like the destination Host is unavailable for cleanup", ae);
}
_networkMgr.setHypervisorHostname(profile, dest, false);
try {
stateTransitTo(vm, Event.OperationFailed, srcHostId);
} catch (final NoTransitionException e) {
logger.warn(e.getMessage());
}
} else {
_networkMgr.commitNicForMigration(vmSrc, profile);
volumeMgr.release(vm.getId(), srcHostId);
_networkMgr.setHypervisorHostname(profile, dest, true);
updateVmPod(vm, dstHostId);
}
work.setStep(Step.Done);
_workDao.update(work.getId(), work);
}
}