package org.ovirt.engine.core.bll.storage.disk; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.inject.Inject; import org.ovirt.engine.core.bll.LockMessagesMatchUtil; import org.ovirt.engine.core.bll.ValidationResult; import org.ovirt.engine.core.bll.context.CommandContext; import org.ovirt.engine.core.bll.utils.PermissionSubject; import org.ovirt.engine.core.bll.validator.VmValidator; import org.ovirt.engine.core.bll.validator.storage.DiskValidator; 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.VdcObjectType; import org.ovirt.engine.core.common.action.AttachDetachVmDiskParameters; import org.ovirt.engine.core.common.action.LockProperties; import org.ovirt.engine.core.common.action.LockProperties.Scope; import org.ovirt.engine.core.common.businessentities.ActionGroup; import org.ovirt.engine.core.common.businessentities.Snapshot; import org.ovirt.engine.core.common.businessentities.Snapshot.SnapshotType; import org.ovirt.engine.core.common.businessentities.StorageDomain; import org.ovirt.engine.core.common.businessentities.StoragePoolIsoMapId; 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.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.businessentities.storage.ImageStatus; 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.utils.Pair; import org.ovirt.engine.core.common.utils.VmDeviceType; import org.ovirt.engine.core.common.validation.group.UpdateEntity; import org.ovirt.engine.core.common.vdscommands.VDSCommandType; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.dao.DiskVmElementDao; import org.ovirt.engine.core.dao.ImageDao; import org.ovirt.engine.core.dao.SnapshotDao; import org.ovirt.engine.core.dao.StorageDomainDao; import org.ovirt.engine.core.dao.StoragePoolIsoMapDao; import org.ovirt.engine.core.dao.VmDeviceDao; import org.ovirt.engine.core.dao.VmStaticDao; public class AttachDiskToVmCommand<T extends AttachDetachVmDiskParameters> extends AbstractDiskVmCommand<T> { @Inject private DiskHandler diskHandler; @Inject private VmDeviceDao vmDeviceDao; @Inject private StoragePoolIsoMapDao storagePoolIsoMapDao; @Inject private StorageDomainDao storageDomainDao; @Inject private VmStaticDao vmStaticDao; @Inject private DiskVmElementDao diskVmElementDao; @Inject private ImageDao imageDao; @Inject private SnapshotDao snapshotDao; private List<PermissionSubject> permsList = null; private Disk disk; public AttachDiskToVmCommand(T parameters, CommandContext commandContext) { super(parameters, commandContext); } @Override protected void init() { disk = diskHandler.loadDiskFromSnapshot(getDiskVmElement().getDiskId(), getParameters().getSnapshotId()); } @Override protected LockProperties applyLockProperties(LockProperties lockProperties) { return lockProperties.withScope(Scope.Execution); } @Override protected boolean validate() { if (disk == null) { return failValidation(EngineMessage.ACTION_TYPE_FAILED_VM_IMAGE_DOES_NOT_EXIST); } DiskValidator oldDiskValidator = new DiskValidator(disk); ValidationResult isHostedEngineDisk = oldDiskValidator.validateNotHostedEngineDisk(); if (!isHostedEngineDisk.isValid()) { return validate(isHostedEngineDisk); } DiskValidator diskValidator = getDiskValidator(disk); if (!checkDiskUsedAsOvfStore(diskValidator)) { return false; } if (isOperationPerformedOnDiskSnapshot() && (!validate(snapshotsValidator.snapshotExists(getSnapshot())) || !validate(snapshotsValidator.isRegularSnapshot(getSnapshot())))) { return false; } boolean isImageDisk = disk.getDiskStorageType().isInternal(); if (isImageDisk) { //TODO : this load and check of the active disk will be removed //after inspecting upgrade Disk activeDisk = diskHandler.loadActiveDisk(disk.getId()); if (((DiskImage) activeDisk).getImageStatus() == ImageStatus.ILLEGAL) { return failValidation(EngineMessage.ACTION_TYPE_FAILED_ILLEGAL_DISK_OPERATION); } if (((DiskImage) disk).getImageStatus() == ImageStatus.LOCKED) { addValidationMessage(EngineMessage.ACTION_TYPE_FAILED_DISKS_LOCKED); addValidationMessageVariable("diskAliases", disk.getDiskAlias()); return false; } } if (!validate(new VmValidator(getVm()).isVmExists()) || !isVmInUpPausedDownStatus()) { return false; } if (!validateDiskVmData()) { return false; } if (!canRunActionOnNonManagedVm()) { return false; } updateDisksFromDb(); if (getDiskVmElement().isBoot() && !validate(diskValidator.isVmNotContainsBootDisk(getVm()))) { return false; } if (!isDiskPassPciAndIdeLimit()) { return false; } if (vmDeviceDao.exists(new VmDeviceId(disk.getId(), getVmId()))) { return failValidation(EngineMessage.ACTION_TYPE_FAILED_DISK_ALREADY_ATTACHED); } if (!isOperationPerformedOnDiskSnapshot() && !disk.isShareable() && disk.getNumberOfVms() > 0) { return failValidation(EngineMessage.ACTION_TYPE_FAILED_NOT_SHAREABLE_DISK_ALREADY_ATTACHED); } if (isImageDisk && storagePoolIsoMapDao.get(new StoragePoolIsoMapId( ((DiskImage) disk).getStorageIds().get(0), getVm().getStoragePoolId())) == null) { return failValidation(EngineMessage.ACTION_TYPE_FAILED_STORAGE_POOL_NOT_MATCH); } if (isImageDisk) { StorageDomain storageDomain = storageDomainDao.getForStoragePool( ((DiskImage) disk).getStorageIds().get(0), ((DiskImage) disk).getStoragePoolId()); StorageDomainValidator storageDomainValidator = getStorageDomainValidator(storageDomain); if (!validate(storageDomainValidator.isDomainExistAndActive())) { return false; } } DiskVmElementValidator diskVmElementValidator = getDiskVmElementValidator(disk, getDiskVmElement()); if (!validate(diskVmElementValidator.isReadOnlyPropertyCompatibleWithInterface())) { return false; } if (!validate(diskVmElementValidator.isVirtIoScsiValid(getVm()))) { return false; } if (!validate(diskVmElementValidator.isDiskInterfaceSupported(getVm()))) { return false; } Guid storageDomainId = disk.getDiskStorageType() == DiskStorageType.IMAGE ? ((DiskImage) disk).getStorageIds().get(0) : null; if (!validate(diskVmElementValidator.isPassDiscardSupported(storageDomainId))) { return false; } if (!isVmNotInPreviewSnapshot()) { return false; } if (getParameters().isPlugUnPlug() && getVm().getStatus() != VMStatus.Down) { return isDiskSupportedForPlugUnPlug(getDiskVmElement(), disk.getDiskAlias()); } return true; } protected StorageDomainValidator getStorageDomainValidator(StorageDomain storageDomain) { return new StorageDomainValidator(storageDomain); } @Override protected void executeVmCommand() { if (!isOperationPerformedOnDiskSnapshot()) { vmStaticDao.incrementDbGeneration(getVm().getId()); } final VmDevice vmDevice = createVmDevice(); vmDeviceDao.save(vmDevice); DiskVmElement diskVmElement = getDiskVmElement(); diskVmElement.getId().setDeviceId(disk.getId()); diskVmElementDao.save(diskVmElement); // When performing hot plug for VirtIO-SCSI or SPAPR_VSCSI the address map calculation needs this info to be populated disk.setDiskVmElements(Collections.singletonList(diskVmElement)); // update cached image List<Disk> imageList = new ArrayList<>(); imageList.add(disk); vmHandler.updateDisksForVm(getVm(), imageList); if (!isOperationPerformedOnDiskSnapshot()) { if (disk.isAllowSnapshot()) { updateDiskVmSnapshotId(); } } if (getParameters().isPlugUnPlug() && getVm().getStatus() != VMStatus.Down) { performPlugCommand(VDSCommandType.HotPlugDisk, disk, vmDevice); } setSucceeded(true); } protected VmDevice createVmDevice() { return new VmDevice(new VmDeviceId(disk.getId(), getVmId()), VmDeviceGeneralType.DISK, VmDeviceType.DISK.getName(), "", null, true, getParameters().isPlugUnPlug(), getParameters().isReadOnly(), "", null, getParameters().getSnapshotId(), null); } protected boolean isOperationPerformedOnDiskSnapshot() { return getParameters().getSnapshotId() != null; } private void updateDiskVmSnapshotId() { Guid snapshotId = snapshotDao.getId(getVmId(), SnapshotType.ACTIVE); if (disk.getDiskStorageType().isInternal()) { DiskImage diskImage = (DiskImage) disk; imageDao.updateImageVmSnapshotId(diskImage.getImageId(), snapshotId); } else { throw new EngineException(EngineError.StorageException, "update of snapshot id was initiated for unsupported disk type"); } } @Override protected void setActionMessageParameters() { addValidationMessage(EngineMessage.VAR__ACTION__ATTACH_ACTION_TO); addValidationMessage(EngineMessage.VAR__TYPE__DISK); } @Override public List<PermissionSubject> getPermissionCheckSubjects() { if (permsList == null) { permsList = super.getPermissionCheckSubjects(); Guid diskId = disk == null ? null : disk.getId(); permsList.add(new PermissionSubject(diskId, VdcObjectType.Disk, ActionGroup.ATTACH_DISK)); } return permsList; } @Override protected Map<String, Pair<String, String>> getExclusiveLocks() { if (disk == null) { return null; } Map<String, Pair<String, String>> locks = new HashMap<>(); if (!disk.isShareable()) { locks.put(disk.getId().toString(), LockMessagesMatchUtil.makeLockingPair(LockingGroup.DISK, EngineMessage.ACTION_TYPE_FAILED_OBJECT_LOCKED)); } if (getDiskVmElement() != null && getDiskVmElement().isBoot()) { locks.put(getParameters().getVmId().toString(), LockMessagesMatchUtil.makeLockingPair(LockingGroup.VM_DISK_BOOT, EngineMessage.ACTION_TYPE_FAILED_OBJECT_LOCKED)); } return locks; } @Override public AuditLogType getAuditLogTypeValue() { return getSucceeded() ? AuditLogType.USER_ATTACH_DISK_TO_VM : AuditLogType.USER_FAILED_ATTACH_DISK_TO_VM; } protected Snapshot getSnapshot() { return snapshotDao.get(getParameters().getSnapshotId()); } @Override public String getDiskAlias() { return disk.getDiskAlias(); } @Override protected List<Class<?>> getValidationGroups() { addValidationGroup(UpdateEntity.class); return super.getValidationGroups(); } }