package org.ovirt.engine.core.bll.storage.disk;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.inject.Inject;
import org.apache.commons.lang.StringUtils;
import org.ovirt.engine.core.bll.Backend;
import org.ovirt.engine.core.bll.CommandBase;
import org.ovirt.engine.core.bll.context.CommandContext;
import org.ovirt.engine.core.bll.utils.PermissionSubject;
import org.ovirt.engine.core.bll.validator.storage.DiskValidator;
import org.ovirt.engine.core.common.VdcObjectType;
import org.ovirt.engine.core.common.action.LiveMigrateDiskParameters;
import org.ovirt.engine.core.common.action.LiveMigrateVmDisksParameters;
import org.ovirt.engine.core.common.action.MoveDiskParameters;
import org.ovirt.engine.core.common.action.MoveDisksParameters;
import org.ovirt.engine.core.common.action.VdcActionParametersBase;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.action.VdcReturnValueBase;
import org.ovirt.engine.core.common.businessentities.ActionGroup;
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.DiskImage;
import org.ovirt.engine.core.common.businessentities.storage.DiskStorageType;
import org.ovirt.engine.core.common.errors.EngineMessage;
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.VmDao;
import org.ovirt.engine.core.utils.collections.MultiValueMapUtils;
public class MoveDisksCommand<T extends MoveDisksParameters> extends CommandBase<T> {
@Inject
private DiskDao diskDao;
@Inject
private DiskImageDao diskImageDao;
@Inject
private VmDao vmDao;
private List<VdcReturnValueBase> vdcReturnValues = new ArrayList<>();
private List<MoveDiskParameters> moveDiskParametersList = new ArrayList<>();
private List<LiveMigrateVmDisksParameters> liveMigrateVmDisksParametersList = new ArrayList<>();
private Map<Guid, DiskImage> diskMap = new HashMap<>();
private Map<MoveDiskParameters, DiskImage> cachedParamsToDisks;
public MoveDisksCommand(Guid commandId) {
super(commandId);
}
public MoveDisksCommand(T parameters, CommandContext cmdContext) {
super(parameters, cmdContext);
}
@Override
protected void init() {
super.init();
cachedParamsToDisks = getParameters().getParametersList().stream()
.collect(Collectors.toMap(Function.identity(), p -> diskImageDao.get(p.getImageId())));
}
@Override
protected void executeCommand() {
updateParameters();
if (!moveDiskParametersList.isEmpty()) {
vdcReturnValues.addAll(Backend.getInstance().runMultipleActions(VdcActionType.MoveOrCopyDisk,
getParametersArrayList(moveDiskParametersList), false));
}
if (!liveMigrateVmDisksParametersList.isEmpty()) {
vdcReturnValues.addAll(Backend.getInstance().runMultipleActions(VdcActionType.LiveMigrateVmDisks,
getParametersArrayList(liveMigrateVmDisksParametersList), false));
}
handleChildReturnValue();
setSucceeded(true);
}
private void handleChildReturnValue() {
for (VdcReturnValueBase vdcReturnValue : vdcReturnValues) {
getReturnValue().getValidationMessages().addAll(vdcReturnValue.getValidationMessages());
getReturnValue().setValid(getReturnValue().isValid() && vdcReturnValue.isValid());
}
}
@Override
protected boolean validate() {
if (getParameters().getParametersList().isEmpty()) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_NO_DISKS_SPECIFIED);
}
return verifyUnsupportedDisks();
}
private boolean verifyUnsupportedDisks() {
for (MoveDiskParameters moveDiskParameters : getParameters().getParametersList()) {
Disk disk = diskDao.get(moveDiskParameters.getImageGroupID());
DiskValidator diskValidator = new DiskValidator(disk);
if (!validate(diskValidator.validateUnsupportedDiskStorageType(
DiskStorageType.LUN, DiskStorageType.CINDER))) {
return false;
}
}
return true;
}
@Override
protected void setActionMessageParameters() {
addValidationMessage(EngineMessage.VAR__ACTION__MOVE);
addValidationMessage(EngineMessage.VAR__TYPE__DISK);
}
private void addDisksDeactivatedMessage(List<MoveDiskParameters> moveDiskParamsList) {
setActionMessageParameters();
addValidationMessageVariable("diskAliases", StringUtils.join(getDisksAliases(moveDiskParamsList), ", "));
addValidationMessage(EngineMessage.ACTION_TYPE_FAILED_MOVE_DISKS_MIXED_PLUGGED_STATUS);
getReturnValue().setValid(false);
}
private void addInvalidVmStateMessage(VM vm){
setActionMessageParameters();
addValidationMessageVariable("VmName", vm.getName());
addValidationMessage(EngineMessage.ACTION_TYPE_FAILED_VM_IS_NOT_DOWN_OR_UP);
getReturnValue().setValid(false);
}
/**
* For each specified MoveDiskParameters, decide whether it should be moved
* using offline move or live migrate command.
*/
protected void updateParameters() {
Map<VM, List<MoveDiskParameters>> vmDiskParamsMap = createVmDiskParamsMap();
for (Map.Entry<VM, List<MoveDiskParameters>> vmDiskParamsEntry : vmDiskParamsMap.entrySet()) {
VM vm = vmDiskParamsEntry.getKey();
List<MoveDiskParameters> moveDiskParamsList = vmDiskParamsEntry.getValue();
if (vm == null || vm.isDown() || areAllDisksPluggedToVm(moveDiskParamsList, false)) {
// Adding parameters for offline move
moveDiskParametersList.addAll(moveDiskParamsList);
}
else if (vm.isRunningAndQualifyForDisksMigration()) {
if (!areAllDisksPluggedToVm(moveDiskParamsList, true)) {
// Cannot live migrate and move disks concurrently
addDisksDeactivatedMessage(moveDiskParamsList);
continue;
}
// Adding parameters for live migrate
liveMigrateVmDisksParametersList.add(createLiveMigrateVmDisksParameters(moveDiskParamsList, vm.getId()));
}
else {
// Live migrate / move disk is not applicable according to VM status
addInvalidVmStateMessage(vm);
}
}
}
/**
* @return a map of VMs to relevant MoveDiskParameters list.
*/
private Map<VM, List<MoveDiskParameters>> createVmDiskParamsMap() {
Map<VM, List<MoveDiskParameters>> vmDisksMap = new HashMap<>();
for (Map.Entry<MoveDiskParameters, DiskImage> entry : cachedParamsToDisks.entrySet()) {
DiskImage diskImage = entry.getValue();
Map<Boolean, List<VM>> allVmsForDisk = vmDao.getForDisk(diskImage.getId(), false);
List<VM> vmsForPluggedDisk = allVmsForDisk.get(Boolean.TRUE);
List<VM> vmsForUnpluggedDisk = allVmsForDisk.get(Boolean.FALSE);
VM vm = vmsForPluggedDisk != null ? vmsForPluggedDisk.get(0) :
vmsForUnpluggedDisk != null ? vmsForUnpluggedDisk.get(0) :
null; // null is used for floating disks indication
addDiskToMap(diskImage, vmsForPluggedDisk, vmsForUnpluggedDisk);
MultiValueMapUtils.addToMap(vm, entry.getKey(), vmDisksMap);
}
return vmDisksMap;
}
/**
* Add the specified diskImage to diskMap; with updated 'plugged' value.
* (diskMap contains all disks specified in the parameters).
*/
private void addDiskToMap(DiskImage diskImage, List<VM> vmsForPluggedDisk, List<VM> vmsForUnpluggedDisk) {
if (vmsForPluggedDisk != null) {
diskImage.setPlugged(true);
}
else if (vmsForUnpluggedDisk != null) {
diskImage.setPlugged(false);
}
diskMap.put(diskImage.getImageId(), diskImage);
}
private LiveMigrateDiskParameters createLiveMigrateDiskParameters(MoveDiskParameters moveDiskParameters, Guid vmId) {
return new LiveMigrateDiskParameters(moveDiskParameters.getImageId(),
moveDiskParameters.getSourceDomainId(),
moveDiskParameters.getStorageDomainId(),
vmId,
moveDiskParameters.getQuotaId(),
moveDiskParameters.getDiskProfileId(),
diskMap.get(moveDiskParameters.getImageId()).getId());
}
private LiveMigrateVmDisksParameters createLiveMigrateVmDisksParameters(List<MoveDiskParameters> moveDiskParamsList, Guid vmId) {
// Create LiveMigrateDiskParameters list
List<LiveMigrateDiskParameters> liveMigrateDiskParametersList = moveDiskParamsList.stream()
.map(moveDiskParameters -> createLiveMigrateDiskParameters(moveDiskParameters, vmId))
.collect(Collectors.toList());
// Create LiveMigrateVmDisksParameters (multiple disks)
LiveMigrateVmDisksParameters liveMigrateDisksParameters =
new LiveMigrateVmDisksParameters(liveMigrateDiskParametersList, vmId);
return liveMigrateDisksParameters;
}
private ArrayList<VdcActionParametersBase> getParametersArrayList(List<? extends VdcActionParametersBase> parametersList) {
parametersList.stream().forEach(p -> p.setSessionId(getParameters().getSessionId()));
return new ArrayList<>(parametersList);
}
/**
* Return true if all specified disks are plugged; otherwise false.
*/
private boolean areAllDisksPluggedToVm(List<MoveDiskParameters> moveDiskParamsList, boolean plugged) {
for (MoveDiskParameters moveDiskParameters : moveDiskParamsList) {
DiskImage diskImage = diskMap.get(moveDiskParameters.getImageId());
if (diskImage.getPlugged() != plugged) {
return false;
}
}
return true;
}
private List<String> getDisksAliases(List<MoveDiskParameters> moveVmDisksParamsList) {
return moveVmDisksParamsList.stream().map(p -> diskMap.get(p.getImageId()).getDiskAlias()).collect(Collectors.toList());
}
@Override
public List<PermissionSubject> getPermissionCheckSubjects() {
List<PermissionSubject> permissionList = new ArrayList<>();
for (DiskImage disk : cachedParamsToDisks.values()) {
Guid diskId = disk == null ? Guid.Empty : disk.getId();
permissionList.add(new PermissionSubject(diskId,
VdcObjectType.Disk,
ActionGroup.CONFIGURE_DISK_STORAGE));
}
return permissionList;
}
protected List<MoveDiskParameters> getMoveDiskParametersList() {
return moveDiskParametersList;
}
protected List<LiveMigrateVmDisksParameters> getLiveMigrateVmDisksParametersList() {
return liveMigrateVmDisksParametersList;
}
}