package org.ovirt.engine.core.bll.storage.disk;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import org.ovirt.engine.core.bll.CommandBase;
import org.ovirt.engine.core.bll.LockMessage;
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.disk.image.ImagesHandler;
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.StoragePoolValidator;
import org.ovirt.engine.core.common.AuditLogType;
import org.ovirt.engine.core.common.VdcObjectType;
import org.ovirt.engine.core.common.action.GetDiskAlignmentParameters;
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.ArchitectureType;
import org.ovirt.engine.core.common.businessentities.StorageDomainStatic;
import org.ovirt.engine.core.common.businessentities.StoragePool;
import org.ovirt.engine.core.common.businessentities.VDS;
import org.ovirt.engine.core.common.businessentities.VDSStatus;
import org.ovirt.engine.core.common.businessentities.VM;
import org.ovirt.engine.core.common.businessentities.storage.Disk;
import org.ovirt.engine.core.common.businessentities.storage.DiskAlignment;
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.ImageStatus;
import org.ovirt.engine.core.common.businessentities.storage.LunDisk;
import org.ovirt.engine.core.common.businessentities.storage.VolumeFormat;
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.vdscommands.GetDiskAlignmentVDSCommandParameters;
import org.ovirt.engine.core.common.vdscommands.GetDiskImageAlignmentVDSCommandParameters;
import org.ovirt.engine.core.common.vdscommands.GetDiskLunAlignmentVDSCommandParameters;
import org.ovirt.engine.core.common.vdscommands.VDSCommandType;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.dao.BaseDiskDao;
import org.ovirt.engine.core.dao.DiskDao;
import org.ovirt.engine.core.dao.StorageDomainStaticDao;
import org.ovirt.engine.core.dao.StoragePoolDao;
import org.ovirt.engine.core.dao.VdsDao;
import org.ovirt.engine.core.dao.VmDao;
import org.ovirt.engine.core.utils.transaction.TransactionSupport;
@NonTransactiveCommandAttribute(forceCompensation = true)
public class GetDiskAlignmentCommand<T extends GetDiskAlignmentParameters> extends CommandBase<T> {
@Inject
private StorageDomainStaticDao storageDomainStaticDao;
@Inject
private StoragePoolDao storagePoolDao;
@Inject
private BaseDiskDao baseDiskDao;
@Inject
private VdsDao vdsDao;
@Inject
private DiskDao diskDao;
@Inject
private VmDao vmDao;
private Disk diskToScan;
private Guid vdsInPool;
private Guid storagePoolId;
private VM diskVm;
private List<PermissionSubject> permsList;
public GetDiskAlignmentCommand(T parameters, CommandContext cmdContext) {
super(parameters, cmdContext);
}
public GetDiskAlignmentCommand(Guid commandId) {
super(commandId);
}
@Override
protected LockProperties applyLockProperties(LockProperties lockProperties) {
return lockProperties.withScope(Scope.Execution);
}
@Override
protected Map<String, Pair<String, String>> getExclusiveLocks() {
if (getDisk() != null && isImageExclusiveLockNeeded()) {
return Collections.singletonMap(getDisk().getId().toString(),
LockMessagesMatchUtil.makeLockingPair(LockingGroup.DISK, getDiskIsUsedByGetAlignment()));
}
return null;
}
@Override
protected Map<String, Pair<String, String>> getSharedLocks() {
if (getDisk() != null && !isImageExclusiveLockNeeded()) {
return Collections.singletonMap(getDisk().getId().toString(),
LockMessagesMatchUtil.makeLockingPair(LockingGroup.DISK, getDiskIsUsedByGetAlignment()));
}
return null;
}
private LockMessage getDiskIsUsedByGetAlignment() {
return new LockMessage(EngineMessage.ACTION_TYPE_FAILED_DISK_IS_USED_BY_GET_ALIGNMENT)
.with("DiskAlias", getDiskAlias());
}
@Override
protected void setActionMessageParameters() {
addValidationMessage(EngineMessage.VAR__ACTION__SCAN_ALIGNMENT);
addValidationMessage(EngineMessage.VAR__TYPE__DISK);
}
@Override
public Map<String, String> getJobMessageProperties() {
if (jobProperties == null) {
jobProperties = super.getJobMessageProperties();
jobProperties.put("diskalias", getDiskAlias());
}
return jobProperties;
}
@Override
protected boolean validate() {
if (getDisk() == null) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_DISK_NOT_EXIST);
}
if (getVm() == null) {
addValidationMessageVariable("diskAliases", getDiskAlias());
return failValidation(EngineMessage.ACTION_TYPE_FAILED_DISK_IS_NOT_VM_DISK);
}
if (ArchitectureType.ppc == getCluster().getArchitecture().getFamily()) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_ALIGNMENT_SCAN_NOT_SUPPORTED_ON_PPC);
}
if (getDiskStorageType() == DiskStorageType.IMAGE) {
DiskImagesValidator diskImagesValidator = new DiskImagesValidator((DiskImage) getDisk());
if (!validate(diskImagesValidator.diskImagesNotLocked()) ||
!validate(diskImagesValidator.diskImagesNotIllegal())) {
return false;
}
StorageDomainStatic sds = storageDomainStaticDao.get(((DiskImage) getDisk()).getStorageIds().get(0));
if (!sds.getStorageType().isBlockDomain()) {
addValidationMessageVariable("diskAlias", getDiskAlias());
return failValidation(EngineMessage.ACTION_TYPE_FAILED_ALIGNMENT_SCAN_STORAGE_TYPE);
}
}
if (getVm().isRunningOrPaused()) {
return failValidation(EngineMessage.ERROR_CANNOT_RUN_ALIGNMENT_SCAN_VM_IS_RUNNING);
}
if (getVdsIdInGroup() == null) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_NO_VDS_IN_POOL);
}
StoragePool sp = storagePoolDao.get(getStoragePoolId());
if (!validate(new StoragePoolValidator(sp).isUp())) {
return false;
}
return true;
}
@Override
protected void executeCommand() {
GetDiskAlignmentVDSCommandParameters parameters;
auditLogDirector.log(this, AuditLogType.DISK_ALIGNMENT_SCAN_START);
acquireExclusiveDiskDbLocks();
// Live scan is not supported yet, this might become: getVm().getId()
Guid vmId = Guid.Empty;
if (getDiskStorageType() == DiskStorageType.IMAGE) {
DiskImage diskImage = (DiskImage) getDisk();
GetDiskImageAlignmentVDSCommandParameters imageParameters =
new GetDiskImageAlignmentVDSCommandParameters(getVdsIdInGroup(), vmId);
imageParameters.setPoolId(getStoragePoolId());
imageParameters.setDomainId(diskImage.getStorageIds().get(0));
imageParameters.setImageGroupId(diskImage.getId());
imageParameters.setImageId(diskImage.getImageId());
parameters = imageParameters;
} else if (getDiskStorageType() == DiskStorageType.LUN) {
LunDisk lunDisk = (LunDisk) getDisk();
GetDiskLunAlignmentVDSCommandParameters lunParameters =
new GetDiskLunAlignmentVDSCommandParameters(getVdsIdInGroup(), vmId);
lunParameters.setLunId(lunDisk.getLun().getLUNId());
parameters = lunParameters;
} else {
throw new EngineException(EngineError.ENGINE, "Unknown DiskStorageType: " +
getDiskStorageType().toString() + " Disk id: " + getDisk().getId().toString());
}
Boolean isDiskAligned = (Boolean) runVdsCommand(
VDSCommandType.GetDiskAlignment, parameters).getReturnValue();
getDisk().setAlignment(isDiskAligned ? DiskAlignment.Aligned : DiskAlignment.Misaligned);
getDisk().setLastAlignmentScan(new Date());
baseDiskDao.update(getDisk());
setSucceeded(true);
releaseExclusiveDiskDbLocks();
}
@Override
public List<PermissionSubject> getPermissionCheckSubjects() {
if (permsList == null && getDisk() != null) {
permsList = new ArrayList<>();
permsList.add(new PermissionSubject(getDisk().getId(),
VdcObjectType.Disk, ActionGroup.ATTACH_DISK));
permsList.add(new PermissionSubject(getDisk().getId(),
VdcObjectType.Disk, ActionGroup.EDIT_DISK_PROPERTIES));
}
return permsList;
}
protected void acquireExclusiveDiskDbLocks() {
if (isImageExclusiveLockNeeded()) {
final DiskImage diskImage = (DiskImage) getDisk();
TransactionSupport.executeInNewTransaction(() -> {
getCompensationContext().snapshotEntityStatus(diskImage.getImage());
getCompensationContext().stateChanged();
diskImage.setImageStatus(ImageStatus.LOCKED);
ImagesHandler.updateImageStatus(diskImage.getImageId(), ImageStatus.LOCKED);
return null;
});
}
}
protected void releaseExclusiveDiskDbLocks() {
if (isImageExclusiveLockNeeded()) {
final DiskImage diskImage = (DiskImage) getDisk();
diskImage.setImageStatus(ImageStatus.OK);
ImagesHandler.updateImageStatus(diskImage.getImageId(), ImageStatus.OK);
}
}
protected boolean isImageExclusiveLockNeeded() {
/* In case the volume format is RAW (same as a direct LUN) the exclusive image
* lock is not needed since the alignment scan can run without any interference
* by a concurrent running VM.
*/
return getDiskStorageType() == DiskStorageType.IMAGE &&
((DiskImage) getDisk()).getVolumeFormat() != VolumeFormat.RAW;
}
@Override
public Guid getStoragePoolId() {
if (storagePoolId == null && getVm() != null) {
storagePoolId = getVm().getStoragePoolId();
}
return storagePoolId;
}
protected Guid getVdsIdInGroup() {
if (vdsInPool == null && getCluster() != null) {
List<VDS> vdsInPoolList = vdsDao.getAllForClusterWithStatus(getCluster().getId(), VDSStatus.Up);
if (!vdsInPoolList.isEmpty()) {
vdsInPool = vdsInPoolList.get(0).getId();
}
}
return vdsInPool;
}
@Override
public VM getVm() {
if (diskVm == null && getDisk() != null) {
diskVm = vmDao.getVmsListForDisk(getDisk().getId(), false).stream().findFirst().orElse(null);
}
return diskVm;
}
public String getDiskAlias() {
if (getDisk() != null) {
return getDisk().getDiskAlias();
}
return "";
}
protected DiskStorageType getDiskStorageType() {
return getDisk().getDiskStorageType();
}
protected Disk getDisk() {
if (diskToScan == null) {
diskToScan = diskDao.get(getParameters().getDiskId());
}
return diskToScan;
}
@Override
public AuditLogType getAuditLogTypeValue() {
return getSucceeded() ? AuditLogType.DISK_ALIGNMENT_SCAN_SUCCESS : AuditLogType.DISK_ALIGNMENT_SCAN_FAILURE;
}
}