package org.ovirt.engine.core.bll; import java.text.MessageFormat; import java.util.Collections; import java.util.List; import java.util.Map; import org.ovirt.engine.core.bll.command.utils.StorageDomainSpaceChecker; import org.ovirt.engine.core.common.AuditLogType; import org.ovirt.engine.core.common.action.AddDiskToVmParameters; import org.ovirt.engine.core.common.action.AddImageFromScratchParameters; import org.ovirt.engine.core.common.action.VdcActionType; import org.ovirt.engine.core.common.action.VdcReturnValueBase; import org.ovirt.engine.core.common.businessentities.DiskImage; import org.ovirt.engine.core.common.businessentities.DiskImageBase; import org.ovirt.engine.core.common.businessentities.DiskType; import org.ovirt.engine.core.common.businessentities.StoragePoolIsoMapId; import org.ovirt.engine.core.common.businessentities.VMStatus; import org.ovirt.engine.core.common.businessentities.VmNetworkInterface; import org.ovirt.engine.core.common.businessentities.VolumeType; import org.ovirt.engine.core.common.businessentities.storage_domain_static; import org.ovirt.engine.core.common.businessentities.storage_domains; import org.ovirt.engine.core.common.config.Config; import org.ovirt.engine.core.common.config.ConfigValues; import org.ovirt.engine.core.common.validation.group.UpdateEntity; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.compat.NGuid; import org.ovirt.engine.core.compat.StringHelper; import org.ovirt.engine.core.dal.VdcBllMessages; import org.ovirt.engine.core.dal.dbbroker.DbFacade; import org.ovirt.engine.core.dal.dbbroker.auditloghandling.CustomLogField; import org.ovirt.engine.core.dal.dbbroker.auditloghandling.CustomLogFields; import org.ovirt.engine.core.dao.StorageDomainDAO; import org.ovirt.engine.core.dao.StorageDomainStaticDAO; import org.ovirt.engine.core.dao.StoragePoolIsoMapDAO; import org.ovirt.engine.core.dao.VmNetworkInterfaceDAO; import org.ovirt.engine.core.utils.linq.Function; import org.ovirt.engine.core.utils.linq.LinqUtils; @CustomLogFields({ @CustomLogField("DiskName") }) @NonTransactiveCommandAttribute(forceCompensation = true) public class AddDiskToVmCommand<T extends AddDiskToVmParameters> extends VmCommand<T> { /** * Constructor for command creation when compensation is applied on startup * * @param commandId */ protected AddDiskToVmCommand(Guid commandId) { super(commandId); } public AddDiskToVmCommand(T parameters) { super(parameters); setVmId(parameters.getVmId()); parameters.setEntityId(parameters.getVmId()); } public String getDiskName() { return getParameters() == null || getParameters().getDiskInfo() == null || getParameters().getDiskInfo().getinternal_drive_mapping() == null ? "[N/A]" : String.format("Disk %1$s", getParameters().getDiskInfo().getinternal_drive_mapping()); } @Override protected boolean canDoAction() { boolean returnValue = true; if (getVm() == null) { returnValue = false; addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_VM_NOT_FOUND); } else if (getVm().getstatus() != VMStatus.Down) { returnValue = false; addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_VM_IS_NOT_DOWN); } else if (hasRunningTasks()) { returnValue = false; addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_VM_TASKS_ARE_ALREADY_RUNNING); } else { // update disks from db VmHandler.updateDisksFromDb(getVm()); // if user sent drive check that its not in use if (!StringHelper.isNullOrEmpty(getParameters().getDiskInfo().getinternal_drive_mapping()) && getVm().getDiskMap().containsKey(getParameters().getDiskInfo().getinternal_drive_mapping())) { returnValue = false; addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_DISK_LETTER_ALREADY_IN_USE); } else { getParameters().getDiskInfo().setinternal_drive_mapping(GetCorrectDriveForDisk()); if (getParameters().getDiskInfo().getinternal_drive_mapping() == null) { returnValue = false; addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_DISK_LIMITATION_EXCEEDED); } } if (returnValue && getParameters().getDiskInfo().getdisk_type().equals(DiskType.System)) { for (DiskImageBase disk : getVm().getDiskMap().values()) { if (disk.getdisk_type().equals(DiskType.System)) { returnValue = false; addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_DISK_SYSTEM_ALREADY_EXISTS); getReturnValue().getCanDoActionMessages().add( String.format("$DiskName %1$s", disk.getinternal_drive_mapping())); break; } } } if (returnValue && getParameters().getDiskInfo().getboot()) { for (DiskImageBase disk : getVm().getDiskMap().values()) { if (disk.getboot()) { returnValue = false; addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_DISK_BOOT_IN_USE); getReturnValue().getCanDoActionMessages().add( String.format("$DiskName %1$s", disk.getinternal_drive_mapping())); break; } } } storage_domains storageDomain = getStorageDomainDao().get( getStorageDomainId().getValue()); if (returnValue) { if (storageDomain == null) { returnValue = false; addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_STORAGE_DOMAIN_NOT_EXIST); } else { if (getStoragePoolIsoMapDao().get(new StoragePoolIsoMapId( getStorageDomainId().getValue(), getVm().getstorage_pool_id())) == null) { returnValue = false; addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_STORAGE_POOL_NOT_MATCH); } else if (!isDomainSameAsOnDisks()) { returnValue = false; addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_DISK_DOMAIN_MISMATCH); storage_domain_static disksStorageDomain = getStorageDomainStaticDao().get(getDisksStorageDomainId()); addCanDoActionMessage(MessageFormat.format("$DomainName {0}", disksStorageDomain == null ? "[N/A]" : disksStorageDomain.getstorage_name())); } else { List<VmNetworkInterface> vmInterfaces = getVmNetworkInterfaceDao().getAllForVm(getVmId()); List<DiskImageBase> allVmDisks = LinqUtils.foreach(getVm().getDiskMap().values(), new Function<DiskImage, DiskImageBase>() { @Override public DiskImageBase eval(DiskImage diskImage) { return diskImage; } }); allVmDisks.add(getParameters().getDiskInfo()); returnValue = ImagesHandler.CheckImagesConfiguration( getStorageDomainId().getValue(), new java.util.ArrayList<DiskImageBase>(java.util.Arrays .asList(new DiskImageBase[] { getParameters().getDiskInfo() })), getReturnValue().getCanDoActionMessages()) && CheckPCIAndIDELimit(getVm().getnum_of_monitors(), vmInterfaces, allVmDisks, getReturnValue().getCanDoActionMessages()); } } } returnValue = returnValue && ImagesHandler.PerformImagesChecks(getVmId(), getReturnValue().getCanDoActionMessages(), getVm() .getstorage_pool_id(), getStorageDomainId().getValue(), false, false, false, false, true, true, true); if (returnValue && !hasFreeSpace(storageDomain)) { returnValue = false; addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_DISK_SPACE_LOW); } } if (returnValue) { if (getRequestDiskSpace() > Config .<Integer> GetValue(ConfigValues.MaxDiskSize)) { addCanDoActionMessage(VdcBllMessages.ACTION_TYPE_FAILED_DISK_MAX_SIZE_EXCEEDED); getReturnValue().getCanDoActionMessages().add( String.format("$max_disk_size %1$s", Config.<Integer> GetValue(ConfigValues.MaxDiskSize))); returnValue = false; } } if (!returnValue) { addCanDoActionMessage(VdcBllMessages.VAR__ACTION__ADD); addCanDoActionMessage(VdcBllMessages.VAR__TYPE__VM_DISK); } return returnValue; } protected boolean hasRunningTasks() { return getAsycTaskManager().EntityHasTasks(getVmId()); } protected AsyncTaskManager getAsycTaskManager() { return AsyncTaskManager.getInstance(); } private VolumeType getVolumeType() { return getParameters().getDiskInfo().getvolume_type(); } private long getRequestDiskSpace() { return getParameters().getDiskInfo().getSizeInGigabytes(); } private boolean hasFreeSpace(storage_domains storageDomain) { if (getVolumeType() == VolumeType.Preallocated) { return StorageDomainSpaceChecker.hasSpaceForRequest(storageDomain, getRequestDiskSpace()); } else { return StorageDomainSpaceChecker.isBelowThresholds(storageDomain); } } /** * @return The VmNetworkInterfaceDAO */ protected VmNetworkInterfaceDAO getVmNetworkInterfaceDao() { return DbFacade.getInstance().getVmNetworkInterfaceDAO(); } /** * @return The StorageDomainStaticDAO */ protected StorageDomainStaticDAO getStorageDomainStaticDao() { return DbFacade.getInstance().getStorageDomainStaticDAO(); } /** * @return The StoragePoolIsoMapDAO */ protected StoragePoolIsoMapDAO getStoragePoolIsoMapDao() { return DbFacade.getInstance().getStoragePoolIsoMapDAO(); } /** * @return The StorageDomainDAO */ protected StorageDomainDAO getStorageDomainDao() { return DbFacade.getInstance().getStorageDomainDAO(); } /** * @return The ID of the storage domain where the VM's disks reside. */ private Guid getDisksStorageDomainId() { return getVm().getDiskMap().values().iterator().next().getstorage_id().getValue(); } /** * @return Whether the domain of the new this is the same as on the existing VM disks. */ private boolean isDomainSameAsOnDisks() { Map<String, DiskImage> vmDisks = getVm().getDiskMap(); return (vmDisks == null || vmDisks.isEmpty() || getStorageDomainId().equals(getDisksStorageDomainId())); } @Override public NGuid getStorageDomainId() { Guid storageDomainId = getParameters().getStorageDomainId(); if (Guid.Empty.equals(storageDomainId) && getVm() != null && getVm().getDiskMap() != null && !getVm().getDiskMap().isEmpty()) { return getDisksStorageDomainId(); } else { return storageDomainId == null ? Guid.Empty : storageDomainId; } } @Override protected void ExecuteVmCommand() { // NOTE: Assuming that we need to lock the vm before adding a disk! VmHandler.checkStatusAndLockVm(getVm().getvm_guid(), getCompensationContext()); // create from blank template, create new vm snapshot id AddImageFromScratchParameters parameters = new AddImageFromScratchParameters(Guid.Empty, getVmId(), getParameters().getDiskInfo()); parameters.setStorageDomainId(getStorageDomainId().getValue()); parameters.setVmSnapshotId(calculateSnapshotId()); parameters.setParentCommand(VdcActionType.AddDiskToVm); parameters.setEntityId(getParameters().getEntityId()); getParameters().getImagesParameters().add(parameters); getParameters().setVmSnapshotId(parameters.getVmSnapshotId()); parameters.setParentParemeters(getParameters()); VdcReturnValueBase tmpRetValue = Backend.getInstance().runInternalAction(VdcActionType.AddImageFromScratch, parameters); getReturnValue().getTaskIdList().addAll(tmpRetValue.getInternalTaskIdList()); getReturnValue().setActionReturnValue(tmpRetValue.getActionReturnValue()); getReturnValue().setFault(tmpRetValue.getFault()); setSucceeded(tmpRetValue.getSucceeded()); } /** * Calculate the correct snapshot id: If the VM already has a disk then take from it, otherwise create a new one. * * @return The snapshot id from a disk or new id. */ private Guid calculateSnapshotId() { final Map<String, DiskImage> disks = getVm().getDiskMap(); if (disks == null || disks.isEmpty()) { return Guid.NewGuid(); } final DiskImage vmDisk = disks.values().iterator().next(); if (vmDisk.getvm_snapshot_id() == null) { return Guid.NewGuid(); } return new Guid(vmDisk.getvm_snapshot_id().getUuid()); } @Override public AuditLogType getAuditLogTypeValue() { switch (getActionState()) { case EXECUTE: return getSucceeded() ? AuditLogType.USER_ADD_DISK_TO_VM : AuditLogType.USER_FAILED_ADD_DISK_TO_VM; case END_SUCCESS: return getSucceeded() ? AuditLogType.USER_ADD_DISK_TO_VM_FINISHED_SUCCESS : AuditLogType.USER_ADD_DISK_TO_VM_FINISHED_FAILURE; default: return AuditLogType.USER_ADD_DISK_TO_VM_FINISHED_FAILURE; } } private String GetCorrectDriveForDisk() { int driveNum = 1; List<Integer> vmDisks = LinqUtils.foreach(getVm().getDiskMap().keySet(), new Function<String, Integer>() { @Override public Integer eval(String s) { return new Integer(s); } }); Collections.sort(vmDisks); for (int disk : vmDisks) { if ((disk - driveNum) == 0) { driveNum++; } else { break; } } return Integer.toString(driveNum); } @Override protected VdcActionType getChildActionType() { return VdcActionType.AddImageFromScratch; } @Override protected List<Class<?>> getValidationGroups() { addValidationGroup(UpdateEntity.class); return super.getValidationGroups(); } }