package org.ovirt.engine.core.bll.memory; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; import javax.inject.Inject; import javax.inject.Singleton; import org.ovirt.engine.core.bll.memory.sdcomparators.StorageDomainNumberOfVmDisksComparator; import org.ovirt.engine.core.bll.memory.sdfilters.StorageDomainSpaceRequirementsFilter; import org.ovirt.engine.core.common.businessentities.StorageDomain; import org.ovirt.engine.core.common.businessentities.StorageDomainStatus; import org.ovirt.engine.core.common.businessentities.VM; import org.ovirt.engine.core.common.businessentities.storage.DiskImage; import org.ovirt.engine.core.common.businessentities.storage.StorageType; import org.ovirt.engine.core.common.businessentities.storage.VolumeType; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.dao.StorageDomainDao; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Singleton public class MemoryStorageHandler { private static final Logger log = LoggerFactory.getLogger(MemoryStorageHandler.class); @Inject private StorageDomainDao storageDomainDao; /** * Returns a <code>StorageDomain</code> in the given <code>StoragePool</code> that has * at least as much as requested free space and can be used to store memory images * * @param storagePoolId * The storage pool where the search for a domain will be made * @param memoryDisks * Disks for which space is needed * @param vmDisks * The vm's active snapshot disks. * @param vmForLogging * The VM which the memory volumes being created belong to.<br/> * Note: This parameter is used for logging purposed only. * @return storage domain in the given pool with at least the required amount of free space, * or null if no such storage domain exists in the pool */ public StorageDomain findStorageDomainForMemory(Guid storagePoolId, List<DiskImage> memoryDisks, Collection<DiskImage> vmDisks, VM vmForLogging) { List<StorageDomain> domainsInPool = storageDomainDao.getAllForStoragePool(storagePoolId); StorageDomain storageDomainForMemory = findStorageDomainForMemory(domainsInPool, memoryDisks, vmDisks); if (storageDomainForMemory != null) { updateDisksStorage(storageDomainForMemory, memoryDisks); if (vmForLogging != null) { log.info("The memory volumes of VM (name '{}', id '{}') will be " + "stored in storage domain (name '{}', id '{}')", vmForLogging.getName(), vmForLogging.getId(), storageDomainForMemory.getName(), storageDomainForMemory.getId()); } } return storageDomainForMemory; } public void updateDisksStorage(StorageDomain storageDomain, List<DiskImage> memoryDisks) { for (DiskImage disk : memoryDisks) { disk.setStorageIds(new ArrayList<>(Collections.singletonList(storageDomain.getId()))); } /* There should be two disks in the disksList, first of which is memory disk. Only its volume type should be modified. */ updateDiskVolumeType(storageDomain.getStorageType(), memoryDisks.get(0)); } protected StorageDomain findStorageDomainForMemory(List<StorageDomain> domainsInPool, List<DiskImage> memoryDisks, Collection<DiskImage> vmDisks) { domainsInPool = filterStorageDomains(domainsInPool, memoryDisks); sortStorageDomains(domainsInPool, vmDisks); return domainsInPool.stream().findFirst().orElse(null); } protected List<Predicate<StorageDomain>> getStorageDomainFilters(List<DiskImage> memoryDisks) { return Arrays.asList(ACTIVE_DOMAINS_PREDICATE, DATA_DOMAINS_PREDICATE, new StorageDomainSpaceRequirementsFilter(this, memoryDisks)); } protected List<Comparator<StorageDomain>> getStorageDomainComparators(Collection<DiskImage> vmDisks) { return Arrays.asList(new StorageDomainNumberOfVmDisksComparator(vmDisks), SHARED_FIRST_COMPARATOR, FILE_FIRST_COMPARATOR, AVAILABLE_SIZE_COMPARATOR); } protected List<StorageDomain> filterStorageDomains(List<StorageDomain> domainsInPool, List<DiskImage> memoryDisks) { Predicate<StorageDomain> predicate = getStorageDomainFilters(memoryDisks).stream().reduce(Predicate::and).orElse(t -> true); return domainsInPool.stream().filter(predicate).collect(Collectors.toList()); } protected void sortStorageDomains(List<StorageDomain> domainsInPool, Collection<DiskImage> vmDisks) { Comparator<StorageDomain> comp = getStorageDomainComparators(vmDisks).stream().reduce(Comparator::thenComparing).orElse(null); domainsInPool.sort(comp); } private void updateDiskVolumeType(StorageType storageType, DiskImage disk) { VolumeType volumeType = storageType.isFileDomain() ? VolumeType.Sparse : VolumeType.Preallocated; disk.setVolumeType(volumeType); } /* Predicates */ public static final Predicate<StorageDomain> ACTIVE_DOMAINS_PREDICATE = d -> d.getStatus() == StorageDomainStatus.Active; public static final Predicate<StorageDomain> DATA_DOMAINS_PREDICATE = d -> d.getStorageDomainType().isDataDomain(); /* Comparators */ public static final Comparator<StorageDomain> SHARED_FIRST_COMPARATOR = Comparator.comparing(StorageDomain::isShared).reversed(); public static final Comparator<StorageDomain> FILE_FIRST_COMPARATOR = Comparator.<StorageDomain, Boolean>comparing(s -> s.getStorageType().isFileDomain()).reversed(); public static final Comparator<StorageDomain> AVAILABLE_SIZE_COMPARATOR = Comparator.comparing(StorageDomain::getAvailableDiskSize).reversed(); }