package org.ovirt.engine.core.bll.storage.disk; import java.util.Collections; import java.util.HashMap; import java.util.Map; import javax.inject.Inject; import org.ovirt.engine.core.bll.LockMessagesMatchUtil; import org.ovirt.engine.core.bll.NonTransactiveCommandAttribute; import org.ovirt.engine.core.bll.context.CommandContext; import org.ovirt.engine.core.bll.validator.VmValidator; import org.ovirt.engine.core.bll.validator.storage.DiskVmElementValidator; import org.ovirt.engine.core.bll.validator.storage.StorageDomainValidator; import org.ovirt.engine.core.common.AuditLogType; import org.ovirt.engine.core.common.action.LockProperties; import org.ovirt.engine.core.common.action.LockProperties.Scope; import org.ovirt.engine.core.common.action.VmDiskOperationParameterBase; import org.ovirt.engine.core.common.businessentities.StorageDomain; import org.ovirt.engine.core.common.businessentities.VmDevice; import org.ovirt.engine.core.common.businessentities.VmDeviceId; import org.ovirt.engine.core.common.businessentities.storage.Disk; import org.ovirt.engine.core.common.businessentities.storage.DiskImage; import org.ovirt.engine.core.common.businessentities.storage.DiskStorageType; import org.ovirt.engine.core.common.businessentities.storage.DiskVmElement; import org.ovirt.engine.core.common.errors.EngineMessage; import org.ovirt.engine.core.common.locks.LockingGroup; import org.ovirt.engine.core.common.utils.Pair; import org.ovirt.engine.core.common.vdscommands.VDSCommandType; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.dao.DiskDao; import org.ovirt.engine.core.dao.DiskImageDao; import org.ovirt.engine.core.dao.DiskVmElementDao; import org.ovirt.engine.core.dao.StorageDomainDao; import org.ovirt.engine.core.dao.VmDeviceDao; import org.ovirt.engine.core.dao.VmStaticDao; @NonTransactiveCommandAttribute public class HotPlugDiskToVmCommand<T extends VmDiskOperationParameterBase> extends AbstractDiskVmCommand<T> { private Disk disk; private DiskVmElement diskVmElement; protected VmDevice oldVmDevice; @Inject private StorageDomainDao storageDomainDao; @Inject private VmDeviceDao vmDeviceDao; @Inject private DiskImageDao diskImageDao; @Inject private DiskDao diskDao; @Inject private VmStaticDao vmStaticDao; @Inject private DiskVmElementDao diskVmElementDao; public HotPlugDiskToVmCommand(T parameters, CommandContext commandContext) { super(parameters, commandContext); } @Override protected LockProperties applyLockProperties(LockProperties lockProperties) { return lockProperties.withScope(Scope.Execution); } @Override protected void setActionMessageParameters() { addValidationMessage(EngineMessage.VAR__ACTION__HOT_PLUG); addValidationMessage(EngineMessage.VAR__TYPE__DISK); } @Override protected boolean validate() { performDbLoads(); return validate(new VmValidator(getVm()).isVmExists()) && isVmInUpPausedDownStatus() && canRunActionOnNonManagedVm() && isDiskExistAndAttachedToVm(getDisk()) && interfaceDiskValidation() && checkCanPerformPlugUnPlugDisk() && isVmNotInPreviewSnapshot() && imageStorageValidation() && virtIoScsiDiskValidation() && isPassDiscardSupported(); } private boolean virtIoScsiDiskValidation() { DiskVmElementValidator diskVmElementValidator = getDiskVmElementValidator(disk, getDiskVmElement()); return validate(diskVmElementValidator.isVirtIoScsiValid(getVm())); } private boolean isPassDiscardSupported() { Guid storageDomainId = getDisk().getDiskStorageType() == DiskStorageType.IMAGE ? ((DiskImage) getDisk()).getStorageIds().get(0) : null; return validate(getDiskVmElementValidator(getDisk(), getDiskVmElement()).isPassDiscardSupported(storageDomainId)); } private boolean interfaceDiskValidation() { DiskVmElementValidator diskVmElementValidator = getDiskVmElementValidator(disk, getDiskVmElement()); return validate(diskVmElementValidator.isDiskInterfaceSupported(getVm())); } private boolean imageStorageValidation() { // If the VM is not an image then it does not use the storage domain. // If the VM is not in UP or PAUSED status, then we know that there is no running qemu process, // so we don't need to check the storage domain activity. if (getDisk().getDiskStorageType() != DiskStorageType.IMAGE || !getVm().getStatus().isRunningOrPaused()) { return true; } DiskImage diskImage = (DiskImage) getDisk(); StorageDomain storageDomain = storageDomainDao.getForStoragePool( diskImage.getStorageIds().get(0), diskImage.getStoragePoolId()); StorageDomainValidator storageDomainValidator = getStorageDomainValidator(storageDomain); return validate(storageDomainValidator.isDomainExistAndActive()); } protected StorageDomainValidator getStorageDomainValidator(StorageDomain storageDomain) { return new StorageDomainValidator(storageDomain); } private void performDbLoads() { if (getDiskVmElement() == null) { return; } oldVmDevice = vmDeviceDao.get(new VmDeviceId(getDiskVmElement().getDiskId(), getVmId())); if (oldVmDevice != null) { if (oldVmDevice.getSnapshotId() != null) { disk = diskImageDao.getDiskSnapshotForVmSnapshot(getDiskVmElement().getDiskId(), oldVmDevice.getSnapshotId()); } else { disk = diskDao.get(getDiskVmElement().getDiskId()); } } } private boolean checkCanPerformPlugUnPlugDisk() { if (getVm().getStatus().isUpOrPaused()) { setVdsId(getVm().getRunOnVds()); if (!isDiskSupportedForPlugUnPlug(getDiskVmElement(), disk.getDiskAlias())) { return false; } } if (getPlugAction() == VDSCommandType.HotPlugDisk && oldVmDevice.isPlugged()) { return failValidation(EngineMessage.HOT_PLUG_DISK_IS_NOT_UNPLUGGED); } if (getPlugAction() == VDSCommandType.HotUnPlugDisk && !oldVmDevice.isPlugged()) { return failValidation(EngineMessage.HOT_UNPLUG_DISK_IS_NOT_PLUGGED); } return true; } protected VDSCommandType getPlugAction() { return VDSCommandType.HotPlugDisk; } @Override protected void executeVmCommand() { if (getVm().getStatus().isUpOrPaused()) { updateDisksFromDb(); performPlugCommand(getPlugAction(), getDisk(), oldVmDevice); } // At this point disk is already plugged to or unplugged from VM (depends on the command), // so we can update the needed device properties updateDeviceProperties(); vmStaticDao.incrementDbGeneration(getVm().getId()); setSucceeded(true); } protected void updateDeviceProperties() { VmDevice device = vmDeviceDao.get(oldVmDevice.getId()); device.setPlugged(true); vmDeviceDao.updateHotPlugDisk(device); } @Override protected Map<String, Pair<String, String>> getSharedLocks() { return Collections.singletonMap(getVmId().toString(), LockMessagesMatchUtil.makeLockingPair(LockingGroup.VM, EngineMessage.ACTION_TYPE_FAILED_VM_IS_LOCKED)); } @Override protected Map<String, Pair<String, String>> getExclusiveLocks() { Map<String, Pair<String, String>> exclusiveLock = null; if (getDisk() != null) { exclusiveLock = new HashMap<>(); exclusiveLock.put(getDisk().getId().toString(), LockMessagesMatchUtil.makeLockingPair(LockingGroup.DISK, EngineMessage.ACTION_TYPE_FAILED_DISKS_LOCKED.name() + String.format("$diskAliases %1$s", getDiskAlias()))); if (getDiskVmElement() != null && getDiskVmElement().isBoot()) { exclusiveLock.put(getVmId().toString(), LockMessagesMatchUtil.makeLockingPair(LockingGroup.VM_DISK_BOOT, EngineMessage.ACTION_TYPE_FAILED_OBJECT_LOCKED)); } } return exclusiveLock; } @Override public AuditLogType getAuditLogTypeValue() { return getSucceeded() ? AuditLogType.USER_HOTPLUG_DISK : AuditLogType.USER_FAILED_HOTPLUG_DISK; } @Override public String getDiskAlias() { return getDisk().getDiskAlias(); } protected Disk getDisk() { if (disk == null) { disk = diskDao.get(super.getDiskVmElement().getDiskId()); } return disk; } // As all the validation should be done against the DiskVmElement loaded from the DB since the parameters may // not contain all relevant data @Override protected DiskVmElement getDiskVmElement() { if (diskVmElement == null && getDisk() != null) { diskVmElement = diskVmElementDao.get(new VmDeviceId(getDisk().getId(), getVmId())); } return diskVmElement; } }