package org.ovirt.engine.core.bll;
import org.ovirt.engine.core.common.AuditLogType;
import org.ovirt.engine.core.common.action.MigrateVmParameters;
import org.ovirt.engine.core.common.businessentities.MigrationMethod;
import org.ovirt.engine.core.common.businessentities.MigrationSupport;
import org.ovirt.engine.core.common.businessentities.VDS;
import org.ovirt.engine.core.common.businessentities.VDSStatus;
import org.ovirt.engine.core.common.businessentities.VM;
import org.ovirt.engine.core.common.businessentities.VMStatus;
import org.ovirt.engine.core.common.errors.VdcBLLException;
import org.ovirt.engine.core.common.errors.VdcBllErrors;
import org.ovirt.engine.core.common.vdscommands.MigrateStatusVDSCommandParameters;
import org.ovirt.engine.core.common.vdscommands.MigrateVDSCommandParameters;
import org.ovirt.engine.core.common.vdscommands.VDSCommandType;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.dal.VdcBllMessages;
import org.ovirt.engine.core.dal.dbbroker.DbFacade;
import org.ovirt.engine.core.dal.dbbroker.auditloghandling.CustomLogField;
import org.ovirt.engine.core.dal.dbbroker.auditloghandling.CustomLogFields;
@CustomLogFields({ @CustomLogField("VdsDestination"), @CustomLogField("DueToMigrationError") })
public class MigrateVmCommand<T extends MigrateVmParameters> extends RunVmCommandBase<T> {
private Guid _vdsDestinationId;
protected boolean forcedMigrationForNonMigratableVM;
/**
* Used to log the migration error.
*/
private VdcBllErrors migrationErrorCode;
public MigrateVmCommand(T parameters) {
super(parameters);
super.setVmId(parameters.getVmId());
setVdsSelector(new VdsSelector(getVm(), getVdsDestinationId(), true));
forcedMigrationForNonMigratableVM = parameters.isForceMigrationForNonMigratableVM();
}
// this property is used for audit log events
public String getVdsDestination() {
if (getDestinationVds() != null) {
return getDestinationVds().getvds_name();
} else {
return null;
}
}
/**
* @return Migration error text which is used in audit log message, if the migration status was queried from VDSM.
*/
public String getDueToMigrationError() {
if (migrationErrorCode == null) {
return " ";
}
return " due to Error: " + Backend.getInstance()
.getVdsErrorsTranslator()
.TranslateErrorTextSingle(migrationErrorCode.name(), true);
}
@Override
protected VDS getDestinationVds() {
if (_destinationVds == null && _vdsDestinationId != null) {
_destinationVds = DbFacade.getInstance().getVdsDAO().get(_vdsDestinationId);
}
return _destinationVds;
}
protected void FailedToRunVm() {
if (getVm().getstatus() != VMStatus.Up) {
super.FailedToRunVm();
}
}
protected void InitVdss() {
setVdsIdRef(new Guid(getVm().getrun_on_vds().toString()));
setVdsDestinationId(getVdsSelector().GetVdsToRunOn());
// make _destinationVds null in order to refresh it from db in case it
// changed.
_destinationVds = null;
if (_vdsDestinationId != null && _vdsDestinationId.equals(Guid.Empty)) {
throw new VdcBLLException(VdcBllErrors.RESOURCE_MANAGER_CANT_ALLOC_VDS_MIGRATION);
}
if (getDestinationVds() == null) {
throw new VdcBLLException(VdcBllErrors.RESOURCE_MANAGER_VDS_NOT_FOUND);
}
if (getVds() == null) {
throw new VdcBLLException(VdcBllErrors.RESOURCE_MANAGER_VDS_NOT_FOUND);
}
}
@Override
protected void ExecuteVmCommand() {
InitVdss();
Perform();
ProcessVm();
setSucceeded(true);
}
private void ProcessVm() {
if (getVm().getstatus() != VMStatus.Up) {
DecreasePendingVms(getVds().getvds_id());
}
}
private void Perform() {
getVm().setmigrating_to_vds(_vdsDestinationId);
String srcVdsHost = getVds().gethost_name();
String dstVdsHost = String.format("%1$s:%2$s", getDestinationVds().gethost_name(), getDestinationVds()
.getport());
// Starting migration at src VDS
setActionReturnValue(Backend
.getInstance()
.getResourceManager()
.RunAsyncVdsCommand(
VDSCommandType.Migrate,
new MigrateVDSCommandParameters(getVdsId(), getVmId(), srcVdsHost, _vdsDestinationId,
dstVdsHost, MigrationMethod.ONLINE), this).getReturnValue());
if ((VMStatus) getActionReturnValue() != VMStatus.MigratingFrom) {
getVm().setMigreatingToPort(0);
getVm().setMigreatingFromPort(0);
getVm().setmigrating_to_vds(null);
throw new VdcBLLException(VdcBllErrors.RESOURCE_MANAGER_MIGRATION_FAILED_AT_DST);
}
}
@Override
public AuditLogType getAuditLogTypeValue() {
// all good, succeeded and the vm is up
// succeeded false, rerun
// succeeded false, rerun false = migration failed
return getSucceeded() ? (VMStatus) getActionReturnValue() == VMStatus.Up ? AuditLogType.VM_MIGRATION_DONE
: AuditLogType.VM_MIGRATION_START
: _isRerun ? AuditLogType.VM_MIGRATION_TRYING_RERUN
: getVds().getstatus() == VDSStatus.PreparingForMaintenance ? AuditLogType.VM_MIGRATION_FAILED_DURING_MOVE_TO_MAINTANANCE
: AuditLogType.VM_MIGRATION_FAILED;
}
protected Guid getVdsDestinationId() {
return _vdsDestinationId;
}
protected void setVdsDestinationId(Guid value) {
_vdsDestinationId = value;
}
@Override
protected boolean canDoAction() {
return canMigrateVm(getVmId(), getReturnValue().getCanDoActionMessages());
}
protected boolean canMigrateVm(Guid vmGuid, java.util.ArrayList<String> reasons) {
boolean retValue = true;
VM vm = getVm();
if (vm == null) {
retValue = false;
reasons.add(VdcBllMessages.ACTION_TYPE_FAILED_VM_NOT_FOUND.toString());
} else {
// If VM is pinned to host, no migration can occur
if (vm.getMigrationSupport() == MigrationSupport.PINNED_TO_HOST) {
retValue = false;
reasons.add(VdcBllMessages.ACTION_TYPE_FAILED_VM_IS_PINNED_TO_HOST.toString());
} else if (vm.getMigrationSupport() == MigrationSupport.IMPLICITLY_NON_MIGRATABLE
&& !forcedMigrationForNonMigratableVM) {
retValue = false;
reasons.add(VdcBllMessages.ACTION_TYPE_FAILED_VM_IS_NON_MIGRTABLE_AND_IS_NOT_FORCED_BY_USER_TO_MIGRATE
.toString());
} else if (vm.getstatus() == VMStatus.MigratingFrom) {
retValue = false;
reasons.add(VdcBllMessages.ACTION_TYPE_FAILED_MIGRATION_IN_PROGRESS.toString());
} else if (vm.getstatus() == VMStatus.NotResponding) {
retValue = false;
reasons.add(VdcBllMessages.ACTION_TYPE_FAILED_VM_STATUS_ILLEGAL.toString());
} else if (!VM.isStatusQualifyToMigrate(vm.getstatus())) {
retValue = false;
reasons.add(VdcBllMessages.ACTION_TYPE_FAILED_VM_IS_NOT_RUNNING.toString());
} else if (getDestinationVds() != null && getDestinationVds().getstatus() != VDSStatus.Up) {
retValue = false;
reasons.add(VdcBllMessages.ACTION_TYPE_FAILED_VDS_STATUS_ILLEGAL.toString());
}
retValue = retValue && getVdsSelector().CanFindVdsToRunOn(reasons, true);
}
if (!retValue) {
reasons.add(VdcBllMessages.VAR__ACTION__MIGRATE.toString());
reasons.add(VdcBllMessages.VAR__TYPE__VM.toString());
}
return retValue;
}
@Override
protected void rerunInternal() {
/**
* make Vm property to null in order to refresh it from db.
*/
setVm(null);
determineMigrationFailueForAuditLog();
/**
* if vm is up and rerun is called then it got up on the source, try to
* rerun
*/
if (getVm() != null && getVm().getstatus() == VMStatus.Up) {
setVdsDestinationId(null);
super.rerunInternal();
} else {
/**
* vm went down on the destination and source, migration failed.
*/
DecreasePendingVms(getDestinationVds().getvds_id());
_isRerun = true;
setSucceeded(false);
log();
}
}
/**
* Log that the migration had failed with the error code that is in the VDS and needs to be retrieved.
*/
protected void determineMigrationFailueForAuditLog() {
if (getVm() != null && getVm().getstatus() == VMStatus.Up) {
try {
Backend.getInstance().getResourceManager().RunVdsCommand(VDSCommandType.MigrateStatus,
new MigrateStatusVDSCommandParameters(getVdsId(), getVmId()));
} catch (VdcBLLException e) {
migrationErrorCode = e.getErrorCode();
}
}
}
@Override
protected Guid getCurrentVdsId() {
if (getVdsDestinationId() != null) {
return getVdsDestinationId();
} else {
return super.getCurrentVdsId();
}
}
}