diff --git a/api/src/main/java/com/cloud/storage/VolumeApiService.java b/api/src/main/java/com/cloud/storage/VolumeApiService.java index d5a31d3365d..cf13cd671a9 100644 --- a/api/src/main/java/com/cloud/storage/VolumeApiService.java +++ b/api/src/main/java/com/cloud/storage/VolumeApiService.java @@ -18,6 +18,8 @@ */ package com.cloud.storage; +import java.net.MalformedURLException; + import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd; import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd; import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd; @@ -26,13 +28,10 @@ import org.apache.cloudstack.api.command.user.volume.GetUploadParamsForVolumeCmd import org.apache.cloudstack.api.command.user.volume.MigrateVolumeCmd; import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd; import org.apache.cloudstack.api.command.user.volume.UploadVolumeCmd; - -import com.cloud.exception.ConcurrentOperationException; -import com.cloud.exception.ResourceAllocationException; -import com.cloud.user.Account; import org.apache.cloudstack.api.response.GetUploadParamsResponse; -import java.net.MalformedURLException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.user.Account; public interface VolumeApiService { /** @@ -72,17 +71,18 @@ public interface VolumeApiService { * * @return Volume object */ - Volume uploadVolume(UploadVolumeCmd cmd) throws ResourceAllocationException; + Volume uploadVolume(UploadVolumeCmd cmd) throws ResourceAllocationException; GetUploadParamsResponse uploadVolume(GetUploadParamsForVolumeCmd cmd) throws ResourceAllocationException, MalformedURLException; - boolean deleteVolume(long volumeId, Account caller) throws ConcurrentOperationException; + boolean deleteVolume(long volumeId, Account caller); Volume attachVolumeToVM(AttachVolumeCmd command); Volume detachVolumeFromVM(DetachVolumeCmd cmd); - Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account, boolean quiescevm, Snapshot.LocationType locationType, boolean asyncBackup) throws ResourceAllocationException; + Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account, boolean quiescevm, Snapshot.LocationType locationType, boolean asyncBackup) + throws ResourceAllocationException; Snapshot allocSnapshot(Long volumeId, Long policyId, String snapshotName, Snapshot.LocationType locationType) throws ResourceAllocationException; @@ -92,10 +92,8 @@ public interface VolumeApiService { * Extracts the volume to a particular location. * * @param cmd - * the command specifying url (where the volume needs to be extracted to), zoneId (zone where the volume - * exists), + * the command specifying url (where the volume needs to be extracted to), zoneId (zone where the volume exists), * id (the id of the volume) - * */ String extractVolume(ExtractVolumeCmd cmd); diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java index f93f4efac83..e27b7516868 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java @@ -25,6 +25,7 @@ import com.cloud.storage.Volume; import com.cloud.vm.VirtualMachine; public interface VolumeInfo extends DataObject, Volume { + boolean isAttachedVM(); void addPayload(Object data); @@ -36,6 +37,7 @@ public interface VolumeInfo extends DataObject, Volume { Long getLastPoolId(); String getAttachedVmName(); + VirtualMachine getAttachedVM(); void processEventOnly(ObjectInDataStoreStateMachine.Event event); diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java index 04aefbec31f..c56fef70b75 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java @@ -20,16 +20,15 @@ package org.apache.cloudstack.engine.subsystem.api.storage; import java.util.Map; -import com.cloud.utils.Pair; import org.apache.cloudstack.engine.cloud.entity.api.VolumeEntity; import org.apache.cloudstack.framework.async.AsyncCallFuture; import org.apache.cloudstack.storage.command.CommandResult; import com.cloud.agent.api.to.VirtualMachineTO; -import com.cloud.exception.ConcurrentOperationException; import com.cloud.host.Host; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.offering.DiskOffering; +import com.cloud.utils.Pair; public interface VolumeService { class VolumeApiResult extends CommandResult { @@ -54,38 +53,24 @@ public interface VolumeService { /** * Creates the volume based on the given criteria * - * @param cmd - * * @return the volume object */ AsyncCallFuture createVolumeAsync(VolumeInfo volume, DataStore store); /** * Delete volume - * - * @param volumeId - * @return - * @throws ConcurrentOperationException */ AsyncCallFuture expungeVolumeAsync(VolumeInfo volume); - /** - * - */ boolean cloneVolume(long volumeId, long baseVolId); - /** - * - */ AsyncCallFuture createVolumeFromSnapshot(VolumeInfo volume, DataStore store, SnapshotInfo snapshot); VolumeEntity getVolumeEntity(long volumeId); - AsyncCallFuture createManagedStorageVolumeFromTemplateAsync(VolumeInfo volumeInfo, long destDataStoreId, - TemplateInfo srcTemplateInfo, long destHostId); + AsyncCallFuture createManagedStorageVolumeFromTemplateAsync(VolumeInfo volumeInfo, long destDataStoreId, TemplateInfo srcTemplateInfo, long destHostId); - AsyncCallFuture createVolumeFromTemplateAsync(VolumeInfo volume, long dataStoreId, - TemplateInfo template); + AsyncCallFuture createVolumeFromTemplateAsync(VolumeInfo volume, long dataStoreId, TemplateInfo template); AsyncCallFuture copyVolume(VolumeInfo srcVolume, DataStore destStore); @@ -93,11 +78,11 @@ public interface VolumeService { AsyncCallFuture migrateVolumes(Map volumeMap, VirtualMachineTO vmTo, Host srcHost, Host destHost); - boolean destroyVolume(long volumeId) throws ConcurrentOperationException; + void destroyVolume(long volumeId); AsyncCallFuture registerVolume(VolumeInfo volume, DataStore store); - public Pair registerVolumeForPostUpload(VolumeInfo volume, DataStore store); + public Pair registerVolumeForPostUpload(VolumeInfo volume, DataStore store); AsyncCallFuture resize(VolumeInfo volume); @@ -108,5 +93,4 @@ public interface VolumeService { SnapshotInfo takeSnapshot(VolumeInfo volume); VolumeInfo updateHypervisorSnapshotReserveForVolume(DiskOffering diskOffering, long volumeId, HypervisorType hyperType); - -} +} \ No newline at end of file diff --git a/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java b/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java index 36313058e92..d2979f7415d 100644 --- a/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java +++ b/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java @@ -85,7 +85,6 @@ import com.cloud.configuration.Resource.ResourceType; import com.cloud.dc.dao.ClusterDao; import com.cloud.event.EventTypes; import com.cloud.event.UsageEventUtils; -import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.ResourceAllocationException; import com.cloud.host.Host; import com.cloud.host.HostVO; @@ -110,6 +109,7 @@ import com.cloud.storage.Volume.State; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.VMTemplatePoolDao; import com.cloud.storage.dao.VolumeDao; +import com.cloud.storage.dao.VolumeDetailsDao; import com.cloud.storage.snapshot.SnapshotManager; import com.cloud.storage.template.TemplateProp; import com.cloud.user.AccountManager; @@ -119,8 +119,6 @@ import com.cloud.utils.Pair; import com.cloud.utils.db.DB; import com.cloud.utils.db.GlobalLock; import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.storage.dao.VolumeDetailsDao; - @Component public class VolumeServiceImpl implements VolumeService { @@ -420,8 +418,7 @@ public class VolumeServiceImpl implements VolumeService { if (!supportsStorageSystemSnapshots) { _snapshotStoreDao.remove(snapStoreVo.getId()); } - } - else { + } else { _snapshotStoreDao.remove(snapStoreVo.getId()); } } @@ -453,8 +450,8 @@ public class VolumeServiceImpl implements VolumeService { private final TemplateInfo _templateInfo; private final AsyncCallFuture _future; - public ManagedCreateBaseImageContext(AsyncCompletionCallback callback, VolumeInfo volumeInfo, - PrimaryDataStore primaryDatastore, TemplateInfo templateInfo, AsyncCallFuture future) { + public ManagedCreateBaseImageContext(AsyncCompletionCallback callback, VolumeInfo volumeInfo, PrimaryDataStore primaryDatastore, TemplateInfo templateInfo, + AsyncCallFuture future) { super(callback); _volumeInfo = volumeInfo; @@ -488,8 +485,8 @@ public class VolumeServiceImpl implements VolumeService { final DataObject destObj; long templatePoolId; - public CreateBaseImageContext(AsyncCompletionCallback callback, VolumeInfo volume, PrimaryDataStore datastore, TemplateInfo srcTemplate, - AsyncCallFuture future, DataObject destObj, long templatePoolId) { + public CreateBaseImageContext(AsyncCompletionCallback callback, VolumeInfo volume, PrimaryDataStore datastore, TemplateInfo srcTemplate, AsyncCallFuture future, + DataObject destObj, long templatePoolId) { super(callback); this.volume = volume; this.dataStore = datastore; @@ -549,13 +546,11 @@ public class VolumeServiceImpl implements VolumeService { throw new CloudRuntimeException("Failed to find template " + template.getUniqueName() + " in storage pool " + dataStore.getId()); } else { if (s_logger.isDebugEnabled()) { - s_logger.debug("Found template " + template.getUniqueName() + " in storage pool " + dataStore.getId() + " with VMTemplateStoragePool id: " + - templatePoolRef.getId()); + s_logger.debug("Found template " + template.getUniqueName() + " in storage pool " + dataStore.getId() + " with VMTemplateStoragePool id: " + templatePoolRef.getId()); } } long templatePoolRefId = templatePoolRef.getId(); - CreateBaseImageContext context = - new CreateBaseImageContext(null, volume, dataStore, template, future, templateOnPrimaryStoreObj, templatePoolRefId); + CreateBaseImageContext context = new CreateBaseImageContext(null, volume, dataStore, template, future, templateOnPrimaryStoreObj, templatePoolRefId); AsyncCallbackDispatcher caller = AsyncCallbackDispatcher.create(this); caller.setCallback(caller.getTarget().copyBaseImageCallback(null, null)).setContext(context); @@ -571,8 +566,8 @@ public class VolumeServiceImpl implements VolumeService { } templatePoolRef = _tmpltPoolDao.findByPoolTemplate(dataStore.getId(), template.getId()); if (templatePoolRef != null && templatePoolRef.getState() == ObjectInDataStoreStateMachine.State.Ready) { - s_logger.info("Unable to acquire lock on VMTemplateStoragePool " + templatePoolRefId + ", But Template " + template.getUniqueName() + - " is already copied to primary storage, skip copying"); + s_logger.info( + "Unable to acquire lock on VMTemplateStoragePool " + templatePoolRefId + ", But Template " + template.getUniqueName() + " is already copied to primary storage, skip copying"); createVolumeFromBaseImageAsync(volume, templateOnPrimaryStoreObj, dataStore, future); return; } @@ -606,8 +601,7 @@ public class VolumeServiceImpl implements VolumeService { return; } - protected Void managedCopyBaseImageCallback(AsyncCallbackDispatcher callback, - ManagedCreateBaseImageContext context) { + protected Void managedCopyBaseImageCallback(AsyncCallbackDispatcher callback, ManagedCreateBaseImageContext context) { CopyCommandResult result = callback.getResult(); VolumeInfo volumeInfo = context.getVolumeInfo(); VolumeApiResult res = new VolumeApiResult(volumeInfo); @@ -626,8 +620,7 @@ public class VolumeServiceImpl implements VolumeService { } volDao.update(volume.getId(), volume); - } - else { + } else { volumeInfo.processEvent(Event.DestroyRequested); res.setResult(result.getResult()); @@ -652,8 +645,7 @@ public class VolumeServiceImpl implements VolumeService { if (result.isSuccess()) { ((TemplateObject)templateOnPrimaryStoreObj).setInstallPath(result.getPath()); templateOnPrimaryStoreObj.processEvent(Event.OperationSuccessed, result.getAnswer()); - } - else { + } else { templateOnPrimaryStoreObj.processEvent(Event.OperationFailed); } @@ -673,8 +665,7 @@ public class VolumeServiceImpl implements VolumeService { if (result.isSuccess()) { templateOnPrimaryStoreObj.processEvent(Event.OperationSuccessed, result.getAnswer()); - } - else { + } else { templateOnPrimaryStoreObj.processEvent(Event.OperationFailed); } @@ -708,8 +699,8 @@ public class VolumeServiceImpl implements VolumeService { private final DataObject templateOnStore; private final SnapshotInfo snapshot; - public CreateVolumeFromBaseImageContext(AsyncCompletionCallback callback, DataObject vo, DataStore primaryStore, DataObject templateOnStore, - AsyncCallFuture future, SnapshotInfo snapshot) { + public CreateVolumeFromBaseImageContext(AsyncCompletionCallback callback, DataObject vo, DataStore primaryStore, DataObject templateOnStore, AsyncCallFuture future, + SnapshotInfo snapshot) { super(callback); this.vo = vo; this.future = future; @@ -727,8 +718,7 @@ public class VolumeServiceImpl implements VolumeService { DataObject volumeOnPrimaryStorage = pd.create(volume); volumeOnPrimaryStorage.processEvent(Event.CreateOnlyRequested); - CreateVolumeFromBaseImageContext context = - new CreateVolumeFromBaseImageContext(null, volumeOnPrimaryStorage, pd, templateOnPrimaryStore, future, null); + CreateVolumeFromBaseImageContext context = new CreateVolumeFromBaseImageContext(null, volumeOnPrimaryStorage, pd, templateOnPrimaryStore, future, null); AsyncCallbackDispatcher caller = AsyncCallbackDispatcher.create(this); caller.setCallback(caller.getTarget().createVolumeFromBaseImageCallBack(null, null)); caller.setContext(context); @@ -738,8 +728,7 @@ public class VolumeServiceImpl implements VolumeService { } @DB - protected Void createVolumeFromBaseImageCallBack(AsyncCallbackDispatcher callback, - CreateVolumeFromBaseImageContext context) { + protected Void createVolumeFromBaseImageCallBack(AsyncCallbackDispatcher callback, CreateVolumeFromBaseImageContext context) { DataObject vo = context.vo; DataObject tmplOnPrimary = context.templateOnStore; CopyCommandResult result = callback.getResult(); @@ -771,7 +760,7 @@ public class VolumeServiceImpl implements VolumeService { _tmpltPoolDao.update(templatePoolRefId, templatePoolRef); } - }finally { + } finally { _tmpltPoolDao.releaseFromLockTable(templatePoolRefId); } } @@ -836,10 +825,9 @@ public class VolumeServiceImpl implements VolumeService { VolumeApiResult result = createTemplateFuture.get(); if (result.isFailed()) { - String errMesg = result.getResult(); + String errMesg = result.getResult(); - throw new CloudRuntimeException("Unable to create template " + templateOnPrimary.getId() + - " on primary storage " + destPrimaryDataStore.getId() + ":" + errMesg); + throw new CloudRuntimeException("Unable to create template " + templateOnPrimary.getId() + " on primary storage " + destPrimaryDataStore.getId() + ":" + errMesg); } } catch (Throwable e) { s_logger.debug("Failed to create template volume on storage", e); @@ -847,8 +835,7 @@ public class VolumeServiceImpl implements VolumeService { templateOnPrimary.processEvent(Event.OperationFailed); throw new CloudRuntimeException(e.getMessage()); - } - finally { + } finally { _tmpltPoolDao.releaseFromLockTable(templatePoolRefId); } @@ -866,9 +853,8 @@ public class VolumeServiceImpl implements VolumeService { * @param destPrimaryDataStore The managed primary storage * @param destHost The host that we will use for the copy */ - private void copyTemplateToManagedTemplateVolume(TemplateInfo srcTemplateInfo, TemplateInfo templateOnPrimary, VMTemplateStoragePoolVO templatePoolRef, - PrimaryDataStore destPrimaryDataStore, Host destHost) - { + private void copyTemplateToManagedTemplateVolume(TemplateInfo srcTemplateInfo, TemplateInfo templateOnPrimary, VMTemplateStoragePoolVO templatePoolRef, PrimaryDataStore destPrimaryDataStore, + Host destHost) { AsyncCallFuture copyTemplateFuture = new AsyncCallFuture<>(); int storagePoolMaxWaitSeconds = NumbersUtil.parseInt(configDao.getValue(Config.StoragePoolMaxWaitSeconds.key()), 3600); long templatePoolRefId = templatePoolRef.getId(); @@ -889,11 +875,9 @@ public class VolumeServiceImpl implements VolumeService { } try { - // copy the template from sec storage to the created volume - CreateBaseImageContext copyContext = new CreateBaseImageContext<>( - null, null, destPrimaryDataStore, srcTemplateInfo, - copyTemplateFuture, templateOnPrimary, templatePoolRefId - ); + // copy the template from sec storage to the created volume + CreateBaseImageContext copyContext = new CreateBaseImageContext<>(null, null, destPrimaryDataStore, srcTemplateInfo, copyTemplateFuture, templateOnPrimary, + templatePoolRefId); AsyncCallbackDispatcher copyCaller = AsyncCallbackDispatcher.create(this); copyCaller.setCallback(copyCaller.getTarget().copyManagedTemplateCallback(null, null)).setContext(copyContext); @@ -930,8 +914,7 @@ public class VolumeServiceImpl implements VolumeService { motionSrv.copyAsync(srcTemplateInfo, templateOnPrimary, destHost, copyCaller); result = copyTemplateFuture.get(); - } - finally { + } finally { revokeAccess(templateOnPrimary, destHost, destPrimaryDataStore); if (HypervisorType.VMware.equals(destHost.getHypervisorType())) { @@ -946,21 +929,18 @@ public class VolumeServiceImpl implements VolumeService { } if (result.isFailed()) { - throw new CloudRuntimeException("Failed to copy template " + templateOnPrimary.getId() + - " to primary storage " + destPrimaryDataStore.getId() + ": " + result.getResult()); + throw new CloudRuntimeException("Failed to copy template " + templateOnPrimary.getId() + " to primary storage " + destPrimaryDataStore.getId() + ": " + result.getResult()); // XXX: I find it is useful to destroy the volume on primary storage instead of another thread trying the copy again because I've seen // something weird happens to the volume (XenServer creates an SR, but the VDI copy can fail). // For now, I just retry the copy. } - } - catch (Throwable e) { + } catch (Throwable e) { s_logger.debug("Failed to create a template on primary storage", e); templateOnPrimary.processEvent(Event.OperationFailed); throw new CloudRuntimeException(e.getMessage()); - } - finally { + } finally { _tmpltPoolDao.releaseFromLockTable(templatePoolRefId); } } @@ -983,8 +963,7 @@ public class VolumeServiceImpl implements VolumeService { String msg = "Unable to get an answer to the modify targets command"; s_logger.warn(msg); - } - else if (!answer.getResult()) { + } else if (!answer.getResult()) { String msg = "Unable to modify target on the following host: " + hostId; s_logger.warn(msg); @@ -999,8 +978,7 @@ public class VolumeServiceImpl implements VolumeService { * @param destPrimaryDataStore Primary storage of the volume * @param future For async */ - private void createManagedVolumeCloneTemplateAsync(VolumeInfo volumeInfo, TemplateInfo templateOnPrimary, PrimaryDataStore destPrimaryDataStore, - AsyncCallFuture future) { + private void createManagedVolumeCloneTemplateAsync(VolumeInfo volumeInfo, TemplateInfo templateOnPrimary, PrimaryDataStore destPrimaryDataStore, AsyncCallFuture future) { VMTemplateStoragePoolVO templatePoolRef = _tmpltPoolDao.findByPoolTemplate(destPrimaryDataStore.getId(), templateOnPrimary.getId()); if (templatePoolRef == null) { @@ -1015,8 +993,7 @@ public class VolumeServiceImpl implements VolumeService { try { volumeInfo.processEvent(Event.CreateOnlyRequested); - CreateVolumeFromBaseImageContext context = - new CreateVolumeFromBaseImageContext<>(null, volumeInfo, destPrimaryDataStore, templateOnPrimary, future, null); + CreateVolumeFromBaseImageContext context = new CreateVolumeFromBaseImageContext<>(null, volumeInfo, destPrimaryDataStore, templateOnPrimary, future, null); AsyncCallbackDispatcher caller = AsyncCallbackDispatcher.create(this); @@ -1033,8 +1010,7 @@ public class VolumeServiceImpl implements VolumeService { } } - private void createManagedVolumeCopyTemplateAsync(VolumeInfo volumeInfo, PrimaryDataStore primaryDataStore, TemplateInfo srcTemplateInfo, Host destHost, - AsyncCallFuture future) { + private void createManagedVolumeCopyTemplateAsync(VolumeInfo volumeInfo, PrimaryDataStore primaryDataStore, TemplateInfo srcTemplateInfo, Host destHost, AsyncCallFuture future) { try { // Create a volume on managed storage. @@ -1050,8 +1026,7 @@ public class VolumeServiceImpl implements VolumeService { // Refresh the volume info from the DB. volumeInfo = volFactory.getVolume(volumeInfo.getId(), primaryDataStore); - ManagedCreateBaseImageContext context = new ManagedCreateBaseImageContext(null, volumeInfo, - primaryDataStore, srcTemplateInfo, future); + ManagedCreateBaseImageContext context = new ManagedCreateBaseImageContext(null, volumeInfo, primaryDataStore, srcTemplateInfo, future); AsyncCallbackDispatcher caller = AsyncCallbackDispatcher.create(this); caller.setCallback(caller.getTarget().managedCopyBaseImageCallback(null, null)).setContext(context); @@ -1081,8 +1056,7 @@ public class VolumeServiceImpl implements VolumeService { try { motionSrv.copyAsync(srcTemplateInfo, destTemplateInfo, destHost, caller); - } - finally { + } finally { revokeAccess(volumeInfo, destHost, primaryDataStore); } } catch (Throwable t) { @@ -1098,8 +1072,7 @@ public class VolumeServiceImpl implements VolumeService { if (expungeVolumeResult.isFailed()) { errMsg += " : Failed to expunge a volume that was created"; } - } - catch (Exception ex) { + } catch (Exception ex) { errMsg += " : " + ex.getMessage(); } @@ -1112,8 +1085,7 @@ public class VolumeServiceImpl implements VolumeService { } @Override - public AsyncCallFuture createManagedStorageVolumeFromTemplateAsync(VolumeInfo volumeInfo, long destDataStoreId, - TemplateInfo srcTemplateInfo, long destHostId) { + public AsyncCallFuture createManagedStorageVolumeFromTemplateAsync(VolumeInfo volumeInfo, long destDataStoreId, TemplateInfo srcTemplateInfo, long destHostId) { PrimaryDataStore destPrimaryDataStore = dataStoreMgr.getPrimaryDataStore(destDataStoreId); Host destHost = _hostDao.findById(destHostId); @@ -1121,9 +1093,7 @@ public class VolumeServiceImpl implements VolumeService { throw new CloudRuntimeException("Destination host should not be null."); } - Boolean storageCanCloneVolume = new Boolean( - destPrimaryDataStore.getDriver().getCapabilities().get(DataStoreCapabilities.CAN_CREATE_VOLUME_FROM_VOLUME.toString()) - ); + Boolean storageCanCloneVolume = new Boolean(destPrimaryDataStore.getDriver().getCapabilities().get(DataStoreCapabilities.CAN_CREATE_VOLUME_FROM_VOLUME.toString())); boolean computeSupportsVolumeClone = computeSupportsVolumeClone(destHost.getDataCenterId(), destHost.getHypervisorType()); @@ -1146,10 +1116,7 @@ public class VolumeServiceImpl implements VolumeService { VMTemplateStoragePoolVO templatePoolRef = _tmpltPoolDao.findByPoolTemplate(destPrimaryDataStore.getId(), templateOnPrimary.getId()); if (templatePoolRef == null) { - throw new CloudRuntimeException("Failed to find template " + - srcTemplateInfo.getUniqueName() + " in storage pool " + - destPrimaryDataStore.getId() - ); + throw new CloudRuntimeException("Failed to find template " + srcTemplateInfo.getUniqueName() + " in storage pool " + destPrimaryDataStore.getId()); } if (templatePoolRef.getDownloadState() == Status.NOT_DOWNLOADED) { @@ -1190,8 +1157,7 @@ public class VolumeServiceImpl implements VolumeService { Collections.shuffle(clusters, new Random(System.nanoTime())); - clusters: - for (Cluster cluster : clusters) { + clusters: for (Cluster cluster : clusters) { if (cluster.getAllocationState() == AllocationState.Enabled) { List hosts = _hostDao.findByClusterId(cluster.getId()); @@ -1203,13 +1169,11 @@ public class VolumeServiceImpl implements VolumeService { if (computeClusterMustSupportResign) { if (clusterDao.getSupportsResigning(cluster.getId())) { return host; - } - else { + } else { // no other host in the cluster in question should be able to satisfy our requirements here, so move on to the next cluster continue clusters; } - } - else { + } else { return host; } } @@ -1237,17 +1201,15 @@ public class VolumeServiceImpl implements VolumeService { return future; } - @Override @DB - public boolean destroyVolume(long volumeId) throws ConcurrentOperationException { + @Override + public void destroyVolume(long volumeId) { // mark volume entry in volumes table as destroy state VolumeInfo vol = volFactory.getVolume(volumeId); vol.stateTransit(Volume.Event.DestroyRequested); snapshotMgr.deletePoliciesForVolume(volumeId); vol.stateTransit(Volume.Event.OperationSucceeded); - - return true; } @Override @@ -1259,8 +1221,7 @@ public class VolumeServiceImpl implements VolumeService { volumeOnStore.processEvent(Event.CreateOnlyRequested); _volumeDetailsDao.addDetail(volume.getId(), SNAPSHOT_ID, Long.toString(snapshot.getId()), false); - CreateVolumeFromBaseImageContext context = - new CreateVolumeFromBaseImageContext(null, volume, store, volumeOnStore, future, snapshot); + CreateVolumeFromBaseImageContext context = new CreateVolumeFromBaseImageContext(null, volume, store, volumeOnStore, future, snapshot); AsyncCallbackDispatcher caller = AsyncCallbackDispatcher.create(this); caller.setCallback(caller.getTarget().createVolumeFromSnapshotCallback(null, null)).setContext(context); motionSrv.copyAsync(snapshot, volumeOnStore, caller); @@ -1274,8 +1235,7 @@ public class VolumeServiceImpl implements VolumeService { return future; } - protected Void createVolumeFromSnapshotCallback(AsyncCallbackDispatcher callback, - CreateVolumeFromBaseImageContext context) { + protected Void createVolumeFromSnapshotCallback(AsyncCallbackDispatcher callback, CreateVolumeFromBaseImageContext context) { CopyCommandResult result = callback.getResult(); VolumeInfo volume = (VolumeInfo)context.templateOnStore; SnapshotInfo snapshot = context.snapshot; @@ -1332,8 +1292,7 @@ public class VolumeServiceImpl implements VolumeService { final VolumeInfo destVolume; final AsyncCallFuture future; - public CopyVolumeContext(AsyncCompletionCallback callback, AsyncCallFuture future, VolumeInfo srcVolume, VolumeInfo destVolume, - DataStore destStore) { + public CopyVolumeContext(AsyncCompletionCallback callback, AsyncCallFuture future, VolumeInfo srcVolume, VolumeInfo destVolume, DataStore destStore) { super(callback); this.srcVolume = srcVolume; this.destVolume = destVolume; @@ -1370,8 +1329,7 @@ public class VolumeServiceImpl implements VolumeService { } } - protected Void - copyVolumeFromImageToPrimaryCallback(AsyncCallbackDispatcher callback, CopyVolumeContext context) { + protected Void copyVolumeFromImageToPrimaryCallback(AsyncCallbackDispatcher callback, CopyVolumeContext context) { VolumeInfo srcVolume = context.srcVolume; VolumeInfo destVolume = context.destVolume; CopyCommandResult result = callback.getResult(); @@ -1424,8 +1382,7 @@ public class VolumeServiceImpl implements VolumeService { } } - protected Void - copyVolumeFromPrimaryToImageCallback(AsyncCallbackDispatcher callback, CopyVolumeContext context) { + protected Void copyVolumeFromPrimaryToImageCallback(AsyncCallbackDispatcher callback, CopyVolumeContext context) { VolumeInfo srcVolume = context.srcVolume; VolumeInfo destVolume = context.destVolume; CopyCommandResult result = callback.getResult(); @@ -1542,8 +1499,7 @@ public class VolumeServiceImpl implements VolumeService { /** * @param callback */ - public MigrateVolumeContext(AsyncCompletionCallback callback, AsyncCallFuture future, VolumeInfo srcVolume, VolumeInfo destVolume, - DataStore destStore) { + public MigrateVolumeContext(AsyncCompletionCallback callback, AsyncCallFuture future, VolumeInfo srcVolume, VolumeInfo destVolume, DataStore destStore) { super(callback); this.srcVolume = srcVolume; this.destVolume = destVolume; @@ -1654,8 +1610,7 @@ public class VolumeServiceImpl implements VolumeService { return future; } - protected Void - migrateVmWithVolumesCallBack(AsyncCallbackDispatcher callback, MigrateVmWithVolumesContext context) { + protected Void migrateVmWithVolumesCallBack(AsyncCallbackDispatcher callback, MigrateVmWithVolumesContext context) { Map volumeToPool = context.volumeToPool; CopyCommandResult result = callback.getResult(); AsyncCallFuture future = context.future; @@ -1715,7 +1670,7 @@ public class VolumeServiceImpl implements VolumeService { } @Override - public Pair registerVolumeForPostUpload(VolumeInfo volume, DataStore store) { + public Pair registerVolumeForPostUpload(VolumeInfo volume, DataStore store) { EndPoint ep = _epSelector.select(store); if (ep == null) { @@ -1724,7 +1679,7 @@ public class VolumeServiceImpl implements VolumeService { throw new CloudRuntimeException(errorMessage); } DataObject volumeOnStore = store.create(volume); - return new Pair<>(ep,volumeOnStore); + return new Pair<>(ep, volumeOnStore); } protected Void registerVolumeCallback(AsyncCallbackDispatcher callback, CreateVolumeContext context) { @@ -1751,21 +1706,20 @@ public class VolumeServiceImpl implements VolumeService { if (volStore != null) { physicalSize = volStore.getPhysicalSize(); } else { - s_logger.warn("No entry found in volume_store_ref for volume id: " + vo.getId() + " and image store id: " + ds.getId() + - " at the end of uploading volume!"); + s_logger.warn("No entry found in volume_store_ref for volume id: " + vo.getId() + " and image store id: " + ds.getId() + " at the end of uploading volume!"); } Scope dsScope = ds.getScope(); if (dsScope.getScopeType() == ScopeType.ZONE) { if (dsScope.getScopeId() != null) { - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_UPLOAD, vo.getAccountId(), dsScope.getScopeId(), vo.getId(), vo.getName(), null, - null, physicalSize, vo.getSize(), Volume.class.getName(), vo.getUuid()); + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_UPLOAD, vo.getAccountId(), dsScope.getScopeId(), vo.getId(), vo.getName(), null, null, physicalSize, vo.getSize(), + Volume.class.getName(), vo.getUuid()); } else { s_logger.warn("Zone scope image store " + ds.getId() + " has a null scope id"); } } else if (dsScope.getScopeType() == ScopeType.REGION) { // publish usage event for region-wide image store using a -1 zoneId for 4.2, need to revisit post-4.2 - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_UPLOAD, vo.getAccountId(), -1, vo.getId(), vo.getName(), null, null, physicalSize, - vo.getSize(), Volume.class.getName(), vo.getUuid()); + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_UPLOAD, vo.getAccountId(), -1, vo.getId(), vo.getName(), null, null, physicalSize, vo.getSize(), + Volume.class.getName(), vo.getUuid()); _resourceLimitMgr.incrementResourceCount(vo.getAccountId(), ResourceType.secondary_storage, vo.getSize()); } @@ -1829,8 +1783,8 @@ public class VolumeServiceImpl implements VolumeService { if (ep != null) { VolumeVO volume = volDao.findById(volumeId); PrimaryDataStore primaryDataStore = this.dataStoreMgr.getPrimaryDataStore(volume.getPoolId()); - ResizeVolumeCommand resizeCmd = new ResizeVolumeCommand(volume.getPath(), new StorageFilerTO(primaryDataStore), - volume.getSize(), newSize, true, instanceName, primaryDataStore.isManaged(), volume.get_iScsiName()); + ResizeVolumeCommand resizeCmd = new ResizeVolumeCommand(volume.getPath(), new StorageFilerTO(primaryDataStore), volume.getSize(), newSize, true, instanceName, + primaryDataStore.isManaged(), volume.get_iScsiName()); answer = ep.sendMessage(resizeCmd); } else { @@ -1903,8 +1857,8 @@ public class VolumeServiceImpl implements VolumeService { for (VolumeDataStoreVO volumeStore : dbVolumes) { VolumeVO volume = volDao.findById(volumeStore.getVolumeId()); if (volume == null) { - s_logger.warn("Volume_store_ref table shows that volume " + volumeStore.getVolumeId() + " is on image store " + storeId + - ", but the volume is not found in volumes table, potentially some bugs in deleteVolume, so we just treat this volume to be deleted and mark it as destroyed"); + s_logger.warn("Volume_store_ref table shows that volume " + volumeStore.getVolumeId() + " is on image store " + storeId + + ", but the volume is not found in volumes table, potentially some bugs in deleteVolume, so we just treat this volume to be deleted and mark it as destroyed"); volumeStore.setDestroyed(true); _volumeStoreDao.update(volumeStore.getId(), volumeStore); continue; @@ -1929,7 +1883,8 @@ public class VolumeServiceImpl implements VolumeService { VolumeObject volObj = (VolumeObject)volFactory.getVolume(volume.getId()); volObj.processEvent(Event.OperationFailed); } else if (volumeStore.getDownloadUrl() == null) { - msg = "Volume (" + volume.getUuid() + ") with install path " + volInfo.getInstallPath() + " is corrupted, please check in image store: " + volumeStore.getDataStoreId(); + msg = "Volume (" + volume.getUuid() + ") with install path " + volInfo.getInstallPath() + " is corrupted, please check in image store: " + + volumeStore.getDataStoreId(); s_logger.warn(msg); } else { s_logger.info("Removing volume_store_ref entry for corrupted volume " + volume.getName()); @@ -1959,8 +1914,8 @@ public class VolumeServiceImpl implements VolumeService { if (volInfo.getSize() > 0) { try { - _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(volume.getAccountId()), - com.cloud.configuration.Resource.ResourceType.secondary_storage, volInfo.getSize() - volInfo.getPhysicalSize()); + _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(volume.getAccountId()), com.cloud.configuration.Resource.ResourceType.secondary_storage, + volInfo.getSize() - volInfo.getPhysicalSize()); } catch (ResourceAllocationException e) { s_logger.warn(e.getMessage()); _alertMgr.sendAlert(AlertManager.AlertType.ALERT_TYPE_RESOURCE_LIMIT_EXCEEDED, volume.getDataCenterId(), volume.getPodId(), e.getMessage(), e.getMessage()); @@ -2021,7 +1976,7 @@ public class VolumeServiceImpl implements VolumeService { } // Delete volumes which are not present on DB. - for (Map.Entry entry : volumeInfos.entrySet()) { + for (Map.Entry entry : volumeInfos.entrySet()) { Long uniqueName = entry.getKey(); TemplateProp tInfo = entry.getValue(); @@ -2091,7 +2046,7 @@ public class VolumeServiceImpl implements VolumeService { s_logger.error("Take snapshot: " + volume.getId() + " failed", cre); throw cre; } catch (Exception e) { - if(s_logger.isDebugEnabled()) { + if (s_logger.isDebugEnabled()) { s_logger.debug("unknown exception while taking snapshot for volume " + volume.getId() + " was caught", e); } throw new CloudRuntimeException("Failed to take snapshot", e); diff --git a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java index 2530db2e5fa..62ec13caa0a 100644 --- a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java @@ -38,7 +38,6 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import javax.inject.Inject; -import javax.naming.ConfigurationException; import org.apache.cloudstack.api.command.admin.storage.CancelPrimaryStorageMaintenanceCmd; import org.apache.cloudstack.api.command.admin.storage.CreateSecondaryStagingStoreCmd; @@ -466,8 +465,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C } @Override - public boolean configure(String name, Map params) throws ConfigurationException { - + public boolean configure(String name, Map params) { Map configs = _configDao.getConfiguration("management-server", params); _storagePoolAcquisitionWaitSeconds = NumbersUtil.parseInt(configs.get("pool.acquisition.wait.seconds"), 1800); @@ -524,7 +522,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C @Override public String getStoragePoolTags(long poolId) { - return StringUtils.listToCsvTags(_storagePoolDao.searchForStoragePoolTags(poolId)); + return com.cloud.utils.StringUtils.listToCsvTags(_storagePoolDao.searchForStoragePoolTags(poolId)); } @Override @@ -1176,10 +1174,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C Host host = _hostDao.findById(ep.getId()); if (host != null && host.getManagementServerId() != null) { if (_serverId == host.getManagementServerId().longValue()) { - if (!volService.destroyVolume(volume.getId())) { - s_logger.warn("Unable to destroy uploaded volume " + volume.getUuid()); - continue; - } + volService.destroyVolume(volume.getId()); // decrement volume resource count _resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.volume, volume.isDisplayVolume()); // expunge volume from secondary if volume is on image store @@ -1844,8 +1839,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C totalOverProvCapacity = overProvFactor.multiply(new BigDecimal(pool.getCapacityBytes())).longValue(); - s_logger.debug("Found storage pool " + poolVO.getName() + " of type " + pool.getPoolType().toString() + " with over-provisioning factor " + - overProvFactor.toString()); + s_logger.debug("Found storage pool " + poolVO.getName() + " of type " + pool.getPoolType().toString() + " with over-provisioning factor " + overProvFactor.toString()); s_logger.debug("Total over-provisioned capacity calculated is " + overProvFactor + " * " + pool.getCapacityBytes()); } else { totalOverProvCapacity = pool.getCapacityBytes(); @@ -1858,18 +1852,16 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C double storageAllocatedThreshold = CapacityManager.StorageAllocatedCapacityDisableThreshold.valueIn(pool.getDataCenterId()); if (s_logger.isDebugEnabled()) { - s_logger.debug("Checking pool with ID " + pool.getId() + " for volume allocation " + volumes.toString() + ", maxSize: " + - totalOverProvCapacity + ", totalAllocatedSize: " + allocatedSizeWithTemplate + ", askingSize: " + totalAskingSize + - ", allocated disable threshold: " + storageAllocatedThreshold); + s_logger.debug("Checking pool with ID " + pool.getId() + " for volume allocation " + volumes.toString() + ", maxSize: " + totalOverProvCapacity + ", totalAllocatedSize: " + + allocatedSizeWithTemplate + ", askingSize: " + totalAskingSize + ", allocated disable threshold: " + storageAllocatedThreshold); } double usedPercentage = (allocatedSizeWithTemplate + totalAskingSize) / (double)(totalOverProvCapacity); if (usedPercentage > storageAllocatedThreshold) { if (s_logger.isDebugEnabled()) { - s_logger.debug("Insufficient un-allocated capacity on the pool with ID " + pool.getId() + " for volume allocation: " + volumes.toString() + - " since its allocated percentage " + usedPercentage + " has crossed the allocated pool.storage.allocated.capacity.disablethreshold " + - storageAllocatedThreshold + ", skipping this pool"); + s_logger.debug("Insufficient un-allocated capacity on the pool with ID " + pool.getId() + " for volume allocation: " + volumes.toString() + " since its allocated percentage " + + usedPercentage + " has crossed the allocated pool.storage.allocated.capacity.disablethreshold " + storageAllocatedThreshold + ", skipping this pool"); } return false; @@ -1877,9 +1869,8 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C if (totalOverProvCapacity < (allocatedSizeWithTemplate + totalAskingSize)) { if (s_logger.isDebugEnabled()) { - s_logger.debug("Insufficient un-allocated capacity on the pool with ID " + pool.getId() + " for volume allocation: " + volumes.toString() + - "; not enough storage, maxSize: " + totalOverProvCapacity + ", totalAllocatedSize: " + allocatedSizeWithTemplate + ", askingSize: " + - totalAskingSize); + s_logger.debug("Insufficient un-allocated capacity on the pool with ID " + pool.getId() + " for volume allocation: " + volumes.toString() + "; not enough storage, maxSize: " + + totalOverProvCapacity + ", totalAllocatedSize: " + allocatedSizeWithTemplate + ", askingSize: " + totalAskingSize); } return false; diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java index 78a009ce45e..9bff4a17295 100644 --- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java @@ -1237,92 +1237,139 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } } - @Override @DB + @Override @ActionEvent(eventType = EventTypes.EVENT_VOLUME_DELETE, eventDescription = "deleting volume") + /** + * Executes the removal of the volume. If the volume is only allocated we do not try to remove it from primary and secondary storage. + * Otherwise, after the removal in the database, we will try to remove the volume from both primary and secondary storage. + */ public boolean deleteVolume(long volumeId, Account caller) throws ConcurrentOperationException { - - VolumeVO volume = _volsDao.findById(volumeId); - if (volume == null) { - throw new InvalidParameterValueException("Unable to find volume with ID: " + volumeId); - } - - if (!_snapshotMgr.canOperateOnVolume(volume)) { - throw new InvalidParameterValueException("There are snapshot operations in progress on the volume, unable to delete it"); - } - - _accountMgr.checkAccess(caller, null, true, volume); - - if (volume.getInstanceId() != null) { - throw new InvalidParameterValueException("Please specify a volume that is not attached to any VM."); - } - - if (volume.getState() == Volume.State.UploadOp) { - VolumeDataStoreVO volumeStore = _volumeStoreDao.findByVolume(volume.getId()); - if (volumeStore.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS) { - throw new InvalidParameterValueException("Please specify a volume that is not uploading"); - } - } - - if (volume.getState() == Volume.State.NotUploaded || volume.getState() == Volume.State.UploadInProgress) { - throw new InvalidParameterValueException("The volume is either getting uploaded or it may be initiated shortly, please wait for it to be completed"); - } - + VolumeVO volume = retrieveAndValidateVolume(volumeId, caller); try { - if (volume.getState() != Volume.State.Destroy && volume.getState() != Volume.State.Expunging && volume.getState() != Volume.State.Expunged) { - Long instanceId = volume.getInstanceId(); - if (!volService.destroyVolume(volume.getId())) { - return false; - } - - VMInstanceVO vmInstance = _vmInstanceDao.findById(instanceId); - if (instanceId == null || (vmInstance.getType().equals(VirtualMachine.Type.User))) { - // Decrement the resource count for volumes and primary storage belonging user VM's only - _resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.volume, volume.isDisplayVolume()); - _resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.primary_storage, volume.isDisplayVolume(), new Long(volume.getSize())); - } - } + destroyVolumeIfPossible(volume); // Mark volume as removed if volume has not been created on primary or secondary if (volume.getState() == Volume.State.Allocated) { _volsDao.remove(volumeId); stateTransitTo(volume, Volume.Event.DestroyRequested); return true; } - // expunge volume from primary if volume is on primary - VolumeInfo volOnPrimary = volFactory.getVolume(volume.getId(), DataStoreRole.Primary); - if (volOnPrimary != null) { - s_logger.info("Expunging volume " + volume.getId() + " from primary data store"); - AsyncCallFuture future = volService.expungeVolumeAsync(volOnPrimary); - future.get(); - //decrement primary storage count - _resourceLimitMgr.decrementResourceCount(volOnPrimary.getAccountId(), ResourceType.volume, volOnPrimary.isDisplayVolume()); - _resourceLimitMgr.decrementResourceCount(volOnPrimary.getAccountId(), ResourceType.primary_storage, volOnPrimary.isDisplayVolume(), new Long(volOnPrimary.getSize())); - } - // expunge volume from secondary if volume is on image store - VolumeInfo volOnSecondary = volFactory.getVolume(volume.getId(), DataStoreRole.Image); - if (volOnSecondary != null) { - s_logger.info("Expunging volume " + volume.getId() + " from secondary data store"); - AsyncCallFuture future2 = volService.expungeVolumeAsync(volOnSecondary); - future2.get(); - //decrement secondary storage count - _resourceLimitMgr.decrementResourceCount(volOnSecondary.getAccountId(), ResourceType.secondary_storage, new Long(volOnSecondary.getSize())); - } - // delete all cache entries for this volume - List cacheVols = volFactory.listVolumeOnCache(volume.getId()); - for (VolumeInfo volOnCache : cacheVols) { - s_logger.info("Delete volume from image cache store: " + volOnCache.getDataStore().getName()); - volOnCache.delete(); - } - + expungeVolumesInPrimaryStorageIfNeeded(volume); + expungeVolumesInSecondaryStorageIfNeeded(volume); + cleanVolumesCache(volume); + return true; } catch (InterruptedException | ExecutionException | NoTransitionException e) { - s_logger.warn("Failed to expunge volume:", e); + s_logger.warn("Failed to expunge volume: " + volume.getUuid(), e); return false; } - - return true; } - private boolean stateTransitTo(Volume vol, Volume.Event event) throws NoTransitionException { + /** + * Retrieves and validates the volume for the {@link #deleteVolume(long, Account)} method. The following validation are executed. + *
    + *
  • if no volume is found in the database, we throw an {@link InvalidParameterValueException}; + *
  • if there are snapshots operation on the volume we cannot delete it. Therefore, an {@link InvalidParameterValueException} is thrown; + *
  • if the volume is still attached to a VM we throw an {@link InvalidParameterValueException}; + *
  • if volume state is in {@link Volume.State#UploadOp}, we check the {@link VolumeDataStoreVO}. Then, if the {@link VolumeDataStoreVO} for the given volume has download status of {@link VMTemplateStorageResourceAssoc.Status#DOWNLOAD_IN_PROGRESS}, an exception is throw; + *
  • if the volume state is in {@link Volume.State#NotUploaded} or if the state is {@link Volume.State#UploadInProgress}, an {@link InvalidParameterValueException} is thrown; + *
  • we also check if the user has access to the given volume using {@link AccountManager#checkAccess(Account, org.apache.cloudstack.acl.SecurityChecker.AccessType, boolean, String)}. + *
+ * + * After all validations we return the volume object. + */ + protected VolumeVO retrieveAndValidateVolume(long volumeId, Account caller) { + VolumeVO volume = _volsDao.findById(volumeId); + if (volume == null) { + throw new InvalidParameterValueException("Unable to find volume with ID: " + volumeId); + } + if (!_snapshotMgr.canOperateOnVolume(volume)) { + throw new InvalidParameterValueException("There are snapshot operations in progress on the volume, unable to delete it"); + } + if (volume.getInstanceId() != null) { + throw new InvalidParameterValueException("Please specify a volume that is not attached to any VM."); + } + if (volume.getState() == Volume.State.UploadOp) { + VolumeDataStoreVO volumeStore = _volumeStoreDao.findByVolume(volume.getId()); + if (volumeStore.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS) { + throw new InvalidParameterValueException("Please specify a volume that is not uploading"); + } + } + if (volume.getState() == Volume.State.NotUploaded || volume.getState() == Volume.State.UploadInProgress) { + throw new InvalidParameterValueException("The volume is either getting uploaded or it may be initiated shortly, please wait for it to be completed"); + } + _accountMgr.checkAccess(caller, null, true, volume); + return volume; + } + + /** + * Destroy the volume if possible and then decrement the following resource types. + *
    + *
  • {@link ResourceType#volume}; + *
  • {@link ResourceType#primary_storage} + *
+ * + * A volume can be destroyed if it is not in any of the following states. + *
    + *
  • {@value Volume.State#Destroy}; + *
  • {@value Volume.State#Expunging}; + *
  • {@value Volume.State#Expunged}. + *
+ * + * The volume is destroyed via {@link VolumeService#destroyVolume(long)} method. + */ + protected void destroyVolumeIfPossible(VolumeVO volume) { + if (volume.getState() != Volume.State.Destroy && volume.getState() != Volume.State.Expunging && volume.getState() != Volume.State.Expunged) { + volService.destroyVolume(volume.getId()); + + // Decrement the resource count for volumes and primary storage belonging user VM's only + _resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.volume, volume.isDisplayVolume()); + _resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.primary_storage, volume.isDisplayVolume(), volume.getSize()); + } + } + + /** + * We will check if the given volume is in the primary storage. If it is, we will execute an asynchronous call to delete it there. + * If the volume is not in the primary storage, we do nothing here. + */ + protected void expungeVolumesInPrimaryStorageIfNeeded(VolumeVO volume) throws InterruptedException, ExecutionException { + VolumeInfo volOnPrimary = volFactory.getVolume(volume.getId(), DataStoreRole.Primary); + if (volOnPrimary != null) { + s_logger.info("Expunging volume " + volume.getId() + " from primary data store"); + AsyncCallFuture future = volService.expungeVolumeAsync(volOnPrimary); + future.get(); + } + } + + /** + * We will check if the given volume is in the secondary storage. If the volume is not in the primary storage, we do nothing here. + * If it is, we will execute an asynchronous call to delete it there. Then, we decrement the {@link ResourceType#secondary_storage} for the account that owns the volume. + */ + protected void expungeVolumesInSecondaryStorageIfNeeded(VolumeVO volume) throws InterruptedException, ExecutionException { + VolumeInfo volOnSecondary = volFactory.getVolume(volume.getId(), DataStoreRole.Image); + if (volOnSecondary != null) { + s_logger.info("Expunging volume " + volume.getId() + " from secondary data store"); + AsyncCallFuture future2 = volService.expungeVolumeAsync(volOnSecondary); + future2.get(); + + _resourceLimitMgr.decrementResourceCount(volOnSecondary.getAccountId(), ResourceType.secondary_storage, volOnSecondary.getSize()); + } + } + + /** + * Clean volumes cache entries (if they exist). + */ + protected void cleanVolumesCache(VolumeVO volume) { + List cacheVols = volFactory.listVolumeOnCache(volume.getId()); + if (CollectionUtils.isEmpty(cacheVols)) { + return; + } + for (VolumeInfo volOnCache : cacheVols) { + s_logger.info("Delete volume from image cache store: " + volOnCache.getDataStore().getName()); + volOnCache.delete(); + } + } + + protected boolean stateTransitTo(Volume vol, Volume.Event event) throws NoTransitionException { return _volStateMachine.transitTo(vol, event, null, _volsDao); } @@ -3198,4 +3245,4 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic return workJob; } -} +} \ No newline at end of file diff --git a/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java b/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java index 55dda533193..ddcf4758eee 100644 --- a/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java +++ b/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java @@ -30,16 +30,20 @@ import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.UUID; +import java.util.concurrent.ExecutionException; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.SecurityChecker.AccessType; import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd; import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd; import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService.VolumeApiResult; +import org.apache.cloudstack.framework.async.AsyncCallFuture; import org.apache.cloudstack.framework.jobs.AsyncJobExecutionContext; import org.apache.cloudstack.framework.jobs.AsyncJobManager; import org.apache.cloudstack.framework.jobs.dao.AsyncJobJoinMapDao; @@ -48,6 +52,8 @@ 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.db.VolumeDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreVO; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -63,6 +69,7 @@ import org.mockito.Spy; import org.mockito.runners.MockitoJUnitRunner; import com.cloud.configuration.Resource; +import com.cloud.configuration.Resource.ResourceType; import com.cloud.dc.DataCenterVO; import com.cloud.dc.dao.DataCenterDao; import com.cloud.exception.InvalidParameterValueException; @@ -73,6 +80,7 @@ import com.cloud.org.Grouping; import com.cloud.serializer.GsonHelper; import com.cloud.storage.Volume.Type; import com.cloud.storage.dao.VolumeDao; +import com.cloud.storage.snapshot.SnapshotManager; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.AccountVO; @@ -81,6 +89,7 @@ import com.cloud.user.User; import com.cloud.user.UserVO; import com.cloud.user.dao.AccountDao; import com.cloud.utils.db.TransactionLegacy; +import com.cloud.utils.fsm.NoTransitionException; import com.cloud.vm.UserVmManager; import com.cloud.vm.UserVmVO; import com.cloud.vm.VirtualMachine; @@ -97,13 +106,17 @@ public class VolumeApiServiceImplTest { @InjectMocks private VolumeApiServiceImpl volumeApiServiceImpl; @Mock - private VolumeDao _volumeDao; + private SnapshotManager snapshotManagerMock; @Mock - private AccountManager _accountMgr; + private VolumeDataStoreDao volumeDataStoreDaoMock; @Mock - private UserVmDao _userVmDao; + private VolumeDao volumeDaoMock; @Mock - private PrimaryDataStoreDao _storagePoolDao; + private AccountManager accountManagerMock; + @Mock + private UserVmDao userVmDaoMock; + @Mock + private PrimaryDataStoreDao primaryDataStoreDaoMock; @Mock private VMSnapshotDao _vmSnapshotDao; @Mock @@ -111,7 +124,7 @@ public class VolumeApiServiceImplTest { @Mock private AsyncJobJoinMapDao _joinMapDao; @Mock - private VolumeDataFactory _volFactory; + private VolumeDataFactory volumeDataFactoryMock; @Mock private VMInstanceDao _vmInstanceDao; @Mock @@ -119,7 +132,7 @@ public class VolumeApiServiceImplTest { @Mock private SnapshotInfo snapshotInfoMock; @Mock - private VolumeService volService; + private VolumeService volumeServiceMock; @Mock private CreateVolumeCmd createVol; @Mock @@ -127,7 +140,7 @@ public class VolumeApiServiceImplTest { @Mock private DataCenterDao _dcDao; @Mock - private ResourceLimitService _resourceLimitMgr; + private ResourceLimitService resourceLimitServiceMock; @Mock private AccountDao _accountDao; @Mock @@ -142,10 +155,22 @@ public class VolumeApiServiceImplTest { private StoragePool storagePoolMock; private long storagePoolMockId = 1; @Mock - private VolumeVO volumeVOMock; - @Mock private DiskOfferingVO newDiskOfferingMock; + @Mock + private VolumeVO volumeVoMock; + @Mock + private Account accountMock; + @Mock + private VolumeDataStoreVO volumeDataStoreVoMock; + @Mock + private AsyncCallFuture asyncCallFutureVolumeapiResultMock; + + private long accountMockId = 456l; + private long volumeMockId = 12313l; + private long vmInstanceMockId = 1123l; + private long volumeSizeMock = 456789921939l; + @Before public void before() throws Exception { Mockito.when(storagePoolMock.getId()).thenReturn(storagePoolMockId); @@ -167,50 +192,50 @@ public class VolumeApiServiceImplTest { try { // volume of running vm id=1 VolumeVO volumeOfRunningVm = new VolumeVO("root", 1L, 1L, 1L, 1L, 1L, "root", "root", Storage.ProvisioningType.THIN, 1, null, null, "root", Volume.Type.ROOT); - when(_volumeDao.findById(1L)).thenReturn(volumeOfRunningVm); + when(volumeDaoMock.findById(1L)).thenReturn(volumeOfRunningVm); UserVmVO runningVm = new UserVmVO(1L, "vm", "vm", 1, HypervisorType.XenServer, 1L, false, false, 1L, 1L, 1, 1L, null, "vm", null); runningVm.setState(State.Running); runningVm.setDataCenterId(1L); - when(_userVmDao.findById(1L)).thenReturn(runningVm); + when(userVmDaoMock.findById(1L)).thenReturn(runningVm); // volume of stopped vm id=2 VolumeVO volumeOfStoppedVm = new VolumeVO("root", 1L, 1L, 1L, 1L, 2L, "root", "root", Storage.ProvisioningType.THIN, 1, null, null, "root", Volume.Type.ROOT); volumeOfStoppedVm.setPoolId(1L); - when(_volumeDao.findById(2L)).thenReturn(volumeOfStoppedVm); + when(volumeDaoMock.findById(2L)).thenReturn(volumeOfStoppedVm); UserVmVO stoppedVm = new UserVmVO(2L, "vm", "vm", 1, HypervisorType.XenServer, 1L, false, false, 1L, 1L, 1, 1L, null, "vm", null); stoppedVm.setState(State.Stopped); stoppedVm.setDataCenterId(1L); - when(_userVmDao.findById(2L)).thenReturn(stoppedVm); + when(userVmDaoMock.findById(2L)).thenReturn(stoppedVm); // volume of hyperV vm id=3 UserVmVO hyperVVm = new UserVmVO(3L, "vm", "vm", 1, HypervisorType.Hyperv, 1L, false, false, 1L, 1L, 1, 1L, null, "vm", null); hyperVVm.setState(State.Stopped); hyperVVm.setDataCenterId(1L); - when(_userVmDao.findById(3L)).thenReturn(hyperVVm); + when(userVmDaoMock.findById(3L)).thenReturn(hyperVVm); VolumeVO volumeOfStoppeHyperVVm = new VolumeVO("root", 1L, 1L, 1L, 1L, 3L, "root", "root", Storage.ProvisioningType.THIN, 1, null, null, "root", Volume.Type.ROOT); volumeOfStoppeHyperVVm.setPoolId(1L); - when(_volumeDao.findById(3L)).thenReturn(volumeOfStoppeHyperVVm); + when(volumeDaoMock.findById(3L)).thenReturn(volumeOfStoppeHyperVVm); StoragePoolVO unmanagedPool = new StoragePoolVO(); - when(_storagePoolDao.findById(1L)).thenReturn(unmanagedPool); + when(primaryDataStoreDaoMock.findById(1L)).thenReturn(unmanagedPool); // volume of managed pool id=4 StoragePoolVO managedPool = new StoragePoolVO(); managedPool.setManaged(true); - when(_storagePoolDao.findById(2L)).thenReturn(managedPool); + when(primaryDataStoreDaoMock.findById(2L)).thenReturn(managedPool); VolumeVO managedPoolVolume = new VolumeVO("root", 1L, 1L, 1L, 1L, 2L, "root", "root", Storage.ProvisioningType.THIN, 1, null, null, "root", Volume.Type.ROOT); managedPoolVolume.setPoolId(2L); - when(_volumeDao.findById(4L)).thenReturn(managedPoolVolume); + when(volumeDaoMock.findById(4L)).thenReturn(managedPoolVolume); // non-root non-datadisk volume VolumeInfo volumeWithIncorrectVolumeType = Mockito.mock(VolumeInfo.class); when(volumeWithIncorrectVolumeType.getId()).thenReturn(5L); when(volumeWithIncorrectVolumeType.getVolumeType()).thenReturn(Volume.Type.ISO); - when(_volFactory.getVolume(5L)).thenReturn(volumeWithIncorrectVolumeType); + when(volumeDataFactoryMock.getVolume(5L)).thenReturn(volumeWithIncorrectVolumeType); // correct root volume VolumeInfo correctRootVolume = Mockito.mock(VolumeInfo.class); @@ -221,10 +246,10 @@ public class VolumeApiServiceImplTest { when(correctRootVolume.getState()).thenReturn(Volume.State.Ready); when(correctRootVolume.getTemplateId()).thenReturn(null); when(correctRootVolume.getPoolId()).thenReturn(1L); - when(_volFactory.getVolume(6L)).thenReturn(correctRootVolume); + when(volumeDataFactoryMock.getVolume(6L)).thenReturn(correctRootVolume); VolumeVO correctRootVolumeVO = new VolumeVO("root", 1L, 1L, 1L, 1L, 2L, "root", "root", Storage.ProvisioningType.THIN, 1, null, null, "root", Volume.Type.ROOT); - when(_volumeDao.findById(6L)).thenReturn(correctRootVolumeVO); + when(volumeDaoMock.findById(6L)).thenReturn(correctRootVolumeVO); // managed root volume VolumeInfo managedVolume = Mockito.mock(VolumeInfo.class); @@ -233,21 +258,21 @@ public class VolumeApiServiceImplTest { when(managedVolume.getVolumeType()).thenReturn(Volume.Type.ROOT); when(managedVolume.getInstanceId()).thenReturn(null); when(managedVolume.getPoolId()).thenReturn(2L); - when(_volFactory.getVolume(7L)).thenReturn(managedVolume); + when(volumeDataFactoryMock.getVolume(7L)).thenReturn(managedVolume); VolumeVO managedVolume1 = new VolumeVO("root", 1L, 1L, 1L, 1L, 2L, "root", "root", Storage.ProvisioningType.THIN, 1, null, null, "root", Volume.Type.ROOT); managedVolume1.setPoolId(2L); managedVolume1.setDataCenterId(1L); - when(_volumeDao.findById(7L)).thenReturn(managedVolume1); + when(volumeDaoMock.findById(7L)).thenReturn(managedVolume1); // vm having root volume UserVmVO vmHavingRootVolume = new UserVmVO(4L, "vm", "vm", 1, HypervisorType.XenServer, 1L, false, false, 1L, 1L, 1, 1L, null, "vm", null); vmHavingRootVolume.setState(State.Stopped); vmHavingRootVolume.setDataCenterId(1L); - when(_userVmDao.findById(4L)).thenReturn(vmHavingRootVolume); + when(userVmDaoMock.findById(4L)).thenReturn(vmHavingRootVolume); List vols = new ArrayList(); vols.add(new VolumeVO()); - when(_volumeDao.findByInstanceAndDeviceId(4L, 0L)).thenReturn(vols); + when(volumeDaoMock.findByInstanceAndDeviceId(4L, 0L)).thenReturn(vols); // volume in uploaded state VolumeInfo uploadedVolume = Mockito.mock(VolumeInfo.class); @@ -257,13 +282,13 @@ public class VolumeApiServiceImplTest { when(uploadedVolume.getInstanceId()).thenReturn(null); when(uploadedVolume.getPoolId()).thenReturn(1L); when(uploadedVolume.getState()).thenReturn(Volume.State.Uploaded); - when(_volFactory.getVolume(8L)).thenReturn(uploadedVolume); + when(volumeDataFactoryMock.getVolume(8L)).thenReturn(uploadedVolume); VolumeVO upVolume = new VolumeVO("root", 1L, 1L, 1L, 1L, 2L, "root", "root", Storage.ProvisioningType.THIN, 1, null, null, "root", Volume.Type.ROOT); upVolume.setPoolId(1L); upVolume.setDataCenterId(1L); upVolume.setState(Volume.State.Uploaded); - when(_volumeDao.findById(8L)).thenReturn(upVolume); + when(volumeDaoMock.findById(8L)).thenReturn(upVolume); // helper dao methods mock when(_vmSnapshotDao.findByVm(any(Long.class))).thenReturn(new ArrayList()); @@ -279,7 +304,7 @@ public class VolumeApiServiceImplTest { } // helper methods mock - doNothing().when(_accountMgr).checkAccess(any(Account.class), any(AccessType.class), any(Boolean.class), any(ControlledEntity.class)); + doNothing().when(accountManagerMock).checkAccess(any(Account.class), any(AccessType.class), any(Boolean.class), any(ControlledEntity.class)); doNothing().when(_jobMgr).updateAsyncJobAttachment(any(Long.class), any(String.class), any(Long.class)); when(_jobMgr.submitAsyncJob(any(AsyncJobVO.class), any(String.class), any(Long.class))).thenReturn(1L); } @@ -374,7 +399,7 @@ public class VolumeApiServiceImplTest { // volume not Ready @Test(expected = InvalidParameterValueException.class) public void testTakeSnapshotF1() throws ResourceAllocationException { - when(_volFactory.getVolume(anyLong())).thenReturn(volumeInfoMock); + when(volumeDataFactoryMock.getVolume(anyLong())).thenReturn(volumeInfoMock); when(volumeInfoMock.getState()).thenReturn(Volume.State.Allocated); when(volumeInfoMock.getPoolId()).thenReturn(1L); volumeApiServiceImpl.takeSnapshot(5L, Snapshot.MANUAL_POLICY_ID, 3L, null, false, null, false); @@ -382,11 +407,11 @@ public class VolumeApiServiceImplTest { @Test public void testTakeSnapshotF2() throws ResourceAllocationException { - when(_volFactory.getVolume(anyLong())).thenReturn(volumeInfoMock); + when(volumeDataFactoryMock.getVolume(anyLong())).thenReturn(volumeInfoMock); when(volumeInfoMock.getState()).thenReturn(Volume.State.Ready); when(volumeInfoMock.getInstanceId()).thenReturn(null); when(volumeInfoMock.getPoolId()).thenReturn(1L); - when(volService.takeSnapshot(Mockito.any(VolumeInfo.class))).thenReturn(snapshotInfoMock); + when(volumeServiceMock.takeSnapshot(Mockito.any(VolumeInfo.class))).thenReturn(snapshotInfoMock); volumeApiServiceImpl.takeSnapshot(5L, Snapshot.MANUAL_POLICY_ID, 3L, null, false, null, false); } @@ -422,7 +447,7 @@ public class VolumeApiServiceImplTest { @Test public void testUpdateMissingRootDiskControllerWithValidChainInfo() { - UserVmVO vm = _userVmDao.findById(1L); + UserVmVO vm = userVmDaoMock.findById(1L); Mockito.doNothing().when(userVmManager).persistDeviceBusInfo(any(UserVmVO.class), eq("scsi")); volumeApiServiceImpl.updateMissingRootDiskController(vm, "{\"diskDeviceBusName\":\"scsi0:0\",\"diskChain\":[\"[somedatastore] i-3-VM-somePath/ROOT-1.vmdk\"]}"); @@ -451,7 +476,7 @@ public class VolumeApiServiceImplTest { */ @Test public void testResourceLimitCheckForUploadedVolume() throws NoSuchFieldException, IllegalAccessException, ResourceAllocationException { - doThrow(new ResourceAllocationException("primary storage resource limit check failed", Resource.ResourceType.primary_storage)).when(_resourceLimitMgr).checkResourceLimit(any(AccountVO.class), + doThrow(new ResourceAllocationException("primary storage resource limit check failed", Resource.ResourceType.primary_storage)).when(resourceLimitServiceMock).checkResourceLimit(any(AccountVO.class), any(Resource.ResourceType.class), any(Long.class)); UserVmVO vm = Mockito.mock(UserVmVO.class); VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class); @@ -459,12 +484,12 @@ public class VolumeApiServiceImplTest { when(volumeToAttach.getDataCenterId()).thenReturn(34L); when(volumeToAttach.getVolumeType()).thenReturn(Volume.Type.DATADISK); when(volumeToAttach.getInstanceId()).thenReturn(null); - when(_userVmDao.findById(anyLong())).thenReturn(vm); + when(userVmDaoMock.findById(anyLong())).thenReturn(vm); when(vm.getType()).thenReturn(VirtualMachine.Type.User); when(vm.getState()).thenReturn(State.Running); when(vm.getDataCenterId()).thenReturn(34L); - when(_volumeDao.findByInstanceAndType(anyLong(), any(Volume.Type.class))).thenReturn(new ArrayList<>(10)); - when(_volFactory.getVolume(9L)).thenReturn(volumeToAttach); + when(volumeDaoMock.findByInstanceAndType(anyLong(), any(Volume.Type.class))).thenReturn(new ArrayList<>(10)); + when(volumeDataFactoryMock.getVolume(9L)).thenReturn(volumeToAttach); when(volumeToAttach.getState()).thenReturn(Volume.State.Uploaded); DataCenterVO zoneWithDisabledLocalStorage = Mockito.mock(DataCenterVO.class); when(_dcDao.findById(anyLong())).thenReturn(zoneWithDisabledLocalStorage); @@ -511,39 +536,39 @@ public class VolumeApiServiceImplTest { @Test public void validateConditionsToReplaceDiskOfferingOfVolumeTestNoNewDiskOffering() { - volumeApiServiceImpl.validateConditionsToReplaceDiskOfferingOfVolume(volumeVOMock, null, storagePoolMock); + volumeApiServiceImpl.validateConditionsToReplaceDiskOfferingOfVolume(volumeVoMock, null, storagePoolMock); - Mockito.verify(volumeVOMock, times(0)).getVolumeType(); + Mockito.verify(volumeVoMock, times(0)).getVolumeType(); } @Test public void validateConditionsToReplaceDiskOfferingOfVolumeTestRootVolume() { - Mockito.when(volumeVOMock.getVolumeType()).thenReturn(Type.ROOT); + Mockito.when(volumeVoMock.getVolumeType()).thenReturn(Type.ROOT); - volumeApiServiceImpl.validateConditionsToReplaceDiskOfferingOfVolume(volumeVOMock, newDiskOfferingMock, storagePoolMock); + volumeApiServiceImpl.validateConditionsToReplaceDiskOfferingOfVolume(volumeVoMock, newDiskOfferingMock, storagePoolMock); } @Test(expected = InvalidParameterValueException.class) public void validateConditionsToReplaceDiskOfferingOfVolumeTestTargetPoolSharedDiskOfferingLocal() { - Mockito.when(volumeVOMock.getVolumeType()).thenReturn(Type.DATADISK); + Mockito.when(volumeVoMock.getVolumeType()).thenReturn(Type.DATADISK); Mockito.when(newDiskOfferingMock.getUseLocalStorage()).thenReturn(true); Mockito.when(storagePoolMock.isShared()).thenReturn(true); - volumeApiServiceImpl.validateConditionsToReplaceDiskOfferingOfVolume(volumeVOMock, newDiskOfferingMock, storagePoolMock); + volumeApiServiceImpl.validateConditionsToReplaceDiskOfferingOfVolume(volumeVoMock, newDiskOfferingMock, storagePoolMock); } @Test(expected = InvalidParameterValueException.class) public void validateConditionsToReplaceDiskOfferingOfVolumeTestTargetPoolLocalDiskOfferingShared() { - Mockito.when(volumeVOMock.getVolumeType()).thenReturn(Type.DATADISK); + Mockito.when(volumeVoMock.getVolumeType()).thenReturn(Type.DATADISK); Mockito.when(newDiskOfferingMock.isShared()).thenReturn(true); Mockito.when(storagePoolMock.isLocal()).thenReturn(true); - volumeApiServiceImpl.validateConditionsToReplaceDiskOfferingOfVolume(volumeVOMock, newDiskOfferingMock, storagePoolMock); + volumeApiServiceImpl.validateConditionsToReplaceDiskOfferingOfVolume(volumeVoMock, newDiskOfferingMock, storagePoolMock); } @Test(expected = InvalidParameterValueException.class) public void validateConditionsToReplaceDiskOfferingOfVolumeTestTagsDoNotMatch() { - Mockito.when(volumeVOMock.getVolumeType()).thenReturn(Type.DATADISK); + Mockito.when(volumeVoMock.getVolumeType()).thenReturn(Type.DATADISK); Mockito.when(newDiskOfferingMock.getUseLocalStorage()).thenReturn(false); Mockito.when(storagePoolMock.isShared()).thenReturn(true); @@ -555,12 +580,12 @@ public class VolumeApiServiceImplTest { Mockito.doReturn(null).when(volumeApiServiceImpl).getStoragePoolTags(storagePoolMock); - volumeApiServiceImpl.validateConditionsToReplaceDiskOfferingOfVolume(volumeVOMock, newDiskOfferingMock, storagePoolMock); + volumeApiServiceImpl.validateConditionsToReplaceDiskOfferingOfVolume(volumeVoMock, newDiskOfferingMock, storagePoolMock); } @Test public void validateConditionsToReplaceDiskOfferingOfVolumeTestEverythingWorking() { - Mockito.when(volumeVOMock.getVolumeType()).thenReturn(Type.DATADISK); + Mockito.when(volumeVoMock.getVolumeType()).thenReturn(Type.DATADISK); Mockito.when(newDiskOfferingMock.getUseLocalStorage()).thenReturn(false); Mockito.when(storagePoolMock.isShared()).thenReturn(true); @@ -572,9 +597,9 @@ public class VolumeApiServiceImplTest { Mockito.doReturn("tag1").when(volumeApiServiceImpl).getStoragePoolTags(storagePoolMock); - volumeApiServiceImpl.validateConditionsToReplaceDiskOfferingOfVolume(volumeVOMock, newDiskOfferingMock, storagePoolMock); + volumeApiServiceImpl.validateConditionsToReplaceDiskOfferingOfVolume(volumeVoMock, newDiskOfferingMock, storagePoolMock); - InOrder inOrder = Mockito.inOrder(volumeVOMock, newDiskOfferingMock, storagePoolMock, volumeApiServiceImpl); + InOrder inOrder = Mockito.inOrder(volumeVoMock, newDiskOfferingMock, storagePoolMock, volumeApiServiceImpl); inOrder.verify(storagePoolMock).isShared(); inOrder.verify(newDiskOfferingMock).getUseLocalStorage(); inOrder.verify(storagePoolMock).isLocal(); @@ -582,7 +607,385 @@ public class VolumeApiServiceImplTest { inOrder.verify(volumeApiServiceImpl).getStoragePoolTags(storagePoolMock); inOrder.verify(newDiskOfferingMock).getTags(); - inOrder.verify(volumeVOMock).getSize(); + inOrder.verify(volumeVoMock).getSize(); inOrder.verify(newDiskOfferingMock).getDiskSize(); } + + @Test(expected = InvalidParameterValueException.class) + public void retrieveAndValidateVolumeTestVolumeNotFound() { + Mockito.doReturn(null).when(volumeDaoMock).findById(volumeMockId); + volumeApiServiceImpl.retrieveAndValidateVolume(volumeMockId, accountMock); + } + + @Test(expected = InvalidParameterValueException.class) + public void retrieveAndValidateVolumeTestCannotOperateOnVolumeDueToSnapshot() { + Mockito.doReturn(volumeVoMock).when(volumeDaoMock).findById(volumeMockId); + Mockito.doReturn(false).when(snapshotManagerMock).canOperateOnVolume(volumeVoMock); + + volumeApiServiceImpl.retrieveAndValidateVolume(volumeMockId, accountMock); + } + + @Test(expected = InvalidParameterValueException.class) + public void retrieveAndValidateVolumeTestVolumePluggedIntoVm() { + Mockito.doReturn(volumeVoMock).when(volumeDaoMock).findById(volumeMockId); + Mockito.doReturn(vmInstanceMockId).when(volumeVoMock).getInstanceId(); + + Mockito.doReturn(true).when(snapshotManagerMock).canOperateOnVolume(volumeVoMock); + + volumeApiServiceImpl.retrieveAndValidateVolume(volumeMockId, accountMock); + } + + @Test(expected = InvalidParameterValueException.class) + public void retrieveAndValidateVolumeTestStateUploadOpAndDownloadInProgress() { + Mockito.doReturn(volumeVoMock).when(volumeDaoMock).findById(volumeMockId); + Mockito.doReturn(null).when(volumeVoMock).getInstanceId(); + Mockito.doReturn(Volume.State.UploadOp).when(volumeVoMock).getState(); + + Mockito.doReturn(true).when(snapshotManagerMock).canOperateOnVolume(volumeVoMock); + Mockito.doReturn(volumeDataStoreVoMock).when(volumeDataStoreDaoMock).findByVolume(volumeMockId); + Mockito.doReturn(VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS).when(volumeDataStoreVoMock).getDownloadState(); + + volumeApiServiceImpl.retrieveAndValidateVolume(volumeMockId, accountMock); + } + + @Test(expected = InvalidParameterValueException.class) + public void retrieveAndValidateVolumeTestStateNotUploaded() { + Mockito.doReturn(volumeVoMock).when(volumeDaoMock).findById(volumeMockId); + Mockito.doReturn(null).when(volumeVoMock).getInstanceId(); + Mockito.doReturn(Volume.State.NotUploaded).when(volumeVoMock).getState(); + + Mockito.doReturn(true).when(snapshotManagerMock).canOperateOnVolume(volumeVoMock); + + volumeApiServiceImpl.retrieveAndValidateVolume(volumeMockId, accountMock); + } + + @Test(expected = InvalidParameterValueException.class) + public void retrieveAndValidateVolumeTestUploadInProgress() { + Mockito.doReturn(volumeVoMock).when(volumeDaoMock).findById(volumeMockId); + Mockito.doReturn(null).when(volumeVoMock).getInstanceId(); + Mockito.doReturn(Volume.State.UploadInProgress).when(volumeVoMock).getState(); + + Mockito.doReturn(true).when(snapshotManagerMock).canOperateOnVolume(volumeVoMock); + + volumeApiServiceImpl.retrieveAndValidateVolume(volumeMockId, accountMock); + } + + @Test + public void retrieveAndValidateVolumeTest() { + Mockito.doReturn(volumeVoMock).when(volumeDaoMock).findById(volumeMockId); + Mockito.doReturn(null).when(volumeVoMock).getInstanceId(); + Mockito.doReturn(Volume.State.Ready).when(volumeVoMock).getState(); + + Mockito.doReturn(true).when(snapshotManagerMock).canOperateOnVolume(volumeVoMock); + Mockito.doNothing().when(accountManagerMock).checkAccess(accountMock, null, true, volumeVoMock); + volumeApiServiceImpl.retrieveAndValidateVolume(volumeMockId, accountMock); + + Mockito.verify(accountManagerMock).checkAccess(accountMock, null, true, volumeVoMock); + } + + @Test + public void destroyVolumeIfPossibleTestVolumeStateIsDestroy() { + Mockito.doReturn(Volume.State.Destroy).when(volumeVoMock).getState(); + configureMocksForTestDestroyVolumeWhenVolume(); + + volumeApiServiceImpl.destroyVolumeIfPossible(volumeVoMock); + + verifyMocksForTestDestroyVolumeWhenVolumeIsNotInRightState(); + } + + @Test + public void destroyVolumeIfPossibleTestVolumeStateIsExpunging() { + Mockito.doReturn(Volume.State.Expunging).when(volumeVoMock).getState(); + configureMocksForTestDestroyVolumeWhenVolume(); + + volumeApiServiceImpl.destroyVolumeIfPossible(volumeVoMock); + + verifyMocksForTestDestroyVolumeWhenVolumeIsNotInRightState(); + } + + @Test + public void destroyVolumeIfPossibleTestVolumeStateIsExpunged() { + Mockito.doReturn(Volume.State.Expunged).when(volumeVoMock).getState(); + configureMocksForTestDestroyVolumeWhenVolume(); + + volumeApiServiceImpl.destroyVolumeIfPossible(volumeVoMock); + + verifyMocksForTestDestroyVolumeWhenVolumeIsNotInRightState(); + } + + @Test + public void destroyVolumeIfPossibleTestVolumeStateReady() { + Mockito.doReturn(Volume.State.Ready).when(volumeVoMock).getState(); + configureMocksForTestDestroyVolumeWhenVolume(); + + volumeApiServiceImpl.destroyVolumeIfPossible(volumeVoMock); + + Mockito.verify(volumeServiceMock, Mockito.times(1)).destroyVolume(volumeMockId); + Mockito.verify(resourceLimitServiceMock, Mockito.times(1)).decrementResourceCount(accountMockId, ResourceType.volume, true); + Mockito.verify(resourceLimitServiceMock, Mockito.times(1)).decrementResourceCount(accountMockId, ResourceType.primary_storage, true, volumeSizeMock); + } + + private void verifyMocksForTestDestroyVolumeWhenVolumeIsNotInRightState() { + Mockito.verify(volumeServiceMock, Mockito.times(0)).destroyVolume(volumeMockId); + Mockito.verify(resourceLimitServiceMock, Mockito.times(0)).decrementResourceCount(accountMockId, ResourceType.volume, true); + Mockito.verify(resourceLimitServiceMock, Mockito.times(0)).decrementResourceCount(accountMockId, ResourceType.primary_storage, true, volumeSizeMock); + } + + private void configureMocksForTestDestroyVolumeWhenVolume() { + Mockito.doReturn(accountMockId).when(volumeVoMock).getAccountId(); + Mockito.doReturn(true).when(volumeVoMock).isDisplayVolume(); + + Mockito.doNothing().when(volumeServiceMock).destroyVolume(volumeMockId); + Mockito.doNothing().when(resourceLimitServiceMock).decrementResourceCount(accountMockId, ResourceType.volume, true); + Mockito.doNothing().when(resourceLimitServiceMock).decrementResourceCount(accountMockId, ResourceType.primary_storage, true, volumeSizeMock); + } + + @Test + public void expungeVolumesInPrimaryStorageIfNeededTestVolumeNotInPrimaryDataStore() throws InterruptedException, ExecutionException { + Mockito.doReturn(asyncCallFutureVolumeapiResultMock).when(volumeServiceMock).expungeVolumeAsync(volumeInfoMock); + Mockito.doReturn(null).when(volumeDataFactoryMock).getVolume(volumeMockId, DataStoreRole.Primary); + + volumeApiServiceImpl.expungeVolumesInPrimaryStorageIfNeeded(volumeVoMock); + + Mockito.verify(volumeServiceMock, Mockito.times(0)).expungeVolumeAsync(volumeInfoMock); + Mockito.verify(asyncCallFutureVolumeapiResultMock, Mockito.times(0)).get(); + } + + @Test + public void expungeVolumesInPrimaryStorageIfNeededTestVolumeInPrimaryDataStore() throws InterruptedException, ExecutionException { + Mockito.doReturn(asyncCallFutureVolumeapiResultMock).when(volumeServiceMock).expungeVolumeAsync(volumeInfoMock); + Mockito.doReturn(volumeInfoMock).when(volumeDataFactoryMock).getVolume(volumeMockId, DataStoreRole.Primary); + + volumeApiServiceImpl.expungeVolumesInPrimaryStorageIfNeeded(volumeVoMock); + + Mockito.verify(volumeServiceMock, Mockito.times(1)).expungeVolumeAsync(volumeInfoMock); + Mockito.verify(asyncCallFutureVolumeapiResultMock, Mockito.times(1)).get(); + } + + @Test(expected = InterruptedException.class) + public void expungeVolumesInPrimaryStorageIfNeededTestThrowingInterruptedException() throws InterruptedException, ExecutionException { + Mockito.doReturn(asyncCallFutureVolumeapiResultMock).when(volumeServiceMock).expungeVolumeAsync(volumeInfoMock); + Mockito.doReturn(volumeInfoMock).when(volumeDataFactoryMock).getVolume(volumeMockId, DataStoreRole.Primary); + Mockito.doThrow(InterruptedException.class).when(asyncCallFutureVolumeapiResultMock).get(); + + volumeApiServiceImpl.expungeVolumesInPrimaryStorageIfNeeded(volumeVoMock); + } + + @Test(expected = ExecutionException.class) + public void expungeVolumesInPrimaryStorageIfNeededTestThrowingExecutionException() throws InterruptedException, ExecutionException { + Mockito.doReturn(asyncCallFutureVolumeapiResultMock).when(volumeServiceMock).expungeVolumeAsync(volumeInfoMock); + Mockito.doReturn(volumeInfoMock).when(volumeDataFactoryMock).getVolume(volumeMockId, DataStoreRole.Primary); + Mockito.doThrow(ExecutionException.class).when(asyncCallFutureVolumeapiResultMock).get(); + + volumeApiServiceImpl.expungeVolumesInPrimaryStorageIfNeeded(volumeVoMock); + } + + @Test + public void expungeVolumesInSecondaryStorageIfNeededTestVolumeNotFoundInSecondaryStorage() throws InterruptedException, ExecutionException { + Mockito.doReturn(asyncCallFutureVolumeapiResultMock).when(volumeServiceMock).expungeVolumeAsync(volumeInfoMock); + Mockito.doReturn(null).when(volumeDataFactoryMock).getVolume(volumeMockId, DataStoreRole.Image); + Mockito.doNothing().when(resourceLimitServiceMock).decrementResourceCount(accountMockId, ResourceType.secondary_storage, volumeSizeMock); + Mockito.doReturn(accountMockId).when(volumeInfoMock).getAccountId(); + Mockito.doReturn(volumeSizeMock).when(volumeInfoMock).getSize(); + + volumeApiServiceImpl.expungeVolumesInSecondaryStorageIfNeeded(volumeVoMock); + + Mockito.verify(volumeServiceMock, Mockito.times(0)).expungeVolumeAsync(volumeInfoMock); + Mockito.verify(asyncCallFutureVolumeapiResultMock, Mockito.times(0)).get(); + Mockito.verify(resourceLimitServiceMock, Mockito.times(0)).decrementResourceCount(accountMockId, ResourceType.secondary_storage, volumeSizeMock); + } + + @Test + public void expungeVolumesInSecondaryStorageIfNeededTestVolumeFoundInSecondaryStorage() throws InterruptedException, ExecutionException { + Mockito.doReturn(asyncCallFutureVolumeapiResultMock).when(volumeServiceMock).expungeVolumeAsync(volumeInfoMock); + Mockito.doReturn(volumeInfoMock).when(volumeDataFactoryMock).getVolume(volumeMockId, DataStoreRole.Image); + Mockito.doNothing().when(resourceLimitServiceMock).decrementResourceCount(accountMockId, ResourceType.secondary_storage, volumeSizeMock); + Mockito.doReturn(accountMockId).when(volumeInfoMock).getAccountId(); + Mockito.doReturn(volumeSizeMock).when(volumeInfoMock).getSize(); + + volumeApiServiceImpl.expungeVolumesInSecondaryStorageIfNeeded(volumeVoMock); + + Mockito.verify(volumeServiceMock, Mockito.times(1)).expungeVolumeAsync(volumeInfoMock); + Mockito.verify(asyncCallFutureVolumeapiResultMock, Mockito.times(1)).get(); + Mockito.verify(resourceLimitServiceMock, Mockito.times(1)).decrementResourceCount(accountMockId, ResourceType.secondary_storage, volumeSizeMock); + } + + @Test(expected = InterruptedException.class) + public void expungeVolumesInSecondaryStorageIfNeededTestthrowinInterruptedException() throws InterruptedException, ExecutionException { + Mockito.doReturn(asyncCallFutureVolumeapiResultMock).when(volumeServiceMock).expungeVolumeAsync(volumeInfoMock); + Mockito.doReturn(volumeInfoMock).when(volumeDataFactoryMock).getVolume(volumeMockId, DataStoreRole.Image); + Mockito.doNothing().when(resourceLimitServiceMock).decrementResourceCount(accountMockId, ResourceType.secondary_storage, volumeSizeMock); + Mockito.doReturn(accountMockId).when(volumeInfoMock).getAccountId(); + Mockito.doReturn(volumeSizeMock).when(volumeInfoMock).getSize(); + + Mockito.doThrow(InterruptedException.class).when(asyncCallFutureVolumeapiResultMock).get(); + + volumeApiServiceImpl.expungeVolumesInSecondaryStorageIfNeeded(volumeVoMock); + + } + + @Test(expected = ExecutionException.class) + public void expungeVolumesInSecondaryStorageIfNeededTestthrowinExecutionException() throws InterruptedException, ExecutionException { + Mockito.doReturn(asyncCallFutureVolumeapiResultMock).when(volumeServiceMock).expungeVolumeAsync(volumeInfoMock); + Mockito.doReturn(volumeInfoMock).when(volumeDataFactoryMock).getVolume(volumeMockId, DataStoreRole.Image); + Mockito.doNothing().when(resourceLimitServiceMock).decrementResourceCount(accountMockId, ResourceType.secondary_storage, volumeSizeMock); + Mockito.doReturn(accountMockId).when(volumeInfoMock).getAccountId(); + Mockito.doReturn(volumeSizeMock).when(volumeInfoMock).getSize(); + + Mockito.doThrow(ExecutionException.class).when(asyncCallFutureVolumeapiResultMock).get(); + + volumeApiServiceImpl.expungeVolumesInSecondaryStorageIfNeeded(volumeVoMock); + + } + + @Test + public void cleanVolumesCacheTest() { + List volumeInfos = new ArrayList<>(); + VolumeInfo volumeInfoMock1 = Mockito.mock(VolumeInfo.class); + VolumeInfo volumeInfoMock2 = Mockito.mock(VolumeInfo.class); + + DataStore dataStoreMock1 = Mockito.mock(DataStore.class); + DataStore dataStoreMock2 = Mockito.mock(DataStore.class); + Mockito.doReturn(dataStoreMock1).when(volumeInfoMock1).getDataStore(); + Mockito.doReturn(dataStoreMock2).when(volumeInfoMock2).getDataStore(); + + volumeInfos.add(volumeInfoMock1); + volumeInfos.add(volumeInfoMock2); + + Mockito.doReturn(volumeInfos).when(volumeDataFactoryMock).listVolumeOnCache(volumeMockId); + + volumeApiServiceImpl.cleanVolumesCache(volumeVoMock); + + Mockito.verify(dataStoreMock1).getName(); + Mockito.verify(dataStoreMock2).getName(); + + Mockito.verify(volumeInfoMock1).delete(); + Mockito.verify(volumeInfoMock2).delete(); + } + + @Test + public void deleteVolumeTestVolumeStateAllocated() throws InterruptedException, ExecutionException, NoTransitionException { + Mockito.doReturn(Volume.State.Allocated).when(volumeVoMock).getState(); + + Mockito.doReturn(volumeVoMock).when(volumeApiServiceImpl).retrieveAndValidateVolume(volumeMockId, accountMock); + Mockito.doNothing().when(volumeApiServiceImpl).destroyVolumeIfPossible(volumeVoMock); + Mockito.doNothing().when(volumeApiServiceImpl).expungeVolumesInPrimaryStorageIfNeeded(volumeVoMock); + Mockito.doNothing().when(volumeApiServiceImpl).expungeVolumesInSecondaryStorageIfNeeded(volumeVoMock); + Mockito.doNothing().when(volumeApiServiceImpl).cleanVolumesCache(volumeVoMock); + + Mockito.doReturn(true).when(volumeDaoMock).remove(volumeMockId); + Mockito.doReturn(true).when(volumeApiServiceImpl).stateTransitTo(volumeVoMock, Volume.Event.DestroyRequested); + + boolean result = volumeApiServiceImpl.deleteVolume(volumeMockId, accountMock); + + Assert.assertTrue(result); + Mockito.verify(volumeApiServiceImpl).retrieveAndValidateVolume(volumeMockId, accountMock); + Mockito.verify(volumeApiServiceImpl).destroyVolumeIfPossible(volumeVoMock); + Mockito.verify(volumeDaoMock).remove(volumeMockId); + Mockito.verify(volumeApiServiceImpl).stateTransitTo(volumeVoMock, Volume.Event.DestroyRequested); + + Mockito.verify(volumeApiServiceImpl, Mockito.times(0)).expungeVolumesInPrimaryStorageIfNeeded(volumeVoMock); + Mockito.verify(volumeApiServiceImpl, Mockito.times(0)).expungeVolumesInSecondaryStorageIfNeeded(volumeVoMock); + Mockito.verify(volumeApiServiceImpl, Mockito.times(0)).cleanVolumesCache(volumeVoMock); + } + + @Test + public void deleteVolumeTestVolumeStateReady() throws InterruptedException, ExecutionException, NoTransitionException { + Mockito.doReturn(Volume.State.Ready).when(volumeVoMock).getState(); + + Mockito.doReturn(volumeVoMock).when(volumeApiServiceImpl).retrieveAndValidateVolume(volumeMockId, accountMock); + Mockito.doNothing().when(volumeApiServiceImpl).destroyVolumeIfPossible(volumeVoMock); + Mockito.doNothing().when(volumeApiServiceImpl).expungeVolumesInPrimaryStorageIfNeeded(volumeVoMock); + Mockito.doNothing().when(volumeApiServiceImpl).expungeVolumesInSecondaryStorageIfNeeded(volumeVoMock); + Mockito.doNothing().when(volumeApiServiceImpl).cleanVolumesCache(volumeVoMock); + + Mockito.doReturn(true).when(volumeDaoMock).remove(volumeMockId); + Mockito.doReturn(true).when(volumeApiServiceImpl).stateTransitTo(volumeVoMock, Volume.Event.DestroyRequested); + + boolean result = volumeApiServiceImpl.deleteVolume(volumeMockId, accountMock); + + Assert.assertTrue(result); + Mockito.verify(volumeApiServiceImpl).retrieveAndValidateVolume(volumeMockId, accountMock); + Mockito.verify(volumeApiServiceImpl).destroyVolumeIfPossible(volumeVoMock); + Mockito.verify(volumeDaoMock, Mockito.times(0)).remove(volumeMockId); + Mockito.verify(volumeApiServiceImpl, Mockito.times(0)).stateTransitTo(volumeVoMock, Volume.Event.DestroyRequested); + + Mockito.verify(volumeApiServiceImpl, Mockito.times(1)).expungeVolumesInPrimaryStorageIfNeeded(volumeVoMock); + Mockito.verify(volumeApiServiceImpl, Mockito.times(1)).expungeVolumesInSecondaryStorageIfNeeded(volumeVoMock); + Mockito.verify(volumeApiServiceImpl, Mockito.times(1)).cleanVolumesCache(volumeVoMock); + } + + @Test + public void deleteVolumeTestVolumeStateReadyThrowingInterruptedException() throws InterruptedException, ExecutionException, NoTransitionException { + Mockito.doReturn(Volume.State.Ready).when(volumeVoMock).getState(); + + Mockito.doReturn(volumeVoMock).when(volumeApiServiceImpl).retrieveAndValidateVolume(volumeMockId, accountMock); + Mockito.doNothing().when(volumeApiServiceImpl).destroyVolumeIfPossible(volumeVoMock); + Mockito.doThrow(InterruptedException.class).when(volumeApiServiceImpl).expungeVolumesInPrimaryStorageIfNeeded(volumeVoMock); + + Mockito.doReturn(true).when(volumeDaoMock).remove(volumeMockId); + Mockito.doReturn(true).when(volumeApiServiceImpl).stateTransitTo(volumeVoMock, Volume.Event.DestroyRequested); + + boolean result = volumeApiServiceImpl.deleteVolume(volumeMockId, accountMock); + + Assert.assertFalse(result); + Mockito.verify(volumeApiServiceImpl).retrieveAndValidateVolume(volumeMockId, accountMock); + Mockito.verify(volumeApiServiceImpl).destroyVolumeIfPossible(volumeVoMock); + Mockito.verify(volumeDaoMock, Mockito.times(0)).remove(volumeMockId); + Mockito.verify(volumeApiServiceImpl, Mockito.times(0)).stateTransitTo(volumeVoMock, Volume.Event.DestroyRequested); + } + + @Test + public void deleteVolumeTestVolumeStateReadyThrowingExecutionException() throws InterruptedException, ExecutionException, NoTransitionException { + Mockito.doReturn(Volume.State.Ready).when(volumeVoMock).getState(); + + Mockito.doReturn(volumeVoMock).when(volumeApiServiceImpl).retrieveAndValidateVolume(volumeMockId, accountMock); + Mockito.doNothing().when(volumeApiServiceImpl).destroyVolumeIfPossible(volumeVoMock); + Mockito.doThrow(ExecutionException.class).when(volumeApiServiceImpl).expungeVolumesInPrimaryStorageIfNeeded(volumeVoMock); + + Mockito.doReturn(true).when(volumeDaoMock).remove(volumeMockId); + Mockito.doReturn(true).when(volumeApiServiceImpl).stateTransitTo(volumeVoMock, Volume.Event.DestroyRequested); + + boolean result = volumeApiServiceImpl.deleteVolume(volumeMockId, accountMock); + + Assert.assertFalse(result); + Mockito.verify(volumeApiServiceImpl).retrieveAndValidateVolume(volumeMockId, accountMock); + Mockito.verify(volumeApiServiceImpl).destroyVolumeIfPossible(volumeVoMock); + Mockito.verify(volumeDaoMock, Mockito.times(0)).remove(volumeMockId); + Mockito.verify(volumeApiServiceImpl, Mockito.times(0)).stateTransitTo(volumeVoMock, Volume.Event.DestroyRequested); + } + + @Test + public void deleteVolumeTestVolumeStateReadyThrowingNoTransitionException() throws InterruptedException, ExecutionException, NoTransitionException { + Mockito.doReturn(Volume.State.Ready).when(volumeVoMock).getState(); + + Mockito.doReturn(volumeVoMock).when(volumeApiServiceImpl).retrieveAndValidateVolume(volumeMockId, accountMock); + Mockito.doNothing().when(volumeApiServiceImpl).destroyVolumeIfPossible(volumeVoMock); + Mockito.doThrow(NoTransitionException.class).when(volumeApiServiceImpl).expungeVolumesInPrimaryStorageIfNeeded(volumeVoMock); + + Mockito.doReturn(true).when(volumeDaoMock).remove(volumeMockId); + Mockito.doReturn(true).when(volumeApiServiceImpl).stateTransitTo(volumeVoMock, Volume.Event.DestroyRequested); + + boolean result = volumeApiServiceImpl.deleteVolume(volumeMockId, accountMock); + + Assert.assertFalse(result); + Mockito.verify(volumeApiServiceImpl).retrieveAndValidateVolume(volumeMockId, accountMock); + Mockito.verify(volumeApiServiceImpl).destroyVolumeIfPossible(volumeVoMock); + Mockito.verify(volumeDaoMock, Mockito.times(0)).remove(volumeMockId); + Mockito.verify(volumeApiServiceImpl, Mockito.times(0)).stateTransitTo(volumeVoMock, Volume.Event.DestroyRequested); + } + + @Test(expected = RuntimeException.class) + public void deleteVolumeTestVolumeStateReadyThrowingRuntimeException() throws InterruptedException, ExecutionException, NoTransitionException { + Mockito.doReturn(Volume.State.Ready).when(volumeVoMock).getState(); + + Mockito.doReturn(volumeVoMock).when(volumeApiServiceImpl).retrieveAndValidateVolume(volumeMockId, accountMock); + Mockito.doNothing().when(volumeApiServiceImpl).destroyVolumeIfPossible(volumeVoMock); + Mockito.doThrow(RuntimeException.class).when(volumeApiServiceImpl).expungeVolumesInPrimaryStorageIfNeeded(volumeVoMock); + + Mockito.doReturn(true).when(volumeDaoMock).remove(volumeMockId); + Mockito.doReturn(true).when(volumeApiServiceImpl).stateTransitTo(volumeVoMock, Volume.Event.DestroyRequested); + + volumeApiServiceImpl.deleteVolume(volumeMockId, accountMock); + } }