package org.ovirt.engine.core.bll; import java.util.ArrayList; import java.util.HashMap; 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.bll.quota.QuotaClusterConsumptionParameter; import org.ovirt.engine.core.bll.quota.QuotaConsumptionParameter; import org.ovirt.engine.core.bll.quota.QuotaVdsDependent; import org.ovirt.engine.core.common.AuditLogType; import org.ovirt.engine.core.common.FeatureSupported; import org.ovirt.engine.core.common.action.HotSetAmountOfMemoryParameters; import org.ovirt.engine.core.common.action.PlugAction; 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.VmDeviceId; import org.ovirt.engine.core.common.config.Config; import org.ovirt.engine.core.common.config.ConfigValues; import org.ovirt.engine.core.common.errors.EngineFault; import org.ovirt.engine.core.common.errors.EngineMessage; import org.ovirt.engine.core.common.utils.VmDeviceType; import org.ovirt.engine.core.common.vdscommands.VDSCommandType; import org.ovirt.engine.core.common.vdscommands.VDSReturnValue; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.dao.VmDeviceDao; import org.ovirt.engine.core.utils.ReplacementUtils; import org.ovirt.engine.core.vdsbroker.SetAmountOfMemoryVDSCommand; @NonTransactiveCommandAttribute public class HotSetAmountOfMemoryCommand<T extends HotSetAmountOfMemoryParameters> extends VmManagementCommandBase<T> implements QuotaVdsDependent { public static final String LOGABLE_FIELD_NEW_MEMORY = "newMem"; public static final String LOGABLE_FIELD_PREVIOUS_MEMORY = "previousMem"; public static final String LOGABLE_FIELD_ERROR_MESSAGE = "ErrorMessage"; public static final String DEVICE_SIZE_FIELD_KEY = "size"; public static final String DEVICE_NODE_FIELD_KEY = "node"; @Inject private VmDeviceDao vmDeviceDao; public HotSetAmountOfMemoryCommand(T parameters, CommandContext commandContext) { super(parameters, commandContext); } @Override protected void init() { super.init(); } @Override protected void setActionMessageParameters() { addValidationMessage(EngineMessage.VAR__ACTION__HOT_SET_MEMORY); addValidationMessage(EngineMessage.VAR__TYPE__VM); addValidationMessageVariable("clusterVersion", getVm().getCompatibilityVersion()); addValidationMessageVariable("architecture", getVm().getClusterArch()); } @Override protected boolean validate() { if (getVm() == null) { return failValidation(EngineMessage.ACTION_TYPE_FAILED_VM_NOT_FOUND); } if (getVm().getStatus() != VMStatus.Up) { return failVmStatusIllegal(); } if (getParameters().getPlugAction() == PlugAction.PLUG) { if (!FeatureSupported.hotPlugMemory(getVm().getCompatibilityVersion(), getVm().getClusterArch())) { return failValidation(EngineMessage.HOT_PLUG_MEMORY_IS_NOT_SUPPORTED); } // check max slots List<VmDevice> memDevices = vmDeviceDao.getVmDeviceByVmIdAndType(getVmId(), VmDeviceGeneralType.MEMORY); if (memDevices.size() == Config.<Integer>getValue(ConfigValues.MaxMemorySlots)) { return failValidation(EngineMessage.ACTION_TYPE_FAILED_NO_MORE_MEMORY_SLOTS, "$maxMemSlots " + Config.getValue(ConfigValues.MaxMemorySlots).toString()); } final int hotplugMemorySizeFactor = getVm().getClusterArch().getHotplugMemorySizeFactorMb(); if (getParameters().getMemoryDeviceSizeMb() % hotplugMemorySizeFactor != 0) { return failValidation(EngineMessage.ACTION_TYPE_FAILED_NOT_PLUGGED_MEMORY_ON_ARCH_MUST_BE_DIVIDABLE_BY, ReplacementUtils.createSetVariableString("architecture", getVm().getClusterArch()), ReplacementUtils.createSetVariableString("memorySize", getParameters().getMemoryDeviceSizeMb()), ReplacementUtils.createSetVariableString("factor", hotplugMemorySizeFactor)); } } else if (!FeatureSupported.hotUnplugMemory(getVm().getCompatibilityVersion(), getVm().getClusterArch())) { return failValidation(EngineMessage.HOT_UNPLUG_MEMORY_IS_NOT_SUPPORTED); } return true; } @Override protected void executeCommand() { VDSReturnValue vdsReturnValue = runVdsCommand(VDSCommandType.SetAmountOfMemory, new SetAmountOfMemoryVDSCommand.Params( getVm().getRunOnVds(), getVm().getId(), createMemoryDevice())); if (vdsReturnValue.getSucceeded()) { setSucceeded(true); } else { EngineFault fault = new EngineFault(); fault.setError(vdsReturnValue.getVdsError().getCode()); fault.setMessage(vdsReturnValue.getVdsError().getMessage()); getReturnValue().setFault(fault); } } private VmDevice createMemoryDevice() { Map<String, Object> specParams = new HashMap<>(); specParams.put(DEVICE_SIZE_FIELD_KEY, getParameters().getMemoryDeviceSizeMb()); specParams.put(DEVICE_NODE_FIELD_KEY, getParameters().getNumaNode()); return new VmDevice(new VmDeviceId(Guid.newGuid(), getVmId()), VmDeviceGeneralType.MEMORY, VmDeviceType.MEMORY.getName(), "", specParams, true, true, false, "", null, null, null); } @Override public List<QuotaConsumptionParameter> getQuotaVdsConsumptionParameters() { List<QuotaConsumptionParameter> list = new ArrayList<>(); // Calculate the change in memory consumption, // result above Zero means we add memory to the VM (consume) // result bellow Zero means we subtracted memory from the VM (release) QuotaConsumptionParameter.QuotaAction quotaAction = (getParameters().getMemoryDeviceSizeMb() > 0) ? QuotaConsumptionParameter.QuotaAction.CONSUME : QuotaConsumptionParameter.QuotaAction.RELEASE; list.add(new QuotaClusterConsumptionParameter(getVm().getQuotaId(), null, quotaAction, getVm().getClusterId(), 0, Math.abs(getParameters().getMemoryDeviceSizeMb()))); return list; } @Override public AuditLogType getAuditLogTypeValue() { if (getSucceeded()) { addCustomValue(LOGABLE_FIELD_NEW_MEMORY, String.valueOf(getParameters().getVm().getMemSizeMb())); addCustomValue(LOGABLE_FIELD_PREVIOUS_MEMORY, String.valueOf(getVm().getMemSizeMb())); return AuditLogType.HOT_SET_MEMORY; } else { addCustomValue(LOGABLE_FIELD_ERROR_MESSAGE, getReturnValue().getFault().getMessage()); return AuditLogType.FAILED_HOT_SET_MEMORY; } } }