http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java ---------------------------------------------------------------------- diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java index 5f647db..af969e1 100644 --- a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java +++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidFirePrimaryDataStoreDriver.java @@ -17,32 +17,13 @@ package org.apache.cloudstack.storage.datastore.driver; import java.text.NumberFormat; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.inject.Inject; -import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo; -import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; -import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult; -import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreCapabilities; -import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; -import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; -import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; -import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; -import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; -import org.apache.cloudstack.framework.async.AsyncCompletionCallback; -import org.apache.cloudstack.storage.command.CommandResult; -import org.apache.cloudstack.storage.command.CreateObjectAnswer; -import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; -import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; -import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; -import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; -import org.apache.cloudstack.storage.datastore.util.SolidFireUtil; -import org.apache.cloudstack.storage.to.SnapshotObjectTO; -import org.apache.log4j.Logger; - import com.cloud.agent.api.Answer; import com.cloud.agent.api.to.DataObjectType; import com.cloud.agent.api.to.DataStoreTO; @@ -55,16 +36,19 @@ import com.cloud.dc.dao.ClusterDao; import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; -import com.cloud.storage.Storage.StoragePoolType; +import com.cloud.storage.DataStoreRole; import com.cloud.storage.ResizeVolumePayload; +import com.cloud.storage.Snapshot.State; +import com.cloud.storage.SnapshotVO; import com.cloud.storage.StoragePool; -import com.cloud.storage.Volume; +import com.cloud.storage.VMTemplateStoragePoolVO; import com.cloud.storage.VolumeDetailVO; import com.cloud.storage.VolumeVO; -import com.cloud.storage.SnapshotVO; +import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.SnapshotDetailsDao; import com.cloud.storage.dao.SnapshotDetailsVO; +import com.cloud.storage.dao.VMTemplatePoolDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.dao.VolumeDetailsDao; import com.cloud.user.AccountDetailVO; @@ -74,27 +58,64 @@ import com.cloud.user.dao.AccountDao; import com.cloud.utils.db.GlobalLock; import com.cloud.utils.exception.CloudRuntimeException; +import com.google.common.base.Preconditions; + +import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; +import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult; +import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreCapabilities; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; +import org.apache.cloudstack.framework.async.AsyncCompletionCallback; +import org.apache.cloudstack.storage.command.CommandResult; +import org.apache.cloudstack.storage.command.CreateObjectAnswer; +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO; +import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.cloudstack.storage.datastore.util.SolidFireUtil; +import org.apache.cloudstack.storage.to.SnapshotObjectTO; +import org.apache.log4j.Logger; + public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { - private static final Logger s_logger = Logger.getLogger(SolidFirePrimaryDataStoreDriver.class); - private static final int s_lowestHypervisorSnapshotReserve = 10; - - @Inject private AccountDao _accountDao; - @Inject private AccountDetailsDao _accountDetailsDao; - @Inject private ClusterDao _clusterDao; - @Inject private ClusterDetailsDao _clusterDetailsDao; - @Inject private HostDao _hostDao; - @Inject private SnapshotDao _snapshotDao; - @Inject private SnapshotDetailsDao _snapshotDetailsDao; - @Inject private PrimaryDataStoreDao _storagePoolDao; - @Inject private StoragePoolDetailsDao _storagePoolDetailsDao; - @Inject private VolumeDao _volumeDao; - @Inject private VolumeDetailsDao _volumeDetailsDao; + private static final Logger LOGGER = Logger.getLogger(SolidFirePrimaryDataStoreDriver.class); + private static final int LOCK_TIME_IN_SECONDS = 300; + private static final int LOWEST_HYPERVISOR_SNAPSHOT_RESERVE = 10; + private static final long MIN_IOPS_FOR_TEMPLATE_VOLUME = 100L; + private static final long MAX_IOPS_FOR_TEMPLATE_VOLUME = 20000L; + private static final long MIN_IOPS_FOR_TEMP_VOLUME = 100L; + private static final long MAX_IOPS_FOR_TEMP_VOLUME = 20000L; + private static final long MIN_IOPS_FOR_SNAPSHOT_VOLUME = 100L; + private static final long MAX_IOPS_FOR_SNAPSHOT_VOLUME = 20000L; + + @Inject private AccountDao accountDao; + @Inject private AccountDetailsDao accountDetailsDao; + @Inject private ClusterDao clusterDao; + @Inject private ClusterDetailsDao clusterDetailsDao; + @Inject private DataStoreManager dataStoreMgr; + @Inject private HostDao hostDao; + @Inject private SnapshotDao snapshotDao; + @Inject private SnapshotDetailsDao snapshotDetailsDao; + @Inject private PrimaryDataStoreDao storagePoolDao; + @Inject private StoragePoolDetailsDao storagePoolDetailsDao; + @Inject private VMTemplatePoolDao tmpltPoolDao; + @Inject private VolumeDao volumeDao; + @Inject private VolumeDetailsDao volumeDetailsDao; + @Inject private VolumeDataFactory volumeFactory; @Override public Map<String, String> getCapabilities() { - Map<String, String> mapCapabilities = new HashMap<String, String>(); + Map<String, String> mapCapabilities = new HashMap<>(); mapCapabilities.put(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString(), Boolean.TRUE.toString()); + mapCapabilities.put(DataStoreCapabilities.CAN_CREATE_VOLUME_FROM_SNAPSHOT.toString(), Boolean.TRUE.toString()); + mapCapabilities.put(DataStoreCapabilities.CAN_CREATE_VOLUME_FROM_VOLUME.toString(), Boolean.TRUE.toString()); return mapCapabilities; } @@ -116,7 +137,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { } @Override - public ChapInfo getChapInfo(VolumeInfo volumeInfo) { + public ChapInfo getChapInfo(DataObject dataObject) { return null; } @@ -128,38 +149,42 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { @Override public boolean grantAccess(DataObject dataObject, Host host, DataStore dataStore) { - if (dataObject == null || host == null || dataStore == null) { - return false; - } + Preconditions.checkArgument(dataObject != null, "'dataObject' should not be 'null'"); + Preconditions.checkArgument(host != null, "'host' should not be 'null'"); + Preconditions.checkArgument(dataStore != null, "'dataStore' should not be 'null'"); long sfVolumeId = getSolidFireVolumeId(dataObject); long clusterId = host.getClusterId(); long storagePoolId = dataStore.getId(); - ClusterVO cluster = _clusterDao.findById(clusterId); + ClusterVO cluster = clusterDao.findById(clusterId); GlobalLock lock = GlobalLock.getInternLock(cluster.getUuid()); - if (!lock.lock(SolidFireUtil.s_lockTimeInSeconds)) { + if (!lock.lock(LOCK_TIME_IN_SECONDS)) { String errMsg = "Couldn't lock the DB (in grantAccess) on the following string: " + cluster.getUuid(); - s_logger.debug(errMsg); + LOGGER.warn(errMsg); throw new CloudRuntimeException(errMsg); } try { - ClusterDetailsVO clusterDetail = _clusterDetailsDao.findDetail(clusterId, SolidFireUtil.getVagKey(storagePoolId)); + ClusterDetailsVO clusterDetail = clusterDetailsDao.findDetail(clusterId, SolidFireUtil.getVagKey(storagePoolId)); String vagId = clusterDetail != null ? clusterDetail.getValue() : null; - List<HostVO> hosts = _hostDao.findByClusterId(clusterId); + List<HostVO> hosts = hostDao.findByClusterId(clusterId); if (!SolidFireUtil.hostsSupport_iScsi(hosts)) { - return false; + String errMsg = "Not all hosts in the compute cluster support iSCSI."; + + LOGGER.warn(errMsg); + + throw new CloudRuntimeException(errMsg); } - SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao); + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao); if (vagId != null) { SolidFireUtil.SolidFireVag sfVag = SolidFireUtil.getSolidFireVag(sfConnection, Long.parseLong(vagId)); @@ -169,7 +194,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { SolidFireUtil.modifySolidFireVag(sfConnection, sfVag.getId(), sfVag.getInitiators(), volumeIds); } else { - SolidFireUtil.placeVolumeInVolumeAccessGroup(sfConnection, sfVolumeId, storagePoolId, cluster.getUuid(), hosts, _clusterDetailsDao); + SolidFireUtil.placeVolumeInVolumeAccessGroup(sfConnection, sfVolumeId, storagePoolId, cluster.getUuid(), hosts, clusterDetailsDao); } return true; @@ -194,25 +219,25 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { long clusterId = host.getClusterId(); long storagePoolId = dataStore.getId(); - ClusterVO cluster = _clusterDao.findById(clusterId); + ClusterVO cluster = clusterDao.findById(clusterId); GlobalLock lock = GlobalLock.getInternLock(cluster.getUuid()); - if (!lock.lock(SolidFireUtil.s_lockTimeInSeconds)) { + if (!lock.lock(LOCK_TIME_IN_SECONDS)) { String errMsg = "Couldn't lock the DB (in revokeAccess) on the following string: " + cluster.getUuid(); - s_logger.debug(errMsg); + LOGGER.debug(errMsg); throw new CloudRuntimeException(errMsg); } try { - ClusterDetailsVO clusterDetail = _clusterDetailsDao.findDetail(clusterId, SolidFireUtil.getVagKey(storagePoolId)); + ClusterDetailsVO clusterDetail = clusterDetailsDao.findDetail(clusterId, SolidFireUtil.getVagKey(storagePoolId)); String vagId = clusterDetail != null ? clusterDetail.getValue() : null; if (vagId != null) { - SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao); + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao); SolidFireUtil.SolidFireVag sfVag = SolidFireUtil.getSolidFireVag(sfConnection, Long.parseLong(vagId)); @@ -233,7 +258,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { } if (dataObject.getType() == DataObjectType.SNAPSHOT) { - SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(dataObject.getId(), SolidFireUtil.VOLUME_ID); + SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(dataObject.getId(), SolidFireUtil.VOLUME_ID); if (snapshotDetails == null || snapshotDetails.getValue() == null) { throw new CloudRuntimeException("Unable to locate the volume ID associated with the following snapshot ID: " + dataObject.getId()); @@ -242,11 +267,26 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { return Long.parseLong(snapshotDetails.getValue()); } + if (dataObject.getType() == DataObjectType.TEMPLATE) { + return getVolumeIdFrom_iScsiPath(((TemplateInfo)dataObject).getInstallPath()); + } + throw new CloudRuntimeException("Invalid DataObjectType (" + dataObject.getType() + ") passed to getSolidFireVolumeId(DataObject)"); } + private long getVolumeIdFrom_iScsiPath(String iScsiPath) { + String[] splits = iScsiPath.split("/"); + String iqn = splits[1]; + + String sequenceToSearchFor = "."; + int lastIndexOf = iqn.lastIndexOf(sequenceToSearchFor); + String volumeIdAsString = iqn.substring(lastIndexOf + sequenceToSearchFor.length()); + + return Long.parseLong(volumeIdAsString); + } + private long getDefaultMinIops(long storagePoolId) { - StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_DEFAULT_MIN_IOPS); + StoragePoolDetailVO storagePoolDetail = storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_DEFAULT_MIN_IOPS); String clusterDefaultMinIops = storagePoolDetail.getValue(); @@ -254,7 +294,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { } private long getDefaultMaxIops(long storagePoolId) { - StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_DEFAULT_MAX_IOPS); + StoragePoolDetailVO storagePoolDetail = storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_DEFAULT_MAX_IOPS); String clusterDefaultMaxIops = storagePoolDetail.getValue(); @@ -262,7 +302,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { } private long getDefaultBurstIops(long storagePoolId, long maxIops) { - StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_DEFAULT_BURST_IOPS_PERCENT_OF_MAX_IOPS); + StoragePoolDetailVO storagePoolDetail = storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_DEFAULT_BURST_IOPS_PERCENT_OF_MAX_IOPS); String clusterDefaultBurstIopsPercentOfMaxIops = storagePoolDetail.getValue(); @@ -271,30 +311,56 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { return (long)(maxIops * fClusterDefaultBurstIopsPercentOfMaxIops); } - private SolidFireUtil.SolidFireVolume createSolidFireVolume(SolidFireUtil.SolidFireConnection sfConnection, VolumeInfo volumeInfo, long sfAccountId) { - long storagePoolId = volumeInfo.getDataStore().getId(); + private SolidFireUtil.SolidFireVolume createSolidFireVolume(SolidFireUtil.SolidFireConnection sfConnection, DataObject dataObject, long sfAccountId) { + long storagePoolId = dataObject.getDataStore().getId(); + Long minIops = null; + Long maxIops = null; + Long volumeSize = dataObject.getSize(); + String volumeName = null; - final Iops iops; + final Map<String, String> mapAttributes; - Long minIops = volumeInfo.getMinIops(); - Long maxIops = volumeInfo.getMaxIops(); + if (dataObject.getType() == DataObjectType.VOLUME) { + VolumeInfo volumeInfo = (VolumeInfo)dataObject; - if (minIops == null || minIops <= 0 || maxIops == null || maxIops <= 0) { - long defaultMaxIops = getDefaultMaxIops(storagePoolId); + minIops = volumeInfo.getMinIops(); + maxIops = volumeInfo.getMaxIops(); + volumeSize = getDataObjectSizeIncludingHypervisorSnapshotReserve(volumeInfo, storagePoolDao.findById(storagePoolId)); + volumeName = volumeInfo.getName(); - iops = new Iops(getDefaultMinIops(storagePoolId), defaultMaxIops, getDefaultBurstIops(storagePoolId, defaultMaxIops)); - } else { - iops = new Iops(volumeInfo.getMinIops(), volumeInfo.getMaxIops(), getDefaultBurstIops(storagePoolId, volumeInfo.getMaxIops())); + mapAttributes = getVolumeAttributes(volumeInfo); + } else if (dataObject.getType() == DataObjectType.TEMPLATE) { + TemplateInfo templateInfo = (TemplateInfo)dataObject; + + minIops = MIN_IOPS_FOR_TEMPLATE_VOLUME; + maxIops = MAX_IOPS_FOR_TEMPLATE_VOLUME; + volumeSize = getDataObjectSizeIncludingHypervisorSnapshotReserve(templateInfo, storagePoolDao.findById(storagePoolId)); + volumeName = templateInfo.getUniqueName(); + + mapAttributes = getTemplateAttributes(templateInfo); + } + else { + throw new CloudRuntimeException("Invalid type passed to createSolidFireVolume: " + dataObject.getType()); } - long volumeSize = getVolumeSizeIncludingHypervisorSnapshotReserve(volumeInfo, _storagePoolDao.findById(storagePoolId)); + final Iops iops = getIops(minIops, maxIops, storagePoolId); - long sfVolumeId = SolidFireUtil.createSolidFireVolume(sfConnection, SolidFireUtil.getSolidFireVolumeName(volumeInfo.getName()), sfAccountId, volumeSize, true, - NumberFormat.getInstance().format(volumeInfo.getSize()), iops.getMinIops(), iops.getMaxIops(), iops.getBurstIops()); + long sfVolumeId = SolidFireUtil.createSolidFireVolume(sfConnection, SolidFireUtil.getSolidFireVolumeName(volumeName), sfAccountId, + volumeSize, true, mapAttributes, iops.getMinIops(), iops.getMaxIops(), iops.getBurstIops()); return SolidFireUtil.getSolidFireVolume(sfConnection, sfVolumeId); } + private Iops getIops(Long minIops, Long maxIops, long storagePoolId) { + if (minIops == null || minIops <= 0 || maxIops == null || maxIops <= 0) { + long defaultMaxIops = getDefaultMaxIops(storagePoolId); + + return new Iops(getDefaultMinIops(storagePoolId), defaultMaxIops, getDefaultBurstIops(storagePoolId, defaultMaxIops)); + } + + return new Iops(minIops, maxIops, getDefaultBurstIops(storagePoolId, maxIops)); + } + @Override public long getUsedBytes(StoragePool storagePool) { return getUsedBytes(storagePool, Long.MIN_VALUE); @@ -303,7 +369,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { private long getUsedBytes(StoragePool storagePool, long volumeIdToIgnore) { long usedSpace = 0; - List<VolumeVO> lstVolumes = _volumeDao.findByPoolId(storagePool.getId(), null); + List<VolumeVO> lstVolumes = volumeDao.findByPoolId(storagePool.getId(), null); if (lstVolumes != null) { for (VolumeVO volume : lstVolumes) { @@ -311,7 +377,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { continue; } - VolumeDetailVO volumeDetail = _volumeDetailsDao.findDetail(volume.getId(), SolidFireUtil.VOLUME_SIZE); + VolumeDetailVO volumeDetail = volumeDetailsDao.findDetail(volume.getId(), SolidFireUtil.VOLUME_SIZE); if (volumeDetail != null && volumeDetail.getValue() != null) { long volumeSize = Long.parseLong(volumeDetail.getValue()); @@ -319,18 +385,22 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { usedSpace += volumeSize; } else { - SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePool.getId(), _storagePoolDetailsDao); + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePool.getId(), storagePoolDetailsDao); try { long lVolumeId = Long.parseLong(volume.getFolder()); SolidFireUtil.SolidFireVolume sfVolume = SolidFireUtil.getSolidFireVolume(sfConnection, lVolumeId); + long volumeSize = sfVolume.getTotalSize(); + // SolidFireUtil.VOLUME_SIZE was introduced in 4.5. // To be backward compatible with releases prior to 4.5, call updateVolumeDetails here. // That way if SolidFireUtil.VOLUME_SIZE wasn't put in the volume_details table when the // volume was initially created, it can be placed in volume_details here. - updateVolumeDetails(volume.getId(), sfVolume.getTotalSize()); + updateVolumeDetails(volume.getId(), volumeSize); + + usedSpace += volumeSize; } catch (NumberFormatException ex) { // can be ignored (the "folder" column didn't have a valid "long" in it (hasn't been placed there yet)) @@ -339,15 +409,15 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { } } - List<SnapshotVO> lstSnapshots = _snapshotDao.listAll(); + List<SnapshotVO> lstSnapshots = snapshotDao.listAll(); if (lstSnapshots != null) { for (SnapshotVO snapshot : lstSnapshots) { - SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(snapshot.getId(), SolidFireUtil.STORAGE_POOL_ID); + SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(snapshot.getId(), SolidFireUtil.STORAGE_POOL_ID); // if this snapshot belongs to the storagePool that was passed in if (snapshotDetails != null && snapshotDetails.getValue() != null && Long.parseLong(snapshotDetails.getValue()) == storagePool.getId()) { - snapshotDetails = _snapshotDetailsDao.findDetail(snapshot.getId(), SolidFireUtil.VOLUME_SIZE); + snapshotDetails = snapshotDetailsDao.findDetail(snapshot.getId(), SolidFireUtil.VOLUME_SIZE); if (snapshotDetails != null && snapshotDetails.getValue() != null) { long snapshotSize = Long.parseLong(snapshotDetails.getValue()); @@ -358,6 +428,14 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { } } + List<VMTemplateStoragePoolVO> lstTemplatePoolRefs = tmpltPoolDao.listByPoolId(storagePool.getId()); + + if (lstTemplatePoolRefs != null) { + for (VMTemplateStoragePoolVO templatePoolRef : lstTemplatePoolRefs) { + usedSpace += templatePoolRef.getTemplateSize(); + } + } + return usedSpace; } @@ -365,7 +443,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { public long getUsedIops(StoragePool storagePool) { long usedIops = 0; - List<VolumeVO> volumes = _volumeDao.findByPoolId(storagePool.getId(), null); + List<VolumeVO> volumes = volumeDao.findByPoolId(storagePool.getId(), null); if (volumes != null) { for (VolumeVO volume : volumes) { @@ -377,12 +455,25 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { } @Override - public long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool) { - long volumeSize = volume.getSize(); - Integer hypervisorSnapshotReserve = volume.getHypervisorSnapshotReserve(); + public long getDataObjectSizeIncludingHypervisorSnapshotReserve(DataObject dataObject, StoragePool pool) { + long volumeSize = 0; + + if (dataObject.getType() == DataObjectType.VOLUME) { + VolumeInfo volume = (VolumeInfo)dataObject; + + volumeSize = getVolumeSizeIncludingHypervisorSnapshotReserve(volume.getSize(), volume.getHypervisorSnapshotReserve()); + } else if (dataObject.getType() == DataObjectType.TEMPLATE) { + TemplateInfo templateInfo = (TemplateInfo)dataObject; + + volumeSize = (long)(templateInfo.getSize() + templateInfo.getSize() * (LOWEST_HYPERVISOR_SNAPSHOT_RESERVE / 100f)); + } + return volumeSize; + } + + private long getVolumeSizeIncludingHypervisorSnapshotReserve(long volumeSize, Integer hypervisorSnapshotReserve) { if (hypervisorSnapshotReserve != null) { - hypervisorSnapshotReserve = Math.max(hypervisorSnapshotReserve, s_lowestHypervisorSnapshotReserve); + hypervisorSnapshotReserve = Math.max(hypervisorSnapshotReserve, LOWEST_HYPERVISOR_SNAPSHOT_RESERVE); volumeSize += volumeSize * (hypervisorSnapshotReserve / 100f); } @@ -390,6 +481,29 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { return volumeSize; } + /** + * This method is only relevant when storagePool is being used with a compute cluster that supports UUID resigning. + */ + @Override + public long getBytesRequiredForTemplate(TemplateInfo templateInfo, StoragePool storagePool) { + List<VMTemplateStoragePoolVO> lstTemplatePoolRefs = tmpltPoolDao.listByPoolId(storagePool.getId()); + + if (lstTemplatePoolRefs != null) { + for (VMTemplateStoragePoolVO templatePoolRef : lstTemplatePoolRefs) { + if (templatePoolRef.getTemplateId() == templateInfo.getId()) { + // This indicates that we already have this template stored on this primary storage, so + // we do not require additional space. + return 0; + } + } + } + + // This indicates that we do not have a copy of this template on this primary storage, so + // we need to take it into consideration from a space standpoint (ex. when a new VM is spun + // up and wants to use this particular template for its root disk). + return getDataObjectSizeIncludingHypervisorSnapshotReserve(templateInfo, storagePool); + } + private static class Iops { private final long _minIops; private final long _maxIops; @@ -436,7 +550,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { long sfVolumeId = Long.parseLong(volumeInfo.getFolder()); - SolidFireUtil.deleteSolidFireVolume(sfConnection, sfVolumeId); + deleteSolidFireVolume(sfConnection, volumeInfo.getId(), sfVolumeId); } @Override @@ -444,114 +558,234 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { String iqn = null; String errMsg = null; - if (dataObject.getType() == DataObjectType.VOLUME) { - VolumeInfo volumeInfo = (VolumeInfo)dataObject; + try { + if (dataObject.getType() == DataObjectType.VOLUME) { + iqn = createVolume((VolumeInfo)dataObject, dataStore.getId()); + } else if (dataObject.getType() == DataObjectType.SNAPSHOT) { + createTempVolume((SnapshotInfo)dataObject, dataStore.getId()); + } else if (dataObject.getType() == DataObjectType.TEMPLATE) { + iqn = createTemplateVolume((TemplateInfo)dataObject, dataStore.getId()); + } else { + errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to createAsync"; + + LOGGER.error(errMsg); + } + } + catch (Exception ex) { + errMsg = ex.getMessage(); - long storagePoolId = dataStore.getId(); + LOGGER.error(errMsg); - SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao); + if (callback == null) { + throw ex; + } + } - AccountDetailVO accountDetail = SolidFireUtil.getAccountDetail(volumeInfo.getAccountId(), storagePoolId, _accountDetailsDao); + if (callback != null) { + // path = iqn + // size is pulled from DataObject instance, if errMsg is null + CreateCmdResult result = new CreateCmdResult(iqn, new Answer(null, errMsg == null, errMsg)); - if (accountDetail == null || accountDetail.getValue() == null) { - AccountVO account = _accountDao.findById(volumeInfo.getAccountId()); - String sfAccountName = SolidFireUtil.getSolidFireAccountName(account.getUuid(), account.getAccountId()); - SolidFireUtil.SolidFireAccount sfAccount = SolidFireUtil.getSolidFireAccount(sfConnection, sfAccountName); + result.setResult(errMsg); - if (sfAccount == null) { - sfAccount = createSolidFireAccount(sfConnection, sfAccountName); - } + callback.complete(result); + } + } + + private long getCreateSolidFireAccountId(SolidFireUtil.SolidFireConnection sfConnection, long csAccountId, long storagePoolId) { + AccountDetailVO accountDetail = SolidFireUtil.getAccountDetail(csAccountId, storagePoolId, accountDetailsDao); - SolidFireUtil.updateCsDbWithSolidFireAccountInfo(account.getId(), sfAccount, storagePoolId, _accountDetailsDao); + if (accountDetail == null || accountDetail.getValue() == null) { + AccountVO account = accountDao.findById(csAccountId); + String sfAccountName = SolidFireUtil.getSolidFireAccountName(account.getUuid(), account.getAccountId()); + SolidFireUtil.SolidFireAccount sfAccount = SolidFireUtil.getSolidFireAccount(sfConnection, sfAccountName); - accountDetail = SolidFireUtil.getAccountDetail(volumeInfo.getAccountId(), storagePoolId, _accountDetailsDao); + if (sfAccount == null) { + sfAccount = createSolidFireAccount(sfConnection, sfAccountName); } - long sfAccountId = Long.parseLong(accountDetail.getValue()); + SolidFireUtil.updateCsDbWithSolidFireAccountInfo(account.getId(), sfAccount, storagePoolId, accountDetailsDao); - SolidFireUtil.SolidFireVolume sfVolume = createSolidFireVolume(sfConnection, volumeInfo, sfAccountId); + accountDetail = SolidFireUtil.getAccountDetail(csAccountId, storagePoolId, accountDetailsDao); + } - iqn = sfVolume.getIqn(); + return Long.parseLong(accountDetail.getValue()); + } - VolumeVO volume = _volumeDao.findById(volumeInfo.getId()); + private void handleSnapshotDetails(long csSnapshotId, String name, String value) { + snapshotDetailsDao.removeDetail(csSnapshotId, name); - volume.set_iScsiName(iqn); - volume.setFolder(String.valueOf(sfVolume.getId())); - volume.setPoolType(StoragePoolType.IscsiLUN); - volume.setPoolId(storagePoolId); + SnapshotDetailsVO snapshotDetails = new SnapshotDetailsVO(csSnapshotId, name, value, false); - _volumeDao.update(volume.getId(), volume); + snapshotDetailsDao.persist(snapshotDetails); + } - updateVolumeDetails(volume.getId(), sfVolume.getTotalSize()); + private void addTempVolumeId(long csSnapshotId, String tempVolumeId) { + SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(csSnapshotId, SolidFireUtil.VOLUME_ID); - StoragePoolVO storagePool = _storagePoolDao.findById(dataStore.getId()); + if (snapshotDetails == null || snapshotDetails.getValue() == null) { + throw new CloudRuntimeException("'addTempVolumeId' should not be invoked unless " + SolidFireUtil.VOLUME_ID + " exists."); + } - long capacityBytes = storagePool.getCapacityBytes(); - // getUsedBytes(StoragePool) will include the bytes of the newly created volume because - // updateVolumeDetails(long, long) has already been called for this volume - long usedBytes = getUsedBytes(storagePool); + String originalVolumeId = snapshotDetails.getValue(); - storagePool.setUsedBytes(usedBytes > capacityBytes ? capacityBytes : usedBytes); + handleSnapshotDetails(csSnapshotId, SolidFireUtil.TEMP_VOLUME_ID, originalVolumeId); + handleSnapshotDetails(csSnapshotId, SolidFireUtil.VOLUME_ID, tempVolumeId); + } - _storagePoolDao.update(storagePoolId, storagePool); - } else { - errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to createAsync"; + private void removeTempVolumeId(long csSnapshotId) { + SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(csSnapshotId, SolidFireUtil.TEMP_VOLUME_ID); + + if (snapshotDetails == null || snapshotDetails.getValue() == null) { + throw new CloudRuntimeException("'removeTempVolumeId' should not be invoked unless " + SolidFireUtil.TEMP_VOLUME_ID + " exists."); } - // path = iqn - // size is pulled from DataObject instance, if errMsg is null - CreateCmdResult result = new CreateCmdResult(iqn, new Answer(null, errMsg == null, errMsg)); + String originalVolumeId = snapshotDetails.getValue(); - result.setResult(errMsg); + handleSnapshotDetails(csSnapshotId, SolidFireUtil.VOLUME_ID, originalVolumeId); - callback.complete(result); + snapshotDetailsDao.remove(snapshotDetails.getId()); } - private void updateVolumeDetails(long volumeId, long sfVolumeSize) { - VolumeDetailVO volumeDetailVo = _volumeDetailsDao.findDetail(volumeId, SolidFireUtil.VOLUME_SIZE); - - if (volumeDetailVo == null || volumeDetailVo.getValue() == null) { - volumeDetailVo = new VolumeDetailVO(volumeId, SolidFireUtil.VOLUME_SIZE, String.valueOf(sfVolumeSize), false); + private long getCsIdForCloning(long volumeId, String cloneOf) { + VolumeDetailVO volumeDetail = volumeDetailsDao.findDetail(volumeId, cloneOf); - _volumeDetailsDao.persist(volumeDetailVo); + if (volumeDetail != null && volumeDetail.getValue() != null) { + return new Long(volumeDetail.getValue()); } + + return Long.MIN_VALUE; } - @Override - public void deleteAsync(DataStore dataStore, DataObject dataObject, AsyncCompletionCallback<CommandResult> callback) { - String errMsg = null; + private boolean shouldTakeSnapshot(long snapshotId) { + SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(snapshotId, "takeSnapshot"); - if (dataObject.getType() == DataObjectType.VOLUME) { - try { - VolumeInfo volumeInfo = (VolumeInfo)dataObject; - long volumeId = volumeInfo.getId(); + if (snapshotDetails != null && snapshotDetails.getValue() != null) { + return new Boolean(snapshotDetails.getValue()); + } - long storagePoolId = dataStore.getId(); + return false; + } - SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao); + private SolidFireUtil.SolidFireVolume createClone(SolidFireUtil.SolidFireConnection sfConnection, long dataObjectId, VolumeInfo volumeInfo, long sfAccountId, + long storagePoolId, DataObjectType dataObjectType) { + String sfNewVolumeName = volumeInfo.getName(); - deleteSolidFireVolume(sfConnection, volumeInfo); + long sfVolumeId = Long.MIN_VALUE; + long sfSnapshotId = Long.MIN_VALUE; - _volumeDetailsDao.removeDetails(volumeId); + if (dataObjectType == DataObjectType.SNAPSHOT) { + SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(dataObjectId, SolidFireUtil.SNAPSHOT_ID); - StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolId); + if (snapshotDetails != null && snapshotDetails.getValue() != null) { + sfSnapshotId = Long.parseLong(snapshotDetails.getValue()); + } - long usedBytes = getUsedBytes(storagePool, volumeId); + snapshotDetails = snapshotDetailsDao.findDetail(dataObjectId, SolidFireUtil.VOLUME_ID); - storagePool.setUsedBytes(usedBytes < 0 ? 0 : usedBytes); + sfVolumeId = Long.parseLong(snapshotDetails.getValue()); + } else if (dataObjectType == DataObjectType.TEMPLATE) { + // get the cached template on this storage + VMTemplateStoragePoolVO templatePoolRef = tmpltPoolDao.findByPoolTemplate(storagePoolId, dataObjectId); - _storagePoolDao.update(storagePoolId, storagePool); + if (templatePoolRef != null) { + sfVolumeId = Long.parseLong(templatePoolRef.getLocalDownloadPath()); } - catch (Exception ex) { - s_logger.debug(SolidFireUtil.LOG_PREFIX + "Failed to delete SolidFire volume", ex); + } + + if (sfVolumeId <= 0) { + throw new CloudRuntimeException("Unable to find SolidFire volume for the following data-object ID: " + dataObjectId + + " and data-object type: " + dataObjectType); + } + + final long newSfVolumeId = SolidFireUtil.createSolidFireClone(sfConnection, sfVolumeId, sfSnapshotId, sfAccountId, sfNewVolumeName, + getVolumeAttributes(volumeInfo)); + + final Iops iops = getIops(volumeInfo.getMinIops(), volumeInfo.getMaxIops(), storagePoolId); + + SolidFireUtil.modifySolidFireVolume(sfConnection, newSfVolumeId, null, null, iops.getMinIops(), iops.getMaxIops(), iops.getBurstIops()); + + return SolidFireUtil.getSolidFireVolume(sfConnection, newSfVolumeId); + } + + private Map<String, String> getVolumeAttributes(VolumeInfo volumeInfo) { + Map<String, String> mapAttributes = new HashMap<>(); + + mapAttributes.put(SolidFireUtil.CloudStackVolumeId, String.valueOf(volumeInfo.getId())); + mapAttributes.put(SolidFireUtil.CloudStackVolumeSize, NumberFormat.getInstance().format(volumeInfo.getSize())); + + return mapAttributes; + } + + private Map<String, String> getSnapshotAttributes(SnapshotInfo snapshotInfo) { + Map<String, String> mapAttributes = new HashMap<>(); + + mapAttributes.put(SolidFireUtil.CloudStackSnapshotId, String.valueOf(snapshotInfo.getId())); + mapAttributes.put(SolidFireUtil.CloudStackSnapshotSize, NumberFormat.getInstance().format(snapshotInfo.getSize())); + + return mapAttributes; + } + + private Map<String, String> getTemplateAttributes(TemplateInfo templateInfo) { + Map<String, String> mapAttributes = new HashMap<>(); + + mapAttributes.put(SolidFireUtil.CloudStackTemplateId, String.valueOf(templateInfo.getId())); + mapAttributes.put(SolidFireUtil.CloudStackTemplateSize, NumberFormat.getInstance().format(templateInfo.getSize())); + + return mapAttributes; + } + + private SolidFireUtil.SolidFireVolume createCloneFromSnapshot(SolidFireUtil.SolidFireConnection sfConnection, long csSnapshotId, long sfAccountId) { + SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(csSnapshotId, SolidFireUtil.VOLUME_ID); + + long sfVolumeId = Long.parseLong(snapshotDetails.getValue()); + + snapshotDetails = snapshotDetailsDao.findDetail(csSnapshotId, SolidFireUtil.SNAPSHOT_ID); + + long sfSnapshotId = Long.parseLong(snapshotDetails.getValue()); + + SolidFireUtil.SolidFireSnapshot sfSnapshot = SolidFireUtil.getSolidFireSnapshot(sfConnection, sfVolumeId, sfSnapshotId); + + long newSfVolumeId = SolidFireUtil.createSolidFireClone(sfConnection, sfVolumeId, sfSnapshotId, sfAccountId, sfSnapshot.getName(), null); + + snapshotDetails = snapshotDetailsDao.findDetail(csSnapshotId, SolidFireUtil.STORAGE_POOL_ID); + + long storagePoolId = Long.parseLong(snapshotDetails.getValue()); + + final Iops iops = getIops(MIN_IOPS_FOR_TEMP_VOLUME, MAX_IOPS_FOR_TEMP_VOLUME, storagePoolId); + + SolidFireUtil.modifySolidFireVolume(sfConnection, newSfVolumeId, null, null, iops.getMinIops(), iops.getMaxIops(), iops.getBurstIops()); + + return SolidFireUtil.getSolidFireVolume(sfConnection, newSfVolumeId); + } + + private void updateVolumeDetails(long volumeId, long sfVolumeSize) { + volumeDetailsDao.removeDetail(volumeId, SolidFireUtil.VOLUME_SIZE); - errMsg = ex.getMessage(); + VolumeDetailVO volumeDetailVo = new VolumeDetailVO(volumeId, SolidFireUtil.VOLUME_SIZE, String.valueOf(sfVolumeSize), false); + + volumeDetailsDao.persist(volumeDetailVo); + } + + @Override + public void deleteAsync(DataStore dataStore, DataObject dataObject, AsyncCompletionCallback<CommandResult> callback) { + String errMsg = null; + + try { + if (dataObject.getType() == DataObjectType.VOLUME) { + deleteVolume((VolumeInfo)dataObject, dataStore.getId()); + } else if (dataObject.getType() == DataObjectType.SNAPSHOT) { + deleteSnapshot((SnapshotInfo)dataObject, dataStore.getId()); + } else if (dataObject.getType() == DataObjectType.TEMPLATE) { + deleteTemplate((TemplateInfo)dataObject, dataStore.getId()); + } else { + errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to deleteAsync"; } - } else if (dataObject.getType() == DataObjectType.SNAPSHOT) { - // should return null when no error message - errMsg = deleteSnapshot((SnapshotInfo)dataObject, dataStore.getId()); - } else { - errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to deleteAsync"; + } + catch (Exception ex) { + errMsg = ex.getMessage(); + + LOGGER.error(errMsg); } CommandResult result = new CommandResult(); @@ -577,49 +811,67 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { try { VolumeInfo volumeInfo = snapshotInfo.getBaseVolume(); - VolumeVO volumeVO = _volumeDao.findById(volumeInfo.getId()); + VolumeVO volumeVO = volumeDao.findById(volumeInfo.getId()); long sfVolumeId = Long.parseLong(volumeVO.getFolder()); long storagePoolId = volumeVO.getPoolId(); - SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao); + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao); SolidFireUtil.SolidFireVolume sfVolume = SolidFireUtil.getSolidFireVolume(sfConnection, sfVolumeId); - StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolId); + StoragePoolVO storagePool = storagePoolDao.findById(storagePoolId); long capacityBytes = storagePool.getCapacityBytes(); - // getUsedBytes(StoragePool) will not include the bytes of the proposed new volume because - // updateSnapshotDetails(long, long, long, long, String) has not yet been called for this new volume + // getUsedBytes(StoragePool) will not include the bytes of the proposed new volume or snapshot because + // updateSnapshotDetails has not yet been called for this new volume or snapshot long usedBytes = getUsedBytes(storagePool); long sfVolumeSize = sfVolume.getTotalSize(); usedBytes += sfVolumeSize; - // For creating a volume, we need to check to make sure a sufficient amount of space remains in the primary storage. - // For the purpose of "charging" these bytes against storage_pool.capacityBytes, we take the full size of the SolidFire volume. + // For creating a volume or a snapshot, we need to check to make sure a sufficient amount of space remains in the primary storage. + // For the purpose of "charging" these bytes against storage_pool.capacity_bytes, we take the full size of the SolidFire volume + // that is serving as the volume the snapshot is of (either a new SolidFire volume or a SolidFire snapshot). if (usedBytes > capacityBytes) { throw new CloudRuntimeException("Insufficient amount of space remains in this primary storage to take a snapshot"); } storagePool.setUsedBytes(usedBytes); - String volumeName = volumeInfo.getName() + "-" + snapshotInfo.getUuid(); + SnapshotObjectTO snapshotObjectTo = (SnapshotObjectTO)snapshotInfo.getTO(); - long sfNewVolumeId = SolidFireUtil.createSolidFireVolume(sfConnection, volumeName, sfVolume.getAccountId(), sfVolumeSize, - sfVolume.isEnable512e(), NumberFormat.getInstance().format(volumeInfo.getSize()), sfVolume.getMinIops(), 50000, 75000); + if (shouldTakeSnapshot(snapshotInfo.getId())) { + // We are supposed to take a SolidFire snapshot to serve as the back-end for our CloudStack volume snapshot. - // Now that we have successfully created a volume, update the space usage in the storage_pool table - // (even though storage_pool.used_bytes is likely no longer in use). - _storagePoolDao.update(storagePoolId, storagePool); + String sfNewSnapshotName = volumeInfo.getName() + "-" + snapshotInfo.getUuid(); - SolidFireUtil.SolidFireVolume sfNewVolume = SolidFireUtil.getSolidFireVolume(sfConnection, sfNewVolumeId); + long sfNewSnapshotId = SolidFireUtil.createSolidFireSnapshot(sfConnection, sfVolumeId, sfNewSnapshotName, getSnapshotAttributes(snapshotInfo)); - updateSnapshotDetails(snapshotInfo.getId(), sfNewVolumeId, storagePoolId, sfVolumeSize, sfNewVolume.getIqn()); + updateSnapshotDetails(snapshotInfo.getId(), sfVolumeId, sfNewSnapshotId, storagePoolId, sfVolumeSize); - SnapshotObjectTO snapshotObjectTo = (SnapshotObjectTO)snapshotInfo.getTO(); + snapshotObjectTo.setPath("SfSnapshotId=" + sfNewSnapshotId); + } + else { + // We are supposed to create a new SolidFire volume to serve as the back-end for our CloudStack volume snapshot. + + String sfNewVolumeName = volumeInfo.getName() + "-" + snapshotInfo.getUuid(); - snapshotObjectTo.setPath(String.valueOf(sfNewVolumeId)); + final Iops iops = getIops(MIN_IOPS_FOR_SNAPSHOT_VOLUME, MAX_IOPS_FOR_SNAPSHOT_VOLUME, storagePoolId); + + long sfNewVolumeId = SolidFireUtil.createSolidFireVolume(sfConnection, sfNewVolumeName, sfVolume.getAccountId(), sfVolumeSize, + sfVolume.isEnable512e(), getSnapshotAttributes(snapshotInfo), iops.getMinIops(), iops.getMaxIops(), iops.getBurstIops()); + + SolidFireUtil.SolidFireVolume sfNewVolume = SolidFireUtil.getSolidFireVolume(sfConnection, sfNewVolumeId); + + updateSnapshotDetails(snapshotInfo.getId(), sfNewVolumeId, storagePoolId, sfVolumeSize, sfNewVolume.getIqn()); + + snapshotObjectTo.setPath("SfVolumeId=" + sfNewVolumeId); + } + + // Now that we have successfully created a volume or a snapshot, update the space usage in the cloud.storage_pool table + // (even though cloud.storage_pool.used_bytes is likely no longer in use). + storagePoolDao.update(storagePoolId, storagePool); CreateObjectAnswer createObjectAnswer = new CreateObjectAnswer(snapshotObjectTo); @@ -628,7 +880,7 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { result.setResult(null); } catch (Exception ex) { - s_logger.debug(SolidFireUtil.LOG_PREFIX + "Failed to take CloudStack snapshot: " + snapshotInfo.getId(), ex); + LOGGER.debug(SolidFireUtil.LOG_PREFIX + "Failed to take CloudStack snapshot: " + snapshotInfo.getId(), ex); result = new CreateCmdResult(null, new CreateObjectAnswer(ex.toString())); @@ -638,69 +890,293 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { callback.complete(result); } + private void updateSnapshotDetails(long csSnapshotId, long sfVolumeId, long sfNewSnapshotId, long storagePoolId, long sfNewVolumeSize) { + SnapshotDetailsVO snapshotDetail = new SnapshotDetailsVO(csSnapshotId, + SolidFireUtil.VOLUME_ID, + String.valueOf(sfVolumeId), + false); + + snapshotDetailsDao.persist(snapshotDetail); + + snapshotDetail = new SnapshotDetailsVO(csSnapshotId, + SolidFireUtil.SNAPSHOT_ID, + String.valueOf(sfNewSnapshotId), + false); + + snapshotDetailsDao.persist(snapshotDetail); + + snapshotDetail = new SnapshotDetailsVO(csSnapshotId, + SolidFireUtil.STORAGE_POOL_ID, + String.valueOf(storagePoolId), + false); + + snapshotDetailsDao.persist(snapshotDetail); + + snapshotDetail = new SnapshotDetailsVO(csSnapshotId, + SolidFireUtil.VOLUME_SIZE, + String.valueOf(sfNewVolumeSize), + false); + + snapshotDetailsDao.persist(snapshotDetail); + } + private void updateSnapshotDetails(long csSnapshotId, long sfNewVolumeId, long storagePoolId, long sfNewVolumeSize, String sfNewVolumeIqn) { SnapshotDetailsVO snapshotDetail = new SnapshotDetailsVO(csSnapshotId, SolidFireUtil.VOLUME_ID, String.valueOf(sfNewVolumeId), false); - _snapshotDetailsDao.persist(snapshotDetail); + snapshotDetailsDao.persist(snapshotDetail); snapshotDetail = new SnapshotDetailsVO(csSnapshotId, SolidFireUtil.STORAGE_POOL_ID, String.valueOf(storagePoolId), false); - _snapshotDetailsDao.persist(snapshotDetail); + snapshotDetailsDao.persist(snapshotDetail); snapshotDetail = new SnapshotDetailsVO(csSnapshotId, SolidFireUtil.VOLUME_SIZE, String.valueOf(sfNewVolumeSize), false); - _snapshotDetailsDao.persist(snapshotDetail); + snapshotDetailsDao.persist(snapshotDetail); snapshotDetail = new SnapshotDetailsVO(csSnapshotId, DiskTO.IQN, sfNewVolumeIqn, false); - _snapshotDetailsDao.persist(snapshotDetail); + snapshotDetailsDao.persist(snapshotDetail); } - // return null for no error message - private String deleteSnapshot(SnapshotInfo snapshotInfo, long storagePoolId) { - String errMsg = null; + private String createVolume(VolumeInfo volumeInfo, long storagePoolId) { + verifySufficientBytesForStoragePool(volumeInfo, storagePoolId); + verifySufficientIopsForStoragePool(volumeInfo.getMinIops() != null ? volumeInfo.getMinIops() : getDefaultMinIops(storagePoolId), storagePoolId); + + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao); + + long sfAccountId = getCreateSolidFireAccountId(sfConnection, volumeInfo.getAccountId(), storagePoolId); + + long csSnapshotId = getCsIdForCloning(volumeInfo.getId(), "cloneOfSnapshot"); + long csTemplateId = getCsIdForCloning(volumeInfo.getId(), "cloneOfTemplate"); + + SolidFireUtil.SolidFireVolume sfVolume; + + if (csSnapshotId > 0) { + // We are supposed to create a clone of the underlying volume or snapshot that supports the CloudStack snapshot. + sfVolume = createClone(sfConnection, csSnapshotId, volumeInfo, sfAccountId, storagePoolId, DataObjectType.SNAPSHOT); + } else if (csTemplateId > 0) { + // Clone from template. + sfVolume = createClone(sfConnection, csTemplateId, volumeInfo, sfAccountId, storagePoolId, DataObjectType.TEMPLATE); + + long volumeSize = getDataObjectSizeIncludingHypervisorSnapshotReserve(volumeInfo, storagePoolDao.findById(storagePoolId)); + + if (volumeSize > sfVolume.getTotalSize()) { + // Expand the volume to include HSR. + SolidFireUtil.modifySolidFireVolume(sfConnection, sfVolume.getId(), volumeSize, getVolumeAttributes(volumeInfo), + sfVolume.getMinIops(), sfVolume.getMaxIops(), sfVolume.getBurstIops()); + + // Get the SolidFire volume from the SAN again because we just updated its size. + sfVolume = SolidFireUtil.getSolidFireVolume(sfConnection, sfVolume.getId()); + } + } + else { + sfVolume = createSolidFireVolume(sfConnection, volumeInfo, sfAccountId); + } + + String iqn = sfVolume.getIqn(); + + VolumeVO volume = volumeDao.findById(volumeInfo.getId()); + + volume.set_iScsiName(iqn); + volume.setFolder(String.valueOf(sfVolume.getId())); + volume.setPoolType(StoragePoolType.IscsiLUN); + volume.setPoolId(storagePoolId); + + volumeDao.update(volume.getId(), volume); + + updateVolumeDetails(volume.getId(), sfVolume.getTotalSize()); + + StoragePoolVO storagePool = storagePoolDao.findById(storagePoolId); + + long capacityBytes = storagePool.getCapacityBytes(); + // getUsedBytes(StoragePool) will include the bytes of the newly created volume because + // updateVolumeDetails(long, long) has already been called for this volume + long usedBytes = getUsedBytes(storagePool); + + storagePool.setUsedBytes(usedBytes > capacityBytes ? capacityBytes : usedBytes); + + storagePoolDao.update(storagePoolId, storagePool); + + return iqn; + } - long snapshotId = snapshotInfo.getId(); + private void createTempVolume(SnapshotInfo snapshotInfo, long storagePoolId) { + long csSnapshotId = snapshotInfo.getId(); + SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(csSnapshotId, SolidFireUtil.SNAPSHOT_ID); + + if (snapshotDetails == null || snapshotDetails.getValue() == null) { + throw new CloudRuntimeException("'createTempVolume(SnapshotInfo, long)' should not be invoked unless " + SolidFireUtil.SNAPSHOT_ID + " exists."); + } + + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao); + + snapshotDetails = snapshotDetailsDao.findDetail(csSnapshotId, "tempVolume"); + + if (snapshotDetails != null && snapshotDetails.getValue() != null && snapshotDetails.getValue().equalsIgnoreCase("create")) { + long sfAccountId = getCreateSolidFireAccountId(sfConnection, snapshotInfo.getAccountId(), storagePoolId); + + SolidFireUtil.SolidFireVolume sfVolume = createCloneFromSnapshot(sfConnection, csSnapshotId, sfAccountId); + + addTempVolumeId(csSnapshotId, String.valueOf(sfVolume.getId())); + + handleSnapshotDetails(csSnapshotId, DiskTO.IQN, sfVolume.getIqn()); + } + else if (snapshotDetails != null && snapshotDetails.getValue() != null && snapshotDetails.getValue().equalsIgnoreCase("delete")) { + snapshotDetails = snapshotDetailsDao.findDetail(csSnapshotId, SolidFireUtil.VOLUME_ID); + + SolidFireUtil.deleteSolidFireVolume(sfConnection, Long.parseLong(snapshotDetails.getValue())); + + removeTempVolumeId(csSnapshotId); + + snapshotDetails = snapshotDetailsDao.findDetail(csSnapshotId, DiskTO.IQN); + + snapshotDetailsDao.remove(snapshotDetails.getId()); + } + else { + throw new CloudRuntimeException("Invalid state in 'createTempVolume(SnapshotInfo, long)'"); + } + } + + private String createTemplateVolume(TemplateInfo templateInfo, long storagePoolId) { + verifySufficientBytesForStoragePool(templateInfo, storagePoolId); + + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao); + + long sfAccountId = getCreateSolidFireAccountId(sfConnection, templateInfo.getAccountId(), storagePoolId); + + SolidFireUtil.SolidFireVolume sfVolume = createSolidFireVolume(sfConnection, templateInfo, sfAccountId); + + String iqn = sfVolume.getIqn(); + + VMTemplateStoragePoolVO templatePoolRef = tmpltPoolDao.findByPoolTemplate(storagePoolId, templateInfo.getId()); + + templatePoolRef.setInstallPath(iqn); + templatePoolRef.setLocalDownloadPath(Long.toString(sfVolume.getId())); + templatePoolRef.setTemplateSize(sfVolume.getTotalSize()); + + tmpltPoolDao.update(templatePoolRef.getId(), templatePoolRef); + + StoragePoolVO storagePool = storagePoolDao.findById(storagePoolId); + + long capacityBytes = storagePool.getCapacityBytes(); + // getUsedBytes(StoragePool) will include the bytes of the newly created template volume because + // _tmpltPoolDao.update(Long, VMTemplateStoragePoolVO) has already been invoked + long usedBytes = getUsedBytes(storagePool); + + storagePool.setUsedBytes(usedBytes > capacityBytes ? capacityBytes : usedBytes); + + storagePoolDao.update(storagePoolId, storagePool); + + return iqn; + } + + private void deleteVolume(VolumeInfo volumeInfo, long storagePoolId) { try { - SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao); + long volumeId = volumeInfo.getId(); + + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao); - SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(snapshotId, SolidFireUtil.VOLUME_ID); + deleteSolidFireVolume(sfConnection, volumeInfo); - long volumeId = Long.parseLong(snapshotDetails.getValue()); + volumeDetailsDao.removeDetails(volumeId); - SolidFireUtil.deleteSolidFireVolume(sfConnection, volumeId); + StoragePoolVO storagePool = storagePoolDao.findById(storagePoolId); - _snapshotDetailsDao.removeDetails(snapshotId); + long usedBytes = getUsedBytes(storagePool, volumeId); - StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolId); + storagePool.setUsedBytes(usedBytes < 0 ? 0 : usedBytes); + + storagePoolDao.update(storagePoolId, storagePool); + } + catch (Exception ex) { + LOGGER.debug(SolidFireUtil.LOG_PREFIX + "Failed to delete SolidFire volume. CloudStack volume ID: " + volumeInfo.getId(), ex); + + throw ex; + } + } + + private void deleteSnapshot(SnapshotInfo snapshotInfo, long storagePoolId) { + long csSnapshotId = snapshotInfo.getId(); + + try { + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao); + + SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(csSnapshotId, SolidFireUtil.SNAPSHOT_ID); + + if (snapshotDetails != null && snapshotDetails.getValue() != null) { + // A SolidFire snapshot is being used to support the CloudStack volume snapshot. + + long sfSnapshotId = Long.parseLong(snapshotDetails.getValue()); + + deleteSolidFireSnapshot(sfConnection, csSnapshotId, sfSnapshotId); + } + else { + // A SolidFire volume is being used to support the CloudStack volume snapshot. + + snapshotDetails = snapshotDetailsDao.findDetail(csSnapshotId, SolidFireUtil.VOLUME_ID); + + long sfVolumeId = Long.parseLong(snapshotDetails.getValue()); + + SolidFireUtil.deleteSolidFireVolume(sfConnection, sfVolumeId); + } + + snapshotDetailsDao.removeDetails(csSnapshotId); + + StoragePoolVO storagePool = storagePoolDao.findById(storagePoolId); // getUsedBytes(StoragePool) will not include the snapshot to delete because it has already been deleted by this point long usedBytes = getUsedBytes(storagePool); storagePool.setUsedBytes(usedBytes < 0 ? 0 : usedBytes); - _storagePoolDao.update(storagePoolId, storagePool); + storagePoolDao.update(storagePoolId, storagePool); } catch (Exception ex) { - s_logger.debug(SolidFireUtil.LOG_PREFIX + "Failed to delete SolidFire volume. CloudStack snapshot ID: " + snapshotId, ex); + LOGGER.debug(SolidFireUtil.LOG_PREFIX + "Issue in 'deleteSnapshot(SnapshotInfo, long)'. CloudStack snapshot ID: " + csSnapshotId, ex); - errMsg = ex.getMessage(); + throw ex; + } + } + + private void deleteTemplate(TemplateInfo template, long storagePoolId) { + try { + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao); + + long sfTemplateVolumeId = getVolumeIdFrom_iScsiPath(template.getInstallPath()); + + SolidFireUtil.deleteSolidFireVolume(sfConnection, sfTemplateVolumeId); + + VMTemplateStoragePoolVO templatePoolRef = tmpltPoolDao.findByPoolTemplate(storagePoolId, template.getId()); + + tmpltPoolDao.remove(templatePoolRef.getId()); + + StoragePoolVO storagePool = storagePoolDao.findById(storagePoolId); + + // getUsedBytes(StoragePool) will not include the template to delete because the "template_spool_ref" table has already been updated by this point + long usedBytes = getUsedBytes(storagePool); + + storagePool.setUsedBytes(usedBytes < 0 ? 0 : usedBytes); + + storagePoolDao.update(storagePoolId, storagePool); } + catch (Exception ex) { + LOGGER.debug(SolidFireUtil.LOG_PREFIX + "Failed to delete SolidFire template volume. CloudStack template ID: " + template.getId(), ex); - return errMsg; + throw ex; + } } @Override @@ -720,26 +1196,49 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { long sfVolumeId = Long.parseLong(volumeInfo.getFolder()); ResizeVolumePayload payload = (ResizeVolumePayload)volumeInfo.getpayload(); - SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao); + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, storagePoolDetailsDao); SolidFireUtil.SolidFireVolume sfVolume = SolidFireUtil.getSolidFireVolume(sfConnection, sfVolumeId); verifySufficientIopsForStoragePool(storagePoolId, volumeInfo.getId(), payload.newMinIops); + verifySufficientBytesForStoragePool(storagePoolId, volumeInfo.getId(), payload.newSize, payload.newHypervisorSnapshotReserve); + + long sfNewVolumeSize = sfVolume.getTotalSize(); + + Integer hsr = volumeInfo.getHypervisorSnapshotReserve(); + + if (payload.newSize != null || payload.newHypervisorSnapshotReserve != null) { + if (payload.newHypervisorSnapshotReserve != null) { + if (hsr != null) { + if (payload.newHypervisorSnapshotReserve > hsr) { + hsr = payload.newHypervisorSnapshotReserve; + } + } + else { + hsr = payload.newHypervisorSnapshotReserve; + } + } + + sfNewVolumeSize = getVolumeSizeIncludingHypervisorSnapshotReserve(payload.newSize, hsr); + } + + Map<String, String> mapAttributes = new HashMap<>(); + + mapAttributes.put(SolidFireUtil.CloudStackVolumeId, String.valueOf(volumeInfo.getId())); + mapAttributes.put(SolidFireUtil.CloudStackVolumeSize, NumberFormat.getInstance().format(payload.newSize)); - SolidFireUtil.modifySolidFireVolume(sfConnection, sfVolumeId, sfVolume.getTotalSize(), NumberFormat.getInstance().format(payload.newSize), + SolidFireUtil.modifySolidFireVolume(sfConnection, sfVolumeId, sfNewVolumeSize, mapAttributes, payload.newMinIops, payload.newMaxIops, getDefaultBurstIops(storagePoolId, payload.newMaxIops)); - VolumeVO volume = _volumeDao.findById(volumeInfo.getId()); + VolumeVO volume = volumeDao.findById(volumeInfo.getId()); volume.setMinIops(payload.newMinIops); volume.setMaxIops(payload.newMaxIops); + volume.setHypervisorSnapshotReserve(hsr); - _volumeDao.update(volume.getId(), volume); + volumeDao.update(volume.getId(), volume); // SolidFireUtil.VOLUME_SIZE was introduced in 4.5. - // To be backward compatible with releases prior to 4.5, call updateVolumeDetails here. - // That way if SolidFireUtil.VOLUME_SIZE wasn't put in the volume_details table when the - // volume was initially created, it can be placed in volume_details if the volume is resized. - updateVolumeDetails(volume.getId(), sfVolume.getTotalSize()); + updateVolumeDetails(volume.getId(), sfNewVolumeSize); } else { errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to resize"; } @@ -751,21 +1250,140 @@ public class SolidFirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { callback.complete(result); } + private void verifySufficientBytesForStoragePool(long requestedBytes, long storagePoolId) { + StoragePoolVO storagePool = storagePoolDao.findById(storagePoolId); + + long capacityBytes = storagePool.getCapacityBytes(); + long usedBytes = getUsedBytes(storagePool); + + usedBytes += requestedBytes; + + if (usedBytes > capacityBytes) { + throw new CloudRuntimeException("Insufficient amount of space remains in this primary storage"); + } + } + + private void verifySufficientBytesForStoragePool(DataObject dataObject, long storagePoolId) { + StoragePoolVO storagePool = storagePoolDao.findById(storagePoolId); + + long requestedBytes = getDataObjectSizeIncludingHypervisorSnapshotReserve(dataObject, storagePool); + + verifySufficientBytesForStoragePool(requestedBytes, storagePoolId); + } + + private void verifySufficientBytesForStoragePool(long storagePoolId, long volumeId, long newSize, Integer newHypervisorSnapshotReserve) { + DataStore primaryDataStore = dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary); + VolumeInfo volumeInfo = volumeFactory.getVolume(volumeId, primaryDataStore); + StoragePoolVO storagePool = storagePoolDao.findById(storagePoolId); + long currentSizeWithHsr = getDataObjectSizeIncludingHypervisorSnapshotReserve(volumeInfo, storagePool); + + newHypervisorSnapshotReserve = newHypervisorSnapshotReserve == null ? LOWEST_HYPERVISOR_SNAPSHOT_RESERVE : + Math.max(newHypervisorSnapshotReserve, LOWEST_HYPERVISOR_SNAPSHOT_RESERVE); + + long newSizeWithHsr = (long)(newSize + newSize * (newHypervisorSnapshotReserve / 100f)); + + if (newSizeWithHsr < currentSizeWithHsr) { + throw new CloudRuntimeException("Storage pool " + storagePoolId + " does not support shrinking a volume."); + } + + long availableBytes = storagePool.getCapacityBytes() - getUsedBytes(storagePool); + + if ((newSizeWithHsr - currentSizeWithHsr) > availableBytes) { + throw new CloudRuntimeException("Storage pool " + storagePoolId + " does not have enough space to expand the volume."); + } + } + + private void verifySufficientIopsForStoragePool(long requestedIops, long storagePoolId) { + StoragePoolVO storagePool = storagePoolDao.findById(storagePoolId); + + long usedIops = getUsedIops(storagePool); + long capacityIops = storagePool.getCapacityIops(); + + if (usedIops + requestedIops > capacityIops) { + throw new CloudRuntimeException("Insufficient number of IOPS available in this storage pool"); + } + } + private void verifySufficientIopsForStoragePool(long storagePoolId, long volumeId, long newMinIops) { - StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolId); - VolumeVO volume = _volumeDao.findById(volumeId); + VolumeVO volume = volumeDao.findById(volumeId); long currentMinIops = volume.getMinIops(); long diffInMinIops = newMinIops - currentMinIops; // if the desire is for more IOPS if (diffInMinIops > 0) { - long usedIops = getUsedIops(storagePool); - long capacityIops = storagePool.getCapacityIops(); + verifySufficientIopsForStoragePool(diffInMinIops, storagePoolId); + } + } + + private void deleteSolidFireVolume(SolidFireUtil.SolidFireConnection sfConnection, long csVolumeId, long sfVolumeId) { + List<SnapshotVO> lstSnapshots = getNonDestroyedSnapshots(csVolumeId); + + boolean deleteVolume = true; + + for (SnapshotVO snapshot : lstSnapshots) { + SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(snapshot.getId(), SolidFireUtil.SNAPSHOT_ID); - if (usedIops + diffInMinIops > capacityIops) { - throw new CloudRuntimeException("Insufficient number of IOPS available in this storage pool"); + if (snapshotDetails != null && snapshotDetails.getValue() != null) { + deleteVolume = false; + + break; } } + + if (deleteVolume) { + SolidFireUtil.deleteSolidFireVolume(sfConnection, sfVolumeId); + } + } + + private void deleteSolidFireSnapshot(SolidFireUtil.SolidFireConnection sfConnection, long csSnapshotId, long sfSnapshotId) { + SolidFireUtil.deleteSolidFireSnapshot(sfConnection, sfSnapshotId); + + SnapshotVO snapshot = snapshotDao.findById(csSnapshotId); + VolumeVO volume = volumeDao.findById(snapshot.getVolumeId()); + + if (volume == null) { // if the CloudStack volume has been deleted + List<SnapshotVO> lstSnapshots = getNonDestroyedSnapshots(snapshot.getVolumeId()); + + List<SnapshotVO> lstSnapshots2 = new ArrayList<>(); + + for (SnapshotVO snapshotVo : lstSnapshots) { + // The CloudStack volume snapshot has not yet been set to the DESTROYED state, so check to make + // sure snapshotVo.getId() != csSnapshotId when determining if any volume snapshots remain for the given CloudStack volume. + if (snapshotVo.getId() != csSnapshotId) { + SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(snapshotVo.getId(), SolidFireUtil.SNAPSHOT_ID); + + // We are only interested here in volume snapshots that make use of SolidFire snapshots (as opposed to ones + // that make use of SolidFire volumes). + if (snapshotDetails != null && snapshotDetails.getValue() != null) { + lstSnapshots2.add(snapshotVo); + } + } + } + + if (lstSnapshots2.isEmpty()) { + volume = volumeDao.findByIdIncludingRemoved(snapshot.getVolumeId()); + + SolidFireUtil.deleteSolidFireVolume(sfConnection, Long.parseLong(volume.getFolder())); + } + } + } + + private List<SnapshotVO> getNonDestroyedSnapshots(long csVolumeId) { + List<SnapshotVO> lstSnapshots = snapshotDao.listByVolumeId(csVolumeId); + + if (lstSnapshots == null) { + lstSnapshots = new ArrayList<>(); + } + + List<SnapshotVO> lstSnapshots2 = new ArrayList<>(); + + for (SnapshotVO snapshot : lstSnapshots) { + if (!State.Destroyed.equals(snapshot.getState())) { + lstSnapshots2.add(snapshot); + } + } + + return lstSnapshots2; } }
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java ---------------------------------------------------------------------- diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java index f89c97a..c47411e 100644 --- a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java +++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/lifecycle/SolidFirePrimaryDataStoreLifeCycle.java @@ -33,6 +33,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreLifeCy import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreParameters; import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.util.SolidFireUtil; import org.apache.cloudstack.storage.volume.datastore.PrimaryDataStoreHelper; @@ -49,23 +50,27 @@ import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.SnapshotDetailsDao; import com.cloud.storage.dao.SnapshotDetailsVO; +import com.cloud.storage.dao.VMTemplatePoolDao; import com.cloud.storage.SnapshotVO; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePoolAutomation; +import com.cloud.storage.VMTemplateStoragePoolVO; import com.cloud.utils.exception.CloudRuntimeException; public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeCycle { private static final Logger s_logger = Logger.getLogger(SolidFirePrimaryDataStoreLifeCycle.class); @Inject private CapacityManager _capacityMgr; - @Inject private DataCenterDao zoneDao; - @Inject private PrimaryDataStoreDao storagePoolDao; - @Inject private PrimaryDataStoreHelper dataStoreHelper; + @Inject private DataCenterDao _zoneDao; + @Inject private PrimaryDataStoreDao _storagePoolDao; + @Inject private PrimaryDataStoreHelper _dataStoreHelper; @Inject private ResourceManager _resourceMgr; @Inject private SnapshotDao _snapshotDao; @Inject private SnapshotDetailsDao _snapshotDetailsDao; @Inject private StorageManager _storageMgr; - @Inject private StoragePoolAutomation storagePoolAutomation; + @Inject private StoragePoolAutomation _storagePoolAutomation; + @Inject private StoragePoolDetailsDao _storagePoolDetailsDao; + @Inject private VMTemplatePoolDao _tmpltPoolDao; // invoked to add primary storage that is based on the SolidFire plug-in @Override @@ -83,7 +88,7 @@ public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeC String storageVip = SolidFireUtil.getStorageVip(url); int storagePort = SolidFireUtil.getStoragePort(url); - DataCenterVO zone = zoneDao.findById(zoneId); + DataCenterVO zone = _zoneDao.findById(zoneId); String uuid = SolidFireUtil.PROVIDER_NAME + "_" + zone.getUuid() + "_" + storageVip; @@ -179,7 +184,7 @@ public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeC details.put(SolidFireUtil.CLUSTER_DEFAULT_BURST_IOPS_PERCENT_OF_MAX_IOPS, String.valueOf(fClusterDefaultBurstIopsPercentOfMaxIops)); // this adds a row in the cloud.storage_pool table for this SolidFire cluster - return dataStoreHelper.createPrimaryDataStore(parameters); + return _dataStoreHelper.createPrimaryDataStore(parameters); } // do not implement this method for SolidFire's plug-in @@ -196,7 +201,7 @@ public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeC @Override public boolean attachZone(DataStore dataStore, ZoneScope scope, HypervisorType hypervisorType) { - dataStoreHelper.attachZone(dataStore); + _dataStoreHelper.attachZone(dataStore); List<HostVO> xenServerHosts = _resourceMgr.listAllUpAndEnabledHostsInOneZoneByHypervisor(HypervisorType.XenServer, scope.getScopeId()); List<HostVO> vmWareServerHosts = _resourceMgr.listAllUpAndEnabledHostsInOneZoneByHypervisor(HypervisorType.VMware, scope.getScopeId()); @@ -220,23 +225,25 @@ public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeC @Override public boolean maintain(DataStore dataStore) { - storagePoolAutomation.maintain(dataStore); - dataStoreHelper.maintain(dataStore); + _storagePoolAutomation.maintain(dataStore); + _dataStoreHelper.maintain(dataStore); return true; } @Override public boolean cancelMaintain(DataStore store) { - dataStoreHelper.cancelMaintain(store); - storagePoolAutomation.cancelMaintain(store); + _dataStoreHelper.cancelMaintain(store); + _storagePoolAutomation.cancelMaintain(store); return true; } // invoked to delete primary storage that is based on the SolidFire plug-in @Override - public boolean deleteDataStore(DataStore store) { + public boolean deleteDataStore(DataStore dataStore) { + long storagePoolId = dataStore.getId(); + List<SnapshotVO> lstSnapshots = _snapshotDao.listAll(); if (lstSnapshots != null) { @@ -244,13 +251,39 @@ public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeC SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(snapshot.getId(), SolidFireUtil.STORAGE_POOL_ID); // if this snapshot belongs to the storagePool that was passed in - if (snapshotDetails != null && snapshotDetails.getValue() != null && Long.parseLong(snapshotDetails.getValue()) == store.getId()) { + if (snapshotDetails != null && snapshotDetails.getValue() != null && Long.parseLong(snapshotDetails.getValue()) == storagePoolId) { throw new CloudRuntimeException("This primary storage cannot be deleted because it currently contains one or more snapshots."); } } } - return dataStoreHelper.deletePrimaryDataStore(store); + List<VMTemplateStoragePoolVO> lstTemplatePoolRefs = _tmpltPoolDao.listByPoolId(storagePoolId); + + if (lstTemplatePoolRefs != null) { + for (VMTemplateStoragePoolVO templatePoolRef : lstTemplatePoolRefs) { + try { + SolidFireUtil.SolidFireConnection sfConnection = SolidFireUtil.getSolidFireConnection(storagePoolId, _storagePoolDetailsDao); + long sfTemplateVolumeId = Long.parseLong(templatePoolRef.getLocalDownloadPath()); + + SolidFireUtil.deleteSolidFireVolume(sfConnection, sfTemplateVolumeId); + } + catch (Exception ex) { + s_logger.error(ex.getMessage() != null ? ex.getMessage() : "Error deleting SolidFire template volume"); + } + + _tmpltPoolDao.remove(templatePoolRef.getId()); + } + } + + StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolId); + + storagePool.setUsedBytes(0); + + _storagePoolDao.update(storagePoolId, storagePool); + + _storagePoolDetailsDao.removeDetails(storagePoolId); + + return _dataStoreHelper.deletePrimaryDataStore(dataStore); } /* (non-Javadoc) @@ -263,7 +296,7 @@ public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeC @Override public void updateStoragePool(StoragePool storagePool, Map<String, String> details) { - StoragePoolVO storagePoolVo = storagePoolDao.findById(storagePool.getId()); + StoragePoolVO storagePoolVo = _storagePoolDao.findById(storagePool.getId()); String strCapacityBytes = details.get(PrimaryDataStoreLifeCycle.CAPACITY_BYTES); Long capacityBytes = strCapacityBytes != null ? Long.parseLong(strCapacityBytes) : null; @@ -290,11 +323,11 @@ public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeC @Override public void enableStoragePool(DataStore dataStore) { - dataStoreHelper.enable(dataStore); + _dataStoreHelper.enable(dataStore); } @Override public void disableStoragePool(DataStore dataStore) { - dataStoreHelper.disable(dataStore); + _dataStoreHelper.disable(dataStore); } }
