http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java ---------------------------------------------------------------------- diff --git a/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java b/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java index df2d37f..0d96660 100644 --- a/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java +++ b/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java @@ -918,6 +918,25 @@ public class TemplateServiceImpl implements TemplateService { return copyAsync(srcTemplate, srcTemplate, (DataStore)pool); } + @Override + public AsyncCallFuture<TemplateApiResult> deleteTemplateOnPrimary(TemplateInfo template, StoragePool pool) { + TemplateObject templateObject = (TemplateObject)_templateFactory.getTemplate(template.getId(), (DataStore)pool); + + templateObject.processEvent(ObjectInDataStoreStateMachine.Event.DestroyRequested); + + DataStore dataStore = _storeMgr.getPrimaryDataStore(pool.getId()); + + AsyncCallFuture<TemplateApiResult> future = new AsyncCallFuture<>(); + TemplateOpContext<TemplateApiResult> context = new TemplateOpContext<>(null, templateObject, future); + AsyncCallbackDispatcher<TemplateServiceImpl, CommandResult> caller = AsyncCallbackDispatcher.create(this); + + caller.setCallback(caller.getTarget().deleteTemplateCallback(null, null)).setContext(context); + + dataStore.getDriver().deleteAsync(dataStore, templateObject, caller); + + return future; + } + protected Void copyTemplateCallBack(AsyncCallbackDispatcher<TemplateServiceImpl, CopyCommandResult> callback, TemplateOpContext<TemplateApiResult> context) { TemplateInfo destTemplate = context.getTemplate(); CopyCommandResult result = callback.getResult();
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java ---------------------------------------------------------------------- diff --git a/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java b/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java index 473959b..6e78f19 100644 --- a/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java +++ b/engine/storage/image/src/org/apache/cloudstack/storage/image/store/TemplateObject.java @@ -28,7 +28,6 @@ import org.apache.log4j.Logger; import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; -import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; import org.apache.cloudstack.storage.command.CopyCmdAnswer; import org.apache.cloudstack.storage.datastore.ObjectInDataStoreManager; @@ -54,6 +53,9 @@ import com.cloud.utils.component.ComponentContext; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.fsm.NoTransitionException; +import com.google.common.base.Strings; + +@SuppressWarnings("serial") public class TemplateObject implements TemplateInfo { private static final Logger s_logger = Logger.getLogger(TemplateObject.class); private VMTemplateVO imageVO; @@ -189,12 +191,15 @@ public class TemplateObject implements TemplateInfo { TemplateObjectTO newTemplate = (TemplateObjectTO)cpyAnswer.getNewData(); VMTemplateStoragePoolVO templatePoolRef = templatePoolDao.findByPoolTemplate(getDataStore().getId(), getId()); templatePoolRef.setDownloadPercent(100); - if (newTemplate.getSize() != null) { - templatePoolRef.setTemplateSize(newTemplate.getSize()); - } + + setTemplateSizeIfNeeded(newTemplate, templatePoolRef); + templatePoolRef.setDownloadState(Status.DOWNLOADED); - templatePoolRef.setLocalDownloadPath(newTemplate.getPath()); - templatePoolRef.setInstallPath(newTemplate.getPath()); + + setDownloadPathIfNeeded(newTemplate, templatePoolRef); + + setInstallPathIfNeeded(newTemplate, templatePoolRef); + templatePoolDao.update(templatePoolRef.getId(), templatePoolRef); } } else if (getDataStore().getRole() == DataStoreRole.Image || getDataStore().getRole() == DataStoreRole.ImageCache) { @@ -243,6 +248,33 @@ public class TemplateObject implements TemplateInfo { } } + /** + * In the case of managed storage, the install path may already be specified (by the storage plug-in), so do not overwrite it. + */ + private void setInstallPathIfNeeded(TemplateObjectTO template, VMTemplateStoragePoolVO templatePoolRef) { + if (Strings.isNullOrEmpty(templatePoolRef.getInstallPath())) { + templatePoolRef.setInstallPath(template.getPath()); + } + } + + /** + * In the case of managed storage, the local download path may already be specified (by the storage plug-in), so do not overwrite it. + */ + private void setDownloadPathIfNeeded(TemplateObjectTO template, VMTemplateStoragePoolVO templatePoolRef) { + if (Strings.isNullOrEmpty(templatePoolRef.getLocalDownloadPath())) { + templatePoolRef.setLocalDownloadPath(template.getPath()); + } + } + + /** + * In the case of managed storage, the template size may already be specified (by the storage plug-in), so do not overwrite it. + */ + private void setTemplateSizeIfNeeded(TemplateObjectTO template, VMTemplateStoragePoolVO templatePoolRef) { + if (templatePoolRef.getTemplateSize() == 0 && template.getSize() != null) { + templatePoolRef.setTemplateSize(template.getSize()); + } + } + @Override public void incRefCount() { if (dataStore == null) { @@ -299,28 +331,17 @@ public class TemplateObject implements TemplateInfo { @Override public String getInstallPath() { - if (installPath != null) + if (installPath != null) { return installPath; + } if (dataStore == null) { return null; } - // managed primary data stores should not have an install path - if (dataStore instanceof PrimaryDataStore) { - PrimaryDataStore primaryDataStore = (PrimaryDataStore)dataStore; - - Map<String, String> details = primaryDataStore.getDetails(); - - boolean managed = details != null && Boolean.parseBoolean(details.get(PrimaryDataStore.MANAGED)); - - if (managed) { - return null; - } - } - DataObjectInStore obj = objectInStoreMgr.findObject(this, dataStore); - return obj.getInstallPath(); + + return obj != null ? obj.getInstallPath() : null; } public void setInstallPath(String installPath) { @@ -435,7 +456,7 @@ public class TemplateObject implements TemplateInfo { } @Override - public Map getDetails() { + public Map<String, String> getDetails() { return imageVO.getDetails(); } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakePrimaryDataStoreDriver.java ---------------------------------------------------------------------- diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakePrimaryDataStoreDriver.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakePrimaryDataStoreDriver.java index 1f1ba24..a86b321 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakePrimaryDataStoreDriver.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakePrimaryDataStoreDriver.java @@ -28,6 +28,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; import org.apache.cloudstack.storage.command.CommandResult; @@ -44,8 +45,8 @@ public class FakePrimaryDataStoreDriver implements PrimaryDataStoreDriver { boolean snapshotResult = true; @Override - public ChapInfo getChapInfo(VolumeInfo volumeInfo) { - return null; // To change body of implemented methods, use File | Settings | File Templates. + public ChapInfo getChapInfo(DataObject dataObject) { + return null; } @Override @@ -65,8 +66,13 @@ public class FakePrimaryDataStoreDriver implements PrimaryDataStoreDriver { } @Override - public long getVolumeSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool) { - return volume.getSize(); + public long getDataObjectSizeIncludingHypervisorSnapshotReserve(DataObject dataObject, StoragePool pool) { + return dataObject.getSize(); + } + + @Override + public long getBytesRequiredForTemplate(TemplateInfo templateInfo, StoragePool storagePool) { + return 0L; } @Override @@ -90,23 +96,21 @@ public class FakePrimaryDataStoreDriver implements PrimaryDataStoreDriver { } @Override - public void revertSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CommandResult> callback) { - //To change body of implemented methods use File | Settings | File Templates. + public void revertSnapshot(SnapshotInfo snapshotOnImageStore, SnapshotInfo snapshotOnPrimaryStore, AsyncCompletionCallback<CommandResult> callback) { } @Override public DataTO getTO(DataObject data) { - return null; //To change body of implemented methods use File | Settings | File Templates. + return null; } @Override public DataStoreTO getStoreTO(DataStore store) { - return null; //To change body of implemented methods use File | Settings | File Templates. + return null; } @Override public void createAsync(DataStore store, DataObject data, AsyncCompletionCallback<CreateCmdResult> callback) { - //To change body of implemented methods use File | Settings | File Templates. } @Override @@ -119,22 +123,19 @@ public class FakePrimaryDataStoreDriver implements PrimaryDataStoreDriver { @Override public void copyAsync(DataObject srcdata, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback) { - //To change body of implemented methods use File | Settings | File Templates. } @Override public boolean canCopy(DataObject srcData, DataObject destData) { - return false; //To change body of implemented methods use File | Settings | File Templates. + return false; } @Override public void resize(DataObject data, AsyncCompletionCallback<CreateCmdResult> callback) { - //To change body of implemented methods use File | Settings | File Templates. } @Override public Map<String, String> getCapabilities() { - // TODO Auto-generated method stub return null; } } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/StorageSystemSnapshotStrategy.java ---------------------------------------------------------------------- diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/StorageSystemSnapshotStrategy.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/StorageSystemSnapshotStrategy.java index c6960eb..d74ee57 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/StorageSystemSnapshotStrategy.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/StorageSystemSnapshotStrategy.java @@ -16,14 +16,16 @@ // under the License. package org.apache.cloudstack.storage.snapshot; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Random; import javax.inject.Inject; import org.apache.log4j.Logger; -import org.springframework.stereotype.Component; import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreCapabilities; @@ -38,11 +40,15 @@ import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService; import org.apache.cloudstack.storage.command.SnapshotAndCopyAnswer; import org.apache.cloudstack.storage.command.SnapshotAndCopyCommand; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; -import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.springframework.stereotype.Component; + +import com.google.common.base.Optional; + import com.cloud.agent.AgentManager; import com.cloud.agent.api.to.DiskTO; +import com.cloud.dc.dao.ClusterDao; import com.cloud.exception.InvalidParameterValueException; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; @@ -71,18 +77,18 @@ import com.cloud.vm.dao.VMInstanceDao; public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase { private static final Logger s_logger = Logger.getLogger(StorageSystemSnapshotStrategy.class); - @Inject private AgentManager _agentMgr; - @Inject private DataStoreManager _dataStoreMgr; - @Inject private HostDao _hostDao; - @Inject private ManagementService _mgr; - @Inject private PrimaryDataStoreDao _storagePoolDao; - @Inject private SnapshotDao _snapshotDao; - @Inject private SnapshotDataFactory _snapshotDataFactory; - @Inject private SnapshotDataStoreDao _snapshotStoreDao; - @Inject private SnapshotDetailsDao _snapshotDetailsDao; - @Inject private VMInstanceDao _vmInstanceDao; - @Inject private VolumeDao _volumeDao; - @Inject private VolumeService _volService; + @Inject private AgentManager agentMgr; + @Inject private ClusterDao clusterDao; + @Inject private DataStoreManager dataStoreMgr; + @Inject private HostDao hostDao; + @Inject private ManagementService mgr; + @Inject private PrimaryDataStoreDao storagePoolDao; + @Inject private SnapshotDao snapshotDao; + @Inject private SnapshotDataFactory snapshotDataFactory; + @Inject private SnapshotDetailsDao snapshotDetailsDao; + @Inject private VMInstanceDao vmInstanceDao; + @Inject private VolumeDao volumeDao; + @Inject private VolumeService volService; @Override public SnapshotInfo backupSnapshot(SnapshotInfo snapshotInfo) { @@ -91,14 +97,14 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase { @Override public boolean deleteSnapshot(Long snapshotId) { - SnapshotVO snapshotVO = _snapshotDao.findById(snapshotId); + SnapshotVO snapshotVO = snapshotDao.findById(snapshotId); if (Snapshot.State.Destroyed.equals(snapshotVO.getState())) { return true; } if (Snapshot.State.Error.equals(snapshotVO.getState())) { - _snapshotDao.remove(snapshotId); + snapshotDao.remove(snapshotId); return true; } @@ -107,12 +113,12 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase { throw new InvalidParameterValueException("Unable to delete snapshotshot " + snapshotId + " because it is in the following state: " + snapshotVO.getState()); } - SnapshotObject snapshotObj = (SnapshotObject)_snapshotDataFactory.getSnapshot(snapshotId, DataStoreRole.Primary); + SnapshotObject snapshotObj = (SnapshotObject)snapshotDataFactory.getSnapshot(snapshotId, DataStoreRole.Primary); if (snapshotObj == null) { s_logger.debug("Can't find snapshot; deleting it in DB"); - _snapshotDao.remove(snapshotId); + snapshotDao.remove(snapshotId); return true; } @@ -165,7 +171,7 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase { throw new CloudRuntimeException("Only the " + ImageFormat.VHD.toString() + " image type is currently supported."); } - SnapshotVO snapshotVO = _snapshotDao.acquireInLockTable(snapshotInfo.getId()); + SnapshotVO snapshotVO = snapshotDao.acquireInLockTable(snapshotInfo.getId()); if (snapshotVO == null) { throw new CloudRuntimeException("Failed to acquire lock on the following snapshot: " + snapshotInfo.getId()); @@ -176,7 +182,24 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase { try { volumeInfo.stateTransit(Volume.Event.SnapshotRequested); - // tell the storage driver to create a back-end volume (eventually used to create a new SR on and to copy the VM snapshot VDI to) + // only XenServer is currently supported + HostVO hostVO = getHost(volumeInfo.getId()); + + boolean canStorageSystemCreateVolumeFromSnapshot = canStorageSystemCreateVolumeFromSnapshot(volumeInfo.getPoolId()); + boolean computeClusterSupportsResign = clusterDao.computeWhetherClusterSupportsResigning(hostVO.getClusterId()); + + // if canStorageSystemCreateVolumeFromSnapshot && computeClusterSupportsResign, then take a back-end snapshot or create a back-end clone; + // else, just create a new back-end volume (eventually used to create a new SR on and to copy a VDI to) + + if (canStorageSystemCreateVolumeFromSnapshot && computeClusterSupportsResign) { + SnapshotDetailsVO snapshotDetail = new SnapshotDetailsVO(snapshotInfo.getId(), + "takeSnapshot", + Boolean.TRUE.toString(), + false); + + snapshotDetailsDao.persist(snapshotDetail); + } + result = snapshotSvr.takeSnapshot(snapshotInfo); if (result.isFailed()) { @@ -185,9 +208,9 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase { throw new CloudRuntimeException(result.getResult()); } - // send a command to XenServer to create a VM snapshot on the applicable SR (get back the VDI UUID of the VM snapshot) - - performSnapshotAndCopyOnHostSide(volumeInfo, snapshotInfo); + if (!canStorageSystemCreateVolumeFromSnapshot || !computeClusterSupportsResign) { + performSnapshotAndCopyOnHostSide(volumeInfo, snapshotInfo); + } markAsBackedUp((SnapshotObject)result.getSnashot()); } @@ -199,19 +222,35 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase { volumeInfo.stateTransit(Volume.Event.OperationFailed); } - _snapshotDao.releaseFromLockTable(snapshotInfo.getId()); + snapshotDao.releaseFromLockTable(snapshotInfo.getId()); } return snapshotInfo; } + private boolean canStorageSystemCreateVolumeFromSnapshot(long storagePoolId) { + boolean supportsCloningVolumeFromSnapshot = false; + + DataStore dataStore = dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary); + + Map<String, String> mapCapabilities = dataStore.getDriver().getCapabilities(); + + if (mapCapabilities != null) { + String value = mapCapabilities.get(DataStoreCapabilities.CAN_CREATE_VOLUME_FROM_SNAPSHOT.toString()); + + supportsCloningVolumeFromSnapshot = Boolean.valueOf(value); + } + + return supportsCloningVolumeFromSnapshot; + } + private void performSnapshotAndCopyOnHostSide(VolumeInfo volumeInfo, SnapshotInfo snapshotInfo) { Map<String, String> sourceDetails = null; - VolumeVO volumeVO = _volumeDao.findById(volumeInfo.getId()); + VolumeVO volumeVO = volumeDao.findById(volumeInfo.getId()); Long vmInstanceId = volumeVO.getInstanceId(); - VMInstanceVO vmInstanceVO = _vmInstanceDao.findById(vmInstanceId); + VMInstanceVO vmInstanceVO = vmInstanceDao.findById(vmInstanceId); Long hostId = null; @@ -233,11 +272,30 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase { sourceDetails = getSourceDetails(volumeInfo); } - HostVO hostVO = getHost(hostId, volumeVO); + HostVO hostVO = null; + + if (hostId != null) { + hostVO = hostDao.findById(hostId); + } + else { + Optional<HostVO> optHostVO = getHost(volumeInfo.getDataCenterId(), false); + + if (optHostVO.isPresent()) { + hostVO = optHostVO.get(); + } + } + + if (hostVO == null) { + final String errMsg = "Unable to locate an applicable host"; + + s_logger.error("performSnapshotAndCopyOnHostSide: " + errMsg); + + throw new CloudRuntimeException(errMsg); + } long storagePoolId = volumeVO.getPoolId(); - StoragePoolVO storagePoolVO = _storagePoolDao.findById(storagePoolId); - DataStore dataStore = _dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary); + StoragePoolVO storagePoolVO = storagePoolDao.findById(storagePoolId); + DataStore dataStore = dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary); Map<String, String> destDetails = getDestDetails(storagePoolVO, snapshotInfo); @@ -248,23 +306,23 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase { try { // if sourceDetails != null, we need to connect the host(s) to the volume if (sourceDetails != null) { - _volService.grantAccess(volumeInfo, hostVO, dataStore); + volService.grantAccess(volumeInfo, hostVO, dataStore); } - _volService.grantAccess(snapshotInfo, hostVO, dataStore); + volService.grantAccess(snapshotInfo, hostVO, dataStore); - snapshotAndCopyAnswer = (SnapshotAndCopyAnswer)_agentMgr.send(hostVO.getId(), snapshotAndCopyCommand); + snapshotAndCopyAnswer = (SnapshotAndCopyAnswer)agentMgr.send(hostVO.getId(), snapshotAndCopyCommand); } catch (Exception ex) { throw new CloudRuntimeException(ex.getMessage()); } finally { try { - _volService.revokeAccess(snapshotInfo, hostVO, dataStore); + volService.revokeAccess(snapshotInfo, hostVO, dataStore); // if sourceDetails != null, we need to disconnect the host(s) from the volume if (sourceDetails != null) { - _volService.revokeAccess(volumeInfo, hostVO, dataStore); + volService.revokeAccess(volumeInfo, hostVO, dataStore); } } catch (Exception ex) { @@ -292,22 +350,22 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase { path, false); - _snapshotDetailsDao.persist(snapshotDetail); + snapshotDetailsDao.persist(snapshotDetail); } private Map<String, String> getSourceDetails(VolumeInfo volumeInfo) { - Map<String, String> sourceDetails = new HashMap<String, String>(); + Map<String, String> sourceDetails = new HashMap<>(); - VolumeVO volumeVO = _volumeDao.findById(volumeInfo.getId()); + VolumeVO volumeVO = volumeDao.findById(volumeInfo.getId()); long storagePoolId = volumeVO.getPoolId(); - StoragePoolVO storagePoolVO = _storagePoolDao.findById(storagePoolId); + StoragePoolVO storagePoolVO = storagePoolDao.findById(storagePoolId); sourceDetails.put(DiskTO.STORAGE_HOST, storagePoolVO.getHostAddress()); sourceDetails.put(DiskTO.STORAGE_PORT, String.valueOf(storagePoolVO.getPort())); sourceDetails.put(DiskTO.IQN, volumeVO.get_iScsiName()); - ChapInfo chapInfo = _volService.getChapInfo(volumeInfo, volumeInfo.getDataStore()); + ChapInfo chapInfo = volService.getChapInfo(volumeInfo, volumeInfo.getDataStore()); if (chapInfo != null) { sourceDetails.put(DiskTO.CHAP_INITIATOR_USERNAME, chapInfo.getInitiatorUsername()); @@ -320,7 +378,7 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase { } private Map<String, String> getDestDetails(StoragePoolVO storagePoolVO, SnapshotInfo snapshotInfo) { - Map<String, String> destDetails = new HashMap<String, String>(); + Map<String, String> destDetails = new HashMap<>(); destDetails.put(DiskTO.STORAGE_HOST, storagePoolVO.getHostAddress()); destDetails.put(DiskTO.STORAGE_PORT, String.valueOf(storagePoolVO.getPort())); @@ -338,7 +396,7 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase { } private String getProperty(long snapshotId, String property) { - SnapshotDetailsVO snapshotDetails = _snapshotDetailsDao.findDetail(snapshotId, property); + SnapshotDetailsVO snapshotDetails = snapshotDetailsDao.findDetail(snapshotId, property); if (snapshotDetails != null) { return snapshotDetails.getValue(); @@ -347,38 +405,87 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase { return null; } - private HostVO getHost(Long hostId, VolumeVO volumeVO) { - HostVO hostVO = _hostDao.findById(hostId); + private HostVO getHost(long volumeId) { + VolumeVO volumeVO = volumeDao.findById(volumeId); + + Long vmInstanceId = volumeVO.getInstanceId(); + VMInstanceVO vmInstanceVO = vmInstanceDao.findById(vmInstanceId); + + Long hostId = null; + + // if the volume to snapshot is associated with a VM + if (vmInstanceVO != null) { + hostId = vmInstanceVO.getHostId(); + + // if the VM is not associated with a host + if (hostId == null) { + hostId = vmInstanceVO.getLastHostId(); + } + } + + return getHost(volumeVO.getDataCenterId(), hostId); + } + + private HostVO getHost(long zoneId, Long hostId) { + Optional<HostVO> optHostVO = getHost(zoneId, true); + + if (optHostVO.isPresent()) { + return optHostVO.get(); + } + + HostVO hostVO = hostDao.findById(hostId); if (hostVO != null) { return hostVO; } - // pick a host in any XenServer cluster that's in the applicable zone + optHostVO = getHost(zoneId, false); - long zoneId = volumeVO.getDataCenterId(); + if (optHostVO.isPresent()) { + return optHostVO.get(); + } - List<? extends Cluster> clusters = _mgr.searchForClusters(zoneId, new Long(0), Long.MAX_VALUE, HypervisorType.XenServer.toString()); + throw new CloudRuntimeException("Unable to locate an applicable host"); + } + + private Optional<HostVO> getHost(long zoneId, boolean computeClusterMustSupportResign) { + List<? extends Cluster> clusters = mgr.searchForClusters(zoneId, 0L, Long.MAX_VALUE, HypervisorType.XenServer.toString()); if (clusters == null) { - throw new CloudRuntimeException("Unable to locate an applicable cluster"); + clusters = new ArrayList<>(); } + Collections.shuffle(clusters, new Random(System.nanoTime())); + + clusters: for (Cluster cluster : clusters) { if (cluster.getAllocationState() == AllocationState.Enabled) { - List<HostVO> hosts = _hostDao.findByClusterId(cluster.getId()); + List<HostVO> hosts = hostDao.findByClusterId(cluster.getId()); if (hosts != null) { + Collections.shuffle(hosts, new Random(System.nanoTime())); + for (HostVO host : hosts) { if (host.getResourceState() == ResourceState.Enabled) { - return host; + if (computeClusterMustSupportResign) { + if (clusterDao.computeWhetherClusterSupportsResigning(cluster.getId())) { + return Optional.of(host); + } + 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 { + return Optional.of(host); + } } } } } } - throw new CloudRuntimeException("Unable to locate an applicable cluster"); + return Optional.absent(); } private void markAsBackedUp(SnapshotObject snapshotObj) { @@ -406,18 +513,18 @@ public class StorageSystemSnapshotStrategy extends SnapshotStrategyBase { long volumeId = snapshot.getVolumeId(); - VolumeVO volumeVO = _volumeDao.findByIdIncludingRemoved(volumeId); + VolumeVO volumeVO = volumeDao.findByIdIncludingRemoved(volumeId); long storagePoolId = volumeVO.getPoolId(); - DataStore dataStore = _dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary); + DataStore dataStore = dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary); if (dataStore != null) { Map<String, String> mapCapabilities = dataStore.getDriver().getCapabilities(); if (mapCapabilities != null) { String value = mapCapabilities.get(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString()); - Boolean supportsStorageSystemSnapshots = new Boolean(value); + Boolean supportsStorageSystemSnapshots = Boolean.valueOf(value); if (supportsStorageSystemSnapshots) { return StrategyPriority.HIGHEST; http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java ---------------------------------------------------------------------- diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java index 99fc161..2544484 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java @@ -78,6 +78,8 @@ public class XenserverSnapshotStrategy extends SnapshotStrategyBase { VolumeDao volumeDao; @Inject SnapshotDataFactory snapshotDataFactory; + @Inject + private SnapshotDao _snapshotDao; @Override public SnapshotInfo backupSnapshot(SnapshotInfo snapshot) { @@ -289,7 +291,7 @@ public class XenserverSnapshotStrategy extends SnapshotStrategyBase { @Override public boolean revertSnapshot(SnapshotInfo snapshot) { if (canHandle(snapshot,SnapshotOperation.REVERT) == StrategyPriority.CANT_HANDLE) { - throw new UnsupportedOperationException("Reverting not supported. Create a template or volume based on the snapshot instead."); + throw new CloudRuntimeException("Reverting not supported. Create a template or volume based on the snapshot instead."); } SnapshotVO snapshotVO = snapshotDao.acquireInLockTable(snapshot.getId()); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/engine/storage/src/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java ---------------------------------------------------------------------- diff --git a/engine/storage/src/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java b/engine/storage/src/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java index 73a8544..194f7bd 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java +++ b/engine/storage/src/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java @@ -223,7 +223,7 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement Volume volume = _volumeDao.findById(dskCh.getVolumeId()); List<Volume> requestVolumes = new ArrayList<Volume>(); requestVolumes.add(volume); - return storageMgr.storagePoolHasEnoughIops(requestVolumes, pool) && storageMgr.storagePoolHasEnoughSpace(requestVolumes, pool); + return storageMgr.storagePoolHasEnoughIops(requestVolumes, pool) && storageMgr.storagePoolHasEnoughSpace(requestVolumes, pool, plan.getClusterId()); } /* http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/engine/storage/volume/pom.xml ---------------------------------------------------------------------- diff --git a/engine/storage/volume/pom.xml b/engine/storage/volume/pom.xml index e790df1..340010b 100644 --- a/engine/storage/volume/pom.xml +++ b/engine/storage/volume/pom.xml @@ -25,6 +25,11 @@ <artifactId>cloud-engine-storage</artifactId> <version>${project.version}</version> </dependency> + <dependency> + <groupId>org.apache.cloudstack</groupId> + <artifactId>cloud-engine-storage-image</artifactId> + <version>${project.version}</version> + </dependency> </dependencies> <build> <plugins> http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java ---------------------------------------------------------------------- diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java index f3c9e79..8196678 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java @@ -26,6 +26,7 @@ import javax.inject.Inject; import org.apache.cloudstack.engine.subsystem.api.storage.ClusterScope; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreCapabilities; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreDriver; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider; import org.apache.cloudstack.engine.subsystem.api.storage.HostScope; @@ -65,6 +66,7 @@ import com.cloud.utils.db.GlobalLock; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.storage.encoding.EncodingType; +@SuppressWarnings("serial") public class PrimaryDataStoreImpl implements PrimaryDataStore { private static final Logger s_logger = Logger.getLogger(PrimaryDataStoreImpl.class); @@ -239,10 +241,34 @@ public class PrimaryDataStoreImpl implements PrimaryDataStore { return pdsv.isManaged(); } + private boolean canCloneVolume() { + return Boolean.valueOf(getDriver().getCapabilities().get(DataStoreCapabilities.CAN_CREATE_VOLUME_FROM_VOLUME.toString())); + } + + /** + * The parameter createEntryInTempSpoolRef in the overloaded create(DataObject, boolean) method only applies to managed storage. We pass + * in "true" here. + * + * In the case of managed storage that can create a volume from a volume (clone), if the DataObject passed in is a TemplateInfo, + * we do want to create an entry in the cloud.template_spool_ref table (so that multiple uses of the template can be leveraged from + * the one copy on managed storage). + * + * In cases where UUID resigning is not available, then the code calling "create" should invoke the overloaded "create" method whose second + * parameter is a boolean. This code can pass in "false" so that an entry in the cloud.template_spool_ref table is not created (no template to share + * on the primary storage). + */ + @Override + public DataObject create(DataObject dataObject) { + return create(dataObject, true); + } + + /** + * Please read the comment for the create(DataObject) method if you are planning on passing in "false" for createEntryInTempSpoolRef. + */ @Override - public DataObject create(DataObject obj) { + public DataObject create(DataObject obj, boolean createEntryInTempSpoolRef) { // create template on primary storage - if (obj.getType() == DataObjectType.TEMPLATE && !isManaged()) { + if (obj.getType() == DataObjectType.TEMPLATE && (!isManaged() || (createEntryInTempSpoolRef && canCloneVolume()))) { try { String templateIdPoolIdString = "templateId:" + obj.getId() + "poolId:" + getId(); VMTemplateStoragePoolVO templateStoragePoolRef; http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java ---------------------------------------------------------------------- diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java index a2fd656..54e5105 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java @@ -19,14 +19,21 @@ package org.apache.cloudstack.storage.volume; import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Random; import javax.inject.Inject; +import com.cloud.dc.dao.ClusterDao; import com.cloud.offering.DiskOffering; +import com.cloud.org.Cluster; +import com.cloud.org.Grouping.AllocationState; +import com.cloud.resource.ResourceState; +import com.cloud.server.ManagementService; import com.cloud.storage.RegisterVolumePayload; import com.cloud.utils.Pair; import org.apache.cloudstack.engine.cloud.entity.api.VolumeEntity; @@ -36,6 +43,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult; import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionService; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreCapabilities; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreDriver; import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; @@ -59,8 +67,10 @@ import org.apache.cloudstack.storage.command.CommandResult; import org.apache.cloudstack.storage.command.CopyCmdAnswer; import org.apache.cloudstack.storage.command.DeleteCommand; import org.apache.cloudstack.storage.datastore.PrimaryDataStoreProviderManager; +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreDao; import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreVO; +import org.apache.cloudstack.storage.image.store.TemplateObject; import org.apache.cloudstack.storage.to.TemplateObjectTO; import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.log4j.Logger; @@ -80,7 +90,9 @@ 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; import com.cloud.host.dao.HostDao; +import com.cloud.host.dao.HostDetailsDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.DataStoreRole; import com.cloud.storage.ScopeType; @@ -134,6 +146,14 @@ public class VolumeServiceImpl implements VolumeService { EndPointSelector _epSelector; @Inject HostDao _hostDao; + @Inject + private PrimaryDataStoreDao storagePoolDao; + @Inject + private HostDetailsDao hostDetailsDao; + @Inject + private ManagementService mgr; + @Inject + private ClusterDao clusterDao; public VolumeServiceImpl() { } @@ -160,11 +180,11 @@ public class VolumeServiceImpl implements VolumeService { } @Override - public ChapInfo getChapInfo(VolumeInfo volumeInfo, DataStore dataStore) { + public ChapInfo getChapInfo(DataObject dataObject, DataStore dataStore) { DataStoreDriver dataStoreDriver = dataStore.getDriver(); if (dataStoreDriver instanceof PrimaryDataStoreDriver) { - return ((PrimaryDataStoreDriver)dataStoreDriver).getChapInfo(volumeInfo); + return ((PrimaryDataStoreDriver)dataStoreDriver).getChapInfo(dataObject); } return null; @@ -554,6 +574,49 @@ public class VolumeServiceImpl implements VolumeService { return null; } + protected Void createManagedTemplateImageCallback(AsyncCallbackDispatcher<VolumeServiceImpl, CreateCmdResult> callback, CreateVolumeContext<CreateCmdResult> context) { + CreateCmdResult result = callback.getResult(); + VolumeApiResult res = new VolumeApiResult(null); + + res.setResult(result.getResult()); + + AsyncCallFuture<VolumeApiResult> future = context.getFuture(); + DataObject templateOnPrimaryStoreObj = context.getVolume(); + + if (result.isSuccess()) { + ((TemplateObject)templateOnPrimaryStoreObj).setInstallPath(result.getPath()); + templateOnPrimaryStoreObj.processEvent(Event.OperationSuccessed, result.getAnswer()); + } + else { + templateOnPrimaryStoreObj.processEvent(Event.OperationFailed); + } + + future.complete(res); + + return null; + } + + protected Void copyManagedTemplateCallback(AsyncCallbackDispatcher<VolumeServiceImpl, CopyCommandResult> callback, CreateBaseImageContext<VolumeApiResult> context) { + CopyCommandResult result = callback.getResult(); + VolumeApiResult res = new VolumeApiResult(context.getVolume()); + + res.setResult(result.getResult()); + + AsyncCallFuture<VolumeApiResult> future = context.getFuture(); + DataObject templateOnPrimaryStoreObj = context.destObj; + + if (result.isSuccess()) { + templateOnPrimaryStoreObj.processEvent(Event.OperationSuccessed, result.getAnswer()); + } + else { + templateOnPrimaryStoreObj.processEvent(Event.OperationFailed); + } + + future.complete(res); + + return null; + } + @DB protected Void copyBaseImageCallback(AsyncCallbackDispatcher<VolumeServiceImpl, CopyCommandResult> callback, CreateBaseImageContext<VolumeApiResult> context) { CopyCommandResult result = callback.getResult(); @@ -636,8 +699,10 @@ public class VolumeServiceImpl implements VolumeService { if (templatePoolRef == null) { s_logger.warn("Reset Template State On Pool failed - unable to lock TemplatePoolRef " + templatePoolRefId); } else { + templatePoolRef.setTemplateSize(0); templatePoolRef.setDownloadState(VMTemplateStorageResourceAssoc.Status.NOT_DOWNLOADED); templatePoolRef.setState(ObjectInDataStoreStateMachine.State.Allocated); + _tmpltPoolDao.update(templatePoolRefId, templatePoolRef); } }finally { @@ -653,50 +718,253 @@ public class VolumeServiceImpl implements VolumeService { return null; } - @Override - public AsyncCallFuture<VolumeApiResult> createManagedStorageAndVolumeFromTemplateAsync(VolumeInfo volumeInfo, long destDataStoreId, - TemplateInfo srcTemplateInfo, long destHostId) { - PrimaryDataStore destPrimaryDataStore = dataStoreMgr.getPrimaryDataStore(destDataStoreId); - TemplateInfo destTemplateInfo = (TemplateInfo)destPrimaryDataStore.create(srcTemplateInfo); - Host destHost = _hostDao.findById(destHostId); + /** + * Creates a template volume on managed storage, which will be used for creating ROOT volumes by cloning. + * + * @param srcTemplateInfo Source template on secondary storage + * @param destPrimaryDataStore Managed storage on which we need to create the volume + */ + private TemplateInfo createManagedTemplateVolume(TemplateInfo srcTemplateInfo, PrimaryDataStore destPrimaryDataStore) { + // create a template volume on primary storage + AsyncCallFuture<VolumeApiResult> createTemplateFuture = new AsyncCallFuture<>(); + TemplateInfo templateOnPrimary = (TemplateInfo)destPrimaryDataStore.create(srcTemplateInfo); - if (destHost == null) { - throw new CloudRuntimeException("Destinatin host should not be null."); + 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()); } - AsyncCallFuture<VolumeApiResult> future = new AsyncCallFuture<VolumeApiResult>(); + // At this point, we have an entry in the DB that points to our cached template. + // We need to lock it as there may be other VMs that may get started using the same template. + // We want to avoid having to create multiple cache copies of the same template. + + int storagePoolMaxWaitSeconds = NumbersUtil.parseInt(configDao.getValue(Config.StoragePoolMaxWaitSeconds.key()), 3600); + long templatePoolRefId = templatePoolRef.getId(); + + templatePoolRef = _tmpltPoolDao.acquireInLockTable(templatePoolRefId, storagePoolMaxWaitSeconds); + + if (templatePoolRef == null) { + throw new CloudRuntimeException("Unable to acquire lock on VMTemplateStoragePool: " + templatePoolRefId); + } + + // Template already exists + if (templatePoolRef.getState() == ObjectInDataStoreStateMachine.State.Ready) { + _tmpltPoolDao.releaseFromLockTable(templatePoolRefId); + + return templateOnPrimary; + } try { - // must call driver to have a volume created - AsyncCallFuture<VolumeApiResult> createVolumeFuture = createVolumeAsync(volumeInfo, destPrimaryDataStore); + // create a cache volume on the back-end + + templateOnPrimary.processEvent(Event.CreateOnlyRequested); + + CreateVolumeContext<CreateCmdResult> createContext = new CreateVolumeContext<>(null, templateOnPrimary, createTemplateFuture); + AsyncCallbackDispatcher<VolumeServiceImpl, CreateCmdResult> createCaller = AsyncCallbackDispatcher.create(this); + + createCaller.setCallback(createCaller.getTarget().createManagedTemplateImageCallback(null, null)).setContext(createContext); + + destPrimaryDataStore.getDriver().createAsync(destPrimaryDataStore, templateOnPrimary, createCaller); + + VolumeApiResult result = createTemplateFuture.get(); + + if (result.isFailed()) { + String errMesg = result.getResult(); + + 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); + + templateOnPrimary.processEvent(Event.OperationFailed); + + throw new CloudRuntimeException(e.getMessage()); + } + finally { + _tmpltPoolDao.releaseFromLockTable(templatePoolRefId); + } + + return templateOnPrimary; + } + + /** + * This function copies a template from secondary storage to a template volume + * created on managed storage. This template volume will be used as a cache. + * Instead of copying the template to a ROOT volume every time, a clone is performed instead. + * + * @param srcTemplateInfo Source from which to copy the template + * @param templateOnPrimary Dest to copy to + * @param templatePoolRef Template reference on primary storage (entry in the template_spool_ref) + * @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) + { + AsyncCallFuture<VolumeApiResult> copyTemplateFuture = new AsyncCallFuture<>(); + int storagePoolMaxWaitSeconds = NumbersUtil.parseInt(configDao.getValue(Config.StoragePoolMaxWaitSeconds.key()), 3600); + long templatePoolRefId = templatePoolRef.getId(); + templatePoolRef = _tmpltPoolDao.acquireInLockTable(templatePoolRefId, storagePoolMaxWaitSeconds); + + if (templatePoolRef == null) { + throw new CloudRuntimeException("Unable to acquire lock on VMTemplateStoragePool: " + templatePoolRefId); + } + + if (templatePoolRef.getDownloadState() == Status.DOWNLOADED) { + // There can be cases where we acquired the lock, but the template + // was already copied by a previous thread. Just return in that case. + + s_logger.debug("Template already downloaded, nothing to do"); + + return; + } + + try { + // copy the template from sec storage to the created volume + CreateBaseImageContext<CreateCmdResult> copyContext = new CreateBaseImageContext<>( + null, null, destPrimaryDataStore, srcTemplateInfo, + copyTemplateFuture, templateOnPrimary, templatePoolRefId + ); + + AsyncCallbackDispatcher<VolumeServiceImpl, CopyCommandResult> copyCaller = AsyncCallbackDispatcher.create(this); + copyCaller.setCallback(copyCaller.getTarget().copyManagedTemplateCallback(null, null)).setContext(copyContext); + + // Populate details which will be later read by the storage subsystem. + Map<String, String> details = new HashMap<String, String>(); + + details.put(PrimaryDataStore.MANAGED, Boolean.TRUE.toString()); + details.put(PrimaryDataStore.STORAGE_HOST, destPrimaryDataStore.getHostAddress()); + details.put(PrimaryDataStore.STORAGE_PORT, String.valueOf(destPrimaryDataStore.getPort())); + details.put(PrimaryDataStore.MANAGED_STORE_TARGET, ((TemplateObject)templateOnPrimary).getInstallPath()); + details.put(PrimaryDataStore.MANAGED_STORE_TARGET_ROOT_VOLUME, srcTemplateInfo.getUniqueName()); + details.put(PrimaryDataStore.REMOVE_AFTER_COPY, Boolean.TRUE.toString()); + details.put(PrimaryDataStore.VOLUME_SIZE, String.valueOf(templateOnPrimary.getSize())); + + ChapInfo chapInfo = getChapInfo(templateOnPrimary, destPrimaryDataStore); + + if (chapInfo != null) { + details.put(PrimaryDataStore.CHAP_INITIATOR_USERNAME, chapInfo.getInitiatorUsername()); + details.put(PrimaryDataStore.CHAP_INITIATOR_SECRET, chapInfo.getInitiatorSecret()); + details.put(PrimaryDataStore.CHAP_TARGET_USERNAME, chapInfo.getTargetUsername()); + details.put(PrimaryDataStore.CHAP_TARGET_SECRET, chapInfo.getTargetSecret()); + } + + templateOnPrimary.processEvent(Event.CopyingRequested); + + destPrimaryDataStore.setDetails(details); + + grantAccess(templateOnPrimary, destHost, destPrimaryDataStore); + + VolumeApiResult result = null; + + try { + motionSrv.copyAsync(srcTemplateInfo, templateOnPrimary, destHost, copyCaller); + + result = copyTemplateFuture.get(); + } + finally { + revokeAccess(templateOnPrimary, destHost, destPrimaryDataStore); + } + + if (result.isFailed()) { + 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) { + s_logger.debug("Failed to create a template on primary storage", e); + + templateOnPrimary.processEvent(Event.OperationFailed); + + throw new CloudRuntimeException(e.getMessage()); + } + finally { + _tmpltPoolDao.releaseFromLockTable(templatePoolRefId); + } + } + + /** + * Clones the template volume on managed storage to the ROOT volume + * + * @param volumeInfo ROOT volume to create + * @param templateOnPrimary Template from which to clone the ROOT volume + * @param destPrimaryDataStore Primary storage of the volume + * @param future For async + */ + private void createManagedVolumeCloneTemplateAsync(VolumeInfo volumeInfo, TemplateInfo templateOnPrimary, PrimaryDataStore destPrimaryDataStore, + AsyncCallFuture<VolumeApiResult> future) { + VMTemplateStoragePoolVO templatePoolRef = _tmpltPoolDao.findByPoolTemplate(destPrimaryDataStore.getId(), templateOnPrimary.getId()); + + if (templatePoolRef == null) { + throw new CloudRuntimeException("Failed to find template " + templateOnPrimary.getUniqueName() + " in storage pool " + destPrimaryDataStore.getId()); + } + + //XXX: not sure if this the right thing to do here. We can always fallback to the "copy from sec storage" + if (templatePoolRef.getDownloadState() == Status.NOT_DOWNLOADED) { + throw new CloudRuntimeException("Template " + templateOnPrimary.getUniqueName() + " has not been downloaded to primary storage."); + } + + try { + volumeInfo.processEvent(Event.CreateOnlyRequested); + + CreateVolumeFromBaseImageContext<VolumeApiResult> context = + new CreateVolumeFromBaseImageContext<>(null, volumeInfo, destPrimaryDataStore, templateOnPrimary, future, null); + + AsyncCallbackDispatcher<VolumeServiceImpl, CopyCommandResult> caller = AsyncCallbackDispatcher.create(this); + + caller.setCallback(caller.getTarget().createVolumeFromBaseImageCallBack(null, null)); + caller.setContext(context); + + motionSrv.copyAsync(templateOnPrimary, volumeInfo, caller); + } catch (Throwable e) { + s_logger.debug("Failed to clone template on primary storage", e); + + volumeInfo.processEvent(Event.OperationFailed); + + throw new CloudRuntimeException(e.getMessage()); + } + } + + private void createManagedVolumeCopyTemplateAsync(VolumeInfo volumeInfo, PrimaryDataStore primaryDataStore, TemplateInfo srcTemplateInfo, Host destHost, + AsyncCallFuture<VolumeApiResult> future) { + try { + // Create a volume on managed storage. + + TemplateInfo destTemplateInfo = (TemplateInfo)primaryDataStore.create(srcTemplateInfo, false); + + AsyncCallFuture<VolumeApiResult> createVolumeFuture = createVolumeAsync(volumeInfo, primaryDataStore); VolumeApiResult createVolumeResult = createVolumeFuture.get(); if (createVolumeResult.isFailed()) { throw new CloudRuntimeException("Creation of a volume failed: " + createVolumeResult.getResult()); } - // refresh the volume from the DB - volumeInfo = volFactory.getVolume(volumeInfo.getId(), destPrimaryDataStore); - - grantAccess(volumeInfo, destHost, destPrimaryDataStore); + // Refresh the volume info from the DB. + volumeInfo = volFactory.getVolume(volumeInfo.getId(), primaryDataStore); ManagedCreateBaseImageContext<CreateCmdResult> context = new ManagedCreateBaseImageContext<CreateCmdResult>(null, volumeInfo, - destPrimaryDataStore, srcTemplateInfo, future); + primaryDataStore, srcTemplateInfo, future); AsyncCallbackDispatcher<VolumeServiceImpl, CopyCommandResult> caller = AsyncCallbackDispatcher.create(this); + caller.setCallback(caller.getTarget().managedCopyBaseImageCallback(null, null)).setContext(context); Map<String, String> details = new HashMap<String, String>(); details.put(PrimaryDataStore.MANAGED, Boolean.TRUE.toString()); - details.put(PrimaryDataStore.STORAGE_HOST, destPrimaryDataStore.getHostAddress()); - details.put(PrimaryDataStore.STORAGE_PORT, String.valueOf(destPrimaryDataStore.getPort())); + details.put(PrimaryDataStore.STORAGE_HOST, primaryDataStore.getHostAddress()); + details.put(PrimaryDataStore.STORAGE_PORT, String.valueOf(primaryDataStore.getPort())); // for managed storage, the storage repository (XenServer) or datastore (ESX) name is based off of the iScsiName property of a volume details.put(PrimaryDataStore.MANAGED_STORE_TARGET, volumeInfo.get_iScsiName()); details.put(PrimaryDataStore.MANAGED_STORE_TARGET_ROOT_VOLUME, volumeInfo.getName()); details.put(PrimaryDataStore.VOLUME_SIZE, String.valueOf(volumeInfo.getSize())); - ChapInfo chapInfo = getChapInfo(volumeInfo, destPrimaryDataStore); + ChapInfo chapInfo = getChapInfo(volumeInfo, primaryDataStore); if (chapInfo != null) { details.put(PrimaryDataStore.CHAP_INITIATOR_USERNAME, chapInfo.getInitiatorUsername()); @@ -705,17 +973,21 @@ public class VolumeServiceImpl implements VolumeService { details.put(PrimaryDataStore.CHAP_TARGET_SECRET, chapInfo.getTargetSecret()); } - destPrimaryDataStore.setDetails(details); + primaryDataStore.setDetails(details); - motionSrv.copyAsync(srcTemplateInfo, destTemplateInfo, destHost, caller); - } - catch (Throwable t) { + grantAccess(volumeInfo, destHost, primaryDataStore); + + try { + motionSrv.copyAsync(srcTemplateInfo, destTemplateInfo, destHost, caller); + } + finally { + revokeAccess(volumeInfo, destHost, primaryDataStore); + } + } catch (Throwable t) { String errMsg = t.toString(); volumeInfo.processEvent(Event.DestroyRequested); - revokeAccess(volumeInfo, destHost, destPrimaryDataStore); - try { AsyncCallFuture<VolumeApiResult> expungeVolumeFuture = expungeVolumeAsync(volumeInfo); @@ -735,10 +1007,112 @@ public class VolumeServiceImpl implements VolumeService { future.complete(result); } + } + + @Override + public AsyncCallFuture<VolumeApiResult> createManagedStorageVolumeFromTemplateAsync(VolumeInfo volumeInfo, long destDataStoreId, + TemplateInfo srcTemplateInfo, long destHostId) { + PrimaryDataStore destPrimaryDataStore = dataStoreMgr.getPrimaryDataStore(destDataStoreId); + Host destHost = _hostDao.findById(destHostId); + + if (destHost == null) { + 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 computeZoneSupportsResign = computeZoneSupportsResign(destHost.getDataCenterId(), destHost.getHypervisorType()); + + AsyncCallFuture<VolumeApiResult> future = new AsyncCallFuture<>(); + + if (storageCanCloneVolume && computeZoneSupportsResign) { + s_logger.debug("Storage " + destDataStoreId + " can support cloning using a cached template and host cluster can perform UUID resigning."); + + TemplateInfo templateOnPrimary = destPrimaryDataStore.getTemplate(srcTemplateInfo.getId()); + + if (templateOnPrimary == null) { + templateOnPrimary = createManagedTemplateVolume(srcTemplateInfo, destPrimaryDataStore); + + if (templateOnPrimary == null) { + throw new CloudRuntimeException("Failed to create template " + srcTemplateInfo.getUniqueName() + " on primary storage: " + destDataStoreId); + } + } + + // Copy the template to the template volume. + 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() + ); + } + + if (templatePoolRef.getDownloadState() == Status.NOT_DOWNLOADED) { + copyTemplateToManagedTemplateVolume(srcTemplateInfo, templateOnPrimary, templatePoolRef, destPrimaryDataStore, destHost); + } + + // We have a template on primary storage. Clone it to new volume. + s_logger.debug("Creating a clone from template on primary storage " + destDataStoreId); + createManagedVolumeCloneTemplateAsync(volumeInfo, templateOnPrimary, destPrimaryDataStore, future); + } else { + s_logger.debug("Primary storage does not support cloning or no support for UUID resigning on the host side; copying the template normally"); + createManagedVolumeCopyTemplateAsync(volumeInfo, destPrimaryDataStore, srcTemplateInfo, destHost, future); + } return future; } + private boolean computeZoneSupportsResign(long zoneId, HypervisorType hypervisorType) { + return getHost(zoneId, hypervisorType, true) != null; + } + + private HostVO getHost(Long zoneId, HypervisorType hypervisorType, boolean computeClusterMustSupportResign) { + if (zoneId == null) { + throw new CloudRuntimeException("Zone ID cannot be null."); + } + + List<? extends Cluster> clusters = mgr.searchForClusters(zoneId, new Long(0), Long.MAX_VALUE, hypervisorType.toString()); + + if (clusters == null) { + clusters = new ArrayList<>(); + } + + Collections.shuffle(clusters, new Random(System.nanoTime())); + + clusters: + for (Cluster cluster : clusters) { + if (cluster.getAllocationState() == AllocationState.Enabled) { + List<HostVO> hosts = _hostDao.findByClusterId(cluster.getId()); + + if (hosts != null) { + Collections.shuffle(hosts, new Random(System.nanoTime())); + + for (HostVO host : hosts) { + if (host.getResourceState() == ResourceState.Enabled) { + if (computeClusterMustSupportResign) { + if (clusterDao.computeWhetherClusterSupportsResigning(cluster.getId())) { + return host; + } + 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 { + return host; + } + } + } + } + } + } + + return null; + } + @DB @Override public AsyncCallFuture<VolumeApiResult> createVolumeFromTemplateAsync(VolumeInfo volume, long dataStoreId, TemplateInfo template) { @@ -1332,7 +1706,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); + ResizeVolumeCommand resizeCmd = new ResizeVolumeCommand(volume.getPath(), new StorageFilerTO(primaryDataStore), + volume.getSize(), newSize, true, instanceName, primaryDataStore.isManaged(), volume.get_iScsiName()); answer = ep.sendMessage(resizeCmd); } else { http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/plugins/api/solidfire-intg-test/pom.xml ---------------------------------------------------------------------- diff --git a/plugins/api/solidfire-intg-test/pom.xml b/plugins/api/solidfire-intg-test/pom.xml index a15d7b3..fa5302b 100644 --- a/plugins/api/solidfire-intg-test/pom.xml +++ b/plugins/api/solidfire-intg-test/pom.xml @@ -19,7 +19,7 @@ <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-plugin-api-solidfire-intg-test</artifactId> - <name>Apache CloudStack Plugin - API SolidFire</name> + <name>Apache CloudStack Plugin - API SolidFire Integration Testing</name> <parent> <groupId>org.apache.cloudstack</groupId> <artifactId>cloudstack-plugins</artifactId> http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/plugins/api/solidfire-intg-test/resources/META-INF/cloudstack/solidfire-intg-test/spring-solidfire-intg-test-context.xml ---------------------------------------------------------------------- diff --git a/plugins/api/solidfire-intg-test/resources/META-INF/cloudstack/solidfire-intg-test/spring-solidfire-intg-test-context.xml b/plugins/api/solidfire-intg-test/resources/META-INF/cloudstack/solidfire-intg-test/spring-solidfire-intg-test-context.xml index 1bab734..2fe875a 100644 --- a/plugins/api/solidfire-intg-test/resources/META-INF/cloudstack/solidfire-intg-test/spring-solidfire-intg-test-context.xml +++ b/plugins/api/solidfire-intg-test/resources/META-INF/cloudstack/solidfire-intg-test/spring-solidfire-intg-test-context.xml @@ -27,6 +27,8 @@ http://www.springframework.org/schema/context/spring-context-3.0.xsd" > - <bean id="apiSolidFireServiceImpl" class="org.apache.cloudstack.solidfire.ApiSolidFireServiceImpl"/> + <bean id="sfIntgTestUtil" class="org.apache.cloudstack.util.solidfire.SolidFireIntegrationTestUtil"/> + <bean id="solidFireIntegrationTestManagerImpl" class="org.apache.cloudstack.solidfire.SolidFireIntegrationTestManagerImpl"/> + <bean id="apiSolidFireIntegrationTestServiceImpl" class="org.apache.cloudstack.api.solidfire.ApiSolidFireIntegrationTestServiceImpl" /> </beans> http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetPathForVolumeCmd.java ---------------------------------------------------------------------- diff --git a/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetPathForVolumeCmd.java b/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetPathForVolumeCmd.java new file mode 100644 index 0000000..5ff178a --- /dev/null +++ b/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetPathForVolumeCmd.java @@ -0,0 +1,67 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.solidfire; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.solidfire.ApiPathForVolumeResponse; +import org.apache.cloudstack.util.solidfire.SolidFireIntegrationTestUtil; + +@APICommand(name = "getPathForVolume", responseObject = ApiPathForVolumeResponse.class, description = "Get the path associated with the provided volume UUID", + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class GetPathForVolumeCmd extends BaseCmd { + private static final Logger LOGGER = Logger.getLogger(GetPathForVolumeCmd.class.getName()); + private static final String NAME = "getpathforvolumeresponse"; + + @Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.STRING, description = "CloudStack Volume UUID", required = true) + private String _volumeUuid; + + @Inject private SolidFireIntegrationTestUtil _util; + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return NAME; + } + + @Override + public long getEntityOwnerId() { + return _util.getAccountIdForVolumeUuid(_volumeUuid); + } + + @Override + public void execute() { + LOGGER.info("'GetPathForVolumeIdCmd.execute' method invoked"); + + String pathForVolume = _util.getPathForVolumeUuid(_volumeUuid); + + ApiPathForVolumeResponse response = new ApiPathForVolumeResponse(pathForVolume); + + response.setResponseName(getCommandName()); + response.setObjectName("apipathforvolume"); + + setResponseObject(response); + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireAccountIdCmd.java ---------------------------------------------------------------------- diff --git a/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireAccountIdCmd.java b/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireAccountIdCmd.java new file mode 100644 index 0000000..9bb8481 --- /dev/null +++ b/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireAccountIdCmd.java @@ -0,0 +1,72 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.solidfire; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.solidfire.ApiSolidFireAccountIdResponse; +import org.apache.cloudstack.solidfire.SolidFireIntegrationTestManager; +import org.apache.cloudstack.util.solidfire.SolidFireIntegrationTestUtil; + +@APICommand(name = "getSolidFireAccountId", responseObject = ApiSolidFireAccountIdResponse.class, description = "Get SolidFire Account ID", + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class GetSolidFireAccountIdCmd extends BaseCmd { + private static final Logger LOGGER = Logger.getLogger(GetSolidFireAccountIdCmd.class.getName()); + private static final String NAME = "getsolidfireaccountidresponse"; + + @Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.STRING, description = "CloudStack Account UUID", required = true) + private String csAccountUuid; + @Parameter(name = ApiConstants.STORAGE_ID, type = CommandType.STRING, description = "Storage Pool UUID", required = true) + private String storagePoolUuid; + + @Inject private SolidFireIntegrationTestManager manager; + @Inject private SolidFireIntegrationTestUtil util; + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return NAME; + } + + @Override + public long getEntityOwnerId() { + return util.getAccountIdForAccountUuid(csAccountUuid); + } + + @Override + public void execute() { + LOGGER.info("'GetSolidFireAccountIdCmd.execute' method invoked"); + + long sfAccountId = manager.getSolidFireAccountId(csAccountUuid, storagePoolUuid); + + ApiSolidFireAccountIdResponse response = new ApiSolidFireAccountIdResponse(sfAccountId); + + response.setResponseName(getCommandName()); + response.setObjectName("apisolidfireaccountid"); + + this.setResponseObject(response); + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeAccessGroupIdCmd.java ---------------------------------------------------------------------- diff --git a/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeAccessGroupIdCmd.java b/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeAccessGroupIdCmd.java new file mode 100644 index 0000000..5c15e01 --- /dev/null +++ b/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeAccessGroupIdCmd.java @@ -0,0 +1,81 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.solidfire; + +import com.cloud.user.Account; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.solidfire.ApiSolidFireVolumeAccessGroupIdResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.solidfire.SolidFireIntegrationTestManager; +import org.apache.cloudstack.util.solidfire.SolidFireIntegrationTestUtil; + +@APICommand(name = "getSolidFireVolumeAccessGroupId", responseObject = ApiSolidFireVolumeAccessGroupIdResponse.class, description = "Get the SF Volume Access Group ID", + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class GetSolidFireVolumeAccessGroupIdCmd extends BaseCmd { + private static final Logger LOGGER = Logger.getLogger(GetSolidFireVolumeAccessGroupIdCmd.class.getName()); + private static final String NAME = "getsolidfirevolumeaccessgroupidresponse"; + + @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.STRING, description = "Cluster UUID", required = true) + private String clusterUuid; + @Parameter(name = ApiConstants.STORAGE_ID, type = CommandType.STRING, description = "Storage Pool UUID", required = true) + private String storagePoolUuid; + + @Inject private SolidFireIntegrationTestManager manager; + @Inject private SolidFireIntegrationTestUtil util; + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return NAME; + } + + @Override + public long getEntityOwnerId() { + Account account = CallContext.current().getCallingAccount(); + + if (account != null) { + return account.getId(); + } + + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } + + @Override + public void execute() { + LOGGER.info("'GetSolidFireVolumeAccessGroupIdCmd.execute' method invoked"); + + long sfVagId = manager.getSolidFireVolumeAccessGroupId(clusterUuid, storagePoolUuid); + + ApiSolidFireVolumeAccessGroupIdResponse response = new ApiSolidFireVolumeAccessGroupIdResponse(sfVagId); + + response.setResponseName(getCommandName()); + response.setObjectName("apisolidfirevolumeaccessgroupid"); + + this.setResponseObject(response); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeSizeCmd.java ---------------------------------------------------------------------- diff --git a/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeSizeCmd.java b/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeSizeCmd.java new file mode 100644 index 0000000..d7c8acf --- /dev/null +++ b/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetSolidFireVolumeSizeCmd.java @@ -0,0 +1,70 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.solidfire; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; + +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.solidfire.ApiSolidFireVolumeSizeResponse; +import org.apache.cloudstack.solidfire.SolidFireIntegrationTestManager; +import org.apache.cloudstack.util.solidfire.SolidFireIntegrationTestUtil; + +@APICommand(name = "getSolidFireVolumeSize", responseObject = ApiSolidFireVolumeSizeResponse.class, description = "Get the SF volume size including Hypervisor Snapshot Reserve", + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) +public class GetSolidFireVolumeSizeCmd extends BaseCmd { + private static final Logger LOGGER = Logger.getLogger(GetSolidFireVolumeSizeCmd.class.getName()); + private static final String NAME = "getsolidfirevolumesizeresponse"; + + @Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.STRING, description = "Volume UUID", required = true) + private String volumeUuid; + + @Inject private SolidFireIntegrationTestManager manager; + @Inject private SolidFireIntegrationTestUtil util; + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return NAME; + } + + @Override + public long getEntityOwnerId() { + return util.getAccountIdForVolumeUuid(volumeUuid); + } + + @Override + public void execute() { + LOGGER.info("'GetSolidFireVolumeSizeCmd.execute' method invoked"); + + long sfVolumeSize = manager.getSolidFireVolumeSize(volumeUuid); + + ApiSolidFireVolumeSizeResponse response = new ApiSolidFireVolumeSizeResponse(sfVolumeSize); + + response.setResponseName(getCommandName()); + response.setObjectName("apisolidfirevolumesize"); + + this.setResponseObject(response); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cloudstack/blob/2bd035d1/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetVolumeSnapshotDetailsCmd.java ---------------------------------------------------------------------- diff --git a/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetVolumeSnapshotDetailsCmd.java b/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetVolumeSnapshotDetailsCmd.java new file mode 100644 index 0000000..5b9ce37 --- /dev/null +++ b/plugins/api/solidfire-intg-test/src/org/apache/cloudstack/api/command/admin/solidfire/GetVolumeSnapshotDetailsCmd.java @@ -0,0 +1,73 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.solidfire; + +import java.util.List; + +import javax.inject.Inject; + +import org.apache.log4j.Logger; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.solidfire.ApiVolumeSnapshotDetailsResponse; +import org.apache.cloudstack.api.response.solidfire.ApiVolumeiScsiNameResponse; +import org.apache.cloudstack.util.solidfire.SolidFireIntegrationTestUtil; + +@APICommand(name = "getVolumeSnapshotDetails", responseObject = ApiVolumeiScsiNameResponse.class, description = "Get Volume Snapshot Details", + requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) + +public class GetVolumeSnapshotDetailsCmd extends BaseCmd { + private static final Logger LOGGER = Logger.getLogger(GetVolumeSnapshotDetailsCmd.class.getName()); + private static final String NAME = "getvolumesnapshotdetailsresponse"; + + @Parameter(name = ApiConstants.SNAPSHOT_ID, type = CommandType.STRING, description = "CloudStack Snapshot UUID", required = true) + private String snapshotUuid; + + @Inject private SolidFireIntegrationTestUtil util; + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return NAME; + } + + @Override + public long getEntityOwnerId() { + return util.getAccountIdForSnapshotUuid(snapshotUuid); + } + + @Override + public void execute() { + LOGGER.info("'" + GetVolumeSnapshotDetailsCmd.class.getSimpleName() + ".execute' method invoked"); + + List<ApiVolumeSnapshotDetailsResponse> responses = util.getSnapshotDetails(snapshotUuid); + + ListResponse<ApiVolumeSnapshotDetailsResponse> listReponse = new ListResponse<>(); + + listReponse.setResponses(responses); + listReponse.setResponseName(getCommandName()); + listReponse.setObjectName("apivolumesnapshotdetails"); + + this.setResponseObject(listReponse); + } +} \ No newline at end of file
