package org.ovirt.engine.core.bll.validator.storage; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import org.ovirt.engine.core.bll.ValidationResult; import org.ovirt.engine.core.bll.provider.storage.OpenStackVolumeProviderProxy; import org.ovirt.engine.core.common.businessentities.storage.CinderDisk; import org.ovirt.engine.core.common.businessentities.storage.CinderVolumeType; import org.ovirt.engine.core.common.businessentities.storage.Disk; import org.ovirt.engine.core.common.businessentities.storage.VolumeClassification; import org.ovirt.engine.core.common.errors.EngineMessage; import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.dal.dbbroker.DbFacade; import org.ovirt.engine.core.dao.DiskDao; import org.ovirt.engine.core.dao.StorageDomainDao; import com.woorea.openstack.base.client.OpenStackResponseException; import com.woorea.openstack.cinder.model.Limits; public class CinderDisksValidator { private Iterable<CinderDisk> cinderDisks; private Map<Guid, OpenStackVolumeProviderProxy> diskProxyMap; private Map<Guid, CinderStorageRelatedDisksAndProxy> cinderStorageToRelatedDisks; public CinderDisksValidator(Iterable<CinderDisk> cinderDisks) { this.cinderDisks = cinderDisks; this.diskProxyMap = initializeVolumeProviderProxyMap(); } public CinderDisksValidator(CinderDisk cinderDisk) { this(Collections.singleton(cinderDisk)); } private ValidationResult validate(Callable<ValidationResult> callable) { try { return callable.call(); } catch (OpenStackResponseException e) { return new ValidationResult(EngineMessage.ACTION_TYPE_FAILED_CINDER, String.format("$cinderException %1$s", e.getMessage())); } catch (Exception e) { throw new RuntimeException(e); } } public ValidationResult validateCinderDiskLimits() { return validate(() -> { Map<Guid, CinderStorageRelatedDisksAndProxy> relatedCinderDisksByStorageMap = getRelatedCinderDisksToStorageDomainMap(); Collection<CinderStorageRelatedDisksAndProxy> relatedCinderDisksByStorageCollection = relatedCinderDisksByStorageMap.values(); for (CinderStorageRelatedDisksAndProxy relatedCinderDisksByStorage : relatedCinderDisksByStorageCollection) { Limits limits = relatedCinderDisksByStorage.getProxy().getLimits(); int numOfDisks = relatedCinderDisksByStorage.getCinderDisks().size(); if (isLimitExceeded(limits, VolumeClassification.Volume, numOfDisks)) { String storageName = getStorageDomainDao().get(relatedCinderDisksByStorage.getStorageDomainId()) .getStorageName(); return new ValidationResult(EngineMessage.CANNOT_ADD_CINDER_DISK_VOLUME_LIMIT_EXCEEDED, String.format("$maxTotalVolumes %d", limits.getAbsolute().getMaxTotalVolumes()), String.format("$storageName %s", storageName)); } } return ValidationResult.VALID; }); } public ValidationResult validateCinderDiskSnapshotsLimits() { return validate(() -> { Map<Guid, CinderStorageRelatedDisksAndProxy> relatedCinderDisksByStorageMap = getRelatedCinderDisksToStorageDomainMap(); Collection<CinderStorageRelatedDisksAndProxy> relatedCinderDisksByStorageCollection = relatedCinderDisksByStorageMap.values(); for (CinderStorageRelatedDisksAndProxy relatedCinderDisksByStorage : relatedCinderDisksByStorageCollection) { Limits limits = relatedCinderDisksByStorage.getProxy().getLimits(); int numOfDisks = relatedCinderDisksByStorage.getCinderDisks().size(); if (isLimitExceeded(limits, VolumeClassification.Snapshot, numOfDisks)) { String storageName = getStorageDomainDao().get(relatedCinderDisksByStorage.getStorageDomainId()) .getStorageName(); return new ValidationResult(EngineMessage.CANNOT_ADD_CINDER_DISK_SNAPSHOT_LIMIT_EXCEEDED, String.format("$maxTotalSnapshots %d", limits.getAbsolute().getMaxTotalVolumes()), String.format("$storageName %s", storageName)); } } return ValidationResult.VALID; }); } private boolean isLimitExceeded(Limits limits, VolumeClassification cinderType, int diskCount) { if (cinderType == VolumeClassification.Snapshot) { return limits.getAbsolute().getTotalSnapshotsUsed() + diskCount > limits.getAbsolute().getMaxTotalSnapshots(); } if (cinderType == VolumeClassification.Volume) { return limits.getAbsolute().getTotalVolumesUsed() + diskCount > limits.getAbsolute().getMaxTotalVolumes(); } return false; } private Map<Guid, CinderStorageRelatedDisksAndProxy> getRelatedCinderDisksToStorageDomainMap() { if (cinderStorageToRelatedDisks == null) { cinderStorageToRelatedDisks = new HashMap<>(); for (CinderDisk cinderDisk : cinderDisks) { Guid storageDomainId = cinderDisk.getStorageIds().get(0); CinderStorageRelatedDisksAndProxy cinderRelatedDisksAndProxy = cinderStorageToRelatedDisks.get(storageDomainId); if (cinderRelatedDisksAndProxy == null) { List<CinderDisk> cinderDisks = new ArrayList<>(); cinderDisks.add(cinderDisk); OpenStackVolumeProviderProxy proxy = diskProxyMap.get(cinderDisk.getId()); CinderStorageRelatedDisksAndProxy newCinderRelatedDisksAndProxy = new CinderStorageRelatedDisksAndProxy(storageDomainId, cinderDisks, proxy); cinderStorageToRelatedDisks.put(storageDomainId, newCinderRelatedDisksAndProxy); } else { cinderRelatedDisksAndProxy.getCinderDisks().add(cinderDisk); } } } return cinderStorageToRelatedDisks; } private static class CinderStorageRelatedDisksAndProxy { private Guid storageDomainId; private List<CinderDisk> cinderDisks = new ArrayList<>(); private OpenStackVolumeProviderProxy proxy; public CinderStorageRelatedDisksAndProxy(Guid storageDomainId, List<CinderDisk> cinderDisks, OpenStackVolumeProviderProxy proxy) { setStorageDomainId(storageDomainId); setCinderDisks(cinderDisks); setProxy(proxy); } public Guid getStorageDomainId() { return storageDomainId; } public void setStorageDomainId(Guid storageDomainId) { this.storageDomainId = storageDomainId; } public List<CinderDisk> getCinderDisks() { return cinderDisks; } public void setCinderDisks(List<CinderDisk> cinderDisks) { this.cinderDisks = cinderDisks; } public OpenStackVolumeProviderProxy getProxy() { return proxy; } public void setProxy(OpenStackVolumeProviderProxy proxy) { this.proxy = proxy; } } public ValidationResult validateCinderDisksAlreadyRegistered() { return validate(() -> { for (CinderDisk disk : cinderDisks) { Disk diskFromDB = getDiskDao().get(disk.getId()); if (diskFromDB != null) { return new ValidationResult(EngineMessage.CINDER_DISK_ALREADY_REGISTERED, String.format("$diskAlias %s", diskFromDB.getDiskAlias())); } } return ValidationResult.VALID; }); } /** * Validates that the disk's volume type exists in Cinder * (note that this method validates only against a single disk). */ public ValidationResult validateCinderVolumeTypesExist() { return validate(() -> { final CinderDisk disk = cinderDisks.iterator().next(); OpenStackVolumeProviderProxy proxy = diskProxyMap.get(disk.getId()); List<CinderVolumeType> volumeTypes = proxy.getVolumeTypes(); boolean volumeTypeExists = volumeTypes.stream().anyMatch(v -> v.getName().equals(disk.getCinderVolumeType())); if (!volumeTypeExists) { return new ValidationResult(EngineMessage.CINDER_VOLUME_TYPE_NOT_EXISTS, String.format("$cinderVolumeType %s", disk.getCinderVolumeType())); } return ValidationResult.VALID; }); } private Map<Guid, OpenStackVolumeProviderProxy> initializeVolumeProviderProxyMap() { if (diskProxyMap == null) { diskProxyMap = new HashMap<>(); for (CinderDisk cinderDisk : cinderDisks) { OpenStackVolumeProviderProxy volumeProviderProxy = getVolumeProviderProxy(cinderDisk); diskProxyMap.put(cinderDisk.getId(), volumeProviderProxy); } } return diskProxyMap; } private OpenStackVolumeProviderProxy getVolumeProviderProxy(CinderDisk cinderDisk) { if (cinderDisk == null || cinderDisk.getStorageIds().isEmpty()) { return null; } return OpenStackVolumeProviderProxy.getFromStorageDomainId(cinderDisk.getStorageIds().get(0)); } protected DiskDao getDiskDao() { return DbFacade.getInstance().getDiskDao(); } protected StorageDomainDao getStorageDomainDao() { return DbFacade.getInstance().getStorageDomainDao(); } }