package org.ovirt.engine.core.bll; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.inject.Inject; import org.apache.commons.lang.StringUtils; import org.ovirt.engine.core.bll.context.CommandContext; import org.ovirt.engine.core.bll.job.ExecutionHandler; import org.ovirt.engine.core.common.VdcObjectType; import org.ovirt.engine.core.common.action.AddVmParameters; import org.ovirt.engine.core.common.action.LockProperties; import org.ovirt.engine.core.common.action.LockProperties.Scope; import org.ovirt.engine.core.common.action.RemoveVmFromPoolParameters; import org.ovirt.engine.core.common.action.RemoveVmParameters; import org.ovirt.engine.core.common.action.UpdateVmVersionParameters; import org.ovirt.engine.core.common.action.VdcActionType; import org.ovirt.engine.core.common.action.VdcReturnValueBase; import org.ovirt.engine.core.common.asynctasks.EntityInfo; import org.ovirt.engine.core.common.businessentities.Permission; import org.ovirt.engine.core.common.businessentities.VMStatus; import org.ovirt.engine.core.common.businessentities.VmDevice; import org.ovirt.engine.core.common.businessentities.VmDeviceGeneralType; import org.ovirt.engine.core.common.businessentities.VmPayload; import org.ovirt.engine.core.common.businessentities.VmWatchdog; import org.ovirt.engine.core.common.businessentities.storage.Disk; import org.ovirt.engine.core.common.errors.EngineMessage; import org.ovirt.engine.core.common.locks.LockingGroup; import org.ovirt.engine.core.common.queries.IdQueryParameters; import org.ovirt.engine.core.common.queries.VdcQueryType; import org.ovirt.engine.core.common.utils.Pair; import org.ovirt.engine.core.common.utils.VmDeviceType; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.compat.TransactionScopeOption; import org.ovirt.engine.core.dao.DiskDao; import org.ovirt.engine.core.dao.PermissionDao; import org.ovirt.engine.core.dao.VmDeviceDao; import org.ovirt.engine.core.dao.VmTemplateDao; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class updates VM to the required template version for stateless VMs */ @InternalCommandAttribute public class UpdateVmVersionCommand<T extends UpdateVmVersionParameters> extends VmCommand<T> { private static final Logger log = LoggerFactory.getLogger(UpdateVmVersionCommand.class); @Inject private VmTemplateDao vmTemplateDao; @Inject private DiskDao diskDao; @Inject private PermissionDao permissionDao; @Inject private VmDeviceDao vmDeviceDao; /** * Constructor for command creation when compensation is applied on startup */ protected UpdateVmVersionCommand(Guid commandId) { super(commandId); } public UpdateVmVersionCommand(T parameters, CommandContext cmdContext) { super(parameters, cmdContext); } @Override protected void init() { super.init(); getParameters().setEntityInfo(new EntityInfo(VdcObjectType.VM, getParameters().getVmId())); if (getVm() != null) { if (getParameters().getNewTemplateVersion() != null) { setVmTemplate(vmTemplateDao.get(getParameters().getNewTemplateVersion())); } else { setVmTemplate(vmTemplateDao.getTemplateWithLatestVersionInChain(getVm().getVmtGuid())); } } } @Override protected LockProperties applyLockProperties(LockProperties lockProperties) { return lockProperties.withScope(Scope.Execution); } @Override protected boolean validate() { if (getVm() == null) { return failValidation(EngineMessage.ACTION_TYPE_FAILED_VM_NOT_FOUND); } if (getVm().getStatus() != VMStatus.Down) { return failValidation(EngineMessage.ACTION_TYPE_FAILED_VM_IS_NOT_DOWN); } if (!getVm().isUseLatestVersion() && getParameters().getNewTemplateVersion() == null) { return failValidation(EngineMessage.ACTION_TYPE_FAILED_VM_NOT_SET_FOR_LATEST); } if (getVmTemplate() == null) { return failValidation(EngineMessage.ACTION_TYPE_FAILED_TEMPLATE_DOES_NOT_EXIST); } if (getVmTemplateId().equals(getVm().getVmtGuid())) { return failValidation(EngineMessage.ACTION_TYPE_FAILED_VM_ALREADY_IN_LATEST_VERSION); } getVm().setVmtGuid(getVmTemplate().getId()); return true; } @Override protected void setActionMessageParameters() { addValidationMessage(EngineMessage.VAR__ACTION__UPDATE_VM_VERSION); addValidationMessage(EngineMessage.VAR__TYPE__VM); } @Override protected void executeVmCommand() { // load vm init from db vmHandler.updateVmInitFromDB(getVmTemplate(), false); if (!VmHandler.copyData(getVmTemplate(), getVm().getStaticData())) { return; } getParameters().setPreviousDiskOperatorAuthzPrincipalDbId(getIdOfDiskOperator()); getParameters().setVmStaticData(getVm().getStaticData()); if (getParameters().getUseLatestVersion() != null) { getParameters().getVmStaticData().setUseLatestVersion(getParameters().getUseLatestVersion()); } if (getVm().getVmPoolId() != null) { getParameters().setVmPoolId(getVm().getVmPoolId()); VdcReturnValueBase result = runInternalActionWithTasksContext( VdcActionType.RemoveVmFromPool, buildRemoveVmFromPoolParameters(), getLock()); if (!result.getSucceeded()) { log.error("Could not detach vm '{}' ({}) from vm-pool '{}'.", getVm().getName(), getVmId(), getVm().getVmPoolName()); return; } } VdcReturnValueBase result = runInternalActionWithTasksContext( VdcActionType.RemoveVm, buildRemoveVmParameters(), getLock()); if (result.getSucceeded()) { if (result.getHasAsyncTasks()) { getReturnValue().getVdsmTaskIdList().addAll(result.getInternalVdsmTaskIdList()); } else { endVmCommand(); } setSucceeded(true); } } private Guid getIdOfDiskOperator() { List<Disk> diskIds = diskDao.getAllForVm(getVmId()); if (diskIds.isEmpty()) { return null; } List<Permission> perms = permissionDao.getAllForRoleAndObject(PredefinedRoles.DISK_OPERATOR.getId(), diskIds.iterator().next().getId()); if (perms.isEmpty()) { return null; } return perms.iterator().next().getAdElementId(); } private RemoveVmFromPoolParameters buildRemoveVmFromPoolParameters() { RemoveVmFromPoolParameters parameters = new RemoveVmFromPoolParameters(getVmId(), false, false); parameters.setTransactionScopeOption(TransactionScopeOption.RequiresNew); return parameters; } private RemoveVmParameters buildRemoveVmParameters() { RemoveVmParameters parameters = new RemoveVmParameters(getVmId(), false); parameters.setParentCommand(getActionType()); parameters.setParentParameters(getParameters()); parameters.setEntityInfo(getParameters().getEntityInfo()); parameters.setRemovePermissions(false); return parameters; } private void addUpdatedVm() { runInternalAction(VdcActionType.AddVm, buildAddVmParameters(), ExecutionHandler.createDefaultContextForTasks(getContext(), getLock())); } private AddVmParameters buildAddVmParameters() { AddVmParameters addVmParams = new AddVmParameters(getParameters().getVmStaticData()); addVmParams.setPoolId(getParameters().getVmPoolId()); addVmParams.setDiskInfoDestinationMap(new HashMap<>()); addVmParams.setConsoleEnabled(deviceExists(VmDeviceGeneralType.CONSOLE)); addVmParams.setBalloonEnabled(deviceExists(VmDeviceGeneralType.BALLOON, VmDeviceType.MEMBALLOON)); addVmParams.setSoundDeviceEnabled(deviceExists(VmDeviceGeneralType.SOUND)); addVmParams.setVirtioScsiEnabled(deviceExists(VmDeviceGeneralType.CONTROLLER, VmDeviceType.VIRTIOSCSI)); List<VmWatchdog> watchdogs = runInternalQuery(VdcQueryType.GetWatchdog, new IdQueryParameters(getVmTemplateId())).getReturnValue(); if (!watchdogs.isEmpty()) { addVmParams.setWatchdog(watchdogs.get(0)); } loadVmPayload(addVmParams); // when this initiated from down vm event (restore stateless vm) // then there is no session, so using the current user. if (StringUtils.isEmpty(getParameters().getSessionId())) { addVmParams.setParametersCurrentUser(getCurrentUser()); } else { addVmParams.setSessionId(getParameters().getSessionId()); } addVmParams.setDiskOperatorAuthzPrincipalDbId(getParameters().getPreviousDiskOperatorAuthzPrincipalDbId()); // reset vm to not initialized addVmParams.getVmStaticData().setInitialized(false); return addVmParams; } private void loadVmPayload(AddVmParameters addVmParams) { List<VmDevice> vmDevices = vmDeviceDao.getVmDeviceByVmIdAndType(getVmTemplateId(), VmDeviceGeneralType.DISK); for (VmDevice vmDevice : vmDevices) { if (VmPayload.isPayload(vmDevice.getSpecParams())) { addVmParams.setVmPayload(new VmPayload(vmDevice)); return; } } } private boolean deviceExists(VmDeviceGeneralType generalType, VmDeviceType deviceType) { return !vmDeviceDao.getVmDeviceByVmIdTypeAndDevice( getVmTemplateId(), generalType, deviceType.getName()).isEmpty(); } private boolean deviceExists(VmDeviceGeneralType generalType) { return !vmDeviceDao.getVmDeviceByVmIdAndType(getVmTemplateId(), generalType).isEmpty(); } @Override protected Map<String, Pair<String, String>> getExclusiveLocks() { if (getParameters().isLockVm() && getVmId() != null) { return Collections.singletonMap(getVmId().toString(), LockMessagesMatchUtil.makeLockingPair(LockingGroup.VM, EngineMessage.ACTION_TYPE_FAILED_OBJECT_LOCKED)); } return null; } @Override protected Map<String, Pair<String, String>> getSharedLocks() { // take shared lock on required template, since we will add vm from it if (getVmTemplateId() != null) { return Collections.singletonMap(getVmTemplateId().toString(), LockMessagesMatchUtil.makeLockingPair(LockingGroup.TEMPLATE, EngineMessage.ACTION_TYPE_FAILED_OBJECT_LOCKED)); } return null; } @Override protected void endVmCommand() { addUpdatedVm(); setSucceeded(true); } @Override protected void endWithFailure() { // nothing to do } }