package org.ovirt.engine.core.bll.storage.disk;
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.NonTransactiveCommandAttribute;
import org.ovirt.engine.core.bll.context.CommandContext;
import org.ovirt.engine.core.bll.storage.StorageJobCommand;
import org.ovirt.engine.core.bll.storage.disk.image.ImagesHandler;
import org.ovirt.engine.core.bll.storage.utils.VdsCommandsHelper;
import org.ovirt.engine.core.bll.utils.PermissionSubject;
import org.ovirt.engine.core.bll.validator.storage.DiskImagesValidator;
import org.ovirt.engine.core.bll.validator.storage.DiskValidator;
import org.ovirt.engine.core.common.AuditLogType;
import org.ovirt.engine.core.common.VdcObjectType;
import org.ovirt.engine.core.common.action.LockProperties;
import org.ovirt.engine.core.common.action.StorageJobCommandParameters;
import org.ovirt.engine.core.common.businessentities.VM;
import org.ovirt.engine.core.common.businessentities.VmDevice;
import org.ovirt.engine.core.common.businessentities.VmTemplate;
import org.ovirt.engine.core.common.businessentities.storage.DiskImage;
import org.ovirt.engine.core.common.businessentities.storage.ImageStatus;
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.SparsifyImageVDSCommandParameters;
import org.ovirt.engine.core.common.vdscommands.VDSCommandType;
import org.ovirt.engine.core.common.vdscommands.VDSReturnValue;
import org.ovirt.engine.core.compat.CommandStatus;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.dao.DiskImageDao;
import org.ovirt.engine.core.dao.VmDao;
import org.ovirt.engine.core.dao.VmTemplateDao;
import org.ovirt.engine.core.utils.transaction.TransactionSupport;
@NonTransactiveCommandAttribute(forceCompensation = true)
public class SparsifyImageCommand<T extends StorageJobCommandParameters> extends StorageJobCommand<T> {
private List<Pair<VM, VmDevice>> vmsForDisk;
private DiskImage diskImage;
@Inject
private VmDao vmDao;
@Inject
private DiskImageDao diskImageDao;
@Inject
private VmTemplateDao vmTemplateDao;
public SparsifyImageCommand(T parameters, CommandContext commandContext) {
super(parameters, commandContext);
}
public SparsifyImageCommand(Guid commandId) {
super(commandId);
}
private DiskImage getDiskImage() {
if (diskImage == null) {
diskImage = diskImageDao.get(getParameters().getImageId());
}
return diskImage;
}
private List<Pair<VM, VmDevice>> getVmsForDisk() {
if (vmsForDisk == null && getDiskImage() != null) {
vmsForDisk = vmDao.getVmsWithPlugInfo(getDiskImage().getId());
}
return vmsForDisk;
}
@Override
protected void init() {
super.init();
if (getDiskImage() != null && getDiskImage().getVmEntityType() != null
&& getDiskImage().getVmEntityType().isTemplateType()) {
initVmTemplateId();
}
}
private void initVmTemplateId() {
Map<Boolean, VmTemplate> templateMap = vmTemplateDao.getAllForImage(getDiskImage().getImageId());
if (!templateMap.isEmpty()) {
setVmTemplateId(templateMap.values().iterator().next().getId());
}
}
@Override
protected boolean validate() {
DiskValidator diskValidator = new DiskValidator(getDiskImage());
if (!validate(diskValidator.isDiskExists()) ||
!validate(diskValidator.isDiskPluggedToVmsThatAreNotDown(false, getVmsForDisk())) ||
!validate(diskValidator.isSparsifySupported())) {
return false;
}
if (diskImageDao.getAllSnapshotsForImageGroup(getDiskImage().getId()).size() > 1) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_DISK_HAS_SNAPSHOTS);
}
DiskImagesValidator diskImagesValidator = new DiskImagesValidator(Collections.singletonList(getDiskImage()));
return validate(diskImagesValidator.diskImagesNotIllegal()) &&
validate(diskImagesValidator.diskImagesNotLocked()) &&
validate(diskImagesValidator.diskImagesHaveNoDerivedDisks(null));
}
@Override
protected void executeCommand() {
lockImageInDb();
VDSReturnValue vdsReturnValue = VdsCommandsHelper.runVdsCommandWithFailover(
VDSCommandType.SparsifyImage,
new SparsifyImageVDSCommandParameters(
getParameters().getStorageJobId(),
getDiskImage().getStorageIds().get(0),
getDiskImage().getId(),
getDiskImage().getImageId()),
getDiskImage().getStoragePoolId(), this);
if (!vdsReturnValue.getSucceeded()) {
setCommandStatus(CommandStatus.FAILED);
unlockImageInDb();
}
setSucceeded(vdsReturnValue.getSucceeded());
}
@Override
protected void endSuccessfully() {
super.endSuccessfully();
unlockImageInDb();
}
@Override
protected void endWithFailure() {
super.endWithFailure();
unlockImageInDb();
}
@Override
public List<PermissionSubject> getPermissionCheckSubjects() {
DiskImage diskImage = getDiskImage();
if (diskImage == null) {
return Collections.emptyList();
}
return Collections.singletonList(
new PermissionSubject(diskImage.getId(),
VdcObjectType.Disk,
getActionType().getActionGroup()));
}
private void lockImageInDb() {
final DiskImage diskImage = getDiskImage();
TransactionSupport.executeInNewTransaction(() -> {
getCompensationContext().snapshotEntityStatus(diskImage.getImage());
diskImage.setImageStatus(ImageStatus.LOCKED);
ImagesHandler.updateImageStatus(diskImage.getImageId(), ImageStatus.LOCKED);
getCompensationContext().stateChanged();
return null;
});
}
private void unlockImageInDb() {
DiskImage diskImage = getDiskImage();
diskImage.setImageStatus(ImageStatus.OK);
ImagesHandler.updateImageStatus(diskImage.getImageId(), ImageStatus.OK);
}
@Override
protected LockProperties applyLockProperties(LockProperties lockProperties) {
return lockProperties.withScope(LockProperties.Scope.Execution);
}
@Override
protected Map<String, Pair<String, String>> getExclusiveLocks() {
return getDiskImage() != null ? Collections.singletonMap(getDiskImage().getId().toString(),
LockMessagesMatchUtil.makeLockingPair(LockingGroup.DISK, EngineMessage.ACTION_TYPE_FAILED_DISKS_LOCKED))
: null;
}
@Override
protected Map<String, Pair<String, String>> getSharedLocks() {
if (getDiskImage() == null || getDiskImage().getVmEntityType() == null) {
return null;
}
if (getDiskImage().getVmEntityType().isVmType()) {
return getSharedLocksForVmDisk();
}
if (getDiskImage().getVmEntityType().isTemplateType()) {
return getSharedLocksForTemplateDisk();
}
log.warn("No shared locks are taken while sparsifying disk of entity: {}", getDiskImage().getVmEntityType());
return null;
}
private Map<String, Pair<String, String>> getSharedLocksForVmDisk() {
Map<String, Pair<String, String>> sharedLocks = new HashMap<>();
if (getVmsForDisk() != null) {
for (Pair<VM, VmDevice> vmForDisk : getVmsForDisk()) {
sharedLocks.put(vmForDisk.getFirst().getId().toString(),
LockMessagesMatchUtil.makeLockingPair(LockingGroup.VM, EngineMessage.ACTION_TYPE_FAILED_VM_IS_LOCKED));
}
}
return sharedLocks;
}
private Map<String, Pair<String, String>> getSharedLocksForTemplateDisk() {
return Collections.singletonMap(getVmTemplateId().toString(),
LockMessagesMatchUtil.makeLockingPair(LockingGroup.TEMPLATE, EngineMessage.VM_TEMPLATE_IS_LOCKED));
}
@Override
protected void setActionMessageParameters() {
addValidationMessage(EngineMessage.VAR__ACTION__SPARSIFY);
addValidationMessage(EngineMessage.VAR__TYPE__DISK);
}
@Override
public AuditLogType getAuditLogTypeValue() {
addCustomValue("DiskAlias", getDiskImage().getDiskAlias());
switch (getActionState()) {
case EXECUTE:
return getSucceeded() ? AuditLogType.USER_SPARSIFY_IMAGE_START
: AuditLogType.USER_SPARSIFY_IMAGE_FINISH_FAILURE;
case END_SUCCESS:
return getSucceeded() ? AuditLogType.USER_SPARSIFY_IMAGE_FINISH_SUCCESS
: AuditLogType.USER_SPARSIFY_IMAGE_FINISH_FAILURE;
default:
return AuditLogType.USER_SPARSIFY_IMAGE_FINISH_FAILURE;
}
}
}