package org.ovirt.engine.core.bll;
import static org.ovirt.engine.core.bll.validator.CpuPinningValidator.isCpuPinningValid;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import javax.inject.Inject;
import org.apache.commons.codec.CharEncoding;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.ovirt.engine.core.bll.context.CommandContext;
import org.ovirt.engine.core.bll.network.cluster.NetworkHelper;
import org.ovirt.engine.core.bll.quota.QuotaClusterConsumptionParameter;
import org.ovirt.engine.core.bll.quota.QuotaConsumptionParameter;
import org.ovirt.engine.core.bll.quota.QuotaSanityParameter;
import org.ovirt.engine.core.bll.quota.QuotaVdsDependent;
import org.ovirt.engine.core.bll.storage.ovfstore.OvfDataUpdater;
import org.ovirt.engine.core.bll.utils.IconUtils;
import org.ovirt.engine.core.bll.utils.PermissionSubject;
import org.ovirt.engine.core.bll.utils.RngDeviceUtils;
import org.ovirt.engine.core.bll.validator.IconValidator;
import org.ovirt.engine.core.bll.validator.InClusterUpgradeValidator;
import org.ovirt.engine.core.bll.validator.VmValidator;
import org.ovirt.engine.core.bll.validator.VmWatchdogValidator;
import org.ovirt.engine.core.common.AuditLogType;
import org.ovirt.engine.core.common.FeatureSupported;
import org.ovirt.engine.core.common.VdcObjectType;
import org.ovirt.engine.core.common.action.GraphicsParameters;
import org.ovirt.engine.core.common.action.HotSetAmountOfMemoryParameters;
import org.ovirt.engine.core.common.action.HotSetNumberOfCpusParameters;
import org.ovirt.engine.core.common.action.LockProperties;
import org.ovirt.engine.core.common.action.LockProperties.Scope;
import org.ovirt.engine.core.common.action.PlugAction;
import org.ovirt.engine.core.common.action.RngDeviceParameters;
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.action.VmManagementParametersBase;
import org.ovirt.engine.core.common.action.VmNumaNodeOperationParameters;
import org.ovirt.engine.core.common.action.WatchdogParameters;
import org.ovirt.engine.core.common.businessentities.ActionGroup;
import org.ovirt.engine.core.common.businessentities.ArchitectureType;
import org.ovirt.engine.core.common.businessentities.DisplayType;
import org.ovirt.engine.core.common.businessentities.GraphicsDevice;
import org.ovirt.engine.core.common.businessentities.GraphicsType;
import org.ovirt.engine.core.common.businessentities.MigrationSupport;
import org.ovirt.engine.core.common.businessentities.Provider;
import org.ovirt.engine.core.common.businessentities.ProviderType;
import org.ovirt.engine.core.common.businessentities.UsbPolicy;
import org.ovirt.engine.core.common.businessentities.VM;
import org.ovirt.engine.core.common.businessentities.VMStatus;
import org.ovirt.engine.core.common.businessentities.VmBase;
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.businessentities.VmNumaNode;
import org.ovirt.engine.core.common.businessentities.VmPayload;
import org.ovirt.engine.core.common.businessentities.VmRngDevice;
import org.ovirt.engine.core.common.businessentities.VmStatic;
import org.ovirt.engine.core.common.businessentities.VmTemplate;
import org.ovirt.engine.core.common.businessentities.VmWatchdog;
import org.ovirt.engine.core.common.businessentities.network.Network;
import org.ovirt.engine.core.common.businessentities.network.VmNic;
import org.ovirt.engine.core.common.businessentities.storage.DiskVmElement;
import org.ovirt.engine.core.common.config.Config;
import org.ovirt.engine.core.common.config.ConfigValues;
import org.ovirt.engine.core.common.errors.EngineError;
import org.ovirt.engine.core.common.errors.EngineException;
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.utils.VmCommonUtils;
import org.ovirt.engine.core.common.utils.VmCpuCountHelper;
import org.ovirt.engine.core.common.utils.VmDeviceCommonUtils;
import org.ovirt.engine.core.common.utils.customprop.VmPropertiesUtils;
import org.ovirt.engine.core.common.validation.group.UpdateVm;
import org.ovirt.engine.core.compat.DateTime;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.compat.Version;
import org.ovirt.engine.core.dal.dbbroker.auditloghandling.AuditLogableBase;
import org.ovirt.engine.core.dao.DiskVmElementDao;
import org.ovirt.engine.core.dao.SnapshotDao;
import org.ovirt.engine.core.dao.VmDeviceDao;
import org.ovirt.engine.core.dao.VmNumaNodeDao;
import org.ovirt.engine.core.dao.VmStaticDao;
import org.ovirt.engine.core.dao.network.NetworkDao;
import org.ovirt.engine.core.dao.network.VmNicDao;
import org.ovirt.engine.core.dao.provider.ProviderDao;
import org.ovirt.engine.core.utils.transaction.TransactionCompletionListener;
import org.ovirt.engine.core.vdsbroker.ResourceManager;
import org.ovirt.engine.core.vdsbroker.monitoring.VmDevicesMonitoring;
public class UpdateVmCommand<T extends VmManagementParametersBase> extends VmManagementCommandBase<T>
implements QuotaVdsDependent, RenamedEntityInfoProvider{
private static final Base64 BASE_64 = new Base64(0, null);
@Inject
private VmSlaPolicyUtils vmSlaPolicyUtils;
@Inject
private VmDevicesMonitoring vmDevicesMonitoring;
@Inject
private ResourceManager resourceManager;
@Inject
private InClusterUpgradeValidator clusterUpgradeValidator;
@Inject
private RngDeviceUtils rngDeviceUtils;
@Inject
private OvfDataUpdater ovfDataUpdater;
@Inject
private VmNumaNodeDao vmNumaNodeDao;
@Inject
private VmStaticDao vmStaticDao;
@Inject
private VmDeviceDao vmDeviceDao;
@Inject
private NetworkDao networkDao;
@Inject
private VmNicDao vmNicDao;
@Inject
private ProviderDao providerDao;
@Inject
private DiskVmElementDao diskVmElementDao;
@Inject
private SnapshotDao snapshotDao;
private VM oldVm;
private boolean quotaSanityOnly = false;
private VmStatic newVmStatic;
private VdcReturnValueBase setNumberOfCpusResult;
private List<GraphicsDevice> cachedGraphics;
private boolean isUpdateVmTemplateVersion = false;
public UpdateVmCommand(T parameters, CommandContext commandContext) {
super(parameters, commandContext);
}
@Override
protected void init() {
super.init();
if (getCluster() != null) {
setStoragePoolId(getCluster().getStoragePoolId());
}
if (isVmExist() && isCompatibilityVersionSupportedByCluster(getEffectiveCompatibilityVersion())) {
Version compatibilityVersion = getEffectiveCompatibilityVersion();
getVmPropertiesUtils().separateCustomPropertiesToUserAndPredefined(
compatibilityVersion, getParameters().getVmStaticData());
getVmPropertiesUtils().separateCustomPropertiesToUserAndPredefined(
compatibilityVersion, getVm().getStaticData());
}
vmHandler.updateDefaultTimeZone(getParameters().getVmStaticData());
vmHandler.autoSelectUsbPolicy(getParameters().getVmStaticData());
vmHandler.autoSelectDefaultDisplayType(getVmId(),
getParameters().getVmStaticData(),
getCluster(),
getParameters().getGraphicsDevices());
updateParametersVmFromInstanceType();
// we always need to verify new or existing numa nodes with the updated VM configuration
if (!getParameters().isUpdateNuma()) {
getParameters().getVm().setvNumaNodeList(vmNumaNodeDao.getAllVmNumaNodeByVmId(getParameters().getVmId()));
}
if (getParameters().getVmStaticData().getDefaultDisplayType() == DisplayType.none && !getParameters().isConsoleEnabled()) {
getParameters().getVmStaticData().setUsbPolicy(UsbPolicy.DISABLED);
}
}
private VmPropertiesUtils getVmPropertiesUtils() {
return VmPropertiesUtils.getInstance();
}
@Override
protected LockProperties applyLockProperties(LockProperties lockProperties) {
return lockProperties.withScope(Scope.Execution);
}
@Override
public AuditLogType getAuditLogTypeValue() {
return isInternalExecution() ?
getSucceeded() ? AuditLogType.SYSTEM_UPDATE_VM : AuditLogType.SYSTEM_FAILED_UPDATE_VM
: getSucceeded() ? AuditLogType.USER_UPDATE_VM : AuditLogType.USER_FAILED_UPDATE_VM;
}
@Override
protected void executeVmCommand() {
oldVm = getVm(); // needs to be here for post-actions
if (isUpdateVmTemplateVersion) {
updateVmTemplateVersion();
return; // template version was changed, no more work is required
}
newVmStatic = getParameters().getVmStaticData();
if (isRunningConfigurationNeeded()) {
vmHandler.createNextRunSnapshot(
getVm(), getParameters().getVmStaticData(), getParameters(), getCompensationContext());
} else if (!updateVmLease()) {
return;
}
vmHandler.warnMemorySizeLegal(getParameters().getVm().getStaticData(), getEffectiveCompatibilityVersion());
vmStaticDao.incrementDbGeneration(getVm().getId());
newVmStatic.setCreationDate(oldVm.getStaticData().getCreationDate());
newVmStatic.setQuotaId(getQuotaId());
// Trigger OVF update for hosted engine VM only
if (getVm().isHostedEngine()) {
registerRollbackHandler(new TransactionCompletionListener() {
@Override
public void onSuccess() {
ovfDataUpdater.triggerNow();
}
@Override
public void onRollback() {
// No notification is needed
}
});
}
// save user selected value for hotplug before overriding with db values (when updating running vm)
VM userVm = new VM();
userVm.setStaticData(new VmStatic(newVmStatic));
if (newVmStatic.getCreationDate().equals(DateTime.getMinValue())) {
newVmStatic.setCreationDate(new Date());
}
if (getVm().isRunningOrPaused() && !getVm().isHostedEngine()) {
if (!vmHandler.copyNonEditableFieldsToDestination(oldVm.getStaticData(), newVmStatic, isHotSetEnabled())) {
// fail update vm if some fields could not be copied
throw new EngineException(EngineError.FAILED_UPDATE_RUNNING_VM);
}
}
if ((getVm().isRunningOrPaused() || getVm().isPreviewSnapshot() || getVm().isSuspended()) && !getVm().isHostedEngine()) {
if (getVm().getCustomCompatibilityVersion() == null && getParameters().getClusterLevelChangeFromVersion() != null) {
// For backward compatibility after cluster version change
// When running/paused: Set temporary custom compatibility version till the NextRun is applied (VM cold reboot)
// When snapshot in preview: keep the custom compatibility version even after commit or roll back by undo
newVmStatic.setCustomCompatibilityVersion(getParameters().getClusterLevelChangeFromVersion());
}
}
updateVmNetworks();
updateVmNumaNodes();
if (isHotSetEnabled()) {
hotSetCpus(userVm);
updateCurrentMemory(userVm);
}
final List<Guid> oldIconIds = IconUtils.updateVmIcon(
oldVm.getStaticData(), newVmStatic, getParameters().getVmLargeIcon());
resourceManager.getVmManager(getVmId()).update(newVmStatic);
if (getVm().isNotRunning()) {
updateVmPayload();
getVmDeviceUtils().updateVmDevices(getParameters(), oldVm);
updateWatchdog();
updateRngDevice();
updateGraphicsDevices();
updateVmHostDevices();
updateDeviceAddresses();
}
IconUtils.removeUnusedIcons(oldIconIds);
vmHandler.updateVmInitToDB(getParameters().getVmStaticData());
checkTrustedService();
liveUpdateCpuProfile();
setSucceeded(true);
}
private boolean updateVmLease() {
if (Objects.equals(oldVm.getLeaseStorageDomainId(), newVmStatic.getLeaseStorageDomainId())) {
return true;
}
if (!addVmLease(newVmStatic.getLeaseStorageDomainId(), newVmStatic.getId())) {
return false;
}
// best effort to remove the lease from the previous storage domain
removeVmLease(oldVm.getLeaseStorageDomainId(), oldVm.getId());
return true;
}
private void liveUpdateCpuProfile(){
if (getVm().getStatus().isQualifiedForQosChange() &&
!Objects.equals(oldVm.getCpuProfileId(), newVmStatic.getCpuProfileId())) {
vmSlaPolicyUtils.refreshCpuQosOfRunningVm(getVm());
}
}
private void updateVmHostDevices() {
if (isDedicatedVmForVdsChanged()) {
log.info("Pinned host changed for VM: {}. Dropping configured host devices.", getVm().getName());
vmDeviceDao.removeVmDevicesByVmIdAndType(getVmId(), VmDeviceGeneralType.HOSTDEV);
}
}
/**
* Handles a template-version update use case.
* If vm is down -> updateVmVersionCommand will handle the rest and will preform the actual change.
* if it's running -> a NEXT_RUN snapshot will be created and the change will take affect only on power down.
* in both cases the command should end after this function as no more changes are possible.
*/
private void updateVmTemplateVersion() {
if (getVm().getStatus() == VMStatus.Down) {
VdcReturnValueBase result =
runInternalActionWithTasksContext(
VdcActionType.UpdateVmVersion,
new UpdateVmVersionParameters(getVmId(),
getParameters().getVm().getVmtGuid(),
getParameters().getVm().isUseLatestVersion()),
getLock()
);
if (result.getSucceeded()) {
getTaskIdList().addAll(result.getInternalVdsmTaskIdList());
}
setSucceeded(result.getSucceeded());
setActionReturnValue(VdcActionType.UpdateVmVersion);
} else {
vmHandler.createNextRunSnapshot(
getVm(), getParameters().getVmStaticData(), getParameters(), getCompensationContext());
setSucceeded(true);
}
}
private void updateRngDevice() {
if (!getParameters().isUpdateRngDevice()) {
return;
}
VdcQueryReturnValue query =
runInternalQuery(VdcQueryType.GetRngDevice, new IdQueryParameters(getParameters().getVmId()));
List<VmRngDevice> rngDevs = query.getReturnValue();
VdcReturnValueBase rngCommandResult = null;
if (rngDevs.isEmpty()) {
if (getParameters().getRngDevice() != null) {
RngDeviceParameters params = new RngDeviceParameters(getParameters().getRngDevice(), true);
rngCommandResult = runInternalAction(VdcActionType.AddRngDevice, params, cloneContextAndDetachFromParent());
}
} else {
if (getParameters().getRngDevice() == null) {
RngDeviceParameters params = new RngDeviceParameters(rngDevs.get(0), true);
rngCommandResult = runInternalAction(VdcActionType.RemoveRngDevice, params, cloneContextAndDetachFromParent());
} else {
RngDeviceParameters params = new RngDeviceParameters(getParameters().getRngDevice(), true);
params.getRngDevice().setDeviceId(rngDevs.get(0).getDeviceId());
rngCommandResult = runInternalAction(VdcActionType.UpdateRngDevice, params, cloneContextAndDetachFromParent());
}
}
if (rngCommandResult != null && !rngCommandResult.getSucceeded()) {
log.error("Updating RNG device of VM {} ({}) failed. Old RNG device = {}. New RNG device = {}.",
getVm().getName(),
getVm().getId(),
rngDevs.isEmpty() ? null : rngDevs.get(0),
getParameters().getRngDevice());
}
}
private void updateDeviceAddresses() {
if (isEmulatedMachineChanged()) {
log.info("Emulated machine changed for VM: {} ({}). Clearing device addresses.",
getVm().getName(),
getVm().getId());
vmDeviceDao.clearAllDeviceAddressesByVmId(getVmId());
VmDevicesMonitoring.Change change = vmDevicesMonitoring.createChange(System.nanoTime());
change.updateVm(getVmId(), VmDevicesMonitoring.EMPTY_HASH);
change.flush();
}
}
private void hotSetCpus(VM newVm) {
int currentSockets = getVm().getNumOfSockets();
int newNumOfSockets = newVm.getNumOfSockets();
// try hotplug only if topology (cpuPerSocket, threadsPerCpu) hasn't changed
if (getVm().getStatus() == VMStatus.Up
&& VmCommonUtils.isCpusToBeHotplugged(getVm(), newVm)) {
HotSetNumberOfCpusParameters params =
new HotSetNumberOfCpusParameters(
newVmStatic,
currentSockets < newNumOfSockets ? PlugAction.PLUG : PlugAction.UNPLUG);
setNumberOfCpusResult =
runInternalAction(
VdcActionType.HotSetNumberOfCpus,
params, cloneContextAndDetachFromParent());
// Hosted engine VM does not care if hotplug failed. The requested CPU count is serialized
// into the OVF store and automatically used during the next HE VM start
if (!getVm().isHostedEngine()) {
newVmStatic.setNumOfSockets(setNumberOfCpusResult.getSucceeded() ? newNumOfSockets : currentSockets);
}
hotSetCpusLog(params);
}
}
private void updateCurrentMemory(VM newVm) {
int currentMemory = getVm().getMemSizeMb();
int newAmountOfMemory = newVm.getMemSizeMb();
if (getVm().getStatus().isNotRunning()) {
newVmStatic.setMemSizeMb(newAmountOfMemory);
return;
}
if (getVm().getStatus() != VMStatus.Up) {
newVmStatic.setMemSizeMb(currentMemory);
log.warn("Memory update {}MB -> {}MB of VM {} ({}) left out. Memory can't be updated in current VM state ({}).",
currentMemory,
newAmountOfMemory,
getVm().getName(),
getVm().getId(),
getVm().getStatus());
return;
}
if (!VmCommonUtils.isMemoryToBeHotplugged(getVm(), newVm)) {
return;
}
final int memoryAddedMb = newAmountOfMemory - currentMemory;
final int factor = Config.<Integer>getValue(ConfigValues.HotPlugMemoryBlockSizeMb);
final boolean memoryDividable = memoryAddedMb % factor == 0;
if (!memoryDividable) {
addCustomValue("memoryAdded", String.valueOf(memoryAddedMb));
addCustomValue("requiredFactor", String.valueOf(factor));
auditLogDirector.log(this, AuditLogType.FAILED_HOT_SET_MEMORY_NOT_DIVIDABLE);
return;
}
hotSetMemory(currentMemory, newAmountOfMemory);
}
/**
* Hot plug a memory device.
*
* <p>If there isn't memory device of minimal size (i.e. size of memory block), then hot plugged memory is split
* to two devices of sizes: minimal + the rest.</p>
*
* <p>Such behavior is shortcut of "the first hot plugged device has to be of minimal size". The reason for this is
* that the hot plugged memory is intended to be onlined (make available to the guest OS) as 'online_movable' for it
* to be unpluggable later on. Memory blocks can be onlined as movable only in order from higher addresses to lower
* addresses. Trying to online them in a different order fails. Kernel produces events to online memory blocks
* in arbitrary order, so not all of the blocks may be successfully onlined. But when a memory device of minimal
* size is hot plugged, it contains just a single memory block, so there is no ambiguity with memory block ordering
* and the block is always successfully onlined.</p>
*
* <p>Following memory hot plugs are not affected by this constraint because the first hot plug extends movable zone
* of the memory from the first hot plugged device to the end of the memory address space and memory blocks in this
* movable zone can be onlined as online movable in arbitrary order. Movable zone is not shrunk when memory devices
* are offlined and hot unplugged.</p>
*/
private void hotSetMemory(int currentMemoryMb, int newAmountOfMemoryMb) {
final int minimalHotPlugDeviceSizeMb = getVm().getClusterArch().getHotplugMemorySizeFactorMb();
final List<VmDevice> memoryDevices = getVmDeviceUtils().getMemoryDevices(getVmId());
final boolean minimalMemoryDevicePresent = memoryDevices.stream()
.anyMatch(device -> VmDeviceCommonUtils.getSizeOfMemoryDeviceMb(device) == minimalHotPlugDeviceSizeMb);
final int secondPartSizeMb = (newAmountOfMemoryMb - currentMemoryMb) - minimalHotPlugDeviceSizeMb;
if (minimalMemoryDevicePresent || secondPartSizeMb == 0) {
hotPlugMemoryDevice(currentMemoryMb, newAmountOfMemoryMb);
return;
}
hotPlugMemoryDevice(currentMemoryMb, currentMemoryMb + minimalHotPlugDeviceSizeMb);
hotPlugMemoryDevice(currentMemoryMb + minimalHotPlugDeviceSizeMb, newAmountOfMemoryMb);
}
private void hotPlugMemoryDevice(int currentMemoryMb, int newAmountOfMemoryMb) {
HotSetAmountOfMemoryParameters params =
new HotSetAmountOfMemoryParameters(
newVmStatic,
currentMemoryMb < newAmountOfMemoryMb ? PlugAction.PLUG : PlugAction.UNPLUG,
// We always use node 0, auto-numa should handle the allocation
0,
newAmountOfMemoryMb - currentMemoryMb);
VdcReturnValueBase setAmountOfMemoryResult =
runInternalAction(
VdcActionType.HotSetAmountOfMemory,
params, cloneContextAndDetachFromParent());
// Hosted engine VM does not care if hostplug failed. The requested memory size is serialized
// into the OVF store and automatically used during the next HE VM start
if (!getVm().isHostedEngine()) {
newVmStatic.setMemSizeMb(setAmountOfMemoryResult.getSucceeded() ? newAmountOfMemoryMb : currentMemoryMb);
}
hotSetMemlog(params, setAmountOfMemoryResult);
}
/**
* add audit log msg for failed hot set in case error was in CDA
* otherwise internal command will audit log the result
*/
private void hotSetCpusLog(HotSetNumberOfCpusParameters params) {
if (!setNumberOfCpusResult.isValid()) {
AuditLogableBase logable = CommandsFactory.createCommand(VdcActionType.HotSetNumberOfCpus, params);
List<String> validationMessages = getBackend().getErrorsTranslator().
translateErrorText(setNumberOfCpusResult.getValidationMessages());
logable.addCustomValue(HotSetNumberOfCpusCommand.LOGABLE_FIELD_ERROR_MESSAGE, StringUtils.join(validationMessages, ","));
auditLogDirector.log(logable, AuditLogType.FAILED_HOT_SET_NUMBER_OF_CPUS);
}
}
/**
* add audit log msg for failed hot set in case error was in CDA
* otherwise internal command will audit log the result
*/
private void hotSetMemlog(HotSetAmountOfMemoryParameters params, VdcReturnValueBase setAmountOfMemoryResult) {
if (!setAmountOfMemoryResult.isValid()) {
AuditLogableBase logable = CommandsFactory.createCommand(VdcActionType.HotSetAmountOfMemory, params);
List<String> validationMessages = getBackend().getErrorsTranslator().
translateErrorText(setAmountOfMemoryResult.getValidationMessages());
logable.addCustomValue(HotSetAmountOfMemoryCommand.LOGABLE_FIELD_ERROR_MESSAGE, StringUtils.join(validationMessages, ","));
auditLogDirector.log(logable, AuditLogType.FAILED_HOT_SET_MEMORY);
}
}
private void checkTrustedService() {
if (getParameters().getVm().isTrustedService() && !getCluster().supportsTrustedService()) {
auditLogDirector.log(this, AuditLogType.USER_UPDATE_VM_FROM_TRUSTED_TO_UNTRUSTED);
}
else if (!getParameters().getVm().isTrustedService() && getCluster().supportsTrustedService()) {
auditLogDirector.log(this, AuditLogType.USER_UPDATE_VM_FROM_UNTRUSTED_TO_TRUSTED);
}
}
private void updateWatchdog() {
// do not update if this flag is not set
if (getParameters().isUpdateWatchdog()) {
VdcQueryReturnValue query =
runInternalQuery(VdcQueryType.GetWatchdog, new IdQueryParameters(getParameters().getVmId()));
List<VmWatchdog> watchdogs = query.getReturnValue();
if (watchdogs.isEmpty()) {
if (getParameters().getWatchdog() == null) {
// nothing to do, no watchdog and no watchdog to create
} else {
WatchdogParameters parameters = new WatchdogParameters();
parameters.setId(getParameters().getVmId());
parameters.setAction(getParameters().getWatchdog().getAction());
parameters.setModel(getParameters().getWatchdog().getModel());
runInternalAction(VdcActionType.AddWatchdog, parameters, cloneContextAndDetachFromParent());
}
} else {
WatchdogParameters watchdogParameters = new WatchdogParameters();
watchdogParameters.setId(getParameters().getVmId());
if (getParameters().getWatchdog() == null) {
// there is a watchdog in the vm, there should not be any, so let's delete
runInternalAction(VdcActionType.RemoveWatchdog, watchdogParameters, cloneContextAndDetachFromParent());
} else {
// there is a watchdog in the vm, we have to update.
watchdogParameters.setAction(getParameters().getWatchdog().getAction());
watchdogParameters.setModel(getParameters().getWatchdog().getModel());
runInternalAction(VdcActionType.UpdateWatchdog, watchdogParameters, cloneContextAndDetachFromParent());
}
}
}
}
private void updateGraphicsDevices() {
Set<Map.Entry<GraphicsType, GraphicsDevice>> entries =
getParameters().getGraphicsDevices().entrySet();
/* Devices have to be removed first and then added to prevent having multiple devices at the same time
which is sometimes prohibited (FeatureSupported.multipleGraphicsSupported) */
entries.stream().filter(entry -> entry.getValue() == null).forEach(entry -> removeGraphicsDevice(entry.getKey()));
entries.stream().filter(entry -> entry.getValue() != null).forEach(entry -> addOrUpdateGraphicsDevice(entry.getValue()));
}
private void removeGraphicsDevice(GraphicsType type) {
GraphicsDevice existingGraphicsDevice = getGraphicsDevOfType(type);
if (existingGraphicsDevice != null) {
getBackend().runInternalAction(VdcActionType.RemoveGraphicsDevice,
new GraphicsParameters(existingGraphicsDevice));
}
}
private void addOrUpdateGraphicsDevice(GraphicsDevice device) {
GraphicsDevice existingGraphicsDevice = getGraphicsDevOfType(device.getGraphicsType());
device.setVmId(getVmId());
getBackend().runInternalAction(
existingGraphicsDevice == null ? VdcActionType.AddGraphicsDevice : VdcActionType.UpdateGraphicsDevice,
new GraphicsParameters(device));
}
private GraphicsDevice getGraphicsDevOfType(GraphicsType type) {
return getGraphicsDevices().stream().filter(dev -> dev.getGraphicsType() == type).findFirst().orElse(null);
}
private List<GraphicsDevice> getGraphicsDevices() {
if (cachedGraphics == null) {
cachedGraphics = getBackend()
.runInternalQuery(VdcQueryType.GetGraphicsDevices, new IdQueryParameters(getParameters().getVmId())).getReturnValue();
}
return cachedGraphics;
}
protected void updateVmPayload() {
VmPayload payload = getParameters().getVmPayload();
if (payload != null || getParameters().isClearPayload()) {
List<VmDevice> disks = vmDeviceDao.getVmDeviceByVmIdAndType(getVmId(), VmDeviceGeneralType.DISK);
VmDevice oldPayload = null;
for (VmDevice disk : disks) {
if (VmPayload.isPayload(disk.getSpecParams())) {
oldPayload = disk;
break;
}
}
if (oldPayload != null) {
List<VmDeviceId> devs = new ArrayList<>();
devs.add(oldPayload.getId());
vmDeviceDao.removeAll(devs);
}
if (!getParameters().isClearPayload()) {
getVmDeviceUtils().addManagedDevice(new VmDeviceId(Guid.newGuid(), getVmId()),
VmDeviceGeneralType.DISK,
payload.getDeviceType(),
payload.getSpecParams(),
true,
true);
}
}
}
private void updateVmNetworks() {
// check if the cluster has changed
if (!Objects.equals(getVm().getClusterId(), getParameters().getVmStaticData().getClusterId())) {
List<Network> networks =
networkDao.getAllForCluster(getParameters().getVmStaticData().getClusterId());
List<VmNic> interfaces = vmNicDao.getAllForVm(getParameters().getVmStaticData().getId());
for (final VmNic iface : interfaces) {
final Network network = NetworkHelper.getNetworkByVnicProfileId(iface.getVnicProfileId());
boolean networkFound = networks.stream().anyMatch(n -> Objects.equals(n.getId(), network.getId()));
// if network not exists in cluster we remove the network from the interface
if (!networkFound) {
iface.setVnicProfileId(null);
vmNicDao.update(iface);
}
}
}
}
private void updateVmNumaNodes() {
if (!getParameters().isUpdateNuma()) {
return;
}
List<VmNumaNode> newList = getParameters().getVmStaticData().getvNumaNodeList();
VmNumaNodeOperationParameters params =
new VmNumaNodeOperationParameters(getParameters().getVm(), new ArrayList<>(newList));
addLogMessages(getBackend().runInternalAction(VdcActionType.SetVmNumaNodes, params));
}
private void addLogMessages(VdcReturnValueBase returnValueBase) {
if (!returnValueBase.getSucceeded()) {
auditLogDirector.log(this, AuditLogType.NUMA_UPDATE_VM_NUMA_NODE_FAILED);
}
}
@Override
protected List<Class<?>> getValidationGroups() {
addValidationGroup(UpdateVm.class);
return super.getValidationGroups();
}
@Override
protected void setActionMessageParameters() {
addValidationMessage(EngineMessage.VAR__ACTION__UPDATE);
addValidationMessage(EngineMessage.VAR__TYPE__VM);
}
@Override
protected boolean validate() {
if (!super.validate()) {
return false;
}
VM vmFromDB = getVm();
VM vmFromParams = getParameters().getVm();
if (Math.abs(vmFromDB.getVmCreationDate().getTime() - vmFromParams.getVmCreationDate().getTime()) > 1000) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_INVALID_CREATION_DATE);
}
vmFromParams.setVmCreationDate(vmFromDB.getVmCreationDate());
// check if VM was changed to use latest
if (vmFromDB.isUseLatestVersion() != vmFromParams.isUseLatestVersion() && vmFromParams.isUseLatestVersion()) {
// check if a version change is actually required or just let the local command to update this field
vmFromParams.setVmtGuid(vmTemplateDao.getTemplateWithLatestVersionInChain(getVm().getVmtGuid()).getId());
}
// It is not allowed to edit hosted engine VM until it is imported to the engine properly
if (vmFromDB.isHostedEngine() && !vmFromDB.isManagedHostedEngine()) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_UNMANAGED_HOSTED_ENGINE);
}
// pool VMs are allowed to change template id, this verifies that the change is only between template versions.
if (!vmFromDB.getVmtGuid().equals(vmFromParams.getVmtGuid())) {
VmTemplate origTemplate = vmTemplateDao.get(vmFromDB.getVmtGuid());
VmTemplate newTemplate = vmTemplateDao.get(vmFromParams.getVmtGuid());
if (newTemplate == null) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_TEMPLATE_DOES_NOT_EXIST);
} else if (origTemplate != null && !origTemplate.getBaseTemplateId().equals(newTemplate.getBaseTemplateId())) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_TEMPLATE_IS_ON_DIFFERENT_CHAIN);
} else if (vmFromDB.getVmPoolId() != null) {
isUpdateVmTemplateVersion = true;
return true; // no more tests are needed because no more changes are allowed in this state
} else {// template id can be changed for pool VMs only
return failValidation(EngineMessage.ACTION_TYPE_FAILED_TEMPLATE_ID_CANT_BE_CHANGED);
}
}
if (getCluster() == null) {
addValidationMessage(EngineMessage.ACTION_TYPE_FAILED_CLUSTER_CAN_NOT_BE_EMPTY);
return false;
}
if (vmFromDB.getClusterId() == null) {
failValidation(EngineMessage.ACTION_TYPE_FAILED_CLUSTER_CAN_NOT_BE_EMPTY);
return false;
}
if (!isVmExist()) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_VM_NOT_FOUND);
}
if (!canRunActionOnNonManagedVm()) {
return false;
}
if (StringUtils.isEmpty(vmFromParams.getName())) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_NAME_MAY_NOT_BE_EMPTY);
}
// check that VM name is not too long
boolean vmNameValidLength = isVmNameValidLength(vmFromParams);
if (!vmNameValidLength) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_NAME_LENGTH_IS_TOO_LONG);
}
// Checking if a desktop with same name already exists
if (!StringUtils.equals(vmFromDB.getName(), vmFromParams.getName())) {
boolean exists = isVmWithSameNameExists(vmFromParams.getName(), getStoragePoolId());
if (exists) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_NAME_ALREADY_USED);
}
}
Version customCompatibilityVersionFromParams = vmFromParams.getStaticData().getCustomCompatibilityVersion();
if (customCompatibilityVersionFromParams != null && !isCompatibilityVersionSupportedByCluster(customCompatibilityVersionFromParams)) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_CUSTOM_COMPATIBILITY_VERSION_NOT_SUPPORTED,
String.format("$Ccv %s", customCompatibilityVersionFromParams));
}
if (!validateCustomProperties(vmFromParams.getStaticData(), getReturnValue().getValidationMessages())) {
return false;
}
if (!vmHandler.isOsTypeSupported(vmFromParams.getOs(),
getCluster().getArchitecture(), getReturnValue().getValidationMessages())) {
return false;
}
if (!vmHandler.isCpuSupported(
vmFromParams.getVmOsId(),
getEffectiveCompatibilityVersion(),
getCluster().getCpuName(),
getReturnValue().getValidationMessages())) {
return false;
}
if (getParameters().getVmStaticData().getDefaultDisplayType() != DisplayType.none &&
vmFromParams.getSingleQxlPci() &&
!vmHandler.isSingleQxlDeviceLegal(vmFromParams.getDefaultDisplayType(),
vmFromParams.getOs(),
getReturnValue().getValidationMessages())) {
return false;
}
if (vmFromParams.isAutoStartup() && vmFromDB.isHostedEngine()) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_VM_CANNOT_BE_HIGHLY_AVAILABLE_AND_HOSTED_ENGINE);
}
if (!areUpdatedFieldsLegal()) {
return failValidation(vmFromDB.isHostedEngine() ? EngineMessage.VM_CANNOT_UPDATE_HOSTED_ENGINE_FIELD : EngineMessage.VM_CANNOT_UPDATE_ILLEGAL_FIELD);
}
if (!vmFromDB.getClusterId().equals(vmFromParams.getClusterId())) {
return failValidation(EngineMessage.VM_CANNOT_UPDATE_CLUSTER);
}
if (!isDedicatedVdsExistOnSameCluster(vmFromParams.getStaticData(), getReturnValue().getValidationMessages())) {
return false;
}
// Check if number of monitors passed is legal
if (getParameters().getVmStaticData().getDefaultDisplayType() != DisplayType.none &&
!vmHandler.isNumOfMonitorsLegal(
vmHandler.getResultingVmGraphics(
getVmDeviceUtils().getGraphicsTypesOfEntity(getVmId()),
getParameters().getGraphicsDevices()),
getParameters().getVmStaticData().getNumOfMonitors(),
getReturnValue().getValidationMessages())) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_ILLEGAL_NUM_OF_MONITORS);
}
// Check PCI and IDE limits are ok
if (!isValidPciAndIdeLimit(vmFromParams)) {
return false;
}
if (!VmHandler.isVmPriorityValueLegal(vmFromParams.getPriority(),
getReturnValue().getValidationMessages())) {
return false;
}
if (!validate(VmValidator.validateCpuSockets(vmFromParams.getStaticData(),
getEffectiveCompatibilityVersion()))) {
return false;
}
// check for Vm Payload
if (getParameters().getVmPayload() != null) {
if (!checkPayload(getParameters().getVmPayload(), vmFromParams.getIsoPath())) {
return false;
}
// we save the content in base64 string
for (Map.Entry<String, String> entry : getParameters().getVmPayload().getFiles().entrySet()) {
entry.setValue(new String(BASE_64.encode(entry.getValue().getBytes()), Charset.forName(CharEncoding.UTF_8)));
}
}
// check for Vm Watchdog Model
if (getParameters().getWatchdog() != null) {
if (!validate(new VmWatchdogValidator(vmFromParams.getOs(),
getParameters().getWatchdog(),
getEffectiveCompatibilityVersion()).isValid())) {
return false;
}
}
// Check if the graphics and display from parameters are supported
if (!vmHandler.isGraphicsAndDisplaySupported(vmFromParams.getOs(),
vmHandler.getResultingVmGraphics(getVmDeviceUtils().getGraphicsTypesOfEntity(getVmId()),
getParameters().getGraphicsDevices()),
vmFromParams.getDefaultDisplayType(),
getReturnValue().getValidationMessages(),
getEffectiveCompatibilityVersion())) {
return false;
}
if (!FeatureSupported.isMigrationSupported(getCluster().getArchitecture(), getEffectiveCompatibilityVersion())
&& vmFromParams.getMigrationSupport() != MigrationSupport.PINNED_TO_HOST) {
return failValidation(EngineMessage.VM_MIGRATION_IS_NOT_SUPPORTED);
}
// check cpuPinning
if (!validate(isCpuPinningValid(vmFromParams.getCpuPinning(), vmFromParams.getStaticData()))) {
return false;
}
if (!validatePinningAndMigration(getReturnValue().getValidationMessages(),
getParameters().getVm().getStaticData(), getParameters().getVm().getCpuPinning())) {
return false;
}
if (vmFromParams.isUseHostCpuFlags()
&& vmFromParams.getMigrationSupport() != MigrationSupport.PINNED_TO_HOST) {
return failValidation(EngineMessage.VM_HOSTCPU_MUST_BE_PINNED_TO_HOST);
}
if (!isCpuSharesValid(vmFromParams)) {
return failValidation(EngineMessage.QOS_CPU_SHARES_OUT_OF_RANGE);
}
if (!VmCpuCountHelper.validateCpuCounts(vmFromParams)) {
return failValidation(EngineMessage.TOO_MANY_CPU_COMPONENTS);
}
if (vmFromParams.isUseHostCpuFlags() && (ArchitectureType.ppc == getCluster().getArchitecture().getFamily())) {
return failValidation(EngineMessage.USE_HOST_CPU_REQUESTED_ON_UNSUPPORTED_ARCH);
}
if (!validateCPUHotplug(getParameters().getVmStaticData())) {
return failValidation(EngineMessage.CPU_HOTPLUG_TOPOLOGY_INVALID);
}
if (!validateMemoryAlignment(getParameters().getVmStaticData())) {
return false;
}
if (isVirtioScsiEnabled()) {
// Verify OS compatibility
if (!VmHandler.isOsTypeSupportedForVirtioScsi(vmFromParams.getOs(), getEffectiveCompatibilityVersion(),
getReturnValue().getValidationMessages())) {
return false;
}
}
VmValidator vmValidator = createVmValidator(vmFromParams);
// A pinned VM, must run on one of its hosts
if (!validate(vmValidator.isPinnedVmRunningOnDedicatedHost(vmFromDB, vmFromParams.getStaticData()))){
return false;
}
if (Boolean.FALSE.equals(getParameters().isVirtioScsiEnabled()) && !validate(vmValidator.canDisableVirtioScsi(null))) {
return false;
}
if (vmFromParams.getMinAllocatedMem() > vmFromParams.getMemSizeMb()) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_MIN_MEMORY_CANNOT_EXCEED_MEMORY_SIZE);
}
if (vmFromParams.getCpuProfileId() == null ||
!Objects.equals(vmFromDB.getCpuProfileId(), vmFromParams.getCpuProfileId())) {
if (!setAndValidateCpuProfile()) {
return false;
}
}
if (isBalloonEnabled() && !osRepository.isBalloonEnabled(getParameters().getVmStaticData().getOsId(),
getEffectiveCompatibilityVersion())) {
addValidationMessageVariable("clusterArch", getCluster().getArchitecture());
return failValidation(EngineMessage.BALLOON_REQUESTED_ON_NOT_SUPPORTED_ARCH);
}
if (isSoundDeviceEnabled() && !osRepository.isSoundDeviceEnabled(getParameters().getVmStaticData().getOsId(),
getEffectiveCompatibilityVersion())) {
addValidationMessageVariable("clusterArch", getCluster().getArchitecture());
return failValidation(EngineMessage.SOUND_DEVICE_REQUESTED_ON_NOT_SUPPORTED_ARCH);
}
if (!validate(getNumaValidator().checkVmNumaNodesIntegrity(
getParameters().getVm(),
getParameters().getVm().getvNumaNodeList()))) {
return false;
}
if (getParameters().getVmLargeIcon() != null && !validate(IconValidator.validate(
IconValidator.DimensionsType.LARGE_CUSTOM_ICON,
getParameters().getVmLargeIcon()))) {
return false;
}
if (getParameters().getVmStaticData() != null
&& getParameters().getVmStaticData().getSmallIconId() != null
&& getParameters().getVmLargeIcon() == null // icon id is ignored if large icon is sent
&& !validate(IconValidator.validateIconId(getParameters().getVmStaticData().getSmallIconId(), "Small"))) {
return false;
}
if (getParameters().getVmStaticData() != null
&& getParameters().getVmStaticData().getLargeIconId() != null
&& getParameters().getVmLargeIcon() == null // icon id is ignored if large icon is sent
&& !validate(IconValidator.validateIconId(getParameters().getVmStaticData().getLargeIconId(), "Large"))) {
return false;
}
if (vmFromParams.getProviderId() != null) {
Provider<?> provider = providerDao.get(vmFromParams.getProviderId());
if (provider == null) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_PROVIDER_DOESNT_EXIST);
}
if (provider.getType() != ProviderType.FOREMAN) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_HOST_PROVIDER_TYPE_MISMATCH);
}
}
if (getCluster().isInUpgradeMode()) {
getParameters().getVm().setClusterCompatibilityVersion(getCluster().getCompatibilityVersion());
if (!validate(getClusterUpgradeValidator().isVmReadyForUpgrade(getParameters().getVm()))) {
return false;
}
}
if (!validateQuota(getParameters().getVmStaticData().getQuotaId())) {
return false;
}
if (!validate(VmHandler.validateMaxMemorySize(
getParameters().getVmStaticData(),
getEffectiveCompatibilityVersion()))) {
return false;
}
if (shouldAddLease(getParameters().getVmStaticData())) {
if (!getParameters().getVmStaticData().isAutoStartup()) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_VM_LEASES_ARE_NOT_SUPPORTED_WITH_HA_OFF);
}
if (!FeatureSupported.isVmLeasesSupported(getEffectiveCompatibilityVersion())) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_VM_LEASES_ARE_NOT_SUPPORTED);
}
if (!validateLeaseStorageDomain(getParameters().getVmStaticData().getLeaseStorageDomainId())) {
return false;
}
}
return true;
}
@Override
protected boolean shouldAddLease(VmStatic newVm) {
return super.shouldAddLease(newVm) && !newVm.getLeaseStorageDomainId().equals(getVm().getLeaseStorageDomainId());
}
protected boolean isDedicatedVdsExistOnSameCluster(VmBase vm,
ArrayList<String> validationMessages) {
return vmHandler.validateDedicatedVdsExistOnSameCluster(vm, validationMessages);
}
protected boolean isValidPciAndIdeLimit(VM vmFromParams) {
List<DiskVmElement> diskVmElements = diskVmElementDao.getAllForVm(getVmId());
List<VmNic> interfaces = vmNicDao.getAllForVm(getVmId());
return validate(VmValidator.checkPciAndIdeLimit(
vmFromParams.getOs(),
getEffectiveCompatibilityVersion(),
vmFromParams.getNumOfMonitors(),
interfaces,
diskVmElements,
isVirtioScsiEnabled(),
hasWatchdog(),
isBalloonEnabled(),
isSoundDeviceEnabled()));
}
private boolean isVmExist() {
return getParameters().getVmStaticData() != null && getVm() != null;
}
protected boolean areUpdatedFieldsLegal() {
return vmHandler.isUpdateValid(getVm().getStaticData(),
getParameters().getVmStaticData(),
VMStatus.Down);
}
/**
* check if we need to use running-configuration. Hosted Engine VM always returns false.
* @return true if vm is running and we change field that has @EditableOnVmStatusField annotation
* or runningConfiguration already exist
*/
private boolean isRunningConfigurationNeeded() {
if (getVm().isHostedEngine()) {
// Hosted Engine never uses the next run configuration
return false;
}
return getVm().isNextRunConfigurationExists()
|| !vmHandler.isUpdateValid(
getVm().getStaticData(),
getParameters().getVmStaticData(),
getVm().getStatus(),
isHotSetEnabled())
|| !vmHandler.isUpdateValidForVmDevices(getVmId(), getVm().getStatus(), getParameters())
|| isClusterLevelChange();
}
private boolean isClusterLevelChange() {
Version oldVersion = getParameters().getClusterLevelChangeFromVersion();
return oldVersion != null &&
(getVm().isRunningOrPaused() || getVm().isSuspended()) &&
getVm().getCustomCompatibilityVersion() == null;
}
private boolean isHotSetEnabled() {
return !getParameters().isApplyChangesLater();
}
@Override
public List<PermissionSubject> getPermissionCheckSubjects() {
final List<PermissionSubject> permissionList = super.getPermissionCheckSubjects();
if (isVmExist()) {
// user need specific permission to change custom properties
if (!StringUtils.equals(
getVm().getPredefinedProperties(),
getParameters().getVmStaticData().getPredefinedProperties())
|| !StringUtils.equals(
getVm().getUserDefinedProperties(),
getParameters().getVmStaticData().getUserDefinedProperties())) {
permissionList.add(new PermissionSubject(getParameters().getVmId(),
VdcObjectType.VM,
ActionGroup.CHANGE_VM_CUSTOM_PROPERTIES));
}
// host-specific parameters can be changed by administration role only
if (isDedicatedVmForVdsChanged() || isCpuPinningChanged()) {
permissionList.add(
new PermissionSubject(getParameters().getVmId(),
VdcObjectType.VM,
ActionGroup.EDIT_ADMIN_VM_PROPERTIES));
}
}
return permissionList;
}
private boolean isDedicatedVmForVdsChanged() {
List<Guid> paramList = getParameters().getVmStaticData().getDedicatedVmForVdsList();
List<Guid> vmList = getVm().getDedicatedVmForVdsList();
if (vmList == null && paramList == null){
return false;
}
if (vmList == null || paramList == null){
return true;
}
// vmList.equals(paramList) not good enough, the lists order could change
if (vmList.size() != paramList.size()){
return true;
}
return !paramList.containsAll(vmList);
}
private boolean isCpuPinningChanged() {
return !(getVm().getCpuPinning() == null ?
getParameters().getVmStaticData().getCpuPinning() == null :
getVm().getCpuPinning().equals(getParameters().getVmStaticData().getCpuPinning()));
}
private boolean isEmulatedMachineChanged() {
return !Objects.equals(
getParameters().getVm().getCustomEmulatedMachine(),
getVm().getCustomEmulatedMachine());
}
@Override
public Guid getVmId() {
if (super.getVmId().equals(Guid.Empty)) {
super.setVmId(getParameters().getVmStaticData().getId());
}
return super.getVmId();
}
public static Map<String, Pair<String, String>> getExclusiveLocksForUpdateVm(VM vm) {
// When updating, please also update UpdateClusterCommand#getExclusiveLocks
if (!StringUtils.isBlank(vm.getName())) {
return Collections.singletonMap(vm.getName(),
LockMessagesMatchUtil.makeLockingPair(LockingGroup.VM_NAME, EngineMessage.ACTION_TYPE_FAILED_VM_IS_BEING_UPDATED));
}
return null;
}
public static Map<String, Pair<String, String>> getSharedLocksForUpdateVm(VM vm) {
// When updating, please also update UpdateClusterCommand#getSharedLocks
return Collections.singletonMap(
vm.getId().toString(),
LockMessagesMatchUtil.makeLockingPair(
LockingGroup.VM,
EngineMessage.ACTION_TYPE_FAILED_VM_IS_BEING_UPDATED));
}
@Override
protected Map<String, Pair<String, String>> getExclusiveLocks() {
return getExclusiveLocksForUpdateVm(getParameters().getVm());
}
@Override
protected Map<String, Pair<String, String>> getSharedLocks() {
return getSharedLocksForUpdateVm(getParameters().getVm());
}
@Override
public List<QuotaConsumptionParameter> getQuotaVdsConsumptionParameters() {
List<QuotaConsumptionParameter> list = new ArrayList<>();
// The cases must be persistent with the create_functions_sp
if (!getQuotaManager().isVmStatusQuotaCountable(getVm().getStatus())) {
list.add(new QuotaSanityParameter(getQuotaId(), null));
quotaSanityOnly = true;
} else {
if (!getQuotaId().equals(getVm().getQuotaId())) {
list.add(new QuotaClusterConsumptionParameter(getVm().getQuotaId(),
null,
QuotaConsumptionParameter.QuotaAction.RELEASE,
getClusterId(),
getVm().getNumOfCpus(),
getVm().getMemSizeMb()));
list.add(new QuotaClusterConsumptionParameter(getQuotaId(),
null,
QuotaConsumptionParameter.QuotaAction.CONSUME,
getParameters().getVmStaticData().getClusterId(),
getParameters().getVmStaticData().getNumOfCpus(),
getParameters().getVmStaticData().getMemSizeMb()));
}
}
return list;
}
private Guid getQuotaId() {
return getQuotaManager().getDefaultQuotaIfNull(
getParameters().getVmStaticData().getQuotaId(),
getStoragePoolId());
}
@Override
public String getEntityType() {
return VdcObjectType.VM.getVdcObjectTranslation();
}
@Override
public String getEntityOldName() {
return oldVm.getName();
}
@Override
public String getEntityNewName() {
return getParameters().getVmStaticData().getName();
}
@Override
public void setEntityId(AuditLogableBase logable) {
logable.setVmId(oldVm.getId());
}
@Override
public void addQuotaPermissionSubject(List<PermissionSubject> quotaPermissionList) {
// if only quota sanity is checked the user may use a quota he cannot consume
// (it will be consumed only when the vm will run)
if (!quotaSanityOnly) {
super.addQuotaPermissionSubject(quotaPermissionList);
}
}
protected boolean isVirtioScsiEnabled() {
Boolean virtioScsiEnabled = getParameters().isVirtioScsiEnabled();
return virtioScsiEnabled != null ? virtioScsiEnabled : isVirtioScsiEnabledForVm(getVmId());
}
public boolean isVirtioScsiEnabledForVm(Guid vmId) {
return getVmDeviceUtils().hasVirtioScsiController(vmId);
}
protected boolean isBalloonEnabled() {
Boolean balloonEnabled = getParameters().isBalloonEnabled();
return balloonEnabled != null ? balloonEnabled : getVmDeviceUtils().hasMemoryBalloon(getVmId());
}
protected boolean isSoundDeviceEnabled() {
Boolean soundDeviceEnabled = getParameters().isSoundDeviceEnabled();
return soundDeviceEnabled != null ? soundDeviceEnabled :
getVmDeviceUtils().hasSoundDevice(getVmId());
}
protected boolean hasWatchdog() {
return getParameters().getWatchdog() != null;
}
public VmValidator createVmValidator(VM vm) {
return new VmValidator(vm);
}
protected InClusterUpgradeValidator getClusterUpgradeValidator() {
return clusterUpgradeValidator;
}
}