package org.ovirt.engine.core.bll;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import org.ovirt.engine.core.bll.context.CommandContext;
import org.ovirt.engine.core.common.AuditLogType;
import org.ovirt.engine.core.common.VdcObjectType;
import org.ovirt.engine.core.common.action.AddVmPoolParameters;
import org.ovirt.engine.core.common.action.LockProperties;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.action.VdcReturnValueBase;
import org.ovirt.engine.core.common.action.VmManagementParametersBase;
import org.ovirt.engine.core.common.businessentities.VM;
import org.ovirt.engine.core.common.businessentities.VmPool;
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.VdcQueryReturnValue;
import org.ovirt.engine.core.common.queries.VdcQueryType;
import org.ovirt.engine.core.common.utils.Pair;
import org.ovirt.engine.core.common.validation.group.CreateEntity;
import org.ovirt.engine.core.common.validation.group.UpdateEntity;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogableBase;
import org.ovirt.engine.core.dao.VmPoolDao;
@DisableInPrepareMode
@NonTransactiveCommandAttribute(forceCompensation = true)
public class UpdateVmPoolCommand<T extends AddVmPoolParameters> extends CommonVmPoolCommand<T>
implements RenamedEntityInfoProvider {
@Inject
private VmPoolMonitor vmPoolMonitor;
@Inject
private VmPoolDao vmPoolDao;
private VmPool oldPool;
/**
* Constructor for command creation when compensation is applied on startup
*/
protected UpdateVmPoolCommand(Guid commandId) {
super(commandId);
}
public UpdateVmPoolCommand(T parameters, CommandContext commandContext) {
super(parameters, commandContext);
}
@Override
protected void createOrUpdateVmPool() {
vmPoolDao.update(getVmPool());
}
@Override
protected LockProperties applyLockProperties(LockProperties lockProperties) {
return lockProperties.withScope(LockProperties.Scope.Execution);
}
@Override
protected Map<String, Pair<String, String>> getExclusiveLocks() {
return Collections.singletonMap(getVmPoolId().toString(),
LockMessagesMatchUtil.makeLockingPair(LockingGroup.VM_POOL,
new LockMessage(EngineMessage.ACTION_TYPE_FAILED_VM_POOL_IS_BEING_UPDATED)
.withOptional("VmPoolName", getVmPool() != null ? getVmPool().getName() : null)));
}
@Override
protected boolean validate() {
if (!super.validate()) {
return false;
}
oldPool = vmPoolDao.get(getVmPool().getVmPoolId());
if (oldPool == null) {
return failValidation(EngineMessage.VM_POOL_CANNOT_UPDATE_POOL_NOT_FOUND);
}
if (getParameters().getVmsCount() < 0) {
return failValidation(EngineMessage.VM_POOL_CANNOT_DECREASE_VMS_FROM_POOL);
}
if (oldPool.getVmPoolType() != getParameters().getVmPool().getVmPoolType()) {
return failValidation(EngineMessage.VM_POOL_CANNOT_CHANGE_POOL_TYPE);
}
if (oldPool.isStateful() != getParameters().getVmPool().isStateful()) {
return failValidation(EngineMessage.VM_POOL_CANNOT_CHANGE_POOL_STATEFUL_OPTION);
}
if (!oldPool.getName().equals(getParameters().getVmPool().getName())) {
return failValidation(EngineMessage.VM_POOL_CANNOT_CHANGE_POOL_NAME);
}
return true;
}
@Override
protected void setActionMessageParameters() {
super.setActionMessageParameters();
addValidationMessage(EngineMessage.VAR__ACTION__UPDATE);
}
@Override
public AuditLogType getAuditLogTypeValue() {
return isAllAddVmsSucceeded() ? AuditLogType.USER_UPDATE_VM_POOL_WITH_VMS
: AuditLogType.USER_UPDATE_VM_POOL_WITH_VMS_FAILED;
}
@Override
protected void executeCommand() {
VdcQueryReturnValue currentVmsInPoolQuery =
runInternalQuery(VdcQueryType.GetAllPoolVms, new IdQueryParameters(getVmPool().getVmPoolId()));
List<VM> poolVmsBeforeAdd =
currentVmsInPoolQuery.getSucceeded() ? currentVmsInPoolQuery.getReturnValue() : null;
super.executeCommand();
getCompensationContext().cleanupCompensationDataAfterSuccessfulCommand();
if (getSucceeded() && isUpdateVmRequired(poolVmsBeforeAdd)) {
// if a change in template version was detected --> update all pool vms
updatePoolVms(poolVmsBeforeAdd);
}
vmPoolMonitor.triggerPoolMonitoringJob();
}
private void updatePoolVms(List<VM> vmsInPool) {
boolean isUpdatedPoolLatest = getParameters().getVmStaticData().isUseLatestVersion(); // new latest value
vmTemplateHandler.lockVmTemplateInTransaction(getParameters().getVmStaticData().getVmtGuid(),
getCompensationContext());
for (VM vm : vmsInPool) {
VmManagementParametersBase updateParams = new VmManagementParametersBase(vm);
updateParams.getVmStaticData().setUseLatestVersion(isUpdatedPoolLatest);
if (!isUpdatedPoolLatest) {
updateParams.getVmStaticData().setVmtGuid(getParameters().getVmStaticData().getVmtGuid());
}
VdcReturnValueBase result =
runInternalActionWithTasksContext(
VdcActionType.UpdateVm,
updateParams,
getLock());
getTaskIdList().addAll(result.getInternalVdsmTaskIdList());
setSucceeded(getSucceeded() && result.getSucceeded());
}
vmTemplateHandler.unlockVmTemplate(getParameters().getVmStaticData().getVmtGuid());
}
private boolean isUpdateVmRequired(List<VM> vmsInPool) {
if (vmsInPool == null || vmsInPool.isEmpty()) {
return false;
}
// Check one VM in order to decide if template version was changed
VM poolVm = vmsInPool.get(0);
Guid currentTemplateVersion = null; // old template version (based on current vm data)
boolean isCurrentLatest = false; // old latest status
if (poolVm.isNextRunConfigurationExists()) {
VdcQueryReturnValue qRetNextRun =
getBackend().runInternalQuery(VdcQueryType.GetVmNextRunConfiguration,
new IdQueryParameters(poolVm.getId()));
if (qRetNextRun.getSucceeded()) {
final VM nextRunVm =
qRetNextRun.getReturnValue();
if (nextRunVm != null) { // template version was changed, the cause still needs to be checked
currentTemplateVersion = nextRunVm.getVmtGuid();
isCurrentLatest = nextRunVm.isUseLatestVersion();
}
}
} else {
currentTemplateVersion = poolVm.getVmtGuid();
isCurrentLatest = poolVm.isUseLatestVersion();
}
boolean isLatestPropertyChanged = isCurrentLatest != getParameters().getVmStaticData().isUseLatestVersion();
// template ID changed but latest is not set, as it would cause false-positives
boolean isTemplateIdChanged = false;
Guid newPoolTemplateVersion = getParameters().getVmStaticData().getVmtGuid();
if (newPoolTemplateVersion != null) {
isTemplateIdChanged = !newPoolTemplateVersion.equals(currentTemplateVersion) && !isCurrentLatest;
}
return isLatestPropertyChanged || isTemplateIdChanged;
}
@Override
public String getEntityType() {
return VdcObjectType.VmPool.getVdcObjectTranslation();
}
@Override
public String getEntityOldName() {
return oldPool.getName();
}
@Override
public String getEntityNewName() {
return getParameters().getVmPool().getName();
}
@Override
public void setEntityId(AuditLogableBase logable) {
}
@Override
protected List<Class<?>> getValidationGroups() {
addValidationGroup(CreateEntity.class);
addValidationGroup(UpdateEntity.class);
return super.getValidationGroups();
}
}