diff --git a/api/src/com/cloud/storage/Snapshot.java b/api/src/com/cloud/storage/Snapshot.java index 0d58a1401bc..27a2fe43f9f 100644 --- a/api/src/com/cloud/storage/Snapshot.java +++ b/api/src/com/cloud/storage/Snapshot.java @@ -60,6 +60,9 @@ public interface Snapshot extends ControlledEntity, Identity, InternalIdentity, CreatedOnPrimary, BackingUp, BackedUp, + Copying, + Destroying, + Destroyed,//it's a state, user can't see the snapshot from ui, while the snapshot may still exist on the storage Error; public String toString() { @@ -76,6 +79,8 @@ public interface Snapshot extends ControlledEntity, Identity, InternalIdentity, OperationNotPerformed, BackupToSecondary, BackedupToSecondary, + DestroyRequested, + CopyingRequested, OperationSucceeded, OperationFailed } diff --git a/api/src/com/cloud/storage/VolumeApiService.java b/api/src/com/cloud/storage/VolumeApiService.java index 2f5364fe525..462ff6433d1 100644 --- a/api/src/com/cloud/storage/VolumeApiService.java +++ b/api/src/com/cloud/storage/VolumeApiService.java @@ -80,6 +80,9 @@ public interface VolumeApiService { Volume detachVolumeFromVM(DetachVolumeCmd cmmd); - Snapshot takeSnapshot(Long volumeId, Long policyId) + Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account) throws ResourceAllocationException; + + Snapshot allocSnapshot(Long volumeId, Long policyId) + throws ResourceAllocationException; } diff --git a/api/src/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotCmd.java b/api/src/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotCmd.java index 95d76599f70..25cdedd7d87 100644 --- a/api/src/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/snapshot/CreateSnapshotCmd.java @@ -152,7 +152,7 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd { @Override public void create() throws ResourceAllocationException { - Snapshot snapshot = _snapshotService.allocSnapshot(getVolumeId(), getPolicyId()); + Snapshot snapshot = this._volumeService.allocSnapshot(getVolumeId(), getPolicyId()); if (snapshot != null) { this.setEntityId(snapshot.getId()); this.setEntityUuid(snapshot.getUuid()); @@ -164,14 +164,20 @@ public class CreateSnapshotCmd extends BaseAsyncCreateCmd { @Override public void execute() { UserContext.current().setEventDetails("Volume Id: "+getVolumeId()); - Snapshot snapshot = _snapshotService.createSnapshot(getVolumeId(), getPolicyId(), getEntityId(), _accountService.getAccount(getEntityOwnerId())); - if (snapshot != null) { - SnapshotResponse response = _responseGenerator.createSnapshotResponse(snapshot); - response.setResponseName(getCommandName()); - this.setResponseObject(response); - } else { + Snapshot snapshot; + try { + snapshot = _volumeService.takeSnapshot(this.getVolumeId(), this.getPolicyId(), this.getEntityId(), _accountService.getAccount(getEntityOwnerId())); + if (snapshot != null) { + SnapshotResponse response = _responseGenerator.createSnapshotResponse(snapshot); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create snapshot due to an internal error creating snapshot for volume " + volumeId); + } + } catch (Exception e) { throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to create snapshot due to an internal error creating snapshot for volume " + volumeId); } + } diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotStrategy.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotStrategy.java index a642b5e4edf..6b90c31a465 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotStrategy.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotStrategy.java @@ -3,9 +3,14 @@ package org.apache.cloudstack.engine.subsystem.api.storage; import com.cloud.storage.Snapshot; + public interface SnapshotStrategy { public SnapshotInfo takeSnapshot(SnapshotInfo snapshot); public SnapshotInfo backupSnapshot(SnapshotInfo snapshot); public boolean deleteSnapshot(Long snapshotId); - public boolean canHandle(Snapshot snapshot); + /** + * @param snapshot + * @return + */ + boolean canHandle(Snapshot snapshot); } diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/StorageCacheManager.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/StorageCacheManager.java index 70332c70db8..0aa30d39b1d 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/StorageCacheManager.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/StorageCacheManager.java @@ -22,4 +22,11 @@ package org.apache.cloudstack.engine.subsystem.api.storage; public interface StorageCacheManager { public DataStore getCacheStorage(Scope scope); public DataObject createCacheObject(DataObject data, Scope scope); + /** only create cache object in db + * @param data + * @param scope + * @return + */ + DataObject getCacheObject(DataObject data, Scope scope); + DataObject deleteCacheObject(DataObject data); } diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java index 349325af45d..7165f370bac 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java @@ -27,4 +27,5 @@ public interface VolumeInfo extends DataObject, Volume { public Object getpayload(); public HypervisorType getHypervisorType(); public Long getLastPoolId(); + public String getAttachedVmName(); } diff --git a/engine/api/src/org/apache/cloudstack/storage/command/CopyCommand.java b/engine/api/src/org/apache/cloudstack/storage/command/CopyCommand.java index 5fe1cbed842..5bceb03ff93 100644 --- a/engine/api/src/org/apache/cloudstack/storage/command/CopyCommand.java +++ b/engine/api/src/org/apache/cloudstack/storage/command/CopyCommand.java @@ -23,12 +23,12 @@ import com.cloud.agent.api.Command; public class CopyCommand extends Command implements StorageSubSystemCommand { private DataTO srcTO; private DataTO destTO; + private DataTO cacheTO; - - public CopyCommand(DataTO srcUri, DataTO destUri, int timeout) { + public CopyCommand(DataTO srcData, DataTO destData, int timeout) { super(); - this.srcTO = srcUri; - this.destTO = destUri; + this.srcTO = srcData; + this.destTO = destData; this.setWait(timeout); } @@ -45,4 +45,12 @@ public class CopyCommand extends Command implements StorageSubSystemCommand { return true; } + public DataTO getCacheTO() { + return cacheTO; + } + + public void setCacheTO(DataTO cacheTO) { + this.cacheTO = cacheTO; + } + } diff --git a/engine/api/src/org/apache/cloudstack/storage/to/SnapshotObjectTO.java b/engine/api/src/org/apache/cloudstack/storage/to/SnapshotObjectTO.java index 38fea16155d..6d98045cc7a 100644 --- a/engine/api/src/org/apache/cloudstack/storage/to/SnapshotObjectTO.java +++ b/engine/api/src/org/apache/cloudstack/storage/to/SnapshotObjectTO.java @@ -2,21 +2,43 @@ package org.apache.cloudstack.storage.to; import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType; import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import com.cloud.agent.api.to.DataStoreTO; public class SnapshotObjectTO implements DataTO { private String path; + private VolumeObjectTO volume; + private String parentSnapshotPath; + private DataStoreTO dataStore; + private String vmName; + private String name; + private long id; + + public SnapshotObjectTO() { + + } + + public SnapshotObjectTO(SnapshotInfo snapshot) { + this.path = snapshot.getPath(); + this.setId(snapshot.getId()); + this.volume = (VolumeObjectTO)snapshot.getBaseVolume().getTO(); + this.setVmName(snapshot.getBaseVolume().getAttachedVmName()); + if (snapshot.getParent() != null) { + this.parentSnapshotPath = snapshot.getParent().getPath(); + } + this.dataStore = snapshot.getDataStore().getTO(); + this.setName(snapshot.getName()); + } + @Override public DataObjectType getObjectType() { - // TODO Auto-generated method stub - return null; + return DataObjectType.SNAPSHOT; } @Override public DataStoreTO getDataStore() { - // TODO Auto-generated method stub - return null; + return this.dataStore; } @Override @@ -27,4 +49,44 @@ public class SnapshotObjectTO implements DataTO { public void setPath(String path) { this.path = path; } + + public VolumeObjectTO getVolume() { + return volume; + } + + public void setVolume(VolumeObjectTO volume) { + this.volume = volume; + } + + public String getParentSnapshotPath() { + return parentSnapshotPath; + } + + public void setParentSnapshotPath(String parentSnapshotPath) { + this.parentSnapshotPath = parentSnapshotPath; + } + + public String getVmName() { + return vmName; + } + + public void setVmName(String vmName) { + this.vmName = vmName; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } } diff --git a/engine/api/src/org/apache/cloudstack/storage/to/VolumeObjectTO.java b/engine/api/src/org/apache/cloudstack/storage/to/VolumeObjectTO.java index bf1bb3c8047..4ca1f9b3e59 100644 --- a/engine/api/src/org/apache/cloudstack/storage/to/VolumeObjectTO.java +++ b/engine/api/src/org/apache/cloudstack/storage/to/VolumeObjectTO.java @@ -32,6 +32,7 @@ public class VolumeObjectTO implements DataTO { private String name; private long size; private String path; + private Long volumeId; public VolumeObjectTO() { @@ -49,6 +50,7 @@ public class VolumeObjectTO implements DataTO { } //this.name = volume.getName(); this.size = volume.getSize(); + this.setVolumeId(volume.getId()); } public String getUuid() { @@ -103,4 +105,12 @@ public class VolumeObjectTO implements DataTO { this.path = path; } + public Long getVolumeId() { + return volumeId; + } + + public void setVolumeId(Long volumeId) { + this.volumeId = volumeId; + } + } diff --git a/engine/storage/cache/src/org/apache/cloudstack/storage/cache/manager/StorageCacheManagerImpl.java b/engine/storage/cache/src/org/apache/cloudstack/storage/cache/manager/StorageCacheManagerImpl.java index d2aacdea076..3a445d4d70e 100644 --- a/engine/storage/cache/src/org/apache/cloudstack/storage/cache/manager/StorageCacheManagerImpl.java +++ b/engine/storage/cache/src/org/apache/cloudstack/storage/cache/manager/StorageCacheManagerImpl.java @@ -164,10 +164,24 @@ public class StorageCacheManagerImpl implements StorageCacheManager, Manager { return null; } + @Override + public DataObject getCacheObject(DataObject data, Scope scope) { + DataStore cacheStore = this.getCacheStorage(scope); + DataObject objOnCacheStore = cacheStore.create(data); + + return objOnCacheStore; + } + protected Void createCacheObjectCallBack(AsyncCallbackDispatcher callback, CreateCacheObjectContext context) { AsyncCallFuture future = context.future; future.complete(callback.getResult()); return null; } + + @Override + public DataObject deleteCacheObject(DataObject data) { + // TODO Auto-generated method stub + return null; + } } \ No newline at end of file diff --git a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java index 79ae28964a0..2075ef60b9c 100644 --- a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java +++ b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java @@ -60,6 +60,8 @@ import com.cloud.agent.api.UpgradeSnapshotCommand; import com.cloud.agent.api.storage.CopyVolumeAnswer; import com.cloud.agent.api.storage.CopyVolumeCommand; import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer; +import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.NfsTO; import com.cloud.agent.api.to.S3TO; import com.cloud.agent.api.to.SwiftTO; import com.cloud.configuration.Config; @@ -127,10 +129,6 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { @Inject VolumeManager volumeMgr; @Inject - private SwiftManager _swiftMgr; - @Inject - private S3Manager _s3Mgr; - @Inject StorageCacheManager cacheMgr; @Override @@ -179,112 +177,55 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { return answer; } } - - protected Answer copyFromSnapshot(DataObject snapObj, DataObject volObj) { - SnapshotVO snapshot = this.snapshotDao.findById(snapObj.getId()); - StoragePool pool = (StoragePool) volObj.getDataStore(); - String vdiUUID = null; - Long snapshotId = snapshot.getId(); - Long volumeId = snapshot.getVolumeId(); - Long dcId = snapshot.getDataCenterId(); - String secondaryStoragePoolUrl = this.snapshotMgr - .getSecondaryStorageURL(snapshot); - long accountId = snapshot.getAccountId(); - - String backedUpSnapshotUuid = snapshot.getBackupSnapshotId(); - snapshot = snapshotDao.findById(snapshotId); - if (snapshot.getVersion().trim().equals("2.1")) { - VolumeVO volume = this.volDao.findByIdIncludingRemoved(volumeId); - if (volume == null) { - throw new CloudRuntimeException("failed to upgrade snapshot " - + snapshotId + " due to unable to find orignal volume:" - + volumeId + ", try it later "); - } - if (volume.getTemplateId() == null) { - snapshotDao.updateSnapshotVersion(volumeId, "2.1", "2.2"); - } else { - VMTemplateVO template = templateDao - .findByIdIncludingRemoved(volume.getTemplateId()); - if (template == null) { - throw new CloudRuntimeException( - "failed to upgrade snapshot " - + snapshotId - + " due to unalbe to find orignal template :" - + volume.getTemplateId() - + ", try it later "); - } - Long templateId = template.getId(); - Long tmpltAccountId = template.getAccountId(); - if (!snapshotDao.lockInLockTable(snapshotId.toString(), 10)) { - throw new CloudRuntimeException( - "failed to upgrade snapshot " - + snapshotId - + " due to this snapshot is being used, try it later "); - } - UpgradeSnapshotCommand cmd = new UpgradeSnapshotCommand(null, - secondaryStoragePoolUrl, dcId, accountId, volumeId, - templateId, tmpltAccountId, null, - snapshot.getBackupSnapshotId(), snapshot.getName(), - "2.1"); - Answer answer = null; - try { - answer = this.storageMgr.sendToPool(pool, cmd); - } catch (StorageUnavailableException e) { - } finally { - snapshotDao.unlockFromLockTable(snapshotId.toString()); - } - if ((answer != null) && answer.getResult()) { - snapshotDao.updateSnapshotVersion(volumeId, "2.1", "2.2"); - } else { - throw new CloudRuntimeException("Unable to upgrade snapshot from 2.1 to 2.2 for " - + snapshot.getId()); - } + + protected DataObject cacheSnapshotChain(SnapshotInfo snapshot) { + DataObject leafData = null; + while(snapshot != null) { + DataObject cacheData = cacheMgr.createCacheObject(snapshot, snapshot.getDataStore().getScope()); + if (leafData == null) { + leafData = cacheData; } + snapshot = snapshot.getParent(); } + return leafData; + } + + protected void deleteSnapshotCacheChain(SnapshotInfo snapshot) { + + } + + protected Answer copyVolumeFromSnapshot(DataObject snapObj, DataObject volObj) { + SnapshotInfo snapshot = (SnapshotInfo)snapObj; + StoragePool pool = (StoragePool) volObj.getDataStore(); + String basicErrMsg = "Failed to create volume from " + snapshot.getName() + " on pool " + pool; - + DataStore store = snapObj.getDataStore(); + DataStoreTO storTO = store.getTO(); + DataObject srcData = snapObj; try { - if (snapshot.getSwiftId() != null && snapshot.getSwiftId() != 0) { - snapshotMgr.downloadSnapshotsFromSwift(snapshot); - } else if (snapshot.getS3Id() != null && snapshot.getS3Id() != 0) { - snapshotMgr.downloadSnapshotsFromS3(snapshot); + if (!(storTO instanceof NfsTO)) { + srcData = cacheSnapshotChain(snapshot); } + String value = configDao .getValue(Config.CreateVolumeFromSnapshotWait.toString()); int _createVolumeFromSnapshotWait = NumbersUtil.parseInt(value, Integer.parseInt(Config.CreateVolumeFromSnapshotWait .getDefaultValue())); - CreateVolumeFromSnapshotCommand createVolumeFromSnapshotCommand = new CreateVolumeFromSnapshotCommand( - pool, secondaryStoragePoolUrl, dcId, accountId, volumeId, - backedUpSnapshotUuid, snapshot.getName(), - _createVolumeFromSnapshotWait); - CreateVolumeFromSnapshotAnswer answer; - if (!snapshotDao.lockInLockTable(snapshotId.toString(), 10)) { - throw new CloudRuntimeException("failed to create volume from " - + snapshotId - + " due to this snapshot is being used, try it later "); - } - answer = (CreateVolumeFromSnapshotAnswer) this.storageMgr - .sendToPool(pool, createVolumeFromSnapshotCommand); - if (answer != null && answer.getResult()) { - vdiUUID = answer.getVdi(); - VolumeVO vol = this.volDao.findById(volObj.getId()); - vol.setPath(vdiUUID); - this.volDao.update(vol.getId(), vol); - return null; - } else { - s_logger.error(basicErrMsg + " due to " - + ((answer == null) ? "null" : answer.getDetails())); - throw new CloudRuntimeException(basicErrMsg); - } + + CopyCommand cmd = new CopyCommand(srcData.getTO(), volObj.getTO(), _createVolumeFromSnapshotWait); + + + Answer answer = this.storageMgr + .sendToPool(pool, cmd); + return answer; } catch (StorageUnavailableException e) { s_logger.error(basicErrMsg, e); throw new CloudRuntimeException(basicErrMsg); } finally { - if (snapshot.getSwiftId() != null) { - snapshotMgr.deleteSnapshotsDirForVolume( - secondaryStoragePoolUrl, dcId, accountId, volumeId); + if (!(storTO instanceof NfsTO)) { + deleteSnapshotCacheChain((SnapshotInfo)srcData); } } } @@ -328,7 +269,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { answer = copyTemplate(srcData, destData); } else if (srcData.getType() == DataObjectType.SNAPSHOT && destData.getType() == DataObjectType.VOLUME) { - answer = copyFromSnapshot(srcData, destData); + answer = copyVolumeFromSnapshot(srcData, destData); } else if (srcData.getType() == DataObjectType.SNAPSHOT && destData.getType() == DataObjectType.TEMPLATE) { answer = createTemplateFromSnapshot(srcData, destData); @@ -359,49 +300,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { @DB protected Answer createTemplateFromSnapshot(DataObject srcData, DataObject destData) { - long snapshotId = srcData.getId(); - SnapshotVO snapshot = snapshotDao.findById(snapshotId); - if (snapshot == null) { - throw new CloudRuntimeException("Unable to find Snapshot for Id " - + srcData.getId()); - } - Long zoneId = snapshot.getDataCenterId(); - DataStore secStore = destData.getDataStore(); - /* - HostVO secondaryStorageHost = this.templateMgr - .getSecondaryStorageHost(zoneId); - */ - String secondaryStorageURL = snapshotMgr - .getSecondaryStorageURL(snapshot); - VMTemplateVO template = this.templateDao.findById(destData.getId()); - String name = template.getName(); - String backupSnapshotUUID = snapshot.getBackupSnapshotId(); - if (backupSnapshotUUID == null) { - throw new CloudRuntimeException( - "Unable to create private template from snapshot " - + snapshotId - + " due to there is no backupSnapshotUUID for this snapshot"); - } - - Long dcId = snapshot.getDataCenterId(); - Long accountId = snapshot.getAccountId(); - Long volumeId = snapshot.getVolumeId(); - - String origTemplateInstallPath = null; - List pools = this.storageMgr - .ListByDataCenterHypervisor(zoneId, - snapshot.getHypervisorType()); - if (pools == null || pools.size() == 0) { - throw new CloudRuntimeException( - "Unable to find storage pools in zone " + zoneId); - } - StoragePoolVO poolvo = pools.get(0); - StoragePool pool = (StoragePool) this.dataStoreMgr.getDataStore( - poolvo.getId(), DataStoreRole.Primary); - - if (snapshot.getSwiftId() != null && snapshot.getSwiftId() != 0) { - snapshotMgr.downloadSnapshotsFromSwift(snapshot); - } + String value = configDao .getValue(Config.CreatePrivateTemplateFromSnapshotWait .toString()); @@ -410,172 +309,71 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { .parseInt(Config.CreatePrivateTemplateFromSnapshotWait .getDefaultValue())); - CreatePrivateTemplateFromSnapshotCommand cmd = new CreatePrivateTemplateFromSnapshotCommand( - pool, secondaryStorageURL, dcId, accountId, - snapshot.getVolumeId(), backupSnapshotUUID, snapshot.getName(), - origTemplateInstallPath, template.getId(), name, - _createprivatetemplatefromsnapshotwait); - - return sendCommand(cmd, pool, template.getId(), dcId, secStore); - } - - @DB - protected Answer sendCommand(Command cmd, StoragePool pool, - long templateId, long zoneId, DataStore secStore) { - - CreatePrivateTemplateAnswer answer = null; - try { - answer = (CreatePrivateTemplateAnswer) this.storageMgr.sendToPool( - pool, cmd); - } catch (StorageUnavailableException e) { - throw new CloudRuntimeException( - "Failed to execute CreatePrivateTemplateFromSnapshotCommand", - e); + if (srcData.getDataStore().getRole() != DataStoreRole.ImageCache && destData.getDataStore().getRole() != DataStoreRole.ImageCache) { + SnapshotInfo snapshot = (SnapshotInfo)srcData; + srcData = cacheSnapshotChain(snapshot); } - if (answer == null || !answer.getResult()) { - return answer; - } - - VMTemplateVO privateTemplate = templateDao.findById(templateId); - String answerUniqueName = answer.getUniqueName(); - if (answerUniqueName != null) { - privateTemplate.setUniqueName(answerUniqueName); - } - ImageFormat format = answer.getImageFormat(); - if (format != null) { - privateTemplate.setFormat(format); - } else { - // This never occurs. - // Specify RAW format makes it unusable for snapshots. - privateTemplate.setFormat(ImageFormat.RAW); - } - - String checkSum = this.templateMgr - .getChecksum(secStore, answer.getPath()); - - Transaction txn = Transaction.currentTxn(); - - txn.start(); - - privateTemplate.setChecksum(checkSum); - templateDao.update(privateTemplate.getId(), privateTemplate); - - // add template zone ref for this template - templateDao.addTemplateToZone(privateTemplate, zoneId); - TemplateDataStoreVO templateHostVO = new TemplateDataStoreVO(secStore.getId(), - privateTemplate.getId()); - templateHostVO.setDownloadPercent(100); - templateHostVO.setDownloadState(Status.DOWNLOADED); - templateHostVO.setInstallPath(answer.getPath()); - templateHostVO.setLastUpdated(new Date()); - templateHostVO.setSize(answer.getVirtualSize()); - templateHostVO.setPhysicalSize(answer.getphysicalSize()); - templateStoreDao.persist(templateHostVO); - txn.close(); + CopyCommand cmd = new CopyCommand(srcData.getTO(), destData.getTO(), _createprivatetemplatefromsnapshotwait); + EndPoint ep = selector.select(srcData, destData); + Answer answer = ep.sendMessage(cmd); return answer; } - private Answer createTemplateFromVolume(DataObject srcObj, - DataObject destObj) { - long volumeId = srcObj.getId(); - VolumeVO volume = this.volDao.findById(volumeId); - if (volume == null) { - throw new CloudRuntimeException("Unable to find volume for Id " - + volumeId); - } - long accountId = volume.getAccountId(); - String vmName = this.volumeMgr.getVmNameOnVolume(volume); - Long zoneId = volume.getDataCenterId(); - DataStore secStore = destObj.getDataStore(); - String secondaryStorageURL = secStore.getUri(); - VMTemplateVO template = this.templateDao.findById(destObj.getId()); - StoragePool pool = (StoragePool) this.dataStoreMgr.getDataStore( - volume.getPoolId(), DataStoreRole.Primary); + private Answer createTemplateFromVolume(DataObject srcData, + DataObject destData) { + String value = configDao .getValue(Config.CreatePrivateTemplateFromVolumeWait.toString()); int _createprivatetemplatefromvolumewait = NumbersUtil.parseInt(value, Integer.parseInt(Config.CreatePrivateTemplateFromVolumeWait .getDefaultValue())); - - CreatePrivateTemplateFromVolumeCommand cmd = new CreatePrivateTemplateFromVolumeCommand( - pool, secondaryStorageURL, destObj.getId(), accountId, - template.getName(), template.getUniqueName(), volume.getPath(), - vmName, _createprivatetemplatefromvolumewait); - - return sendCommand(cmd, pool, template.getId(), zoneId, secStore); - } - - private DataStore getSecHost(long volumeId, long dcId) { - Long id = snapshotDao.getSecHostId(volumeId); - if ( id != null) { - return this.dataStoreMgr.getDataStore(id, DataStoreRole.Image); + + if (srcData.getDataStore().getRole() != DataStoreRole.ImageCache && destData.getDataStore().getRole() != DataStoreRole.ImageCache) { + //need to copy it to image cache store + DataObject cacheData = cacheMgr.createCacheObject(srcData, destData.getDataStore().getScope()); + CopyCommand cmd = new CopyCommand(cacheData.getTO(), destData.getTO(), _createprivatetemplatefromvolumewait); + EndPoint ep = selector.select(cacheData, destData); + Answer answer = ep.sendMessage(cmd); + return answer; + } else { + //handle copy it to/from cache store + CopyCommand cmd = new CopyCommand(srcData.getTO(), destData.getTO(), _createprivatetemplatefromvolumewait); + EndPoint ep = selector.select(srcData, destData); + Answer answer = ep.sendMessage(cmd); + return answer; } - return this.dataStoreMgr.getImageStore(dcId); } - protected Answer copySnapshot(DataObject srcObject, DataObject destObject) { - SnapshotInfo srcSnapshot = (SnapshotInfo)srcObject; - VolumeInfo baseVolume = srcSnapshot.getBaseVolume(); - Long dcId = baseVolume.getDataCenterId(); - Long accountId = baseVolume.getAccountId(); + protected Answer copySnapshot(DataObject srcData, DataObject destData) { + String value = configDao.getValue(Config.BackupSnapshotWait.toString()); + int _backupsnapshotwait = NumbersUtil.parseInt(value, Integer.parseInt(Config.BackupSnapshotWait.getDefaultValue())); - DataStore secStore = getSecHost(baseVolume.getId(), baseVolume.getDataCenterId()); - Long secHostId = secStore.getId(); - String secondaryStoragePoolUrl = secStore.getUri(); - String snapshotUuid = srcSnapshot.getPath(); - // In order to verify that the snapshot is not empty, - // we check if the parent of the snapshot is not the same as the parent of the previous snapshot. - // We pass the uuid of the previous snapshot to the plugin to verify this. - SnapshotVO prevSnapshot = null; - String prevSnapshotUuid = null; - String prevBackupUuid = null; - - - SwiftTO swift = _swiftMgr.getSwiftTO(); - S3TO s3 = _s3Mgr.getS3TO(); - - long prevSnapshotId = srcSnapshot.getPrevSnapshotId(); - if (prevSnapshotId > 0) { - prevSnapshot = snapshotDao.findByIdIncludingRemoved(prevSnapshotId); - if ( prevSnapshot.getBackupSnapshotId() != null && swift == null) { - if (prevSnapshot.getVersion() != null && prevSnapshot.getVersion().equals("2.2")) { - prevBackupUuid = prevSnapshot.getBackupSnapshotId(); - prevSnapshotUuid = prevSnapshot.getPath(); - } - } else if ((prevSnapshot.getSwiftId() != null && swift != null) - || (prevSnapshot.getS3Id() != null && s3 != null)) { - prevBackupUuid = prevSnapshot.getBackupSnapshotId(); - prevSnapshotUuid = prevSnapshot.getPath(); - } - } - boolean isVolumeInactive = this.volumeMgr.volumeInactive(baseVolume); - String vmName = this.volumeMgr.getVmNameOnVolume(baseVolume); - StoragePool srcPool = (StoragePool)dataStoreMgr.getPrimaryDataStore(baseVolume.getPoolId()); - String value = configDao.getValue(Config.BackupSnapshotWait.toString()); - int _backupsnapshotwait = NumbersUtil.parseInt(value, Integer.parseInt(Config.BackupSnapshotWait.getDefaultValue())); - BackupSnapshotCommand backupSnapshotCommand = new BackupSnapshotCommand(secondaryStoragePoolUrl, dcId, accountId, baseVolume.getId(), srcSnapshot.getId(), secHostId, baseVolume.getPath(), srcPool, snapshotUuid, - srcSnapshot.getName(), prevSnapshotUuid, prevBackupUuid, isVolumeInactive, vmName, _backupsnapshotwait); - - if ( swift != null ) { - backupSnapshotCommand.setSwift(swift); - } else if (s3 != null) { - backupSnapshotCommand.setS3(s3); - } - BackupSnapshotAnswer answer = (BackupSnapshotAnswer) this.snapshotMgr.sendToPool(baseVolume, backupSnapshotCommand); - if (answer != null && answer.getResult()) { - SnapshotVO snapshotVO = this.snapshotDao.findById(srcSnapshot.getId()); - snapshotVO.setBackupSnapshotId(answer.getBackupSnapshotName()); - // persist an entry in snapshot_store_ref - SnapshotDataStoreVO snapshotStore = new SnapshotDataStoreVO(secStore.getId(), snapshotVO.getId()); - this._snapshotStoreDao.persist(snapshotStore); - if (answer.isFull()) { - snapshotVO.setPrevSnapshotId(0L); - } - this.snapshotDao.update(srcSnapshot.getId(), snapshotVO); - } - return answer; + DataObject cacheData = null; + try { + if (destData.getDataStore().getRole() != DataStoreRole.ImageCache) { + cacheData = cacheMgr.getCacheObject(srcData, destData.getDataStore().getScope()); + + CopyCommand cmd = new CopyCommand(srcData.getTO(), destData.getTO(), _backupsnapshotwait); + cmd.setCacheTO(cacheData.getTO()); + EndPoint ep = selector.select(srcData, destData); + Answer answer = ep.sendMessage(cmd); + return answer; + } else { + CopyCommand cmd = new CopyCommand(srcData.getTO(), destData.getTO(), _backupsnapshotwait); + EndPoint ep = selector.select(srcData, destData); + Answer answer = ep.sendMessage(cmd); + return answer; + } + } catch (Exception e) { + s_logger.debug("copy snasphot failed: " + e.toString()); + if (cacheData != null) { + cacheMgr.deleteCacheObject(cacheData); + } + throw new CloudRuntimeException(e.toString()); + } + } } diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotObject.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotObject.java index 50f3819d543..6fddcdacbbe 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotObject.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotObject.java @@ -30,6 +30,7 @@ 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.disktype.DiskFormat; +import org.apache.cloudstack.storage.command.CopyCmdAnswer; import org.apache.cloudstack.storage.command.CreateObjectAnswer; import org.apache.cloudstack.storage.datastore.ObjectInDataStoreManager; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; @@ -234,6 +235,13 @@ public class SnapshotObject implements SnapshotInfo { SnapshotObjectTO snapshotTO = (SnapshotObjectTO)((CreateObjectAnswer) answer).getData(); snapshotStore.setInstallPath(snapshotTO.getPath()); this.snapshotStore.update(snapshotStore.getId(), snapshotStore); + } else if (answer instanceof CopyCmdAnswer) { + SnapshotObjectTO snapshotTO = (SnapshotObjectTO)((CopyCmdAnswer) answer).getNewData(); + snapshotStore.setInstallPath(snapshotTO.getPath()); + if (snapshotTO.getParentSnapshotPath() == null) { + snapshotStore.setParentSnapshotId(0L); + } + this.snapshotStore.update(snapshotStore.getId(), snapshotStore); } else { throw new CloudRuntimeException("Unknown answer: " + answer.getClass()); } diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java index 2fb4609a3c0..615ed2002f3 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java @@ -399,7 +399,7 @@ public class SnapshotServiceImpl implements SnapshotService { @Override public boolean deleteSnapshot(SnapshotInfo snapInfo) { - + return true; } diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotStateMachineManagerImpl.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotStateMachineManagerImpl.java index aa1cf684d7a..489ba280e7d 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotStateMachineManagerImpl.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotStateMachineManagerImpl.java @@ -44,6 +44,12 @@ SnapshotStateMachineManager { stateMachine.addTransition(Snapshot.State.CreatedOnPrimary, Event.BackupToSecondary, Snapshot.State.BackingUp); stateMachine.addTransition(Snapshot.State.BackingUp, Event.OperationSucceeded, Snapshot.State.BackedUp); stateMachine.addTransition(Snapshot.State.BackingUp, Event.OperationFailed, Snapshot.State.CreatedOnPrimary); + stateMachine.addTransition(Snapshot.State.BackedUp, Event.DestroyRequested, Snapshot.State.Destroying); + stateMachine.addTransition(Snapshot.State.BackedUp, Event.CopyingRequested, Snapshot.State.Copying); + stateMachine.addTransition(Snapshot.State.Copying, Event.OperationSucceeded, Snapshot.State.BackedUp); + stateMachine.addTransition(Snapshot.State.Copying, Event.OperationFailed, Snapshot.State.BackedUp); + stateMachine.addTransition(Snapshot.State.Destroying, Event.OperationSucceeded, Snapshot.State.Destroyed); + stateMachine.addTransition(Snapshot.State.Destroying, Event.OperationFailed, Snapshot.State.Error); stateMachine.registerListener(new SnapshotStateListener()); } diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotStrategyBase.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotStrategyBase.java index 70c30a0359d..fa0006ae76a 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotStrategyBase.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotStrategyBase.java @@ -9,9 +9,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotStrategy; public abstract class SnapshotStrategyBase implements SnapshotStrategy { @Inject SnapshotService snapshotSvr; - //the default strategy is: - //create snapshot, - //backup, then delete snapshot on primary storage + @Override public SnapshotInfo takeSnapshot(SnapshotInfo snapshot) { return snapshotSvr.takeSnapshot(snapshot).getSnashot(); 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 0fa044b74aa..df2264a3a4a 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 @@ -27,6 +27,7 @@ import com.cloud.storage.DataStoreRole; import com.cloud.storage.Snapshot; import com.cloud.storage.SnapshotVO; import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.snapshot.SnapshotManager; import com.cloud.utils.NumbersUtil; import com.cloud.utils.exception.CloudRuntimeException; @@ -48,6 +49,8 @@ public class XenserverSnapshotStrategy extends SnapshotStrategyBase { @Inject ConfigurationDao configDao; @Inject + SnapshotDao snapshotDao; + @Inject SnapshotDataFactory snapshotDataFactory; @Override @@ -114,82 +117,72 @@ public class XenserverSnapshotStrategy extends SnapshotStrategyBase { snapshot.addPayload(fullBackup); return this.snapshotSvr.backupSnapshot(snapshot); } + + protected void deleteSnapshotChain(SnapshotInfo snapshot) { + while(snapshot != null) { + SnapshotInfo child = snapshot.getChild(); + SnapshotInfo parent = snapshot.getParent(); + if (child == null) { + if (!parent.getPath().equalsIgnoreCase(snapshot.getPath())) { + this.snapshotSvr.deleteSnapshot(snapshot); + snapshot = parent; + continue; + } + break; + } else { + break; + } + } + } @Override - public boolean deleteSnapshot(SnapshotInfo snapshot) { - Long snapshotId = snapshot.getId(); - SnapshotObject snapObj = (SnapshotObject)snapshot; - - if (!Snapshot.State.BackedUp.equals(snapshot.getState()) || !Snapshot) { + public boolean deleteSnapshot(Long snapshotId) { + SnapshotVO snapshotVO = snapshotDao.findById(snapshotId); + if (snapshotVO.getState() == Snapshot.State.Destroyed) { + return true; + } + + if (!Snapshot.State.BackedUp.equals(snapshotVO.getState())) { throw new InvalidParameterValueException("Can't delete snapshotshot " + snapshotId + " due to it is not in BackedUp Status"); } if (s_logger.isDebugEnabled()) { s_logger.debug("Calling deleteSnapshot for snapshotId: " + snapshotId); } - SnapshotVO lastSnapshot = null; - if (snapshot.getPrevSnapshotId() != null) { - List snaps = _snapshotDao.listByBackupUuid(snapshot.getVolumeId(), snapshot.getBackupSnapshotId()); - if (snaps != null && snaps.size() > 1) { - snapshot.setBackupSnapshotId(null); - SnapshotVO snapshotVO = this._snapshotDao.findById(snapshotId); - _snapshotDao.update(snapshot.getId(), snapshotVO); - } - } + + + //firt mark the snapshot as destroyed, so that ui can't see it, but we may not destroy the snapshot on the storage, as other snaphosts may depend on it. + SnapshotInfo snapshotOnPrimary = this.snapshotDataFactory.getSnapshot(snapshotId, DataStoreRole.Primary); + SnapshotObject obj = (SnapshotObject)snapshotOnPrimary; + try { + obj.processEvent(Snapshot.Event.DestroyRequested); + } catch (NoTransitionException e) { + s_logger.debug("Failed to destroy snapshot: " + e.toString()); + return false; + } + + try { + if (snapshotOnPrimary != null) { + deleteSnapshotChain(snapshotOnPrimary); + } - _snapshotDao.remove(snapshotId); - - long lastId = snapshotId; - boolean destroy = false; - while (true) { - lastSnapshot = _snapshotDao.findNextSnapshot(lastId); - if (lastSnapshot == null) { - // if all snapshots after this snapshot in this chain are removed, remove those snapshots. - destroy = true; - break; - } - if (lastSnapshot.getRemoved() == null) { - // if there is one child not removed, then can not remove back up snapshot. - break; - } - lastId = lastSnapshot.getId(); - } - if (destroy) { - lastSnapshot = _snapshotDao.findByIdIncludingRemoved(lastId); - while (lastSnapshot.getRemoved() != null) { - String BackupSnapshotId = lastSnapshot.getBackupSnapshotId(); - if (BackupSnapshotId != null) { - List snaps = _snapshotDao.listByBackupUuid(lastSnapshot.getVolumeId(), BackupSnapshotId); - if (snaps != null && snaps.size() > 1) { - lastSnapshot.setBackupSnapshotId(null); - _snapshotDao.update(lastSnapshot.getId(), lastSnapshot); - } else { - if (destroySnapshotBackUp(lastSnapshot)) { - - } else { - s_logger.debug("Destroying snapshot backup failed " + lastSnapshot); - break; - } - } - } - lastId = lastSnapshot.getPrevSnapshotId(); - if (lastId == 0) { - break; - } - lastSnapshot = _snapshotDao.findByIdIncludingRemoved(lastId); - } + SnapshotInfo snapshotOnImage = this.snapshotDataFactory.getSnapshot(snapshotId, DataStoreRole.Image); + if (snapshotOnImage != null) { + deleteSnapshotChain(snapshotOnImage); + } + + obj.processEvent(Snapshot.Event.OperationSucceeded); + } catch (Exception e) { + s_logger.debug("Failed to delete snapshot: " + e.toString()); + try { + obj.processEvent(Snapshot.Event.OperationFailed); + } catch (NoTransitionException e1) { + s_logger.debug("Failed to change snapshot state: " + e.toString()); + } } + return true; } - - @Override - public boolean canHandle(SnapshotInfo snapshot) { - if (snapshot.getHypervisorType() == HypervisorType.XenServer) { - return true; - } else { - return false; - } - } @Override public SnapshotInfo takeSnapshot(SnapshotInfo snapshot) { @@ -197,4 +190,13 @@ public class XenserverSnapshotStrategy extends SnapshotStrategyBase { //TODO: add async return this.backupSnapshot(snapshot); } + + @Override + public boolean canHandle(Snapshot snapshot) { + if (snapshot.getHypervisorType() == HypervisorType.XenServer) { + return true; + } else { + return false; + } + } } diff --git a/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java b/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java index 7ff22c6aa3f..3b01475f90b 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java +++ b/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java @@ -84,33 +84,17 @@ public class ObjectInDataStoreManagerImpl implements ObjectInDataStoreManager { public ObjectInDataStoreManagerImpl() { stateMachines = new StateMachine2(); - stateMachines.addTransition(State.Allocated, Event.CreateRequested, + stateMachines.addTransition(State.Allocated, Event.CreateOnlyRequested, State.Creating); - stateMachines.addTransition(State.Creating, Event.OperationSuccessed, - State.Created); stateMachines.addTransition(State.Creating, Event.OperationFailed, - State.Failed); - stateMachines.addTransition(State.Failed, Event.CreateRequested, - State.Creating); - stateMachines.addTransition(State.Ready, Event.DestroyRequested, - State.Destroying); - stateMachines.addTransition(State.Destroying, Event.OperationSuccessed, - State.Destroyed); - stateMachines.addTransition(State.Destroying, Event.OperationFailed, - State.Destroying); - stateMachines.addTransition(State.Destroying, Event.DestroyRequested, - State.Destroying); - stateMachines.addTransition(State.Created, Event.CopyingRequested, + State.Allocated); + stateMachines.addTransition(State.Creating, Event.OperationSuccessed, + State.Ready); + stateMachines.addTransition(State.Ready, Event.CopyingRequested, State.Copying); - stateMachines.addTransition(State.Copying, Event.OperationFailed, - State.Created); stateMachines.addTransition(State.Copying, Event.OperationSuccessed, State.Ready); - stateMachines.addTransition(State.Allocated, Event.CreateOnlyRequested, - State.Creating2); - stateMachines.addTransition(State.Creating2, Event.OperationFailed, - State.Allocated); - stateMachines.addTransition(State.Creating2, Event.OperationSuccessed, + stateMachines.addTransition(State.Copying, Event.OperationFailed, State.Ready); } diff --git a/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java b/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java index e95a1f37fc5..11babcbabf3 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java +++ b/engine/storage/src/org/apache/cloudstack/storage/image/db/SnapshotDataStoreDaoImpl.java @@ -149,4 +149,10 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase future; private final DataStore primaryStore; private final DataObject templateOnStore; + private final SnapshotInfo snapshot; public CreateVolumeFromBaseImageContext(AsyncCompletionCallback callback, VolumeObject vo, DataStore primaryStore, DataObject templateOnStore, - AsyncCallFuture future) { + AsyncCallFuture future, SnapshotInfo snapshot) { super(callback); this.vo = vo; this.future = future; this.primaryStore = primaryStore; this.templateOnStore = templateOnStore; + this.snapshot = snapshot; } @@ -428,7 +430,7 @@ public class VolumeServiceImpl implements VolumeService { @DB protected void createVolumeFromBaseImageAsync(VolumeInfo volume, DataObject templateOnPrimaryStore, PrimaryDataStore pd, AsyncCallFuture future) { VolumeObject vo = (VolumeObject)volume; - CreateVolumeFromBaseImageContext context = new CreateVolumeFromBaseImageContext(null, vo, pd, templateOnPrimaryStore, future); + CreateVolumeFromBaseImageContext context = new CreateVolumeFromBaseImageContext(null, vo, pd, templateOnPrimaryStore, future, null); AsyncCallbackDispatcher caller = AsyncCallbackDispatcher.create(this); caller.setCallback(caller.getTarget().createVolumeFromBaseImageCallBack(null, null)) .setContext(context); @@ -497,8 +499,9 @@ public class VolumeServiceImpl implements VolumeService { try { DataObject volumeOnStore = store.create(volume); volume.processEvent(Event.CreateOnlyRequested); + snapshot.processEvent(Event.CopyingRequested); CreateVolumeFromBaseImageContext context = new CreateVolumeFromBaseImageContext(null, - (VolumeObject)volume, store, volumeOnStore, future); + (VolumeObject)volume, store, volumeOnStore, future, snapshot); AsyncCallbackDispatcher caller = AsyncCallbackDispatcher.create(this); caller.setCallback(caller.getTarget().createVolumeFromSnapshotCallback(null, null)) .setContext(context); @@ -517,6 +520,7 @@ public class VolumeServiceImpl implements VolumeService { CreateVolumeFromBaseImageContext context) { CopyCommandResult result = callback.getResult(); VolumeInfo volume = context.vo; + SnapshotInfo snapshot = context.snapshot; VolumeApiResult apiResult = new VolumeApiResult(volume); Event event = null; if (result.isFailed()) { @@ -528,6 +532,7 @@ public class VolumeServiceImpl implements VolumeService { try { volume.processEvent(event); + snapshot.processEvent(event); } catch (Exception e) { s_logger.debug("create volume from snapshot failed", e); apiResult.setResult(e.toString()); diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index 046827d79b7..cdc8da9e8f9 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -3620,7 +3620,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return false; } - private void swiftBackupSnapshot(Connection conn, SwiftTO swift, String srUuid, String snapshotUuid, String container, Boolean isISCSI, int wait) { + public void swiftBackupSnapshot(Connection conn, SwiftTO swift, String srUuid, String snapshotUuid, String container, Boolean isISCSI, int wait) { String lfilename; String ldir; if ( isISCSI ) { diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageResource.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageResource.java index 98527a9a226..8d762411fad 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageResource.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageResource.java @@ -18,12 +18,20 @@ */ package com.cloud.hypervisor.xen.resource; +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.net.URI; import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -43,6 +51,7 @@ import org.apache.cloudstack.storage.command.StorageSubSystemCommand; import org.apache.cloudstack.storage.datastore.protocol.DataStoreProtocol; import org.apache.cloudstack.storage.to.ImageStoreTO; import org.apache.cloudstack.storage.to.PrimaryDataStoreTO; +import org.apache.cloudstack.storage.to.SnapshotObjectTO; import org.apache.cloudstack.storage.to.TemplateObjectTO; import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.http.HttpEntity; @@ -54,18 +63,25 @@ import org.apache.log4j.Logger; import org.apache.xmlrpc.XmlRpcException; import com.cloud.agent.api.Answer; +import com.cloud.agent.api.BackupSnapshotAnswer; import com.cloud.agent.api.Command; +import com.cloud.agent.api.ManageSnapshotAnswer; +import com.cloud.agent.api.ManageSnapshotCommand; import com.cloud.agent.api.storage.CopyVolumeAnswer; import com.cloud.agent.api.storage.CreateAnswer; import com.cloud.agent.api.storage.DeleteVolumeCommand; import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer; import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.NfsTO; +import com.cloud.agent.api.to.S3TO; import com.cloud.agent.api.to.StorageFilerTO; +import com.cloud.agent.api.to.SwiftTO; import com.cloud.agent.api.to.VolumeTO; import com.cloud.exception.InternalErrorException; import com.cloud.hypervisor.xen.resource.CitrixResourceBase.SRType; import com.cloud.storage.DataStoreRole; +import com.cloud.utils.S3Utils; +import com.cloud.utils.StringUtils; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.storage.encoding.DecodedDataObject; import com.cloud.utils.storage.encoding.DecodedDataStore; @@ -80,8 +96,6 @@ import com.xensource.xenapi.Types.BadServerResponse; import com.xensource.xenapi.Types.XenAPIException; import com.xensource.xenapi.VDI; -import edu.emory.mathcs.backport.java.util.Arrays; - public class XenServerStorageResource { private static final Logger s_logger = Logger.getLogger(XenServerStorageResource.class); protected CitrixResourceBase hypervisorResource; @@ -146,12 +160,62 @@ public class XenServerStorageResource { return new CreateObjectAnswer(cmd, templateUrl, size);*/ return null; } + + protected CreateObjectAnswer createSnapshot(SnapshotObjectTO snapshotTO) { + Connection conn = hypervisorResource.getConnection(); + long snapshotId = snapshotTO.getId(); + String snapshotName = snapshotTO.getName(); + String details = "create snapshot operation Failed for snapshotId: " + snapshotId; + String snapshotUUID = null; + + try { + String volumeUUID = snapshotTO.getVolume().getPath(); + VDI volume = VDI.getByUuid(conn, volumeUUID); + + VDI snapshot = volume.snapshot(conn, new HashMap()); + + if (snapshotName != null) { + snapshot.setNameLabel(conn, snapshotName); + } + + snapshotUUID = snapshot.getUuid(conn); + String preSnapshotUUID = snapshotTO.getPath(); + //check if it is a empty snapshot + if( preSnapshotUUID != null) { + SR sr = volume.getSR(conn); + String srUUID = sr.getUuid(conn); + String type = sr.getType(conn); + Boolean isISCSI = IsISCSI(type); + String snapshotParentUUID = getVhdParent(conn, srUUID, snapshotUUID, isISCSI); + + String preSnapshotParentUUID = getVhdParent(conn, srUUID, preSnapshotUUID, isISCSI); + if( snapshotParentUUID != null && snapshotParentUUID.equals(preSnapshotParentUUID)) { + // this is empty snapshot, remove it + snapshot.destroy(conn); + snapshotUUID = preSnapshotUUID; + } + } + SnapshotObjectTO newSnapshot = new SnapshotObjectTO(); + newSnapshot.setPath(snapshotUUID); + return new CreateObjectAnswer(newSnapshot); + } catch (XenAPIException e) { + details += ", reason: " + e.toString(); + s_logger.warn(details, e); + } catch (Exception e) { + details += ", reason: " + e.toString(); + s_logger.warn(details, e); + } + + return new CreateObjectAnswer(details); + } protected CreateObjectAnswer execute(CreateObjectCommand cmd) { DataTO data = cmd.getData(); try { if (data.getObjectType() == DataObjectType.VOLUME) { return createVolume(data); - } + } else if (data.getObjectType() == DataObjectType.SNAPSHOT) { + return createSnapshot((SnapshotObjectTO)data); + } return new CreateObjectAnswer("not supported type"); } catch (Exception e) { s_logger.debug("Failed to create object: " + data.getObjectType() + ": " + e.toString()); @@ -821,6 +885,330 @@ public class XenServerStorageResource { return new CopyCmdAnswer("unsupported protocol"); } + boolean swiftUpload(Connection conn, SwiftTO swift, String container, String ldir, String lfilename, Boolean isISCSI, int wait) { + String result = null; + try { + result = hypervisorResource.callHostPluginAsync(conn, "swiftxen", "swift", wait, + "op", "upload", "url", swift.getUrl(), "account", swift.getAccount(), + "username", swift.getUserName(), "key", swift.getKey(), "container", container, + "ldir", ldir, "lfilename", lfilename, "isISCSI", isISCSI.toString()); + if( result != null && result.equals("true")) { + return true; + } + } catch (Exception e) { + s_logger.warn("swift upload failed due to " + e.toString(), e); + } + return false; + } + + protected String deleteSnapshotBackup(Connection conn, String path, String secondaryStorageMountPath, String backupUUID) { + + // If anybody modifies the formatting below again, I'll skin them + String result = hypervisorResource.callHostPlugin(conn, "vmopsSnapshot", "deleteSnapshotBackup", "backupUUID", backupUUID, "path", path, "secondaryStorageMountPath", secondaryStorageMountPath); + + return result; + } + + public void swiftBackupSnapshot(Connection conn, SwiftTO swift, String srUuid, String snapshotUuid, String container, Boolean isISCSI, int wait) { + String lfilename; + String ldir; + if ( isISCSI ) { + ldir = "/dev/VG_XenStorage-" + srUuid; + lfilename = "VHD-" + snapshotUuid; + } else { + ldir = "/var/run/sr-mount/" + srUuid; + lfilename = snapshotUuid + ".vhd"; + } + swiftUpload(conn, swift, container, ldir, lfilename, isISCSI, wait); + } + + private static List serializeProperties(final Object object, + final Class propertySet) { + + assert object != null; + assert propertySet != null; + assert propertySet.isAssignableFrom(object.getClass()); + + try { + + final BeanInfo beanInfo = Introspector.getBeanInfo(propertySet); + final PropertyDescriptor[] descriptors = beanInfo + .getPropertyDescriptors(); + + final List serializedProperties = new ArrayList(); + for (final PropertyDescriptor descriptor : descriptors) { + + serializedProperties.add(descriptor.getName()); + final Object value = descriptor.getReadMethod().invoke(object); + serializedProperties.add(value != null ? value.toString() + : "null"); + + } + + return Collections.unmodifiableList(serializedProperties); + + } catch (IntrospectionException e) { + s_logger.warn( + "Ignored IntrospectionException when serializing class " + + object.getClass().getCanonicalName(), e); + } catch (IllegalArgumentException e) { + s_logger.warn( + "Ignored IllegalArgumentException when serializing class " + + object.getClass().getCanonicalName(), e); + } catch (IllegalAccessException e) { + s_logger.warn( + "Ignored IllegalAccessException when serializing class " + + object.getClass().getCanonicalName(), e); + } catch (InvocationTargetException e) { + s_logger.warn( + "Ignored InvocationTargetException when serializing class " + + object.getClass().getCanonicalName(), e); + } + + return Collections.emptyList(); + + } + + private boolean backupSnapshotToS3(final Connection connection, + final S3TO s3, final String srUuid, final String snapshotUuid, + final Boolean iSCSIFlag, final int wait) { + + final String filename = iSCSIFlag ? "VHD-" + snapshotUuid + : snapshotUuid + ".vhd"; + final String dir = (iSCSIFlag ? "/dev/VG_XenStorage-" + : "/var/run/sr-mount/") + srUuid; + final String key = StringUtils.join("/", "snapshots", snapshotUuid); + + try { + + final List parameters = new ArrayList( + serializeProperties(s3, S3Utils.ClientOptions.class)); + parameters.addAll(Arrays.asList("operation", "put", "directory", + dir, "filename", filename, "iSCSIFlag", + iSCSIFlag.toString(), "key", key)); + final String result = hypervisorResource.callHostPluginAsync(connection, "s3xen", + "s3", wait, + parameters.toArray(new String[parameters.size()])); + + if (result != null && result.equals("true")) { + return true; + } + + } catch (Exception e) { + s_logger.error(String.format( + "S3 upload failed of snapshot %1$s due to %2$s.", + snapshotUuid, e.toString()), e); + } + + return false; + + } + + protected String backupSnapshot(Connection conn, String primaryStorageSRUuid, String path, String secondaryStorageMountPath, String snapshotUuid, String prevBackupUuid, Boolean isISCSI, int wait) { + String backupSnapshotUuid = null; + + if (prevBackupUuid == null) { + prevBackupUuid = ""; + } + + // Each argument is put in a separate line for readability. + // Using more lines does not harm the environment. + String backupUuid = UUID.randomUUID().toString(); + String results = hypervisorResource.callHostPluginAsync(conn, "vmopsSnapshot", "backupSnapshot", wait, + "primaryStorageSRUuid", primaryStorageSRUuid, "path", path, "secondaryStorageMountPath", secondaryStorageMountPath, + "snapshotUuid", snapshotUuid, "prevBackupUuid", prevBackupUuid, "backupUuid", backupUuid, "isISCSI", isISCSI.toString()); + String errMsg = null; + if (results == null || results.isEmpty()) { + errMsg = "Could not copy backupUuid: " + backupSnapshotUuid + + " from primary storage " + primaryStorageSRUuid + " to secondary storage " + + secondaryStorageMountPath + " due to null"; + } else { + + String[] tmp = results.split("#"); + String status = tmp[0]; + backupSnapshotUuid = tmp[1]; + // status == "1" if and only if backupSnapshotUuid != null + // So we don't rely on status value but return backupSnapshotUuid as an + // indicator of success. + if (status != null && status.equalsIgnoreCase("1") && backupSnapshotUuid != null) { + s_logger.debug("Successfully copied backupUuid: " + backupSnapshotUuid + + " to secondary storage"); + return backupSnapshotUuid; + } else { + errMsg = "Could not copy backupUuid: " + backupSnapshotUuid + + " from primary storage " + primaryStorageSRUuid + " to secondary storage " + + secondaryStorageMountPath + " due to " + tmp[1]; + } + } + String source = backupUuid + ".vhd"; + hypervisorResource.killCopyProcess(conn, source); + s_logger.warn(errMsg); + return null; + + } + + private boolean destroySnapshotOnPrimaryStorageExceptThis(Connection conn, String volumeUuid, String avoidSnapshotUuid){ + try { + VDI volume = getVDIbyUuid(conn, volumeUuid); + if (volume == null) { + throw new InternalErrorException("Could not destroy snapshot on volume " + volumeUuid + " due to can not find it"); + } + Set snapshots = volume.getSnapshots(conn); + for( VDI snapshot : snapshots ) { + try { + if(! snapshot.getUuid(conn).equals(avoidSnapshotUuid)) { + snapshot.destroy(conn); + } + } catch (Exception e) { + String msg = "Destroying snapshot: " + snapshot+ " on primary storage failed due to " + e.toString(); + s_logger.warn(msg, e); + } + } + s_logger.debug("Successfully destroyed snapshot on volume: " + volumeUuid + " execept this current snapshot "+ avoidSnapshotUuid ); + return true; + } catch (XenAPIException e) { + String msg = "Destroying snapshot on volume: " + volumeUuid + " execept this current snapshot "+ avoidSnapshotUuid + " failed due to " + e.toString(); + s_logger.error(msg, e); + } catch (Exception e) { + String msg = "Destroying snapshot on volume: " + volumeUuid + " execept this current snapshot "+ avoidSnapshotUuid + " failed due to " + e.toString(); + s_logger.warn(msg, e); + } + + return false; + } + + protected Answer backupSnasphot(DataTO srcData, DataTO destData, DataTO cacheData, int wait) { + Connection conn = hypervisorResource.getConnection(); + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)srcData.getDataStore(); + String primaryStorageNameLabel = primaryStore.getUuid(); + String secondaryStorageUrl = null; + NfsTO cacheStore = null; + String destPath = null; + if (cacheData != null) { + cacheStore = (NfsTO)cacheData.getDataStore(); + secondaryStorageUrl = cacheStore.getUrl(); + destPath = cacheData.getPath(); + } else { + cacheStore = (NfsTO)destData.getDataStore(); + secondaryStorageUrl = cacheStore.getUrl(); + destPath = destData.getPath(); + } + + SnapshotObjectTO snapshotTO = (SnapshotObjectTO)srcData; + SnapshotObjectTO snapshotOnImage = (SnapshotObjectTO)destData; + String snapshotUuid = snapshotTO.getPath(); + + String prevBackupUuid = snapshotOnImage.getParentSnapshotPath(); + String prevSnapshotUuid = snapshotTO.getParentSnapshotPath(); + + // By default assume failure + String details = null; + String snapshotBackupUuid = null; + boolean fullbackup = true; + try { + SR primaryStorageSR = hypervisorResource.getSRByNameLabelandHost(conn, primaryStorageNameLabel); + if (primaryStorageSR == null) { + throw new InternalErrorException("Could not backup snapshot because the primary Storage SR could not be created from the name label: " + primaryStorageNameLabel); + } + String psUuid = primaryStorageSR.getUuid(conn); + Boolean isISCSI = IsISCSI(primaryStorageSR.getType(conn)); + + VDI snapshotVdi = getVDIbyUuid(conn, snapshotUuid); + String snapshotPaUuid = null; + if ( prevBackupUuid != null ) { + try { + snapshotPaUuid = getVhdParent(conn, psUuid, snapshotUuid, isISCSI); + if( snapshotPaUuid != null ) { + String snashotPaPaPaUuid = getVhdParent(conn, psUuid, snapshotPaUuid, isISCSI); + String prevSnashotPaUuid = getVhdParent(conn, psUuid, prevSnapshotUuid, isISCSI); + if (snashotPaPaPaUuid != null && prevSnashotPaUuid!= null && prevSnashotPaUuid.equals(snashotPaPaPaUuid)) { + fullbackup = false; + } + } + } catch (Exception e) { + } + } + + URI uri = new URI(secondaryStorageUrl); + String secondaryStorageMountPath = uri.getHost() + ":" + uri.getPath(); + DataStoreTO destStore = destData.getDataStore(); + String folder = destPath; + if (fullbackup) { + // the first snapshot is always a full snapshot + + if( !hypervisorResource.createSecondaryStorageFolder(conn, secondaryStorageMountPath, folder)) { + details = " Filed to create folder " + folder + " in secondary storage"; + s_logger.warn(details); + return new CopyCmdAnswer(details); + } + String snapshotMountpoint = secondaryStorageUrl + "/" + folder; + SR snapshotSr = null; + try { + snapshotSr = hypervisorResource.createNfsSRbyURI(conn, new URI(snapshotMountpoint), false); + VDI backedVdi = hypervisorResource.cloudVDIcopy(conn, snapshotVdi, snapshotSr, wait); + snapshotBackupUuid = backedVdi.getUuid(conn); + + if( destStore instanceof SwiftTO) { + try { + hypervisorResource.swiftBackupSnapshot(conn, (SwiftTO)destStore, snapshotSr.getUuid(conn), snapshotBackupUuid, "S-" + snapshotTO.getVolume().getVolumeId().toString(), false, wait); + snapshotBackupUuid = snapshotBackupUuid + ".vhd"; + } finally { + deleteSnapshotBackup(conn, folder, secondaryStorageMountPath, snapshotBackupUuid); + } + } else if (destStore instanceof S3TO) { + try { + backupSnapshotToS3(conn, (S3TO)destStore, snapshotSr.getUuid(conn), snapshotBackupUuid, isISCSI, wait); + snapshotBackupUuid = snapshotBackupUuid + ".vhd"; + } finally { + deleteSnapshotBackup(conn, folder, secondaryStorageMountPath, snapshotBackupUuid); + } + } + + } finally { + if( snapshotSr != null) { + hypervisorResource.removeSR(conn, snapshotSr); + } + } + } else { + String primaryStorageSRUuid = primaryStorageSR.getUuid(conn); + if( destStore instanceof SwiftTO ) { + swiftBackupSnapshot(conn, (SwiftTO)destStore, primaryStorageSRUuid, snapshotPaUuid, "S-" + snapshotTO.getVolume().getVolumeId().toString(), isISCSI, wait); + if ( isISCSI ) { + snapshotBackupUuid = "VHD-" + snapshotPaUuid; + } else { + snapshotBackupUuid = snapshotPaUuid + ".vhd"; + } + + } else if (destStore instanceof S3TO ) { + backupSnapshotToS3(conn, (S3TO)destStore, primaryStorageSRUuid, snapshotPaUuid, isISCSI, wait); + } else { + snapshotBackupUuid = backupSnapshot(conn, primaryStorageSRUuid, folder + File.separator + UUID.nameUUIDFromBytes(secondaryStorageMountPath.getBytes()) + , secondaryStorageMountPath, snapshotUuid, prevBackupUuid, isISCSI, wait); + + } + } + String volumeUuid = snapshotTO.getVolume().getPath(); + destroySnapshotOnPrimaryStorageExceptThis(conn, volumeUuid, snapshotUuid); + + SnapshotObjectTO newSnapshot = new SnapshotObjectTO(); + newSnapshot.setPath(snapshotBackupUuid); + if (fullbackup) { + newSnapshot.setParentSnapshotPath(null); + } else { + newSnapshot.setParentSnapshotPath(prevBackupUuid); + } + return new CopyCmdAnswer(newSnapshot); + } catch (XenAPIException e) { + details = "BackupSnapshot Failed due to " + e.toString(); + s_logger.warn(details, e); + } catch (Exception e) { + details = "BackupSnapshot Failed due to " + e.getMessage(); + s_logger.warn(details, e); + } + + return new CopyCmdAnswer(details); + } + protected Answer execute(CopyCommand cmd) { DataTO srcData = cmd.getSrcTO(); DataTO destData = cmd.getDestTO(); @@ -838,6 +1226,9 @@ public class XenServerStorageResource { return copyVolumeFromImageCacheToPrimary(srcData, destData, cmd.getWait()); } else if (srcData.getObjectType() == DataObjectType.VOLUME && srcData.getDataStore().getRole() == DataStoreRole.Primary) { return copyVolumeFromPrimaryToSecondary(srcData, destData, cmd.getWait()); + } else if (srcData.getObjectType() == DataObjectType.SNAPSHOT && srcData.getDataStore().getRole() == DataStoreRole.Primary) { + DataTO cacheData = cmd.getCacheTO(); + return backupSnasphot(srcData, destData, cacheData, cmd.getWait()); } return new Answer(cmd, false, "not implemented yet"); diff --git a/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java b/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java index 5e82a7eaec1..93d37ded506 100644 --- a/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java +++ b/plugins/storage/volume/default/src/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java @@ -36,6 +36,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; import org.apache.cloudstack.storage.command.CreateObjectCommand; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.to.SnapshotObjectTO; import org.apache.cloudstack.storage.volume.VolumeObject; import org.apache.log4j.Logger; @@ -193,24 +194,17 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri AsyncCompletionCallback callback) { CreateCmdResult result = null; try { - VolumeInfo volume = snapshot.getBaseVolume(); - String vmName = this.volumeMgr.getVmNameOnVolume(volume); - SnapshotVO preSnapshotVO = this.snapshotMgr.getParentSnapshot(volume, snapshot); - String parentSnapshotPath = null; - if (preSnapshotVO != null) { - parentSnapshotPath = preSnapshotVO.getPath(); + DataTO snapshotTO = snapshot.getTO(); + + CreateObjectCommand cmd = new CreateObjectCommand(snapshotTO); + Answer answer = storageMgr.sendToPool((StoragePool)snapshot.getDataStore(), null, cmd); + + result = new CreateCmdResult(null, answer); + if (answer != null && !answer.getResult()) { + result.setResult(answer.getDetails()); } - StoragePool srcPool = (StoragePool)volume.getDataStore(); - ManageSnapshotCommand cmd = new ManageSnapshotCommand(snapshot.getId(), volume.getPath(), srcPool, parentSnapshotPath, snapshot.getName(), vmName); - - ManageSnapshotAnswer answer = (ManageSnapshotAnswer) this.snapshotMgr.sendToPool(volume, cmd); - - if ((answer != null) && answer.getResult()) { - result = new CreateCmdResult(answer.getSnapshotPath(), null); - } else { - result = new CreateCmdResult(null, null); - } + callback.complete(result); } catch (Exception e) { s_logger.debug("Failed to take snapshot: " + snapshot.getId(), e); result = new CreateCmdResult(null, null); diff --git a/server/src/com/cloud/storage/CreateSnapshotPayload.java b/server/src/com/cloud/storage/CreateSnapshotPayload.java index cafe46c8814..2eec00d39c3 100644 --- a/server/src/com/cloud/storage/CreateSnapshotPayload.java +++ b/server/src/com/cloud/storage/CreateSnapshotPayload.java @@ -1,7 +1,11 @@ package com.cloud.storage; +import com.cloud.user.Account; + public class CreateSnapshotPayload { private Long snapshotPolicyId; + private Long snapshotId; + private Account account; public Long getSnapshotPolicyId() { return snapshotPolicyId; @@ -11,4 +15,20 @@ public class CreateSnapshotPayload { this.snapshotPolicyId = snapshotPolicyId; } + public Long getSnapshotId() { + return snapshotId; + } + + public void setSnapshotId(Long snapshotId) { + this.snapshotId = snapshotId; + } + + public Account getAccount() { + return account; + } + + public void setAccount(Account account) { + this.account = account; + } + } diff --git a/server/src/com/cloud/storage/VolumeManagerImpl.java b/server/src/com/cloud/storage/VolumeManagerImpl.java index 3177a591728..23e329558c7 100644 --- a/server/src/com/cloud/storage/VolumeManagerImpl.java +++ b/server/src/com/cloud/storage/VolumeManagerImpl.java @@ -133,6 +133,7 @@ import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.download.DownloadMonitor; import com.cloud.storage.s3.S3Manager; import com.cloud.storage.secondary.SecondaryStorageVmManager; +import com.cloud.storage.snapshot.SnapshotApiService; import com.cloud.storage.snapshot.SnapshotManager; import com.cloud.storage.snapshot.SnapshotScheduler; import com.cloud.tags.dao.ResourceTagDao; @@ -307,6 +308,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { TemplateDataFactory tmplFactory; @Inject SnapshotDataFactory snapshotFactory; + @Inject + SnapshotApiService snapshotMgr; private int _copyvolumewait; @Inject protected HypervisorCapabilitiesDao _hypervisorCapabilitiesDao; @@ -526,7 +529,7 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { VolumeInfo vol = this.volFactory.getVolume(volume.getId()); DataStore store = this.dataStoreMgr.getDataStore(pool.getId(), DataStoreRole.Primary); - SnapshotInfo snapInfo = this.snapshotFactory.getSnapshot(snapshot.getId()); + SnapshotInfo snapInfo = this.snapshotFactory.getSnapshot(snapshot.getId(), DataStoreRole.Image); AsyncCallFuture future = this.volService.createVolumeFromSnapshot(vol, store, snapInfo); try { VolumeApiResult result = future.get(); @@ -2470,8 +2473,26 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { @Override - public Snapshot takeSnapshot(Long volumeId, Long policyId) throws ResourceAllocationException { - Account caller = UserContext.current().getCaller(); + public Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account) throws ResourceAllocationException { + VolumeInfo volume = this.volFactory.getVolume(volumeId); + if (volume == null) { + throw new InvalidParameterValueException("Creating snapshot failed due to volume:" + volumeId + " doesn't exist"); + } + + if (volume.getState() != Volume.State.Ready) { + throw new InvalidParameterValueException("VolumeId: " + volumeId + " is not in " + Volume.State.Ready + " state but " + volume.getState() + ". Cannot take snapshot."); + } + + CreateSnapshotPayload payload = new CreateSnapshotPayload(); + payload.setSnapshotId(snapshotId); + payload.setSnapshotPolicyId(policyId); + payload.setAccount(account); + return this.volService.takeSnapshot(volume); + } + + @Override + public Snapshot allocSnapshot(Long volumeId, Long policyId) throws ResourceAllocationException { + Account caller = UserContext.current().getCaller(); VolumeInfo volume = this.volFactory.getVolume(volumeId); if (volume == null) { @@ -2502,10 +2523,7 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { throw new InvalidParameterValueException("VolumeId: " + volumeId + " please attach this volume to a VM before create snapshot for it"); } - CreateSnapshotPayload payload = new CreateSnapshotPayload(); - payload.setSnapshotPolicyId(policyId); - return this.volService.takeSnapshot(volume); - + return this.snapshotMgr.allocSnapshot(volumeId, policyId); } } diff --git a/server/src/com/cloud/storage/dao/SnapshotDaoImpl.java b/server/src/com/cloud/storage/dao/SnapshotDaoImpl.java index 24d4c653aeb..a3493890a59 100644 --- a/server/src/com/cloud/storage/dao/SnapshotDaoImpl.java +++ b/server/src/com/cloud/storage/dao/SnapshotDaoImpl.java @@ -151,7 +151,7 @@ public class SnapshotDaoImpl extends GenericDaoBase implements VolumeIdVersionSearch.and("volumeId", VolumeIdVersionSearch.entity().getVolumeId(), SearchCriteria.Op.EQ); VolumeIdVersionSearch.and("version", VolumeIdVersionSearch.entity().getVersion(), SearchCriteria.Op.EQ); VolumeIdVersionSearch.done(); - +/* ParentIdSearch = createSearchBuilder(); ParentIdSearch.and("prevSnapshotId", ParentIdSearch.entity().getPrevSnapshotId(), SearchCriteria.Op.EQ); ParentIdSearch.done(); @@ -159,7 +159,7 @@ public class SnapshotDaoImpl extends GenericDaoBase implements backupUuidSearch = createSearchBuilder(); backupUuidSearch.and("backupUuid", backupUuidSearch.entity().getBackupSnapshotId(), SearchCriteria.Op.EQ); backupUuidSearch.done(); - +*/ AccountIdSearch = createSearchBuilder(); AccountIdSearch.and("accountId", AccountIdSearch.entity().getAccountId(), SearchCriteria.Op.EQ); AccountIdSearch.done(); diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManager.java b/server/src/com/cloud/storage/snapshot/SnapshotManager.java index 983b50c3e65..d6e0af66da2 100755 --- a/server/src/com/cloud/storage/snapshot/SnapshotManager.java +++ b/server/src/com/cloud/storage/snapshot/SnapshotManager.java @@ -56,10 +56,6 @@ public interface SnapshotManager { * The account which is to be deleted. */ boolean deleteSnapshotDirsForAccount(long accountId); - - void downloadSnapshotsFromSwift(SnapshotVO ss); - - void downloadSnapshotsFromS3(SnapshotVO snapshot); String getSecondaryStorageURL(SnapshotVO snapshot); diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java index c18ffa87092..572bcab691c 100755 --- a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -349,8 +349,10 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, return this.snapshotSrv.backupSnapshot(snapshot); } + /* @Override public void downloadSnapshotsFromSwift(SnapshotVO ss) { + long volumeId = ss.getVolumeId(); VolumeVO volume = _volsDao.findById(volumeId); Long dcId = volume.getDataCenterId(); @@ -432,7 +434,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, e); } - } + }*/ @Override public SnapshotVO getParentSnapshot(VolumeInfo volume) { @@ -974,72 +976,44 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, } @Override public SnapshotInfo takeSnapshot(VolumeInfo volume) throws ResourceAllocationException { - Account caller = UserContext.current().getCaller(); - - supportedByHypervisor(volume); - CreateSnapshotPayload snapInfo = (CreateSnapshotPayload)volume.getpayload(); - Long policyId = snapInfo.getSnapshotPolicyId(); - // Verify permissions - _accountMgr.checkAccess(caller, null, true, volume); - Type snapshotType = getSnapshotType(policyId); - Account owner = _accountMgr.getAccount(volume.getAccountId()); - - try{ - _resourceLimitMgr.checkResourceLimit(owner, ResourceType.snapshot); - if (backup) { - _resourceLimitMgr.checkResourceLimit(owner, ResourceType.secondary_storage, new Long(volume.getSize())); - } else { - _resourceLimitMgr.checkResourceLimit(owner, ResourceType.primary_storage, new Long(volume.getSize())); - } - } catch (ResourceAllocationException e) { - if (snapshotType != Type.MANUAL){ - String msg = "Snapshot resource limit exceeded for account id : " + owner.getId() + ". Failed to create recurring snapshots"; - s_logger.warn(msg); - _alertMgr.sendAlert(AlertManager.ALERT_TYPE_UPDATE_RESOURCE_COUNT, 0L, 0L, msg, - "Snapshot resource limit exceeded for account id : " + owner.getId() + ". Failed to create recurring snapshots; please use updateResourceLimit to increase the limit"); - } - throw e; - } - - // Determine the name for this snapshot - // Snapshot Name: VMInstancename + volumeName + timeString - String timeString = DateUtil.getDateDisplayString(DateUtil.GMT_TIMEZONE, new Date(), DateUtil.YYYYMMDD_FORMAT); - - VMInstanceVO vmInstance = _vmDao.findById(volume.getInstanceId()); - String vmDisplayName = "detached"; - if (vmInstance != null) { - vmDisplayName = vmInstance.getHostName(); - } - String snapshotName = vmDisplayName + "_" + volume.getName() + "_" + timeString; - - HypervisorType hypervisorType = volume.getHypervisorType(); - SnapshotVO snapshotVO = new SnapshotVO(volume.getDataCenterId(), volume.getAccountId(), volume.getDomainId(), volume.getId(), volume.getDiskOfferingId(), snapshotName, - (short) snapshotType.ordinal(), snapshotType.name(), volume.getSize(), hypervisorType); - - SnapshotVO snapshot = _snapshotDao.persist(snapshotVO); - if (snapshot == null) { - throw new CloudRuntimeException("Failed to create snapshot for volume: " + volume.getId()); - } - if (backup) { - _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.secondary_storage, - new Long(volume.getSize())); - } else { - _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.primary_storage, - new Long(volume.getSize())); - } - SnapshotInfo snap = this.snapshotFactory.getSnapshot(snapshot.getId(), volume.getDataStore()); + CreateSnapshotPayload payload = (CreateSnapshotPayload)volume.getpayload(); + Long snapshotId = payload.getSnapshotId(); + Account snapshotOwner = payload.getAccount(); + SnapshotInfo snapshot = this.snapshotFactory.getSnapshot(snapshotId, volume.getDataStore()); boolean processed = false; - for (SnapshotStrategy strategy : snapshotStrategies) { - if (strategy.canHandle(snap)) { - processed = true; - snap = strategy.takeSnapshot(snap); - break; - } + + try { + for (SnapshotStrategy strategy : snapshotStrategies) { + if (strategy.canHandle(snapshot)) { + processed = true; + snapshot = strategy.takeSnapshot(snapshot); + break; + } + } + if (!processed) { + throw new CloudRuntimeException("Can't find snapshot strategy to deal with snapshot:" + snapshotId); + } + postCreateSnapshot(volume.getId(), snapshotId, payload.getSnapshotPolicyId()); + + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SNAPSHOT_CREATE, snapshot.getAccountId(), + snapshot.getDataCenterId(), snapshotId, snapshot.getName(), null, null, + volume.getSize(), snapshot.getClass().getName(), snapshot.getUuid()); + + + _resourceLimitMgr.incrementResourceCount(snapshotOwner.getId(), ResourceType.snapshot); + + } catch(Exception e) { + s_logger.debug("Failed to create snapshot", e); + if (backup) { + _resourceLimitMgr.decrementResourceCount(snapshotOwner.getId(), ResourceType.secondary_storage, + new Long(volume.getSize())); + } else { + _resourceLimitMgr.decrementResourceCount(snapshotOwner.getId(), ResourceType.primary_storage, + new Long(volume.getSize())); + } + throw new CloudRuntimeException("Failed to create snapshot", e); } - if (!processed) { - throw new CloudRuntimeException("Can't find snapshot strategy to deal with snapshot:" + snapshot.getId()); - } - return snap; + return snapshot; } @Override @@ -1128,4 +1102,61 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, } return true; } + + @Override + public Snapshot allocSnapshot(Long volumeId, Long policyId) throws ResourceAllocationException { + Account caller = UserContext.current().getCaller(); + VolumeInfo volume = this.volFactory.getVolume(volumeId); + supportedByHypervisor(volume); + + // Verify permissions + _accountMgr.checkAccess(caller, null, true, volume); + Type snapshotType = getSnapshotType(policyId); + Account owner = _accountMgr.getAccount(volume.getAccountId()); + + try{ + _resourceLimitMgr.checkResourceLimit(owner, ResourceType.snapshot); + if (backup) { + _resourceLimitMgr.checkResourceLimit(owner, ResourceType.secondary_storage, new Long(volume.getSize())); + } else { + _resourceLimitMgr.checkResourceLimit(owner, ResourceType.primary_storage, new Long(volume.getSize())); + } + } catch (ResourceAllocationException e) { + if (snapshotType != Type.MANUAL){ + String msg = "Snapshot resource limit exceeded for account id : " + owner.getId() + ". Failed to create recurring snapshots"; + s_logger.warn(msg); + _alertMgr.sendAlert(AlertManager.ALERT_TYPE_UPDATE_RESOURCE_COUNT, 0L, 0L, msg, + "Snapshot resource limit exceeded for account id : " + owner.getId() + ". Failed to create recurring snapshots; please use updateResourceLimit to increase the limit"); + } + throw e; + } + + // Determine the name for this snapshot + // Snapshot Name: VMInstancename + volumeName + timeString + String timeString = DateUtil.getDateDisplayString(DateUtil.GMT_TIMEZONE, new Date(), DateUtil.YYYYMMDD_FORMAT); + + VMInstanceVO vmInstance = _vmDao.findById(volume.getInstanceId()); + String vmDisplayName = "detached"; + if (vmInstance != null) { + vmDisplayName = vmInstance.getHostName(); + } + String snapshotName = vmDisplayName + "_" + volume.getName() + "_" + timeString; + + HypervisorType hypervisorType = volume.getHypervisorType(); + SnapshotVO snapshotVO = new SnapshotVO(volume.getDataCenterId(), volume.getAccountId(), volume.getDomainId(), volume.getId(), volume.getDiskOfferingId(), snapshotName, + (short) snapshotType.ordinal(), snapshotType.name(), volume.getSize(), hypervisorType); + + SnapshotVO snapshot = _snapshotDao.persist(snapshotVO); + if (snapshot == null) { + throw new CloudRuntimeException("Failed to create snapshot for volume: " + volume.getId()); + } + if (backup) { + _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.secondary_storage, + new Long(volume.getSize())); + } else { + _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.primary_storage, + new Long(volume.getSize())); + } + return snapshot; + } } diff --git a/server/src/com/cloud/template/TemplateManagerImpl.java b/server/src/com/cloud/template/TemplateManagerImpl.java index de69c12376a..fbac9b40892 100755 --- a/server/src/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/com/cloud/template/TemplateManagerImpl.java @@ -1355,7 +1355,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, } AsyncCallFuture future = null; if (snapshotId != null) { - SnapshotInfo snapInfo = this._snapshotFactory.getSnapshot(snapshotId); + SnapshotInfo snapInfo = this._snapshotFactory.getSnapshot(snapshotId, DataStoreRole.Image); future = this._tmpltSvr.createTemplateFromSnapshotAsync(snapInfo, tmplInfo, store.get(0)); } else if (volumeId != null) { volume = _volumeDao.findById(volumeId);