package org.ovirt.engine.core.bll.validator.storage;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.ovirt.engine.core.bll.ValidationResult;
import org.ovirt.engine.core.common.action.VdcActionType;
import org.ovirt.engine.core.common.businessentities.SubchainInfo;
import org.ovirt.engine.core.common.businessentities.storage.DiskImage;
import org.ovirt.engine.core.compat.Guid;
import org.ovirt.engine.core.dal.dbbroker.DbFacade;
import org.ovirt.engine.core.dao.StorageDomainDao;
import org.ovirt.engine.core.utils.collections.MultiValueMapUtils;
/**
* A validator for multiple storage domains.
*
* This class offers several validations similar to those offered in
* {@link StorageDomainValidator} which can be performed on several domains.
*
* The guideline of this class is performance and short circuit logic, so any
* of these validations will fail on the first storage domain which fails
* validation, and the others will not be inspected.
*/
public class MultipleStorageDomainsValidator {
/** The ID of the storage pool all the domains belong to */
private Guid storagePoolId;
/** A map from the ids of each domain being validated to its validator */
private Map<Guid, StorageDomainValidator> domainValidators;
/**
* Constructor from Guids
* @param sdIds A {@link Collection} of storage domain IDs to be validated
*/
public MultipleStorageDomainsValidator(Guid storagePoolId, Collection<Guid> sdIds) {
this.storagePoolId = storagePoolId;
domainValidators = new HashMap<>();
for (Guid id : sdIds) {
domainValidators.put(id, null);
}
}
private Map<Guid, List<DiskImage>> getDomainsDisksMap(Collection<DiskImage> diskImages) {
Map<Guid, List<DiskImage>> domainsDisksMap = new HashMap<>();
for (DiskImage disk : diskImages) {
List<Guid> domainIds = disk.getStorageIds();
for (Guid domainId : domainIds) {
MultiValueMapUtils.addToMap(domainId, disk, domainsDisksMap);
}
}
return domainsDisksMap;
}
/**
* Validates that all the domains exist and are active.
* @return {@link ValidationResult#VALID} if all the domains are OK, or a {@link ValidationResult} with the first non-active domain encountered.
*/
public ValidationResult allDomainsExistAndActive() {
return validOrFirstFailure(entry -> getStorageDomainValidator(entry).isDomainExistAndActive());
}
/**
* Validates that all the domains are within free disk space threshold.
* @return {@link ValidationResult#VALID} if all the domains are OK, or a {@link ValidationResult} with the first low space domain encountered.
*/
public ValidationResult allDomainsWithinThresholds() {
return validOrFirstFailure(entry -> getStorageDomainValidator(entry).isDomainWithinThresholds());
}
/**
* Validates that all the domains have enough space for the request
* @return {@link ValidationResult#VALID} if all the domains have enough free space, or a {@link ValidationResult} with the first low-on-space domain encountered.
*/
public ValidationResult allDomainsHaveSpaceForNewDisks(Collection<DiskImage> disksImages) {
final Map<Guid, List<DiskImage>> disksMap = getDomainsDisksMap(disksImages);
return validOrFirstFailure(entry -> {
Guid sdId = entry.getKey();
List<DiskImage> disksList = disksMap.get(sdId);
return getStorageDomainValidator(entry).hasSpaceForNewDisks(disksList);
});
}
/**
* Validates that all the domains have enough space for the request
* @return {@link ValidationResult#VALID} if all the domains have enough free space, or a {@link ValidationResult} with the first low-on-space domain encountered.
*/
public ValidationResult allDomainsHaveSpaceForClonedDisks(Collection<DiskImage> diskImages) {
final Map<Guid, List<DiskImage>> disksMap = getDomainsDisksMap(diskImages);
return validOrFirstFailure(entry -> {
Guid sdId = entry.getKey();
List<DiskImage> disksList = disksMap.get(sdId);
return getStorageDomainValidator(entry).hasSpaceForClonedDisks(disksList);
});
}
/**
* Validates that all the domains have enough space for the request
* @return {@link ValidationResult#VALID} if all the domains have enough free space, or a {@link ValidationResult} with the first low-on-space domain encountered.
*/
public ValidationResult allDomainsHaveSpaceForMerge(List<SubchainInfo> snapshots, VdcActionType snapshotActionType) {
final Map<Guid, SubchainInfo> storageToSnapshots = getDomainsToSnapshotsMap(snapshots);
return validOrFirstFailure(entry -> {
Guid sdId = entry.getKey();
SubchainInfo subchain = storageToSnapshots.get(sdId);
return getStorageDomainValidator(entry).hasSpaceForMerge(subchain, snapshotActionType);
});
}
/**
* Validates that all the domains have enough space for the request
* @return {@link ValidationResult#VALID} if all the domains have enough free space, or a {@link ValidationResult} with the first low-on-space domain encountered.
*/
public ValidationResult allDomainsHaveSpaceForAllDisks(List<DiskImage> newDiskImages, List<DiskImage> clonedDiskImages) {
final Map<Guid, List<DiskImage>> domainsNewDisksMap = getDomainsDisksMap(newDiskImages);
final Map<Guid, List<DiskImage>> domainsClonedDisksMap = getDomainsDisksMap(clonedDiskImages);
return validOrFirstFailure(entry -> {
Guid sdId = entry.getKey();
List<DiskImage> newDisksForDomain = domainsNewDisksMap.get(sdId);
List<DiskImage> clonedDisksForDomain = domainsClonedDisksMap.get(sdId);
return getStorageDomainValidator(entry).hasSpaceForAllDisks(newDisksForDomain, clonedDisksForDomain);
});
}
/**
* Validates that all the domains have enough space for the request
* @return {@link ValidationResult#VALID} if all the domains have enough free space, or a {@link ValidationResult} with the first low-on-space domain encountered.
*/
public ValidationResult allDomainsHaveSpaceForDisksWithSnapshots(Collection<DiskImage> diskImages) {
final Map<Guid, List<DiskImage>> disksMap = getDomainsDisksMap(diskImages);
return validOrFirstFailure(entry -> {
Guid sdId = entry.getKey();
List<DiskImage> diskList = disksMap.get(sdId);
return getStorageDomainValidator(entry).hasSpaceForDisksWithSnapshots(diskList);
});
}
/** @return The lazy-loaded validator for the given map entry */
protected StorageDomainValidator getStorageDomainValidator(Map.Entry<Guid, StorageDomainValidator> entry) {
if (entry.getValue() == null) {
entry.setValue(new StorageDomainValidator(getStorageDomainDao().getForStoragePool(entry.getKey(), storagePoolId)));
}
return entry.getValue();
}
/** @return The Dao object used to retrieve storage domains */
public StorageDomainDao getStorageDomainDao() {
return DbFacade.getInstance().getStorageDomainDao();
}
/**
* Validates all the storage domains by a given predicate.
*
* @return {@link ValidationResult#VALID} if all the domains are OK, or the
* first validation error if they aren't.
*/
private ValidationResult validOrFirstFailure
(Function<Map.Entry<Guid, StorageDomainValidator>, ValidationResult> predicate) {
return domainValidators.entrySet()
.stream()
.map(predicate)
.filter(v -> !v.isValid())
.findFirst()
.orElse(ValidationResult.VALID);
}
private Map<Guid, SubchainInfo> getDomainsToSnapshotsMap(List<SubchainInfo> snapshots) {
return snapshots
.stream()
.collect(Collectors.toMap(SubchainInfo::getStorageDomainId,
Function.identity()));
}
}