package org.ovirt.engine.core.bll.exportimport;
import static org.ovirt.engine.core.bll.storage.disk.image.DisksFilter.ONLY_ACTIVE;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.inject.Inject;
import org.apache.commons.lang.StringUtils;
import org.ovirt.engine.core.bll.DisableInPrepareMode;
import org.ovirt.engine.core.bll.LockMessagesMatchUtil;
import org.ovirt.engine.core.bll.NonTransactiveCommandAttribute;
import org.ovirt.engine.core.bll.VmHandler;
import org.ovirt.engine.core.bll.VmTemplateHandler;
import org.ovirt.engine.core.bll.context.CommandContext;
import org.ovirt.engine.core.bll.memory.MemoryStorageHandler;
import org.ovirt.engine.core.bll.memory.MemoryUtils;
import org.ovirt.engine.core.bll.network.VmInterfaceManager;
import org.ovirt.engine.core.bll.network.macpool.MacPool;
import org.ovirt.engine.core.bll.profiles.DiskProfileHelper;
import org.ovirt.engine.core.bll.quota.QuotaConsumptionParameter;
import org.ovirt.engine.core.bll.quota.QuotaStorageConsumptionParameter;
import org.ovirt.engine.core.bll.quota.QuotaStorageDependent;
import org.ovirt.engine.core.bll.snapshots.SnapshotVmConfigurationHelper;
import org.ovirt.engine.core.bll.storage.disk.image.DisksFilter;
import org.ovirt.engine.core.bll.storage.disk.image.ImagesHandler;
import org.ovirt.engine.core.bll.utils.PermissionSubject;
import org.ovirt.engine.core.bll.utils.VmOverheadCalculator;
import org.ovirt.engine.core.bll.validator.VmNicMacsUtils;
import org.ovirt.engine.core.bll.validator.storage.DiskImagesValidator;
import org.ovirt.engine.core.bll.validator.storage.StorageDomainValidator;
import org.ovirt.engine.core.common.AuditLogType;
import org.ovirt.engine.core.common.FeatureSupported;
import org.ovirt.engine.core.common.VdcObjectType;
import org.ovirt.engine.core.common.action.ImportVmParameters;
import org.ovirt.engine.core.common.action.MoveOrCopyImageGroupParameters;
import org.ovirt.engine.core.common.action.RemoveMemoryVolumesParameters;
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.asynctasks.EntityInfo;
import org.ovirt.engine.core.common.businessentities.ActionGroup;
import org.ovirt.engine.core.common.businessentities.Snapshot;
import org.ovirt.engine.core.common.businessentities.Snapshot.SnapshotStatus;
import org.ovirt.engine.core.common.businessentities.Snapshot.SnapshotType;
import org.ovirt.engine.core.common.businessentities.StorageDomain;
import org.ovirt.engine.core.common.businessentities.StorageDomainStatic;
import org.ovirt.engine.core.common.businessentities.StorageDomainType;
import org.ovirt.engine.core.common.businessentities.VM;
import org.ovirt.engine.core.common.businessentities.VmTemplate;
import org.ovirt.engine.core.common.businessentities.VmTemplateStatus;
import org.ovirt.engine.core.common.businessentities.network.VmNetworkInterface;
import org.ovirt.engine.core.common.businessentities.network.VmNic;
import org.ovirt.engine.core.common.businessentities.storage.CopyVolumeType;
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.DiskImageBase;
import org.ovirt.engine.core.common.businessentities.storage.DiskImageDynamic;
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.ImageDbOperationScope;
import org.ovirt.engine.core.common.businessentities.storage.ImageOperation;
import org.ovirt.engine.core.common.businessentities.storage.QcowCompat;
import org.ovirt.engine.core.common.businessentities.storage.QemuImageInfo;
import org.ovirt.engine.core.common.businessentities.storage.VolumeFormat;
import org.ovirt.engine.core.common.businessentities.storage.VolumeType;
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.queries.GetAllFromExportDomainQueryParameters;
import org.ovirt.engine.core.common.queries.IdQueryParameters;
import org.ovirt.engine.core.common.queries.VdcQueryReturnValue;
import org.ovirt.engine.core.common.queries.VdcQueryType;
import org.ovirt.engine.core.common.utils.Pair;
import org.ovirt.engine.core.common.validation.group.ImportClonedEntity;
import org.ovirt.engine.core.common.validation.group.ImportEntity;
import org.ovirt.engine.core.common.vdscommands.GetImageInfoVDSCommandParameters;
import org.ovirt.engine.core.common.vdscommands.VDSCommandType;
import org.ovirt.engine.core.common.vdscommands.VDSReturnValue;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.dao.BaseDiskDao;
import org.ovirt.engine.core.dao.DiskImageDao;
import org.ovirt.engine.core.dao.DiskImageDynamicDao;
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.StorageDomainStaticDao;
import org.ovirt.engine.core.dao.VmDynamicDao;
import org.ovirt.engine.core.dao.VmStaticDao;
import org.ovirt.engine.core.dao.VmStatisticsDao;
import org.ovirt.engine.core.utils.GuidUtils;
import org.ovirt.engine.core.utils.transaction.TransactionSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@DisableInPrepareMode
@NonTransactiveCommandAttribute(forceCompensation = true)
public class ImportVmCommand<T extends ImportVmParameters> extends ImportVmCommandBase<T>
implements QuotaStorageDependent {
@Inject
private VmOverheadCalculator vmOverheadCalculator;
private static final Logger log = LoggerFactory.getLogger(ImportVmCommand.class);
@Inject
private VmNicMacsUtils vmNicMacsUtils;
@Inject
private SnapshotVmConfigurationHelper snapshotVmConfigurationHelper;
@Inject
private MemoryStorageHandler memoryStorageHandler;
@Inject
private DiskProfileHelper diskProfileHelper;
@Inject
private VmStaticDao vmStaticDao;
@Inject
private VmDynamicDao vmDynamicDao;
@Inject
private VmStatisticsDao vmStatisticsDao;
@Inject
private StorageDomainStaticDao storageDomainStaticDao;
@Inject
private ImageDao imageDao;
@Inject
private BaseDiskDao baseDiskDao;
@Inject
private DiskVmElementDao diskVmElementDao;
@Inject
private DiskImageDynamicDao diskImageDynamicDao;
@Inject
private DiskImageDao diskImageDao;
@Inject
private SnapshotDao snapshotDao;
private List<DiskImage> imageList;
private MacPool macPool;
protected Map<Guid, String> failedDisksToImportForAuditLog = new HashMap<>();
@Override
protected void init() {
super.init();
setVmId(getParameters().getContainerId());
setStoragePoolId(getParameters().getStoragePoolId());
imageToDestinationDomainMap = getParameters().getImageToDestinationDomainMap();
if (getParameters().getVm() != null && getVm().getDiskMap() != null) {
imageList = new ArrayList<>();
for (Disk disk : getVm().getDiskMap().values()) {
if (disk.getDiskStorageType() == DiskStorageType.IMAGE) {
imageList.add((DiskImage) disk);
}
}
}
ensureDomainMap(imageList, getParameters().getDestDomainId());
}
@Override
protected Map<String, Pair<String, String>> getSharedLocks() {
return Collections.singletonMap(getParameters().getContainerId().toString(),
LockMessagesMatchUtil.makeLockingPair(
LockingGroup.REMOTE_VM,
getVmIsBeingImportedMessage()));
}
public ImportVmCommand(Guid commandId) {
super(commandId);
}
public ImportVmCommand(T parameters, CommandContext commandContext) {
super(parameters, commandContext);
}
@Override
protected boolean validate() {
if (!super.validate()) {
return false;
}
macPool = getMacPool();
Map<Guid, StorageDomain> domainsMap = new HashMap<>();
if (!validateBeforeCloneVm(domainsMap)) {
return false;
}
// Since methods #validateBeforeCloneVm > #validateAndSetVmFromExportDomain > #setVmFromExportDomain may
// change this.vm instance, following code can't be in #init() method and has to follow call of
// #validateBeforeCloneVm.
VmHandler.updateMaxMemorySize(getVm().getStaticData(), getEffectiveCompatibilityVersion());
if (getParameters().isImportAsNewEntity()) {
initImportClonedVm();
if (getVm().getInterfaces().size() > macPool.getAvailableMacsCount()) {
return failValidation(EngineMessage.MAC_POOL_NOT_ENOUGH_MAC_ADDRESSES);
}
}
if (!validateBallonDevice()) {
return false;
}
if (!validateSoundDevice()) {
return false;
}
if (!validate(VmHandler.validateMaxMemorySize(getVm().getStaticData(), getEffectiveCompatibilityVersion()))) {
return false;
}
return validateAfterCloneVm(domainsMap);
}
private void initImportClonedVm() {
Guid guid = getParameters().getVm().getId();
getVm().setId(guid);
setVmId(guid);
getVm().setName(getParameters().getVm().getName());
getVm().setStoragePoolId(getParameters().getStoragePoolId());
getParameters().setVm(getVm());
for (VmNic iface : getVm().getInterfaces()) {
iface.setId(Guid.newGuid());
}
}
protected boolean validateBeforeCloneVm(Map<Guid, StorageDomain> domainsMap) {
if (getVm() != null) {
setDescription(getVmName());
}
if (getStoragePool() == null) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_STORAGE_POOL_NOT_EXIST);
}
Set<Guid> destGuids = new HashSet<>(imageToDestinationDomainMap.values());
for (Guid destGuid : destGuids) {
StorageDomain storageDomain = getStorageDomain(destGuid);
StorageDomainValidator validator = new StorageDomainValidator(storageDomain);
if (!validate(validator.isDomainExistAndActive()) || !validate(validator.domainIsValidDestination())) {
return false;
}
domainsMap.put(destGuid, storageDomain);
}
if (!isImagesAlreadyOnTarget() && getParameters().isImportAsNewEntity()
&& isCopyCollapseDisabledWithSnapshotsOrWithTemplate()) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_IMPORT_CLONE_NOT_COLLAPSED,
String.format("$VmName %1$s", getVmName()));
}
// Register can never happen with copyCollapse = true since there's no copy operation involved.
if (isImagesAlreadyOnTarget() && getParameters().getCopyCollapse()) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_IMPORT_UNREGISTERED_NOT_COLLAPSED);
}
if (!isImagesAlreadyOnTarget()) {
setSourceDomainId(getParameters().getSourceDomainId());
StorageDomainValidator validator = new StorageDomainValidator(getSourceDomain());
if (validator.isDomainExistAndActive().isValid()
&& getSourceDomain().getStorageDomainType() != StorageDomainType.ImportExport) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_STORAGE_DOMAIN_TYPE_ILLEGAL);
}
if (!validateAndSetVmFromExportDomain()) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_VM_NOT_FOUND_ON_EXPORT_DOMAIN);
}
}
if (!validateImages(domainsMap)) {
return false;
}
List<VmNetworkInterface> vmNetworkInterfaces = getVm().getInterfaces();
vmNicMacsUtils.replaceInvalidEmptyStringMacAddressesWithNull(vmNetworkInterfaces);
return true;
}
@Override
protected void executeVmCommand() {
if (getVm().isAutoStartup() && shouldAddLease(getVm().getStaticData())) {
if (FeatureSupported.isVmLeasesSupported(getEffectiveCompatibilityVersion())) {
if (validateLeaseStorageDomain(getVm().getLeaseStorageDomainId())) {
if (!addVmLease(getVm().getLeaseStorageDomainId(), getVm().getId())) {
getVm().setLeaseStorageDomainId(null);
}
} else {
getVm().setLeaseStorageDomainId(null);
auditLogDirector.log(this, AuditLogType.CANNOT_IMPORT_VM_WITH_LEASE_STORAGE_DOMAIN);
}
}
else {
getVm().setLeaseStorageDomainId(null);
auditLogDirector.log(this, AuditLogType.CANNOT_IMPORT_VM_WITH_LEASE_COMPAT_VERSION);
}
}
else {
getVm().setLeaseStorageDomainId(null);
}
super.executeVmCommand();
}
private boolean isCopyCollapseDisabledWithSnapshotsOrWithTemplate() {
// If there are no snapshots we may not care if copyCollapse = false
// There's always at least one snapshot (Active).
// In case the VM is based on a template, we need to take copyCollapse in account
return ((getParameters().getVm().getSnapshots().size() > 1) ||
(!VmTemplateHandler.BLANK_VM_TEMPLATE_ID.equals(getVm().getVmtGuid())
&& getVmTemplate() != null))
&& !getParameters().getCopyCollapse();
}
private boolean isCopyCollapseOrNoSnapshots() {
return !isCopyCollapseDisabledWithSnapshotsOrWithTemplate();
}
protected boolean validateAndSetVmFromExportDomain() {
VM vm = getVmFromExportDomain(getParameters().getVmId());
if (vm == null) {
return false;
}
// At this point we should work with the VM that was read from
// the OVF because the VM from the parameters may lack images
setVmFromExportDomain(vm);
return true;
}
protected boolean validateImages(Map<Guid, StorageDomain> domainsMap) {
List<String> validationMessages = getReturnValue().getValidationMessages();
// Iterate over all the VM images (active image and snapshots)
for (DiskImage image : getImages()) {
if (Guid.Empty.equals(image.getVmSnapshotId())) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_CORRUPTED_VM_SNAPSHOT_ID);
}
if (getParameters().getCopyCollapse()) {
// If copy collapse sent then iterate over the images got from the parameters, until we got
// a match with the image from the VM.
for (DiskImage p : imageList) {
// copy the new disk volume format/type if provided,
// only if requested by the user
if (p.getImageId().equals(image.getImageId())) {
if (p.getVolumeFormat() != null) {
image.setVolumeFormat(p.getVolumeFormat());
}
if (p.getVolumeType() != null) {
image.setVolumeType(p.getVolumeType());
}
// Validate the configuration of the image got from the parameters.
if (!validateImageConfig(validationMessages, domainsMap, image)) {
return false;
}
break;
}
}
}
image.setStoragePoolId(getParameters().getStoragePoolId());
// we put the source domain id in order that copy will
// work properly.
// we fix it to DestDomainId in
// MoveOrCopyAllImageGroups();
image.setStorageIds(new ArrayList<>(Arrays.asList(getSourceDomainId(image))));
}
Map<Guid, List<DiskImage>> images = ImagesHandler.getImagesLeaf(getImages());
for (Map.Entry<Guid, List<DiskImage>> entry : images.entrySet()) {
Guid id = entry.getKey();
List<DiskImage> diskList = entry.getValue();
getVm().getDiskMap().put(id, getActiveVolumeDisk(diskList));
}
return true;
}
private void setVmFromExportDomain(VM vm) {
// preserve the given name
if (getVmName() != null) {
vm.setName(getVmName());
}
setVm(vm);
initGraphicsData();
}
private void initGraphicsData() {
ImportUtils.updateGraphicsDevices(getVm().getStaticData(), getEffectiveCompatibilityVersion());
}
protected DiskImage getActiveVolumeDisk(List<DiskImage> diskList) {
return diskList.get(diskList.size() - 1);
}
protected VM getVmFromExportDomain(Guid vmId) {
for (VM vm : getVmsFromExportDomain()) {
if (vmId.equals(vm.getId())) {
return vm;
}
}
return null;
}
/**
* Load images from Import/Export domain.
*
* @return A {@link List} of {@link VM}s from the export domain.
*/
@SuppressWarnings("unchecked")
protected List<VM> getVmsFromExportDomain() {
VdcQueryReturnValue qRetVal = runInternalQuery(
VdcQueryType.GetVmsFromExportDomain,
new GetAllFromExportDomainQueryParameters(
getParameters().getStoragePoolId(),
getParameters().getSourceDomainId()));
return (List<VM>) (qRetVal.getSucceeded() ? qRetVal.getReturnValue() : Collections.emptyList());
}
private boolean validateImageConfig(List<String> validationMessages,
Map<Guid, StorageDomain> domainsMap,
DiskImage image) {
return ImagesHandler.checkImageConfiguration(domainsMap.get(imageToDestinationDomainMap.get(image.getId()))
.getStorageStaticData(),
image,
validationMessages);
}
protected boolean validateAfterCloneVm(Map<Guid, StorageDomain> domainsMap) {
VM vmFromParams = getParameters().getVm();
// check that the imported vm guid is not in engine
if (!validateNoDuplicateVm()) {
return false;
}
if (!validateNoDuplicateDiskImages(imageList)) {
return false;
}
setVmTemplateId(getVm().getVmtGuid());
if (!templateExists() || !checkTemplateInStorageDomain() || !checkImagesGUIDsLegal() || !validateUniqueVmName()) {
return false;
}
if (!VmTemplateHandler.BLANK_VM_TEMPLATE_ID.equals(getVm().getVmtGuid())
&& getVmTemplate() != null
&& getVmTemplate().getStatus() == VmTemplateStatus.Locked) {
return failValidation(EngineMessage.VM_TEMPLATE_IMAGE_IS_LOCKED);
}
if (getParameters().getCopyCollapse() && vmFromParams.getDiskMap() != null) {
for (Disk disk : vmFromParams.getDiskMap().values()) {
if (disk.getDiskStorageType() == DiskStorageType.IMAGE) {
DiskImage key = (DiskImage) getVm().getDiskMap().get(disk.getId());
if (key != null) {
if (!ImagesHandler.checkImageConfiguration(domainsMap.get(imageToDestinationDomainMap.get(key.getId()))
.getStorageStaticData(),
(DiskImageBase) disk,
getReturnValue().getValidationMessages())) {
return false;
}
}
}
}
}
// if collapse true we check that we have the template on source
// (backup) domain
if (getParameters().getCopyCollapse() && !isTemplateExistsOnExportDomain()) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_IMPORTED_TEMPLATE_IS_MISSING,
String.format("$DomainName %1$s",
storageDomainStaticDao.get(getParameters().getSourceDomainId()).getStorageName()));
}
if (!validateVmArchitecture()) {
return false;
}
if (!validateVdsCluster()) {
return false;
}
if (!isImagesAlreadyOnTarget()) {
if (!handleDestStorageDomains()) {
return false;
}
}
if (!validateGraphicsAndDisplay()) {
return false;
}
if (!getParameters().isImportAsNewEntity()) {
List<VmNetworkInterface> vmNetworkInterfaces = getVm().getInterfaces();
if (!validate(vmNicMacsUtils.validateThereIsEnoughOfFreeMacs(vmNetworkInterfaces,
getMacPool(),
getVnicRequiresNewMacPredicate()))) {
return false;
}
if (!validate(vmNicMacsUtils.validateMacAddress(vmNetworkInterfaces))) {
return false;
}
}
if (!setAndValidateDiskProfiles()) {
return false;
}
if (!setAndValidateCpuProfile()) {
return false;
}
return true;
}
private Predicate<VmNetworkInterface> getVnicRequiresNewMacPredicate() {
return ((Predicate<VmNetworkInterface>) this::nicWithoutMacAddress).or(this::shouldMacBeReassigned);
}
private boolean nicWithoutMacAddress(VmNic vmNic) {
return vmNic.getMacAddress() == null;
}
protected boolean handleDestStorageDomains() {
List<DiskImage> dummiesDisksList = createDiskDummiesForSpaceValidations(imageList);
if (getParameters().getCopyCollapse()) {
Snapshot activeSnapshot = getActiveSnapshot();
if (activeSnapshot != null && activeSnapshot.containsMemory()) {
// Checking space for memory volume of the active image (if there is one)
StorageDomain storageDomain = updateStorageDomainInMemoryVolumes(dummiesDisksList);
if (storageDomain == null) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_NO_SUITABLE_DOMAIN_FOUND);
}
}
} else { // Check space for all the snapshot's memory volumes
if (!updateDomainsForMemoryImages(dummiesDisksList)) {
return false;
}
}
return validate(getImportValidator().validateSpaceRequirements(dummiesDisksList));
}
/**
* For each snapshot that has memory volume, this method updates the memory volume with the storage pool and storage
* domain it's going to be imported to.
*
* @return true if we managed to assign storage domain for every memory volume, false otherwise
*/
private boolean updateDomainsForMemoryImages(List<DiskImage> disksList) {
Map<String, String> handledMemoryVolumes = new HashMap<>();
for (Snapshot snapshot : getVm().getSnapshots()) {
String memoryVolume = snapshot.getMemoryVolume();
if (memoryVolume.isEmpty()) {
continue;
}
if (handledMemoryVolumes.containsKey(memoryVolume)) {
// replace the volume representation with the one with the correct domain & pool
snapshot.setMemoryVolume(handledMemoryVolumes.get(memoryVolume));
continue;
}
StorageDomain storageDomain = updateStorageDomainInMemoryVolumes(disksList);
if (storageDomain == null) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_NO_SUITABLE_DOMAIN_FOUND);
}
String modifiedMemoryVolume = MemoryUtils.changeStorageDomainAndPoolInMemoryState(
memoryVolume, storageDomain.getId(), getParameters().getStoragePoolId());
// replace the volume representation with the one with the correct domain & pool
snapshot.setMemoryVolume(modifiedMemoryVolume);
// save it in case we'll find other snapshots with the same memory volume
handledMemoryVolumes.put(memoryVolume, modifiedMemoryVolume);
}
return true;
}
private StorageDomain updateStorageDomainInMemoryVolumes(List<DiskImage> disksList) {
List<DiskImage> memoryDisksList =
MemoryUtils.createDiskDummies(vmOverheadCalculator.getSnapshotMemorySizeInBytes(getVm()),
MemoryUtils.METADATA_SIZE_IN_BYTES);
StorageDomain storageDomain = memoryStorageHandler.findStorageDomainForMemory(
getParameters().getStoragePoolId(), memoryDisksList, getVmDisksDummies(), getVm());
disksList.addAll(memoryDisksList);
return storageDomain;
}
private Collection<DiskImage> getVmDisksDummies() {
Collection<DiskImage> disksDummies = new LinkedList<>();
for (Guid storageDomainId : getParameters().getImageToDestinationDomainMap().values()) {
DiskImage diskImage = new DiskImage();
diskImage.setStorageIds(new ArrayList<>(Arrays.asList(storageDomainId)));
disksDummies.add(diskImage);
}
return disksDummies;
}
protected boolean validateNoDuplicateDiskImages(Iterable<DiskImage> images) {
if (!getParameters().isImportAsNewEntity()) {
DiskImagesValidator diskImagesValidator = new DiskImagesValidator(images);
return validate(diskImagesValidator.diskImagesAlreadyExist());
}
return true;
}
private boolean isTemplateExistsOnExportDomain() {
if (VmTemplateHandler.BLANK_VM_TEMPLATE_ID.equals(getParameters().getVm().getVmtGuid())) {
return true;
}
VdcQueryReturnValue qRetVal = runInternalQuery(
VdcQueryType.GetTemplatesFromExportDomain,
new GetAllFromExportDomainQueryParameters(getParameters().getStoragePoolId(),
getParameters().getSourceDomainId()));
if (qRetVal.getSucceeded()) {
Map<VmTemplate, ?> templates = qRetVal.getReturnValue();
for (VmTemplate template : templates.keySet()) {
if (getParameters().getVm().getVmtGuid().equals(template.getId())) {
return true;
}
}
}
return false;
}
protected boolean checkTemplateInStorageDomain() {
boolean retValue = validate(getImportValidator().verifyDisks(imageList, imageToDestinationDomainMap));
if (retValue && !VmTemplateHandler.BLANK_VM_TEMPLATE_ID.equals(getVm().getVmtGuid())
&& !getParameters().getCopyCollapse()) {
List<StorageDomain> domains = runInternalQuery(VdcQueryType.GetStorageDomainsByVmTemplateId,
new IdQueryParameters(getVm().getVmtGuid())).getReturnValue();
Set<Guid> domainsId = domains.stream().map(StorageDomain::getId).collect(Collectors.toSet());
if (!domainsId.isEmpty() && Collections.disjoint(domainsId, imageToDestinationDomainMap.values())) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_TEMPLATE_NOT_FOUND_ON_DESTINATION_DOMAIN);
}
}
return retValue;
}
private boolean templateExists() {
if (getVmTemplate() == null && !getParameters().getCopyCollapse()) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_TEMPLATE_DOES_NOT_EXIST);
}
return true;
}
protected Guid getSourceDomainId(DiskImage image) {
return getParameters().getSourceDomainId();
}
protected boolean checkImagesGUIDsLegal() {
for (DiskImage image : new ArrayList<>(getImages())) {
Guid imageGUID = image.getImageId();
Guid storagePoolId = image.getStoragePoolId() != null ? image.getStoragePoolId()
: Guid.Empty;
Guid storageDomainId = getSourceDomainId(image);
Guid imageGroupId = image.getId() != null ? image.getId() : Guid.Empty;
VDSReturnValue retValue = runVdsCommand(
VDSCommandType.GetImageInfo,
new GetImageInfoVDSCommandParameters(storagePoolId, storageDomainId, imageGroupId,
imageGUID));
if (!retValue.getSucceeded()) {
if (!getParameters().isAllowPartialImport()) {
return failValidation(EngineMessage.ACTION_TYPE_FAILED_VM_IMAGE_DOES_NOT_EXIST);
}
log.warn("Disk image '{}/{}' doesn't exist on storage domain '{}'. Ignoring since force flag in on",
imageGroupId,
imageGUID,
storageDomainId);
getVm().getImages().remove(image);
failedDisksToImportForAuditLog.putIfAbsent(image.getId(), image.getDiskAlias());
}
}
return true;
}
@Override
protected void processImages() {
processImages(!isImagesAlreadyOnTarget());
// if there are no tasks, we can just unlock the VM
if (getReturnValue().getVdsmTaskIdList().isEmpty()) {
vmHandler.unLockVm(getVm());
}
}
private void processImages(final boolean useCopyImages) {
TransactionSupport.executeInNewTransaction(() -> {
addVmImagesAndSnapshots();
addMemoryImages();
updateSnapshotsFromExport();
if (useCopyImages) {
moveOrCopyAllImageGroups();
}
getVmDeviceUtils().addImportedDevices(getVm().getStaticData(), getParameters().isImportAsNewEntity());
if (getParameters().isImportAsNewEntity()) {
getParameters().setVm(getVm());
setVmId(getVm().getId());
}
return null;
});
}
protected void moveOrCopyAllImageGroups() {
moveOrCopyAllImageGroups(getVm().getId(),
DisksFilter.filterImageDisks(getVm().getDiskMap().values(), ONLY_ACTIVE));
copyAllMemoryImages(getVm().getId());
}
private void copyAllMemoryImages(Guid containerId) {
for (String memoryVolumes : MemoryUtils.getMemoryVolumesFromSnapshots(getVm().getSnapshots())) {
List<Guid> guids = GuidUtils.getGuidListFromString(memoryVolumes);
// copy the memory dump image
VdcReturnValueBase vdcRetValue = runInternalActionWithTasksContext(
VdcActionType.CopyImageGroup,
buildMoveOrCopyImageGroupParametersForMemoryDumpImage(
containerId, guids.get(0), guids.get(2), guids.get(3)));
if (!vdcRetValue.getSucceeded()) {
throw new EngineException(vdcRetValue.getFault().getError(), "Failed to copy memory image");
}
getReturnValue().getVdsmTaskIdList().addAll(vdcRetValue.getInternalVdsmTaskIdList());
// copy the memory configuration (of the VM) image
vdcRetValue = runInternalActionWithTasksContext(
VdcActionType.CopyImageGroup,
buildMoveOrCopyImageGroupParametersForMemoryConfImage(
containerId, guids.get(0), guids.get(4), guids.get(5)));
if (!vdcRetValue.getSucceeded()) {
throw new EngineException(vdcRetValue.getFault().getError(), "Failed to copy metadata image");
}
getReturnValue().getVdsmTaskIdList().addAll(vdcRetValue.getInternalVdsmTaskIdList());
}
}
private MoveOrCopyImageGroupParameters buildMoveOrCopyImageGroupParametersForMemoryDumpImage(Guid containerID,
Guid storageId, Guid imageId, Guid volumeId) {
MoveOrCopyImageGroupParameters params = new MoveOrCopyImageGroupParameters(containerID,
imageId, volumeId, imageId, volumeId, storageId, ImageOperation.Copy);
params.setParentCommand(getActionType());
params.setCopyVolumeType(CopyVolumeType.LeafVol);
params.setForceOverride(getParameters().getForceOverride());
params.setSourceDomainId(getParameters().getSourceDomainId());
params.setStoragePoolId(getParameters().getStoragePoolId());
params.setImportEntity(true);
params.setEntityInfo(new EntityInfo(VdcObjectType.VM, getVm().getId()));
params.setParentParameters(getParameters());
StorageDomainStatic storageDomain = storageDomainStaticDao.get(storageId);
if (storageDomain.getStorageType().isBlockDomain()) {
params.setUseCopyCollapse(true);
params.setVolumeType(VolumeType.Preallocated);
params.setVolumeFormat(VolumeFormat.RAW);
}
return params;
}
private MoveOrCopyImageGroupParameters buildMoveOrCopyImageGroupParametersForMemoryConfImage(Guid containerID,
Guid storageId, Guid imageId, Guid volumeId) {
MoveOrCopyImageGroupParameters params = new MoveOrCopyImageGroupParameters(containerID,
imageId, volumeId, imageId, volumeId, storageId, ImageOperation.Copy);
params.setParentCommand(getActionType());
// This volume is always of type 'sparse' and format 'cow' so no need to convert,
// and there're no snapshots for it so no reason to use copy collapse
params.setUseCopyCollapse(false);
params.setEntityInfo(new EntityInfo(VdcObjectType.VM, getVm().getId()));
params.setCopyVolumeType(CopyVolumeType.LeafVol);
params.setForceOverride(getParameters().getForceOverride());
params.setParentParameters(getParameters());
params.setSourceDomainId(getParameters().getSourceDomainId());
params.setStoragePoolId(getParameters().getStoragePoolId());
params.setImportEntity(true);
return params;
}
protected void moveOrCopyAllImageGroups(Guid containerID, Iterable<DiskImage> disks) {
for (DiskImage disk : disks) {
VdcReturnValueBase vdcRetValue = runInternalActionWithTasksContext(
VdcActionType.CopyImageGroup,
buildMoveOrCopyImageGroupParametersForDisk(disk, containerID));
if (!vdcRetValue.getSucceeded()) {
throw new EngineException(vdcRetValue.getFault().getError(), "Failed to copy disk!");
}
getReturnValue().getVdsmTaskIdList().addAll(vdcRetValue.getInternalVdsmTaskIdList());
}
}
private MoveOrCopyImageGroupParameters buildMoveOrCopyImageGroupParametersForDisk(DiskImage disk, Guid containerID) {
Guid originalDiskId = newDiskIdForDisk.get(disk.getId()).getId();
Guid destinationDomain = imageToDestinationDomainMap.get(originalDiskId);
MoveOrCopyImageGroupParameters params = new MoveOrCopyImageGroupParameters(containerID,
originalDiskId,
newDiskIdForDisk.get(disk.getId()).getImageId(),
disk.getId(),
disk.getImageId(),
destinationDomain, ImageOperation.Copy);
params.setParentCommand(getActionType());
params.setUseCopyCollapse(isCopyCollapseOrNoSnapshots());
params.setCopyVolumeType(CopyVolumeType.LeafVol);
params.setForceOverride(getParameters().getForceOverride());
params.setSourceDomainId(getParameters().getSourceDomainId());
params.setStoragePoolId(getParameters().getStoragePoolId());
params.setImportEntity(true);
params.setEntityInfo(new EntityInfo(VdcObjectType.VM, getVm().getId()));
params.setRevertDbOperationScope(ImageDbOperationScope.IMAGE);
params.setQuotaId(disk.getQuotaId() != null ? disk.getQuotaId() : getParameters().getQuotaId());
params.setDiskProfileId(disk.getDiskProfileId());
if (getParameters().getVm().getDiskMap() != null
&& getParameters().getVm().getDiskMap().containsKey(originalDiskId)) {
DiskImageBase diskImageBase =
(DiskImageBase) getParameters().getVm().getDiskMap().get(originalDiskId);
params.setVolumeType(diskImageBase.getVolumeType());
params.setVolumeFormat(diskImageBase.getVolumeFormat());
}
params.setParentParameters(getParameters());
return params;
}
protected void setQcowCompat(DiskImage diskImage) {
diskImage.setQcowCompat(QcowCompat.QCOW2_V2);
if (FeatureSupported.qcowCompatSupported(getStoragePool().getCompatibilityVersion())) {
QemuImageInfo qemuImageInfo =
ImagesHandler.getQemuImageInfoFromVdsm(diskImage.getStoragePoolId(),
diskImage.getStorageIds().get(0),
diskImage.getId(),
diskImage.getImageId(),
null,
true);
if (qemuImageInfo != null) {
diskImage.setQcowCompat(qemuImageInfo.getQcowCompat());
}
}
imageDao.update(diskImage.getImage());
}
protected void addVmImagesAndSnapshots() {
Map<Guid, List<DiskImage>> images = ImagesHandler.getImagesLeaf(getImages());
if (isCopyCollapseOrNoSnapshots()) {
Guid snapshotId = Guid.newGuid();
int aliasCounter = 0;
for (List<DiskImage> diskList : images.values()) {
DiskImage disk = getActiveVolumeDisk(diskList);
disk.setParentId(VmTemplateHandler.BLANK_VM_TEMPLATE_ID);
disk.setImageTemplateId(VmTemplateHandler.BLANK_VM_TEMPLATE_ID);
disk.setVmSnapshotId(snapshotId);
disk.setActive(true);
if (getParameters().getVm().getDiskMap() != null
&& getParameters().getVm().getDiskMap().containsKey(disk.getId())) {
DiskImageBase diskImageBase =
(DiskImageBase) getParameters().getVm().getDiskMap().get(disk.getId());
disk.setVolumeFormat(diskImageBase.getVolumeFormat());
disk.setVolumeType(diskImageBase.getVolumeType());
}
setDiskStorageDomainInfo(disk);
if (getParameters().isImportAsNewEntity()) {
generateNewDiskId(diskList, disk);
updateManagedDeviceMap(disk, getVm().getStaticData().getManagedDeviceMap());
} else {
newDiskIdForDisk.put(disk.getId(), disk);
}
disk.setCreationDate(new Date());
saveImage(disk);
ImagesHandler.setDiskAlias(disk, getVm(), ++aliasCounter);
saveBaseDisk(disk);
saveDiskVmElement(disk.getId(), getVmId(), disk.getDiskVmElementForVm(getParameters().getVmId()));
saveDiskImageDynamic(disk);
}
Snapshot snapshot = addActiveSnapshot(snapshotId);
getVm().setSnapshots(Arrays.asList(snapshot));
} else {
Guid snapshotId = null;
for (DiskImage disk : getImages()) {
disk.setActive(false);
setDiskStorageDomainInfo(disk);
saveImage(disk);
snapshotId = disk.getVmSnapshotId();
saveSnapshotIfNotExists(snapshotId, disk);
saveDiskImageDynamic(disk);
}
int aliasCounter = 0;
for (List<DiskImage> diskList : images.values()) {
DiskImage disk = getActiveVolumeDisk(diskList);
newDiskIdForDisk.put(disk.getId(), disk);
snapshotId = disk.getVmSnapshotId();
disk.setActive(true);
ImagesHandler.setDiskAlias(disk, getVm(), ++aliasCounter);
updateImage(disk);
saveBaseDisk(disk);
saveDiskVmElement(disk.getId(), getVmId(), disk.getDiskVmElementForVm(getParameters().getVmId()));
}
// Update active snapshot's data, since it was inserted as a regular snapshot.
updateActiveSnapshot(snapshotId);
}
}
private void setDiskStorageDomainInfo(DiskImage disk) {
ArrayList<Guid> storageDomain = new ArrayList<>();
storageDomain.add(imageToDestinationDomainMap.get(disk.getId()));
disk.setStorageIds(storageDomain);
}
/** Saves the base disk object */
protected void saveBaseDisk(DiskImage disk) {
baseDiskDao.save(disk);
}
protected void saveDiskVmElement(Guid diskId, Guid vmId, DiskVmElement diskVmElement) {
DiskVmElement dve = DiskVmElement.copyOf(diskVmElement, diskId, vmId);
updatePassDiscardForDiskVmElement(dve);
diskVmElementDao.save(dve);
}
/** Save the entire image, including it's storage mapping */
protected void saveImage(DiskImage disk) {
ImagesHandler.saveImage(disk);
}
/** Updates an image of a disk */
protected void updateImage(DiskImage disk) {
imageDao.update(disk.getImage());
}
/**
* Generates and saves a {@link DiskImageDynamic} for the given <code>disk</code>
*
* @param disk
* The imported disk
**/
protected void saveDiskImageDynamic(DiskImage disk) {
DiskImageDynamic diskDynamic = new DiskImageDynamic();
diskDynamic.setId(disk.getImageId());
diskDynamic.setActualSize(disk.getActualSizeInBytes());
diskImageDynamicDao.save(diskDynamic);
}
/**
* Saves a new active snapshot for the VM
*
* @param snapshotId
* The ID to assign to the snapshot
* @return The generated snapshot
*/
protected Snapshot addActiveSnapshot(Guid snapshotId) {
Snapshot activeSnapshot = getActiveSnapshot();
// We currently don't support using memory from a
// snapshot that was taken for VM with different id
String memoryVolume = activeSnapshot != null && !getParameters().isImportAsNewEntity() ?
activeSnapshot.getMemoryVolume() : StringUtils.EMPTY;
return getSnapshotsManager().addActiveSnapshot(
snapshotId,
getVm(),
memoryVolume,
getCompensationContext());
}
@Override
protected Snapshot getActiveSnapshot() {
Snapshot activeSnapshot = VmHandler.getActiveSnapshot(getVm());
if (activeSnapshot == null) {
log.warn("VM '{}' doesn't have active snapshot in export domain", getVmId());
}
return activeSnapshot;
}
/**
* Go over the snapshots that were read from the export data. If the snapshot exists (since it was added for the
* images), it will be updated. If it doesn't exist, it will be saved.
*/
private void updateSnapshotsFromExport() {
if (getVm().getSnapshots() == null) {
return;
}
for (Snapshot snapshot : getVm().getSnapshots()) {
if (!StringUtils.isEmpty(snapshot.getMemoryVolume())) {
updateMemoryDisks(snapshot);
}
if (snapshotDao.exists(getVm().getId(), snapshot.getId())) {
snapshotDao.update(snapshot);
} else {
snapshotDao.save(snapshot);
}
}
}
private void updateMemoryDisks(Snapshot snapshot) {
List<Guid> guids = GuidUtils.getGuidListFromString(snapshot.getMemoryVolume());
snapshot.setMemoryDiskId(guids.get(2));
snapshot.setMetadataDiskId(guids.get(4));
}
private void addMemoryImages() {
getVm().getSnapshots().stream()
.filter(snapshot -> !StringUtils.isEmpty(snapshot.getMemoryVolume()))
.forEach(snapshot -> {
addDisk(createMemoryDisk(snapshot));
addDisk(createMetadaaDisk(snapshot));
});
}
private DiskImage createMemoryDisk(Snapshot snapshot) {
List<Guid> guids = GuidUtils.getGuidListFromString(snapshot.getMemoryVolume());
VM vm = snapshotVmConfigurationHelper.getVmFromConfiguration(
snapshot.getVmConfiguration(),
snapshot.getVmId(), snapshot.getId());
DiskImage memoryDisk = MemoryUtils.createMemoryDisk(
vm,
storageDomainStaticDao.get(guids.get(0)).getStorageType(),
vmOverheadCalculator);
memoryDisk.setId(guids.get(2));
memoryDisk.setImageId(guids.get(3));
memoryDisk.setStorageIds(new ArrayList<>(Collections.singletonList(guids.get(0))));
memoryDisk.setStoragePoolId(guids.get(1));
memoryDisk.setCreationDate(snapshot.getCreationDate());
return memoryDisk;
}
private DiskImage createMetadaaDisk(Snapshot snapshot) {
List<Guid> guids = GuidUtils.getGuidListFromString(snapshot.getMemoryVolume());
DiskImage memoryDisk = MemoryUtils.createMetadataDisk();
memoryDisk.setId(guids.get(4));
memoryDisk.setImageId(guids.get(5));
memoryDisk.setStorageIds(new ArrayList<>(Collections.singletonList(guids.get(0))));
memoryDisk.setStoragePoolId(guids.get(1));
memoryDisk.setCreationDate(snapshot.getCreationDate());
return memoryDisk;
}
private void addDisk(DiskImage disk) {
saveImage(disk);
saveBaseDisk(disk);
saveDiskImageDynamic(disk);
}
/**
* Save a snapshot if it does not exist in the database.
*
* @param snapshotId
* The snapshot to save.
* @param disk
* The disk containing the snapshot's information.
*/
protected void saveSnapshotIfNotExists(Guid snapshotId, DiskImage disk) {
if (!snapshotDao.exists(getVm().getId(), snapshotId)) {
snapshotDao.save(
new Snapshot(snapshotId,
SnapshotStatus.OK,
getVm().getId(),
null,
SnapshotType.REGULAR,
disk.getDescription(),
disk.getLastModifiedDate(),
disk.getAppList()));
}
}
/**
* Update a snapshot and make it the active snapshot.
*
* @param snapshotId
* The snapshot to update.
*/
protected void updateActiveSnapshot(Guid snapshotId) {
snapshotDao.update(
new Snapshot(snapshotId,
SnapshotStatus.OK,
getVm().getId(),
null,
SnapshotType.ACTIVE,
"Active VM snapshot",
new Date(),
null));
}
@Override
protected void endSuccessfully() {
checkTrustedService();
endActionOnAllImageGroups();
vmHandler.unLockVm(getVm());
setSucceeded(true);
}
private void checkTrustedService() {
if (getVm().isTrustedService() && !getCluster().supportsTrustedService()) {
auditLogDirector.log(this, AuditLogType.IMPORTEXPORT_IMPORT_VM_FROM_TRUSTED_TO_UNTRUSTED);
}
else if (!getVm().isTrustedService() && getCluster().supportsTrustedService()) {
auditLogDirector.log(this, AuditLogType.IMPORTEXPORT_IMPORT_VM_FROM_UNTRUSTED_TO_TRUSTED);
}
}
protected void endActionOnAllImageGroups() {
for (VdcActionParametersBase p : getParameters().getImagesParameters()) {
p.setTaskGroupSuccess(getParameters().getTaskGroupSuccess());
getBackend().endAction(VdcActionType.CopyImageGroup,
p,
getContext().clone().withoutCompensationContext().withoutExecutionContext().withoutLock());
}
}
protected void initQcowVersionForDisks(Guid imageGroupId) {
List<DiskImage> diskVolumes = diskImageDao.getAllSnapshotsForImageGroup(imageGroupId);
diskVolumes.stream().filter(volume -> volume.getVolumeFormat() == VolumeFormat.COW).forEach(volume -> {
try {
setQcowCompat(volume);
} catch (Exception e) {
log.error("Could not set qcow compat version for disk '{} with id '{}/{}'",
volume.getDiskAlias(),
volume.getId(),
volume.getImageId());
}
});
}
@Override
protected void endWithFailure() {
// Going to try and refresh the VM by re-loading it form DB
setVm(null);
if (getVm() != null) {
removeVmSnapshots();
endActionOnAllImageGroups();
removeVmNetworkInterfaces();
vmDynamicDao.remove(getVmId());
vmStatisticsDao.remove(getVmId());
vmStaticDao.remove(getVmId());
setSucceeded(true);
} else {
setVm(getParameters().getVm()); // Setting VM from params, for logging purposes
// No point in trying to end action again, as the imported VM does not exist in the DB.
getReturnValue().setEndActionTryAgain(false);
}
}
@Override
protected void removeVmSnapshots() {
Guid vmId = getVmId();
Set<String> memoryStates = getSnapshotsManager().removeSnapshots(vmId);
for (String memoryState : memoryStates) {
removeMemoryVolumes(memoryState, vmId);
}
}
private void removeMemoryVolumes(String memoryVolume, Guid vmId) {
VdcReturnValueBase retVal = runInternalAction(
VdcActionType.RemoveMemoryVolumes,
new RemoveMemoryVolumesParameters(memoryVolume, vmId), cloneContextAndDetachFromParent());
if (!retVal.getSucceeded()) {
log.error("Failed to remove memory volumes '{}'", memoryVolume);
}
}
protected void removeVmNetworkInterfaces() {
new VmInterfaceManager(macPool).removeAll(getVmId());
}
@Override
public AuditLogType getAuditLogTypeValue() {
switch (getActionState()) {
case EXECUTE:
return getSucceeded() ? AuditLogType.IMPORTEXPORT_STARTING_IMPORT_VM
: AuditLogType.IMPORTEXPORT_IMPORT_VM_FAILED;
case END_SUCCESS:
return getSucceeded() ? AuditLogType.IMPORTEXPORT_IMPORT_VM : AuditLogType.IMPORTEXPORT_IMPORT_VM_FAILED;
case END_FAILURE:
return AuditLogType.IMPORTEXPORT_IMPORT_VM_FAILED;
}
return super.getAuditLogTypeValue();
}
@Override
protected List<Class<?>> getValidationGroups() {
if (getParameters().isImportAsNewEntity()) {
return addValidationGroup(ImportClonedEntity.class);
}
return addValidationGroup(ImportEntity.class);
}
@Override
public List<PermissionSubject> getPermissionCheckSubjects() {
List<PermissionSubject> permissionList = super.getPermissionCheckSubjects();
// Source domain
permissionList.add(new PermissionSubject(getParameters().getSourceDomainId(),
VdcObjectType.Storage,
getActionType().getActionGroup()));
// special permission is needed to use custom properties
if (getVm() != null && !StringUtils.isEmpty(getVm().getCustomProperties())) {
permissionList.add(new PermissionSubject(getClusterId(),
VdcObjectType.Cluster,
ActionGroup.CHANGE_VM_CUSTOM_PROPERTIES));
}
return permissionList;
}
protected boolean setAndValidateDiskProfiles() {
if (getParameters().getVm().getDiskMap() != null) {
Map<DiskImage, Guid> map = new HashMap<>();
for (Disk disk : getDisksForDiskProfileValidation()) {
if (disk.getDiskStorageType() == DiskStorageType.IMAGE) {
DiskImage diskImage = (DiskImage) disk;
map.put(diskImage, imageToDestinationDomainMap.get(diskImage.getId()));
}
}
return validate(diskProfileHelper.setAndValidateDiskProfiles(map, getCurrentUser()));
}
return true;
}
/**
* If the allow partial import flag is true we should filter out the invalid disks which did not pass the
* validation from the VM's disk map, since those disks will not be part of the VM once it will be imported.
*
* @return All the valid disks to use for disk profile.
*/
private Collection<Disk> getDisksForDiskProfileValidation() {
Collection<Disk> disks = getParameters().getVm().getDiskMap().values();
if (getParameters().isAllowPartialImport()) {
disks = disks.stream().filter(disk -> getImages().stream()
.anyMatch(diskFromImagesList -> diskFromImagesList.getId().equals(disk.getId())))
.collect(Collectors.toList());
}
return disks;
}
@Override
public List<QuotaConsumptionParameter> getQuotaStorageConsumptionParameters() {
List<QuotaConsumptionParameter> list = new ArrayList<>();
for (Disk disk : getParameters().getVm().getDiskMap().values()) {
// TODO: handle import more than once;
if (disk instanceof DiskImage) {
DiskImage diskImage = (DiskImage) disk;
list.add(new QuotaStorageConsumptionParameter(
diskImage.getQuotaId(),
null,
QuotaConsumptionParameter.QuotaAction.CONSUME,
imageToDestinationDomainMap.get(diskImage.getId()),
(double) diskImage.getSizeInGigabytes()));
}
}
return list;
}
protected List<DiskImage> getImages() {
return getVm().getImages();
}
@Override
protected MacPool getMacPool() {
return super.getMacPool();
}
private void updatePassDiscardForDiskVmElement(DiskVmElement diskVmElement) {
if (diskVmElement.isPassDiscard() &&
!FeatureSupported.passDiscardSupported(getStoragePool().getCompatibilityVersion())) {
diskVmElement.setPassDiscard(false);
}
}
}