From 37cbe8890faf2a445b6c34b7220978e982651481 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Mon, 29 Apr 2013 09:31:52 -0700 Subject: [PATCH] refactor snapshot --- api/src/com/cloud/storage/Snapshot.java | 7 +- .../com/cloud/storage/VolumeApiService.java | 3 + core/src/com/cloud/storage/SnapshotVO.java | 61 +--- .../com/cloud/storage/VMTemplateHostVO.java | 6 + .../storage/VMTemplateStoragePoolVO.java | 5 + core/src/com/cloud/storage/VolumeHostVO.java | 6 + .../api/storage/DataObjectInStore.java | 1 + .../api/storage/SnapshotDataFactory.java | 4 +- .../subsystem/api/storage/SnapshotInfo.java | 3 + .../subsystem/api/storage/SnapshotResult.java | 25 ++ .../api/storage/SnapshotService.java | 3 +- .../api/storage/SnapshotStrategy.java | 11 + .../subsystem/api/storage/VolumeService.java | 2 + .../storage/command/CreateObjectAnswer.java | 27 +- .../storage/command/CreateObjectCommand.java | 12 +- .../datastore/db/SnapshotDataStoreDao.java | 9 +- .../datastore/db/SnapshotDataStoreVO.java | 47 ++- .../datastore/db/TemplateDataStoreVO.java | 6 +- .../datastore/db/VolumeDataStoreVO.java | 6 + .../storage/to/SnapshotObjectTO.java | 30 ++ .../motion/AncientDataMotionStrategy.java | 57 +--- engine/storage/snapshot/pom.xml | 5 + .../snapshot/SnapshotDataFactoryImpl.java | 37 ++- .../storage/snapshot/SnapshotObject.java | 53 ++-- .../storage/snapshot/SnapshotServiceImpl.java | 277 +++--------------- .../snapshot/SnapshotStrategyBase.java | 24 ++ .../snapshot/XenserverSnapshotStrategy.java | 200 +++++++++++++ .../ObjectInDataStoreManagerImpl.java | 22 +- .../storage/db/ObjectInDataStoreVO.java | 6 + .../image/db/SnapshotDataStoreDaoImpl.java | 45 +-- .../storage/snapshot/SnapshotService.java | 27 -- .../datastore/PrimaryDataStoreImpl.java | 3 +- .../storage/volume/VolumeServiceImpl.java | 21 ++ .../resource/XenServerStorageResource.java | 86 ++---- .../driver/SampleImageStoreDriverImpl.java | 4 +- .../CloudStackPrimaryDataStoreDriverImpl.java | 48 +-- .../cloud/storage/CreateSnapshotPayload.java | 14 + .../com/cloud/storage/StorageManagerImpl.java | 2 +- .../com/cloud/storage/VolumeManagerImpl.java | 44 +++ .../com/cloud/storage/dao/SnapshotDao.java | 3 +- .../cloud/storage/dao/SnapshotDaoImpl.java | 7 +- .../storage/snapshot/SnapshotManager.java | 6 +- .../storage/snapshot/SnapshotManagerImpl.java | 192 +++++++----- 43 files changed, 775 insertions(+), 682 deletions(-) create mode 100644 engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotResult.java create mode 100644 engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotStrategy.java create mode 100644 engine/api/src/org/apache/cloudstack/storage/to/SnapshotObjectTO.java create mode 100644 engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotStrategyBase.java create mode 100644 engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java delete mode 100644 engine/storage/src/org/apache/cloudstack/storage/snapshot/SnapshotService.java create mode 100644 server/src/com/cloud/storage/CreateSnapshotPayload.java diff --git a/api/src/com/cloud/storage/Snapshot.java b/api/src/com/cloud/storage/Snapshot.java index f71265cd230..0d58a1401bc 100644 --- a/api/src/com/cloud/storage/Snapshot.java +++ b/api/src/com/cloud/storage/Snapshot.java @@ -18,12 +18,13 @@ package com.cloud.storage; import java.util.Date; -import com.cloud.hypervisor.Hypervisor.HypervisorType; -import com.cloud.utils.fsm.StateObject; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.InternalIdentity; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.utils.fsm.StateObject; + public interface Snapshot extends ControlledEntity, Identity, InternalIdentity, StateObject { public enum Type { MANUAL, @@ -85,8 +86,6 @@ public interface Snapshot extends ControlledEntity, Identity, InternalIdentity, long getVolumeId(); - String getPath(); - String getName(); Date getCreated(); diff --git a/api/src/com/cloud/storage/VolumeApiService.java b/api/src/com/cloud/storage/VolumeApiService.java index 09a07d4be13..2f5364fe525 100644 --- a/api/src/com/cloud/storage/VolumeApiService.java +++ b/api/src/com/cloud/storage/VolumeApiService.java @@ -79,4 +79,7 @@ public interface VolumeApiService { Volume attachVolumeToVM(AttachVolumeCmd command); Volume detachVolumeFromVM(DetachVolumeCmd cmmd); + + Snapshot takeSnapshot(Long volumeId, Long policyId) + throws ResourceAllocationException; } diff --git a/core/src/com/cloud/storage/SnapshotVO.java b/core/src/com/cloud/storage/SnapshotVO.java index dbfd7baa617..4fc195cfec4 100644 --- a/core/src/com/cloud/storage/SnapshotVO.java +++ b/core/src/com/cloud/storage/SnapshotVO.java @@ -48,10 +48,6 @@ public class SnapshotVO implements Snapshot { @Column(name="disk_offering_id") Long diskOfferingId; - @Expose - @Column(name="path") - String path; - @Expose @Column(name="name") String name; @@ -76,18 +72,6 @@ public class SnapshotVO implements Snapshot { @Column(name=GenericDao.REMOVED_COLUMN) Date removed; - @Column(name="backup_snap_id") - String backupSnapshotId; - - @Column(name="swift_id") - Long swiftId; - - @Column(name="s3_id") - Long s3Id; - - @Column(name="prev_snap_id") - long prevSnapshotId; - @Column(name="hypervisor_type") @Enumerated(value=EnumType.STRING) HypervisorType hypervisorType; @@ -103,19 +87,17 @@ public class SnapshotVO implements Snapshot { this.uuid = UUID.randomUUID().toString(); } - public SnapshotVO(long dcId, long accountId, long domainId, Long volumeId, Long diskOfferingId, String path, String name, short snapshotType, String typeDescription, long size, HypervisorType hypervisorType ) { + public SnapshotVO(long dcId, long accountId, long domainId, Long volumeId, Long diskOfferingId, String name, short snapshotType, String typeDescription, long size, HypervisorType hypervisorType ) { this.dataCenterId = dcId; this.accountId = accountId; this.domainId = domainId; this.volumeId = volumeId; this.diskOfferingId = diskOfferingId; - this.path = path; this.name = name; this.snapshotType = snapshotType; this.typeDescription = typeDescription; this.size = size; this.state = State.Allocated; - this.prevSnapshotId = 0; this.hypervisorType = hypervisorType; this.version = "2.2"; this.uuid = UUID.randomUUID().toString(); @@ -153,14 +135,6 @@ public class SnapshotVO implements Snapshot { this.volumeId = volumeId; } - @Override - public String getPath() { - return path; - } - - public void setPath(String path) { - this.path = path; - } @Override public String getName() { @@ -179,14 +153,6 @@ public class SnapshotVO implements Snapshot { return Type.values()[snapshotType]; } - public Long getSwiftId() { - return swiftId; - } - - public void setSwiftId(Long swiftId) { - this.swiftId = swiftId; - } - @Override public HypervisorType getHypervisorType() { return hypervisorType; @@ -242,22 +208,6 @@ public class SnapshotVO implements Snapshot { this.state = state; } - public String getBackupSnapshotId(){ - return backupSnapshotId; - } - - public long getPrevSnapshotId(){ - return prevSnapshotId; - } - - public void setBackupSnapshotId(String backUpSnapshotId){ - this.backupSnapshotId = backUpSnapshotId; - } - - public void setPrevSnapshotId(long prevSnapshotId){ - this.prevSnapshotId = prevSnapshotId; - } - public static Type getSnapshotType(String snapshotType) { for ( Type type : Type.values()) { if ( type.equals(snapshotType)) { @@ -275,13 +225,4 @@ public class SnapshotVO implements Snapshot { public void setUuid(String uuid) { this.uuid = uuid; } - - public Long getS3Id() { - return s3Id; - } - - public void setS3Id(Long s3Id) { - this.s3Id = s3Id; - } - } diff --git a/core/src/com/cloud/storage/VMTemplateHostVO.java b/core/src/com/cloud/storage/VMTemplateHostVO.java index 8b257598041..227394248b3 100755 --- a/core/src/com/cloud/storage/VMTemplateHostVO.java +++ b/core/src/com/cloud/storage/VMTemplateHostVO.java @@ -31,6 +31,7 @@ import javax.persistence.TemporalType; import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State; import com.cloud.utils.db.GenericDaoBase; @@ -329,5 +330,10 @@ public class VMTemplateHostVO implements VMTemplateStorageResourceAssoc, DataObj return this.getHostId(); } + @Override + public State getObjectInStoreState() { + return this.state; + } + } diff --git a/core/src/com/cloud/storage/VMTemplateStoragePoolVO.java b/core/src/com/cloud/storage/VMTemplateStoragePoolVO.java index 638ddd090a6..4c6a758ca90 100644 --- a/core/src/com/cloud/storage/VMTemplateStoragePoolVO.java +++ b/core/src/com/cloud/storage/VMTemplateStoragePoolVO.java @@ -281,5 +281,10 @@ public class VMTemplateStoragePoolVO implements VMTemplateStorageResourceAssoc, return this.getPoolId(); } + @Override + public State getObjectInStoreState() { + return this.state; + } + } diff --git a/core/src/com/cloud/storage/VolumeHostVO.java b/core/src/com/cloud/storage/VolumeHostVO.java index 63b1091059e..a6cd4571fa0 100755 --- a/core/src/com/cloud/storage/VolumeHostVO.java +++ b/core/src/com/cloud/storage/VolumeHostVO.java @@ -32,6 +32,7 @@ import javax.persistence.TemporalType; import org.apache.cloudstack.api.InternalIdentity; import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; @@ -354,5 +355,10 @@ public class VolumeHostVO implements InternalIdentity, DataObjectInStore { return this.getHostId(); } + @Override + public State getObjectInStoreState() { + return this.state; + } + } diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataObjectInStore.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataObjectInStore.java index ded2640bb82..0bfbd9a7aa4 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataObjectInStore.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataObjectInStore.java @@ -26,4 +26,5 @@ public interface DataObjectInStore extends StateObject, StateDao { - public List listByStoreId(long id); + public List listByStoreId(long id, DataStoreRole role); public void deletePrimaryRecordsForStore(long id); - public SnapshotDataStoreVO findByStoreSnapshot(long storeId, long snapshotId); + public SnapshotDataStoreVO findByStoreSnapshot(DataStoreRole role, long storeId, long snapshotId); public SnapshotDataStoreVO findByStoreSnapshot(long storeId, long snapshotId, boolean lock); - public SnapshotDataStoreVO findBySnapshot(long snapshotId); - - public List listDestroyed(long storeId); + public SnapshotDataStoreVO findBySnapshot(long snapshotId, DataStoreRole role); } diff --git a/engine/api/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreVO.java b/engine/api/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreVO.java index e4c4942862f..af478ab92ba 100644 --- a/engine/api/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreVO.java +++ b/engine/api/src/org/apache/cloudstack/storage/datastore/db/SnapshotDataStoreVO.java @@ -31,10 +31,10 @@ import javax.persistence.TemporalType; import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State; -import com.cloud.storage.Storage; -import com.cloud.storage.Storage.ImageFormat; -import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; +import com.cloud.storage.DataStoreRole; +import com.cloud.utils.db.GenericDao; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.fsm.StateObject; @@ -51,6 +51,10 @@ public class SnapshotDataStoreVO implements StateObjectcloud-engine-storage ${project.version} + + org.apache.cloudstack + cloud-engine-api + ${project.version} + mysql mysql-connector-java diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotDataFactoryImpl.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotDataFactoryImpl.java index a647715d47e..c530e4a815e 100644 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotDataFactoryImpl.java +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotDataFactoryImpl.java @@ -35,6 +35,9 @@ import com.cloud.storage.DataStoreRole; import com.cloud.storage.Snapshot; import com.cloud.storage.SnapshotVO; import com.cloud.storage.dao.SnapshotDao; +import com.cloud.utils.db.SearchCriteria2; +import com.cloud.utils.db.SearchCriteriaService; +import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.exception.CloudRuntimeException; @Component @@ -49,36 +52,30 @@ public class SnapshotDataFactoryImpl implements SnapshotDataFactory { VolumeDataFactory volumeFactory; @Override public SnapshotInfo getSnapshot(long snapshotId, DataStore store) { - SnapshotVO snapshot = snapshotDao.findByIdIncludingRemoved(snapshotId); + SnapshotVO snapshot = snapshotDao.findById(snapshotId); SnapshotObject so = SnapshotObject.getSnapshotObject(snapshot, store); return so; } - @Override - public SnapshotInfo getSnapshot(long snapshotId) { - SnapshotVO snapshot = snapshotDao.findByIdIncludingRemoved(snapshotId); - SnapshotObject so = null; - if (snapshot.getState() == Snapshot.State.BackedUp) { - DataStore store = null; - SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findBySnapshot(snapshotId); - if ( snapshotStore != null ){ - store = this.storeMgr.getDataStore(snapshotStore.getDataStoreId(), DataStoreRole.Image); - } - so = SnapshotObject.getSnapshotObject(snapshot, store); - } else { - VolumeInfo volume = this.volumeFactory.getVolume(snapshot.getVolumeId()); - so = SnapshotObject.getSnapshotObject(snapshot, volume.getDataStore()); - } - return so; - } - @Override public SnapshotInfo getSnapshot(DataObject obj, DataStore store) { - SnapshotVO snapshot = snapshotDao.findByIdIncludingRemoved(obj.getId()); + SnapshotVO snapshot = snapshotDao.findById(obj.getId()); if (snapshot == null) { throw new CloudRuntimeException("Can't find snapshot: " + obj.getId()); } SnapshotObject so = SnapshotObject.getSnapshotObject(snapshot, store); return so; } + + @Override + public SnapshotInfo getSnapshot(long snapshotId, DataStoreRole role) { + SnapshotVO snapshot = snapshotDao.findById(snapshotId); + SnapshotDataStoreVO snapshotStore = snapshotStoreDao.findBySnapshot(snapshotId, role); + if (snapshotStore == null) { + return null; + } + DataStore store = this.storeMgr.getDataStore(snapshotStore.getDataStoreId(), role); + SnapshotObject so = SnapshotObject.getSnapshotObject(snapshot, store); + return so; + } } 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 e22cfb9b3cc..50f3819d543 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,7 +30,11 @@ 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.CreateObjectAnswer; import org.apache.cloudstack.storage.datastore.ObjectInDataStoreManager; +import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; +import org.apache.cloudstack.storage.to.SnapshotObjectTO; import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; @@ -55,6 +59,8 @@ public class SnapshotObject implements SnapshotInfo { @Inject protected SnapshotStateMachineManager stateMachineMgr; @Inject ObjectInDataStoreManager ojbectInStoreMgr; + @Inject + SnapshotDataStoreDao snapshotStore; public SnapshotObject() { } @@ -76,6 +82,7 @@ public class SnapshotObject implements SnapshotInfo { @Override public SnapshotInfo getParent() { + // TODO Auto-generated method stub return null; } @@ -149,11 +156,7 @@ public class SnapshotObject implements SnapshotInfo { @Override public String getPath() { - return this.snapshot.getPath(); - } - - public void setPath(String path) { - this.snapshot.setPath(path); + return this.ojbectInStoreMgr.findObject(this, getDataStore()).getInstallPath(); } @Override @@ -196,9 +199,6 @@ public class SnapshotObject implements SnapshotInfo { return this.snapshot.getDomainId(); } - public void setPrevSnapshotId(Long id) { - this.snapshot.setPrevSnapshotId(id); - } @Override public Long getDataCenterId() { @@ -212,15 +212,8 @@ public class SnapshotObject implements SnapshotInfo { @Override public Long getPrevSnapshotId() { - return this.snapshot.getPrevSnapshotId(); - } - - public void setBackupSnapshotId(String id) { - this.snapshot.setBackupSnapshotId(id); - } - - public String getBackupSnapshotId() { - return this.snapshot.getBackupSnapshotId(); + SnapshotDataStoreVO snapshotStoreVO = this.snapshotStore.findBySnapshot(this.getId(), this.getDataStore().getRole()); + return snapshotStoreVO.getParentSnapshotId(); } public SnapshotVO getSnapshotVO(){ @@ -234,8 +227,28 @@ public class SnapshotObject implements SnapshotInfo { } @Override - public void processEvent(org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event event, Answer answer) { - // TODO Auto-generated method stub - + public void processEvent(ObjectInDataStoreStateMachine.Event event, Answer answer) { + SnapshotDataStoreVO snapshotStore = this.snapshotStore.findByStoreSnapshot(this.getDataStore().getRole(), + this.getDataStore().getId(), this.getId()); + if (answer instanceof CreateObjectAnswer) { + SnapshotObjectTO snapshotTO = (SnapshotObjectTO)((CreateObjectAnswer) answer).getData(); + snapshotStore.setInstallPath(snapshotTO.getPath()); + this.snapshotStore.update(snapshotStore.getId(), snapshotStore); + } else { + throw new CloudRuntimeException("Unknown answer: " + answer.getClass()); + } + this.processEvent(event); } + + @Override + public ObjectInDataStoreStateMachine.State getStatus() { + return this.ojbectInStoreMgr.findObject(this, store).getObjectInStoreState(); + } + + @Override + public void addPayload(Object data) { + // TODO Auto-generated method stub + + } + } 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 6674880525e..2fb4609a3c0 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 @@ -27,7 +27,6 @@ import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult; import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionService; import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; -import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; @@ -38,6 +37,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotService; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotResult; import org.apache.cloudstack.framework.async.AsyncCallFuture; import org.apache.cloudstack.framework.async.AsyncCallbackDispatcher; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; @@ -51,30 +51,19 @@ import org.springframework.stereotype.Component; import com.cloud.agent.api.BackupSnapshotAnswer; import com.cloud.configuration.dao.ConfigurationDao; -import com.cloud.dc.ClusterVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.exception.InvalidParameterValueException; -import com.cloud.host.HostVO; -import com.cloud.hypervisor.Hypervisor.HypervisorType; -import com.cloud.resource.ResourceManager; import com.cloud.storage.DataStoreRole; import com.cloud.storage.Snapshot; import com.cloud.storage.SnapshotVO; -import com.cloud.storage.StoragePool; import com.cloud.storage.VolumeManager; -import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.snapshot.SnapshotManager; -import com.cloud.utils.NumbersUtil; import com.cloud.utils.db.DB; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.fsm.NoTransitionException; -import com.cloud.vm.UserVmVO; -import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.dao.UserVmDao; -import com.cloud.vm.snapshot.VMSnapshot; -import com.cloud.vm.snapshot.VMSnapshotVO; import com.cloud.vm.snapshot.dao.VMSnapshotDao; @Component @@ -92,8 +81,7 @@ public class SnapshotServiceImpl implements SnapshotService { protected SnapshotDao _snapshotDao; @Inject protected SnapshotDataStoreDao _snapshotStoreDao; - @Inject - private ResourceManager _resourceMgr; + @Inject protected SnapshotManager snapshotMgr; @Inject @@ -164,81 +152,24 @@ public class SnapshotServiceImpl implements SnapshotService { CreateSnapshotContext context) { CreateCmdResult result = callback.getResult(); SnapshotObject snapshot = (SnapshotObject)context.snapshot; - VolumeInfo volume = context.volume; AsyncCallFuture future = context.future; - SnapshotResult snapResult = new SnapshotResult(snapshot); + SnapshotResult snapResult = new SnapshotResult(snapshot, result.getAnswer()); if (result.isFailed()) { s_logger.debug("create snapshot " + context.snapshot.getName() + " failed: " + result.getResult()); try { snapshot.processEvent(Snapshot.Event.OperationFailed); - } catch (NoTransitionException nte) { - s_logger.debug("Failed to update snapshot state due to " + nte.getMessage()); + snapshot.processEvent(Event.OperationFailed); + } catch (Exception e) { + s_logger.debug("Failed to update snapshot state due to " + e.getMessage()); } - - + snapResult.setResult(result.getResult()); future.complete(snapResult); return null; } try { - SnapshotVO preSnapshotVO = this.snapshotMgr.getParentSnapshot(volume, snapshot); - String preSnapshotPath = null; - if (preSnapshotVO != null) { - preSnapshotPath = preSnapshotVO.getPath(); - } - SnapshotVO snapshotVO = this._snapshotDao.findById(snapshot.getId()); - // The snapshot was successfully created - if (preSnapshotPath != null && preSnapshotPath.equals(result.getPath())) { - // empty snapshot - s_logger.debug("CreateSnapshot: this is empty snapshot "); - - snapshotVO.setPath(preSnapshotPath); - snapshotVO.setBackupSnapshotId(preSnapshotVO.getBackupSnapshotId()); - snapshotVO.setPrevSnapshotId(preSnapshotVO.getId()); - snapshot.processEvent(Snapshot.Event.OperationNotPerformed); - } else { - long preSnapshotId = 0; - - if (preSnapshotVO != null && preSnapshotVO.getBackupSnapshotId() != null) { - preSnapshotId = preSnapshotVO.getId(); - int _deltaSnapshotMax = NumbersUtil.parseInt(_configDao.getValue("snapshot.delta.max"), SnapshotManager.DELTAMAX); - int deltaSnap = _deltaSnapshotMax; - - int i; - for (i = 1; i < deltaSnap; i++) { - String prevBackupUuid = preSnapshotVO.getBackupSnapshotId(); - // previous snapshot doesn't have backup, create a full snapshot - if (prevBackupUuid == null) { - preSnapshotId = 0; - break; - } - long preSSId = preSnapshotVO.getPrevSnapshotId(); - if (preSSId == 0) { - break; - } - preSnapshotVO = _snapshotDao.findByIdIncludingRemoved(preSSId); - } - if (i >= deltaSnap) { - preSnapshotId = 0; - } - } - - //If the volume is moved around, backup a full snapshot to secondary storage - if (volume.getLastPoolId() != null && !volume.getLastPoolId().equals(volume.getPoolId())) { - preSnapshotId = 0; - //TODO: fix this hack - VolumeVO volumeVO = this.volumeDao.findById(volume.getId()); - volumeVO.setLastPoolId(volume.getPoolId()); - this.volumeDao.update(volume.getId(), volumeVO); - } - - snapshot.setPath(result.getPath()); - snapshot.setPrevSnapshotId(preSnapshotId); - - snapshot.processEvent(Snapshot.Event.OperationSucceeded); - snapResult = new SnapshotResult(this.snapshotfactory.getSnapshot(snapshot.getId())); - } + snapshot.processEvent(Event.OperationSuccessed, result.getAnswer()); } catch (Exception e) { s_logger.debug("Failed to create snapshot: ", e); snapResult.setResult(e.toString()); @@ -253,36 +184,49 @@ public class SnapshotServiceImpl implements SnapshotService { return null; } - class SnapshotResult extends CommandResult { - SnapshotInfo snashot; - public SnapshotResult(SnapshotInfo snapshot) { - this.snashot = snapshot; - } - } + - protected SnapshotInfo createSnapshotOnPrimary(VolumeInfo volume, Long snapshotId) { - SnapshotObject snapshot = (SnapshotObject)this.snapshotfactory.getSnapshot(snapshotId); - if (snapshot == null) { - throw new CloudRuntimeException("Can not find snapshot " + snapshotId); + @Override + public SnapshotResult takeSnapshot(SnapshotInfo snap) { + SnapshotObject snapshot = (SnapshotObject)snap; + + SnapshotObject snapshotOnPrimary = null; + try { + snapshotOnPrimary = (SnapshotObject)snap.getDataStore().create(snapshot); + } catch(Exception e) { + s_logger.debug("Failed to create snapshot state on data store due to " + e.getMessage()); + throw new CloudRuntimeException(e); } try { - snapshot.processEvent(Snapshot.Event.CreateRequested); - } catch (NoTransitionException nte) { - s_logger.debug("Failed to update snapshot state due to " + nte.getMessage()); - throw new CloudRuntimeException("Failed to update snapshot state due to " + nte.getMessage()); + snapshotOnPrimary.processEvent(Snapshot.Event.CreateRequested); + } catch (NoTransitionException e) { + s_logger.debug("Failed to change snapshot state: " + e.toString()); + throw new CloudRuntimeException(e); + } + + try { + snapshotOnPrimary.processEvent(Event.CreateOnlyRequested); + } catch (Exception e) { + s_logger.debug("Failed to change snapshot state: " + e.toString()); + try { + snapshotOnPrimary.processEvent(Snapshot.Event.OperationFailed); + } catch (NoTransitionException e1) { + s_logger.debug("Failed to change snapshot state: " + e1.toString()); + } + throw new CloudRuntimeException(e); } AsyncCallFuture future = new AsyncCallFuture(); try { CreateSnapshotContext context = new CreateSnapshotContext( - null, volume, snapshot, future); + null, snap.getBaseVolume(), snapshotOnPrimary, future); AsyncCallbackDispatcher caller = AsyncCallbackDispatcher .create(this); caller.setCallback( caller.getTarget().createSnapshotAsyncCallback(null, null)) .setContext(context); - PrimaryDataStoreDriver primaryStore = (PrimaryDataStoreDriver)volume.getDataStore().getDriver(); + PrimaryDataStoreDriver primaryStore = (PrimaryDataStoreDriver)snapshotOnPrimary.getDataStore().getDriver(); primaryStore.takeSnapshot(snapshot, caller); } catch (Exception e) { s_logger.debug("Failed to take snapshot: " + snapshot.getId(), e); @@ -302,7 +246,7 @@ public class SnapshotServiceImpl implements SnapshotService { s_logger.debug("Failed to create snapshot:" + result.getResult()); throw new CloudRuntimeException(result.getResult()); } - return result.snashot; + return result; } catch (InterruptedException e) { s_logger.debug("Failed to create snapshot", e); throw new CloudRuntimeException("Failed to create snapshot", e); @@ -313,78 +257,11 @@ public class SnapshotServiceImpl implements SnapshotService { } - private boolean hostSupportSnapsthot(HostVO host) { - if (host.getHypervisorType() != HypervisorType.KVM) { - return true; - } - // Determine host capabilities - String caps = host.getCapabilities(); - - if (caps != null) { - String[] tokens = caps.split(","); - for (String token : tokens) { - if (token.contains("snapshot")) { - return true; - } - } - } - return false; - } - - protected boolean supportedByHypervisor(VolumeInfo volume) { - if (volume.getHypervisorType().equals(HypervisorType.KVM)) { - StoragePool storagePool = (StoragePool)volume.getDataStore(); - ClusterVO cluster = _clusterDao.findById(storagePool.getClusterId()); - List hosts = _resourceMgr.listAllHostsInCluster(cluster.getId()); - if (hosts != null && !hosts.isEmpty()) { - HostVO host = hosts.get(0); - if (!hostSupportSnapsthot(host)) { - throw new CloudRuntimeException("KVM Snapshot is not supported on cluster: " + host.getId()); - } - } - } - - // if volume is attached to a vm in destroyed or expunging state; disallow - if (volume.getInstanceId() != null) { - UserVmVO userVm = _vmDao.findById(volume.getInstanceId()); - if (userVm != null) { - if (userVm.getState().equals(State.Destroyed) || userVm.getState().equals(State.Expunging)) { - throw new CloudRuntimeException("Creating snapshot failed due to volume:" + volume.getId() + " is associated with vm:" + userVm.getInstanceName() + " is in " - + userVm.getState().toString() + " state"); - } - - if(userVm.getHypervisorType() == HypervisorType.VMware || userVm.getHypervisorType() == HypervisorType.KVM) { - List activeSnapshots = _snapshotDao.listByInstanceId(volume.getInstanceId(), Snapshot.State.Creating, Snapshot.State.CreatedOnPrimary, Snapshot.State.BackingUp); - if(activeSnapshots.size() > 1) - throw new CloudRuntimeException("There is other active snapshot tasks on the instance to which the volume is attached, please try again later"); - } - - List activeVMSnapshots = _vmSnapshotDao.listByInstanceId(userVm.getId(), - VMSnapshot.State.Creating, VMSnapshot.State.Reverting, VMSnapshot.State.Expunging); - if (activeVMSnapshots.size() > 0) { - throw new CloudRuntimeException( - "There is other active vm snapshot tasks on the instance to which the volume is attached, please try again later"); - } - } - } - - return true; - } - - @Override - public SnapshotInfo takeSnapshot(VolumeInfo volume, Long snapshotId) { - - supportedByHypervisor(volume); - - SnapshotInfo snapshot = createSnapshotOnPrimary(volume, snapshotId); - return snapshot; - } - @Override public SnapshotInfo backupSnapshot(SnapshotInfo snapshot) { SnapshotObject snapObj = (SnapshotObject)snapshot; AsyncCallFuture future = new AsyncCallFuture(); - SnapshotResult result = new SnapshotResult(snapshot); + SnapshotResult result = new SnapshotResult(snapshot, null); try { snapObj.processEvent(Snapshot.Event.BackupToSecondary); @@ -420,7 +297,7 @@ public class SnapshotServiceImpl implements SnapshotService { try { SnapshotResult res = future.get(); - SnapshotInfo destSnapshot = res.snashot; + SnapshotInfo destSnapshot = res.getSnashot(); return destSnapshot; } catch (InterruptedException e) { s_logger.debug("failed copy snapshot", e); @@ -438,7 +315,7 @@ public class SnapshotServiceImpl implements SnapshotService { SnapshotInfo destSnapshot = context.destSnapshot; SnapshotObject srcSnapshot = (SnapshotObject)context.srcSnapshot; AsyncCallFuture future = context.future; - SnapshotResult snapResult = new SnapshotResult(destSnapshot); + SnapshotResult snapResult = new SnapshotResult(destSnapshot, result.getAnswer()); if (result.isFailed()) { snapResult.setResult(result.getResult()); future.complete(snapResult); @@ -453,7 +330,7 @@ public class SnapshotServiceImpl implements SnapshotService { objInStoreMgr.update(destSnapshot, Event.OperationSuccessed); srcSnapshot.processEvent(Snapshot.Event.OperationSucceeded); - snapResult = new SnapshotResult(this.snapshotfactory.getSnapshot(destSnapshot.getId())); + snapResult = new SnapshotResult(this.snapshotfactory.getSnapshot(destSnapshot.getId(), destSnapshot.getDataStore()), answer); future.complete(snapResult); } catch (Exception e) { s_logger.debug("Failed to update snapshot state", e); @@ -465,7 +342,7 @@ public class SnapshotServiceImpl implements SnapshotService { @DB protected boolean destroySnapshotBackUp(SnapshotVO snapshot) { - SnapshotDataStoreVO snapshotStore = this._snapshotStoreDao.findBySnapshot(snapshot.getId()); + SnapshotDataStoreVO snapshotStore = this._snapshotStoreDao.findBySnapshot(snapshot.getId(), DataStoreRole.Image); if ( snapshotStore == null ){ s_logger.debug("Can't find snapshot" + snapshot.getId() + " backed up into image store"); return false; @@ -510,81 +387,19 @@ public class SnapshotServiceImpl implements SnapshotService { if (result.isFailed()) { s_logger.debug("delete snapshot failed" + result.getResult()); snapshot.processEvent(ObjectInDataStoreStateMachine.Event.OperationFailed); - SnapshotResult res = new SnapshotResult(context.snapshot); + SnapshotResult res = new SnapshotResult(context.snapshot, null); future.complete(res); return null; } snapshot.processEvent(ObjectInDataStoreStateMachine.Event.OperationSuccessed); - SnapshotResult res = new SnapshotResult(context.snapshot); + SnapshotResult res = new SnapshotResult(context.snapshot, null); future.complete(res); return null; } @Override public boolean deleteSnapshot(SnapshotInfo snapInfo) { - Long snapshotId = snapInfo.getId(); - SnapshotObject snapshot = (SnapshotObject)snapInfo; - - if (!Snapshot.State.BackedUp.equals(snapshot.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.getBackupSnapshotId() != 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); - } - } - - _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); - } - } - return true; + } 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 new file mode 100644 index 00000000000..70c30a0359d --- /dev/null +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotStrategyBase.java @@ -0,0 +1,24 @@ +package org.apache.cloudstack.storage.snapshot; + +import javax.inject.Inject; + +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotService; +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(); + } + + @Override + public SnapshotInfo backupSnapshot(SnapshotInfo snapshot) { + return snapshotSvr.backupSnapshot(snapshot); + } +} 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 new file mode 100644 index 00000000000..0fa044b74aa --- /dev/null +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/XenserverSnapshotStrategy.java @@ -0,0 +1,200 @@ +package org.apache.cloudstack.storage.snapshot; + +import java.util.List; + +import javax.inject.Inject; + +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotResult; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotService; +import org.apache.cloudstack.storage.command.CreateObjectAnswer; +import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; +import org.apache.cloudstack.storage.to.SnapshotObjectTO; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.storage.DataStoreRole; +import com.cloud.storage.Snapshot; +import com.cloud.storage.SnapshotVO; +import com.cloud.storage.VolumeVO; +import com.cloud.storage.snapshot.SnapshotManager; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.fsm.NoTransitionException; + +@Component +public class XenserverSnapshotStrategy extends SnapshotStrategyBase { + private static final Logger s_logger = Logger + .getLogger(XenserverSnapshotStrategy.class); + + @Inject + SnapshotManager snapshotMgr; + @Inject + SnapshotService snapshotSvr; + @Inject + DataStoreManager dataStoreMgr; + @Inject + SnapshotDataStoreDao snapshotStoreDao; + @Inject + ConfigurationDao configDao; + @Inject + SnapshotDataFactory snapshotDataFactory; + + @Override + public SnapshotInfo backupSnapshot(SnapshotInfo snapshot) { + SnapshotInfo parentSnapshot = snapshot.getParent(); + if (parentSnapshot.getPath().equalsIgnoreCase(snapshot.getPath())) { + s_logger.debug("backup an empty snapshot"); + //don't need to backup this snapshot + SnapshotDataStoreVO parentSnapshotOnBackupStore = this.snapshotStoreDao.findBySnapshot(parentSnapshot.getId(), DataStoreRole.Image); + if (parentSnapshotOnBackupStore != null && + parentSnapshotOnBackupStore.getState() == State.Ready) { + DataStore store = dataStoreMgr.getDataStore(parentSnapshotOnBackupStore.getDataStoreId(), + parentSnapshotOnBackupStore.getRole()); + + SnapshotInfo snapshotOnImageStore = (SnapshotInfo)store.create(snapshot); + snapshotOnImageStore.processEvent(Event.CreateOnlyRequested); + + SnapshotObjectTO snapTO = new SnapshotObjectTO(); + snapTO.setPath(parentSnapshotOnBackupStore.getInstallPath()); + CreateObjectAnswer createSnapshotAnswer = new CreateObjectAnswer(snapTO); + + snapshotOnImageStore.processEvent(Event.OperationSuccessed, createSnapshotAnswer); + SnapshotObject snapObj = (SnapshotObject)snapshot; + try { + snapObj.processEvent(Snapshot.Event.OperationNotPerformed); + } catch (NoTransitionException e) { + s_logger.debug("Failed to change state: " + snapshot.getId() + ": " +e.toString()); + throw new CloudRuntimeException(e.toString()); + } + return this.snapshotDataFactory.getSnapshot(snapObj.getId(), store); + } else { + s_logger.debug("parent snapshot hasn't been backed up yet"); + } + } + + //determine full snapshot backup or not + + boolean fullBackup = false; + long preSnapshotId = 0; + if (parentSnapshot != null) { + + preSnapshotId = parentSnapshot.getId(); + int _deltaSnapshotMax = NumbersUtil.parseInt(configDao.getValue("snapshot.delta.max"), SnapshotManager.DELTAMAX); + int deltaSnap = _deltaSnapshotMax; + + int i; + SnapshotDataStoreVO parentSnapshotOnBackupStore = null; + for (i = 1; i < deltaSnap; i++) { + parentSnapshotOnBackupStore = this.snapshotStoreDao.findBySnapshot(parentSnapshot.getId(), DataStoreRole.Image); + + Long prevBackupId = parentSnapshotOnBackupStore.getParentSnapshotId(); + + if (prevBackupId == 0) { + break; + } + + parentSnapshotOnBackupStore = this.snapshotStoreDao.findBySnapshot(prevBackupId, DataStoreRole.Image); + } + if (i >= deltaSnap) { + fullBackup = true; + } + } + + snapshot.addPayload(fullBackup); + return this.snapshotSvr.backupSnapshot(snapshot); + } + + @Override + public boolean deleteSnapshot(SnapshotInfo snapshot) { + Long snapshotId = snapshot.getId(); + SnapshotObject snapObj = (SnapshotObject)snapshot; + + if (!Snapshot.State.BackedUp.equals(snapshot.getState()) || !Snapshot) { + 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); + } + } + + _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); + } + } + return true; + } + + @Override + public boolean canHandle(SnapshotInfo snapshot) { + if (snapshot.getHypervisorType() == HypervisorType.XenServer) { + return true; + } else { + return false; + } + } + + @Override + public SnapshotInfo takeSnapshot(SnapshotInfo snapshot) { + snapshot = snapshotSvr.takeSnapshot(snapshot).getSnashot(); + //TODO: add async + return this.backupSnapshot(snapshot); + } +} 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 b00d1521cf2..7ff22c6aa3f 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java +++ b/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java @@ -120,6 +120,12 @@ public class ObjectInDataStoreManagerImpl implements ObjectInDataStoreManager { if ( obj.getType() == DataObjectType.TEMPLATE){ VMTemplateStoragePoolVO vo = new VMTemplateStoragePoolVO(dataStore.getId(), obj.getId()); vo = templatePoolDao.persist(vo); + } else if (obj.getType() == DataObjectType.SNAPSHOT) { + SnapshotDataStoreVO ss = new SnapshotDataStoreVO(); + ss.setSnapshotId(obj.getId()); + ss.setDataStoreId(dataStore.getId()); + ss.setRole(dataStore.getRole()); + ss = snapshotDataStoreDao.persist(ss); } } else { // Image store @@ -137,6 +143,7 @@ public class ObjectInDataStoreManagerImpl implements ObjectInDataStoreManager { SnapshotDataStoreVO ss = new SnapshotDataStoreVO(); ss.setSnapshotId(obj.getId()); ss.setDataStoreId(dataStore.getId()); + ss.setRole(dataStore.getRole()); if (dataStore.getRole() == DataStoreRole.ImageCache) { ss.setInstallPath("/snapshots/" + snapshotDao.findById(obj.getId()).getAccountId() + "/" + obj.getId()); } @@ -185,7 +192,7 @@ public class ObjectInDataStoreManagerImpl implements ObjectInDataStoreManager { return true; } case SNAPSHOT: - SnapshotDataStoreVO destSnapshotStore = snapshotDataStoreDao.findByStoreSnapshot(dataStore.getId(), objId); + SnapshotDataStoreVO destSnapshotStore = snapshotDataStoreDao.findByStoreSnapshot(dataStore.getRole(), dataStore.getId(), objId); if ( destSnapshotStore != null ){ return snapshotDataStoreDao.remove(destSnapshotStore.getId()); } @@ -262,24 +269,17 @@ public class ObjectInDataStoreManagerImpl implements ObjectInDataStoreManager { public DataObjectInStore findObject(long objId, DataObjectType type, long dataStoreId, DataStoreRole role) { DataObjectInStore vo = null; - if (role == DataStoreRole.Image) { + if (role == DataStoreRole.Image || role == DataStoreRole.ImageCache) { switch (type){ case TEMPLATE: vo = templateDataStoreDao.findByStoreTemplate(dataStoreId, objId); case SNAPSHOT: - vo = snapshotDataStoreDao.findByStoreSnapshot(dataStoreId, objId); + vo = snapshotDataStoreDao.findByStoreSnapshot(role, dataStoreId, objId); case VOLUME: vo = volumeDataStoreDao.findByStoreVolume(dataStoreId, objId); } } else if (type == DataObjectType.TEMPLATE && role == DataStoreRole.Primary) { vo = templatePoolDao.findByPoolTemplate(dataStoreId, objId); - } else if (role == DataStoreRole.ImageCache) { - SearchCriteriaService sc = SearchCriteria2.create(ObjectInDataStoreVO.class); - sc.addAnd(sc.getEntity().getObjectId(), Op.EQ, objId); - sc.addAnd(sc.getEntity().getObjectType(), Op.EQ, type); - sc.addAnd(sc.getEntity().getDataStoreId(), Op.EQ, dataStoreId); - sc.addAnd(sc.getEntity().getDataStoreRole(), Op.EQ, role); - vo = sc.find(); } else { s_logger.debug("Invalid data or store type: " + type + " " + role); throw new CloudRuntimeException("Invalid data or store type: " + type + " " + role); @@ -298,7 +298,7 @@ public class ObjectInDataStoreManagerImpl implements ObjectInDataStoreManager { case TEMPLATE: vo = templateDataStoreDao.findByTemplate(objId); case SNAPSHOT: - vo = snapshotDataStoreDao.findBySnapshot(objId); + vo = snapshotDataStoreDao.findBySnapshot(objId, role); case VOLUME: vo = volumeDataStoreDao.findByVolume(objId); } diff --git a/engine/storage/src/org/apache/cloudstack/storage/db/ObjectInDataStoreVO.java b/engine/storage/src/org/apache/cloudstack/storage/db/ObjectInDataStoreVO.java index 44b91745d61..5b69251f161 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/db/ObjectInDataStoreVO.java +++ b/engine/storage/src/org/apache/cloudstack/storage/db/ObjectInDataStoreVO.java @@ -32,6 +32,7 @@ import javax.persistence.TemporalType; import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.State; import com.cloud.storage.DataStoreRole; import com.cloud.storage.Storage; @@ -191,4 +192,9 @@ public class ObjectInDataStoreVO implements StateObject listByStoreId(long id) { + public List listByStoreId(long id, DataStoreRole role) { SearchCriteria sc = storeSearch.create(); sc.setParameters("store_id", id); - sc.setParameters("destroyed", false); - return listIncludingRemovedBy(sc); + sc.setParameters("store_role", role); + return listBy(sc); } @Override @@ -132,40 +134,19 @@ public class SnapshotDataStoreDaoImpl extends GenericDaoBase sc = storeSnapshotSearch.create(); sc.setParameters("store_id", storeId); sc.setParameters("snapshot_id", snapshotId); - sc.setParameters("destroyed", false); + sc.setParameters("store_role", role); return findOneIncludingRemovedBy(sc); } - @Override - public SnapshotDataStoreVO findByStoreSnapshot(long storeId, long snapshotId, boolean lock) { - SearchCriteria sc = storeSnapshotSearch.create(); - sc.setParameters("store_id", storeId); - sc.setParameters("snapshot_id", snapshotId); - sc.setParameters("destroyed", false); - if (!lock) - return findOneIncludingRemovedBy(sc); - else - return lockOneRandomRow(sc, true); - } - - @Override - public SnapshotDataStoreVO findBySnapshot(long snapshotId) { + public SnapshotDataStoreVO findBySnapshot(long snapshotId, DataStoreRole role) { SearchCriteria sc = snapshotSearch.create(); sc.setParameters("snapshot_id", snapshotId); - sc.setParameters("destroyed", false); - return findOneIncludingRemovedBy(sc); - } - - @Override - public List listDestroyed(long id) { - SearchCriteria sc = storeSearch.create(); - sc.setParameters("store_id", id); - sc.setParameters("destroyed", true); - return listIncludingRemovedBy(sc); + sc.setParameters("store_role", role); + return findOneBy(sc); } } diff --git a/engine/storage/src/org/apache/cloudstack/storage/snapshot/SnapshotService.java b/engine/storage/src/org/apache/cloudstack/storage/snapshot/SnapshotService.java deleted file mode 100644 index f3e5c4aea50..00000000000 --- a/engine/storage/src/org/apache/cloudstack/storage/snapshot/SnapshotService.java +++ /dev/null @@ -1,27 +0,0 @@ -// 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.storage.snapshot; - -import org.apache.cloudstack.engine.cloud.entity.api.SnapshotEntity; -import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; - -public interface SnapshotService { - public SnapshotEntity getSnapshotEntity(long snapshotId); - public boolean takeSnapshot(SnapshotInfo snapshot); - public boolean revertSnapshot(SnapshotInfo snapshot); - public boolean deleteSnapshot(SnapshotInfo snapshot); -} 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 4c214c1b72a..8b20b22af2f 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 @@ -245,7 +245,8 @@ public class PrimaryDataStoreImpl implements PrimaryDataStore { } } } - + } else if (obj.getType() == DataObjectType.SNAPSHOT) { + return objectInStoreMgr.create(obj, this); } return objectInStoreMgr.get(obj, this); 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 47d99bd8612..e5876b2e2a3 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 @@ -840,5 +840,26 @@ public class VolumeServiceImpl implements VolumeService { return null; } + + @Override + public SnapshotInfo takeSnapshot(VolumeInfo volume) { + VolumeObject vol = (VolumeObject)volume; + vol.stateTransit(Volume.Event.SnapshotRequested); + + SnapshotInfo snapshot = null; + try { + snapshot = this.snapshotMgr.takeSnapshot(volume); + } catch (Exception e) { + s_logger.debug("Take snapshot: " + volume.getId() + " failed: " + e.toString()); + } finally { + if (snapshot != null) { + vol.stateTransit(Volume.Event.OperationSucceeded); + } else { + vol.stateTransit(Volume.Event.OperationFailed); + } + } + + return snapshot; + } } 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 9f47e0682a3..98527a9a226 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 @@ -141,58 +141,22 @@ public class XenServerStorageResource { } protected CreateObjectAnswer getTemplateSize(CreateObjectCommand cmd, String templateUrl) { - Connection conn = hypervisorResource.getConnection(); + /*Connection conn = hypervisorResource.getConnection(); long size = this.getTemplateSize(conn, templateUrl); - return new CreateObjectAnswer(cmd, templateUrl, size); + return new CreateObjectAnswer(cmd, templateUrl, size);*/ + return null; } protected CreateObjectAnswer execute(CreateObjectCommand cmd) { - String uriString = cmd.getObjectUri(); - DecodedDataObject obj = null; - - Connection conn = hypervisorResource.getConnection(); - VDI vdi = null; - boolean result = false; - String errorMsg = null; - - try { - obj = Decoder.decode(uriString); - - DecodedDataStore store = obj.getStore(); - if (obj.getObjType().equalsIgnoreCase("template") && store.getRole().equalsIgnoreCase("image")) { - return getTemplateSize(cmd, obj.getPath()); - } - - long size = obj.getSize(); - String name = obj.getName(); - String storeUuid = store.getUuid(); - SR primaryDataStoreSR = getSRByNameLabel(conn, storeUuid); - vdi = createVdi(conn, name, primaryDataStoreSR, size); - VDI.Record record = vdi.getRecord(conn); - result = true; - return new CreateObjectAnswer(cmd, record.uuid, record.virtualSize); - } catch (BadServerResponse e) { - s_logger.debug("Failed to create volume", e); - errorMsg = e.toString(); - } catch (XenAPIException e) { - s_logger.debug("Failed to create volume", e); - errorMsg = e.toString(); - } catch (XmlRpcException e) { - s_logger.debug("Failed to create volume", e); - errorMsg = e.toString(); - } catch (URISyntaxException e) { - s_logger.debug("Failed to create volume", e); - errorMsg = e.toString(); - } finally { - if (!result && vdi != null) { - try { - deleteVDI(conn, vdi); - } catch (Exception e) { - s_logger.debug("Faled to delete vdi: " + vdi.toString()); - } - } - } - - return new CreateObjectAnswer(cmd, false, errorMsg); + DataTO data = cmd.getData(); + try { + if (data.getObjectType() == DataObjectType.VOLUME) { + return createVolume(data); + } + return new CreateObjectAnswer("not supported type"); + } catch (Exception e) { + s_logger.debug("Failed to create object: " + data.getObjectType() + ": " + e.toString()); + return new CreateObjectAnswer(e.toString()); + } } protected Answer execute(DeleteVolumeCommand cmd) { @@ -735,20 +699,30 @@ public class XenServerStorageResource { return new CopyCmdAnswer("not implemented yet"); } - protected CreateObjectAnswer createVolume(DataTO data) { - /* + protected CreateObjectAnswer createVolume(DataTO data) throws BadServerResponse, XenAPIException, XmlRpcException { + VolumeObjectTO volume = (VolumeObjectTO)data; + + Connection conn = hypervisorResource.getConnection(); + PrimaryDataStoreTO primaryStore = (PrimaryDataStoreTO)data.getDataStore(); + SR poolSr = hypervisorResource.getStorageRepository(conn, primaryStore.getUuid()); VDI.Record vdir = new VDI.Record(); - vdir.nameLabel = dskch.getName(); + vdir.nameLabel = volume.getName(); vdir.SR = poolSr; vdir.type = Types.VdiType.USER; - vdir.virtualSize = dskch.getSize(); + vdir.virtualSize = volume.getSize(); + VDI vdi; + vdi = VDI.create(conn, vdir); - VDI.Record vdir; vdir = vdi.getRecord(conn); - s_logger.debug("Succesfully created VDI for " + cmd + ". Uuid = " + vdir.uuid);*/ - return null; + VolumeObjectTO newVol = new VolumeObjectTO(); + newVol.setName(vdir.nameLabel); + newVol.setSize(vdir.virtualSize); + newVol.setPath(vdir.uuid); + + return new CreateObjectAnswer(newVol); } + protected CopyCmdAnswer cloneVolumeFromBaseTemplate(DataTO srcData, DataTO destData) { Connection conn = hypervisorResource.getConnection(); PrimaryDataStoreTO pool = (PrimaryDataStoreTO)destData.getDataStore(); diff --git a/plugins/storage/image/sample/src/org/apache/cloudstack/storage/datastore/driver/SampleImageStoreDriverImpl.java b/plugins/storage/image/sample/src/org/apache/cloudstack/storage/datastore/driver/SampleImageStoreDriverImpl.java index 91f9cd0abe6..e477b570e79 100644 --- a/plugins/storage/image/sample/src/org/apache/cloudstack/storage/datastore/driver/SampleImageStoreDriverImpl.java +++ b/plugins/storage/image/sample/src/org/apache/cloudstack/storage/datastore/driver/SampleImageStoreDriverImpl.java @@ -96,12 +96,12 @@ public class SampleImageStoreDriverImpl implements ImageStoreDriver { callback.complete(result); return; } - CreateObjectCommand createCmd = new CreateObjectCommand(data.getUri()); + CreateObjectCommand createCmd = new CreateObjectCommand(data.getTO()); CreateObjectAnswer answer = (CreateObjectAnswer)ep.sendMessage(createCmd); if (answer.getResult()) { //update imagestorevo - result = new CreateCmdResult(answer.getPath(), null); + result = new CreateCmdResult(null, null); } else { result.setResult(answer.getDetails()); } 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 ea4c5615423..5e82a7eaec1 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 @@ -34,6 +34,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; +import org.apache.cloudstack.storage.command.CreateObjectCommand; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.volume.VolumeObject; import org.apache.log4j.Logger; @@ -108,46 +109,15 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri return null; } - public boolean createVolume( + public Answer createVolume( VolumeInfo volume) throws StorageUnavailableException { if (s_logger.isDebugEnabled()) { s_logger.debug("Creating volume: " + volume); } - - DiskOfferingVO offering = diskOfferingDao.findById(volume.getDiskOfferingId()); - DiskProfile diskProfile = new DiskProfile(volume, offering, - null); - - StoragePool pool = (StoragePool)volume.getDataStore(); - VolumeVO vol = volumeDao.findById(volume.getId()); - if (pool != null) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Trying to create in " + pool); - } - vol.setPoolId(pool.getId()); - - CreateCommand cmd = new CreateCommand(diskProfile, new StorageFilerTO( - pool)); - - Answer answer = storageMgr.sendToPool(pool, null, cmd); - if (answer.getResult()) { - CreateAnswer createAnswer = (CreateAnswer) answer; - vol.setFolder(pool.getPath()); - vol.setPath(createAnswer.getVolume().getPath()); - vol.setSize(createAnswer.getVolume().getSize()); - vol.setPoolType(pool.getPoolType()); - vol.setPoolId(pool.getId()); - vol.setPodId(pool.getPodId()); - this.volumeDao.update(vol.getId(), vol); - return true; - } - - } - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Unable to create volume " + volume.getId()); - } - return false; + + CreateObjectCommand cmd = new CreateObjectCommand(volume.getTO()); + Answer answer = storageMgr.sendToPool((StoragePool)volume.getDataStore(), null, cmd); + return answer; } @Override @@ -155,9 +125,10 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri AsyncCompletionCallback callback) { // TODO Auto-generated method stub String errMsg = null; + Answer answer = null; if (data.getType() == DataObjectType.VOLUME) { try { - createVolume((VolumeInfo)data); + answer = createVolume((VolumeInfo)data); } catch (StorageUnavailableException e) { s_logger.debug("failed to create volume", e); errMsg = e.toString(); @@ -166,13 +137,12 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri errMsg = e.toString(); } } - CreateCmdResult result = new CreateCmdResult(null, null); + CreateCmdResult result = new CreateCmdResult(null, answer); if (errMsg != null) { result.setResult(errMsg); } callback.complete(result); - } @Override diff --git a/server/src/com/cloud/storage/CreateSnapshotPayload.java b/server/src/com/cloud/storage/CreateSnapshotPayload.java new file mode 100644 index 00000000000..cafe46c8814 --- /dev/null +++ b/server/src/com/cloud/storage/CreateSnapshotPayload.java @@ -0,0 +1,14 @@ +package com.cloud.storage; + +public class CreateSnapshotPayload { + private Long snapshotPolicyId; + + public Long getSnapshotPolicyId() { + return snapshotPolicyId; + } + + public void setSnapshotPolicyId(Long snapshotPolicyId) { + this.snapshotPolicyId = snapshotPolicyId; + } + +} diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java index e6159679b63..05b22576c7a 100755 --- a/server/src/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/com/cloud/storage/StorageManagerImpl.java @@ -1931,7 +1931,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C _accountMgr.checkAccessAndSpecifyAuthority(UserContext.current().getCaller(), store.getDataCenterId()); // Verify that there are no live snapshot, template, volume on the image store to be deleted - List snapshots = _snapshotStoreDao.listByStoreId(storeId); + List snapshots = _snapshotStoreDao.listByStoreId(storeId, DataStoreRole.Image); if ( snapshots != null && snapshots.size() > 0 ){ throw new CloudRuntimeException("Cannot delete image store with active snapshots backup!"); } diff --git a/server/src/com/cloud/storage/VolumeManagerImpl.java b/server/src/com/cloud/storage/VolumeManagerImpl.java index 57241b1757f..3177a591728 100644 --- a/server/src/com/cloud/storage/VolumeManagerImpl.java +++ b/server/src/com/cloud/storage/VolumeManagerImpl.java @@ -85,6 +85,7 @@ import com.cloud.configuration.Resource.ResourceType; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.consoleproxy.ConsoleProxyManager; import com.cloud.dc.ClusterVO; +import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenterVO; import com.cloud.dc.HostPodVO; import com.cloud.dc.dao.ClusterDao; @@ -166,6 +167,7 @@ import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.SecondaryStorageVmDao; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; +import com.cloud.vm.snapshot.VMSnapshot; import com.cloud.vm.snapshot.VMSnapshotVO; import com.cloud.vm.snapshot.dao.VMSnapshotDao; @@ -2463,5 +2465,47 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { throw new CloudRuntimeException("Failed to destroy volume" + volume.getId(), e); } } + + + + + @Override + public Snapshot takeSnapshot(Long volumeId, Long policyId) throws ResourceAllocationException { + Account caller = UserContext.current().getCaller(); + + VolumeInfo volume = this.volFactory.getVolume(volumeId); + if (volume == null) { + throw new InvalidParameterValueException("Creating snapshot failed due to volume:" + volumeId + " doesn't exist"); + } + DataCenter zone = _dcDao.findById(volume.getDataCenterId()); + if (zone == null) { + throw new InvalidParameterValueException("Can't find zone by id " + volume.getDataCenterId()); + } + + if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getType())) { + throw new PermissionDeniedException("Cannot perform this operation, Zone is currently disabled: " + zone.getName()); + } + + if (volume.getState() != Volume.State.Ready) { + throw new InvalidParameterValueException("VolumeId: " + volumeId + " is not in " + Volume.State.Ready + " state but " + volume.getState() + ". Cannot take snapshot."); + } + + if ( volume.getTemplateId() != null ) { + VMTemplateVO template = _templateDao.findById(volume.getTemplateId()); + if( template != null && template.getTemplateType() == Storage.TemplateType.SYSTEM ) { + throw new InvalidParameterValueException("VolumeId: " + volumeId + " is for System VM , Creating snapshot against System VM volumes is not supported"); + } + } + + StoragePool storagePool = (StoragePool)volume.getDataStore(); + if (storagePool == null) { + 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); + + } } diff --git a/server/src/com/cloud/storage/dao/SnapshotDao.java b/server/src/com/cloud/storage/dao/SnapshotDao.java index e6aecc7654f..46f2fa9396b 100644 --- a/server/src/com/cloud/storage/dao/SnapshotDao.java +++ b/server/src/com/cloud/storage/dao/SnapshotDao.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.storage.dao; +import com.cloud.storage.DataStoreRole; import com.cloud.storage.Snapshot; import com.cloud.storage.Snapshot.Type; import com.cloud.storage.SnapshotVO; @@ -29,7 +30,7 @@ public interface SnapshotDao extends GenericDao, StateDao listByVolumeId(long volumeId); List listByVolumeId(Filter filter, long volumeId); SnapshotVO findNextSnapshot(long parentSnapId); - long getLastSnapshot(long volumeId, long snapId); + long getLastSnapshot(long volumeId, DataStoreRole role); List listByVolumeIdType(long volumeId, Type type); List listByVolumeIdIncludingRemoved(long volumeId); List listByBackupUuid(long volumeId, String backupUuid); diff --git a/server/src/com/cloud/storage/dao/SnapshotDaoImpl.java b/server/src/com/cloud/storage/dao/SnapshotDaoImpl.java index 2dd5da14265..24d4c653aeb 100644 --- a/server/src/com/cloud/storage/dao/SnapshotDaoImpl.java +++ b/server/src/com/cloud/storage/dao/SnapshotDaoImpl.java @@ -28,6 +28,7 @@ import org.apache.log4j.Logger; import org.springframework.stereotype.Component; import com.cloud.server.ResourceTag.TaggedResourceType; +import com.cloud.storage.DataStoreRole; import com.cloud.storage.Snapshot; import com.cloud.storage.Snapshot.Event; import com.cloud.storage.Snapshot.State; @@ -55,7 +56,7 @@ import com.cloud.vm.dao.VMInstanceDao; public class SnapshotDaoImpl extends GenericDaoBase implements SnapshotDao { public static final Logger s_logger = Logger.getLogger(SnapshotDaoImpl.class.getName()); //TODO: we should remove these direct sqls - private static final String GET_LAST_SNAPSHOT = "SELECT id FROM snapshots where volume_id = ? AND id != ? AND path IS NOT NULL ORDER BY created DESC"; + private static final String GET_LAST_SNAPSHOT = "SELECT snapshots.id FROM snapshot_store_ref, snapshots where snapshots.id = snapshot_store_ref.snapshot_id AND snapshosts.volume_id = ? AND snapshot_store_ref.role = ? ORDER BY created DESC"; private static final String UPDATE_SNAPSHOT_VERSION = "UPDATE snapshots SET version = ? WHERE volume_id = ? AND version = ?"; private static final String GET_SECHOST_ID = "SELECT store_id FROM snapshots, snapshot_store_ref where snapshots.id = snapshot_store_ref.snapshot_id AND volume_id = ? AND backup_snap_id IS NOT NULL AND sechost_id IS NOT NULL LIMIT 1"; private static final String UPDATE_SECHOST_ID = "UPDATE snapshots SET sechost_id = ? WHERE data_center_id = ?"; @@ -212,14 +213,14 @@ public class SnapshotDaoImpl extends GenericDaoBase implements return null; } @Override - public long getLastSnapshot(long volumeId, long snapId) { + public long getLastSnapshot(long volumeId, DataStoreRole role) { Transaction txn = Transaction.currentTxn(); PreparedStatement pstmt = null; String sql = GET_LAST_SNAPSHOT; try { pstmt = txn.prepareAutoCloseStatement(sql); pstmt.setLong(1, volumeId); - pstmt.setLong(2, snapId); + pstmt.setString(2, role.toString()); ResultSet rs = pstmt.executeQuery(); if (rs.next()) { return rs.getLong(1); diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManager.java b/server/src/com/cloud/storage/snapshot/SnapshotManager.java index 818133002c9..983b50c3e65 100755 --- a/server/src/com/cloud/storage/snapshot/SnapshotManager.java +++ b/server/src/com/cloud/storage/snapshot/SnapshotManager.java @@ -18,6 +18,7 @@ package com.cloud.storage.snapshot; import java.util.List; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import com.cloud.agent.api.Answer; @@ -68,7 +69,10 @@ public interface SnapshotManager { Answer sendToPool(Volume vol, Command cmd); - SnapshotVO getParentSnapshot(VolumeInfo volume, Snapshot snapshot); + SnapshotVO getParentSnapshot(VolumeInfo volume); Snapshot backupSnapshot(Long snapshotId); + + SnapshotInfo takeSnapshot(VolumeInfo volume) + throws ResourceAllocationException; } diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java index 11408f9a779..c18ffa87092 100755 --- a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -38,6 +38,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotService; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotStrategy; 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.ZoneScope; @@ -82,7 +83,9 @@ import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.org.Grouping; import com.cloud.projects.Project.ListProjectResourcesCriteria; +import com.cloud.resource.ResourceManager; import com.cloud.server.ResourceTag.TaggedResourceType; +import com.cloud.storage.CreateSnapshotPayload; import com.cloud.storage.Snapshot; import com.cloud.storage.Snapshot.Type; import com.cloud.storage.DataStoreRole; @@ -128,8 +131,12 @@ import com.cloud.utils.db.JoinBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.dao.UserVmDao; +import com.cloud.vm.snapshot.VMSnapshot; +import com.cloud.vm.snapshot.VMSnapshotVO; import com.cloud.vm.snapshot.dao.VMSnapshotDao; @Component @@ -202,6 +209,10 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, @Inject VolumeDataFactory volFactory; @Inject SnapshotDataFactory snapshotFactory; @Inject EndPointSelector _epSelector; + @Inject + private ResourceManager _resourceMgr; + @Inject + protected List snapshotStrategies; private int _totalRetries; @@ -278,32 +289,25 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, throw new InvalidParameterValueException("Volume is not in ready state"); } - SnapshotInfo snapshot = null; - + boolean backedUp = false; // does the caller have the authority to act on this volume _accountMgr.checkAccess(UserContext.current().getCaller(), null, true, volume); - - SnapshotInfo snap = this.snapshotFactory.getSnapshot(snapshotId); + SnapshotInfo snapshot = this.snapshotFactory.getSnapshot(snapshotId, DataStoreRole.Primary); try { - snapshot = this.snapshotSrv.takeSnapshot(volume, snapshotId); - if (snapshot != null) { - postCreateSnapshot(volumeId, snapshot.getId(), policyId); - //Check if the snapshot was removed while backingUp. If yes, do not log snapshot create usage event - SnapshotVO freshSnapshot = _snapshotDao.findById(snapshot.getId()); - if ((freshSnapshot != null) && backedUp) { - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SNAPSHOT_CREATE, snapshot.getAccountId(), - snapshot.getDataCenterId(), snapshotId, snapshot.getName(), null, null, - volume.getSize(), snapshot.getClass().getName(), snapshot.getUuid()); - } + postCreateSnapshot(volumeId, snapshot.getId(), policyId); + //Check if the snapshot was removed while backingUp. If yes, do not log snapshot create usage event + SnapshotVO freshSnapshot = _snapshotDao.findById(snapshot.getId()); + if ((freshSnapshot != null) && backedUp) { + 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); - _resourceLimitMgr.incrementResourceCount(snapshotOwner.getId(), ResourceType.snapshot); - } - if (backup) { - this.backupSnapshot(snapshotId); - } } catch(Exception e) { s_logger.debug("Failed to create snapshot", e); if (backup) { @@ -337,16 +341,11 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, @Override public Snapshot backupSnapshot(Long snapshotId) { - SnapshotInfo snapshot = this.snapshotFactory.getSnapshot(snapshotId); - if (snapshot == null) { - throw new CloudRuntimeException("Can't find snapshot:" + snapshotId); + SnapshotInfo snapshot = this.snapshotFactory.getSnapshot(snapshotId, DataStoreRole.Image); + if (snapshot != null) { + throw new CloudRuntimeException("Already in the backup snapshot:" + snapshotId); } - if (snapshot.getState() == Snapshot.State.BackedUp) { - return snapshot; - } - - return this.snapshotSrv.backupSnapshot(snapshot); } @@ -436,8 +435,8 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, } @Override - public SnapshotVO getParentSnapshot(VolumeInfo volume, Snapshot snapshot) { - long preId = _snapshotDao.getLastSnapshot(volume.getId(), snapshot.getId()); + public SnapshotVO getParentSnapshot(VolumeInfo volume) { + long preId = _snapshotDao.getLastSnapshot(volume.getId(), DataStoreRole.Primary); SnapshotVO preSnapshotVO = null; if (preId != 0 && !(volume.getLastPoolId() != null && !volume.getLastPoolId().equals(volume.getPoolId()))) { @@ -500,15 +499,21 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, Account caller = UserContext.current().getCaller(); // Verify parameters - SnapshotInfo snapshotCheck = this.snapshotFactory.getSnapshot(snapshotId); + SnapshotVO snapshotCheck = this._snapshotDao.findById(snapshotId); if (snapshotCheck == null) { throw new InvalidParameterValueException("unable to find a snapshot with id " + snapshotId); } _accountMgr.checkAccess(caller, null, true, snapshotCheck); - + SnapshotStrategy snapshotStrategy = null; + for (SnapshotStrategy strategy : this.snapshotStrategies) { + if (strategy.canHandle(snapshotCheck)) { + snapshotStrategy = strategy; + break; + } + } try { - boolean result = this.snapshotSrv.deleteSnapshot(snapshotCheck); + boolean result = snapshotStrategy.deleteSnapshot(snapshotId); if (result) { if (snapshotCheck.getState() == Snapshot.State.BackedUp) { UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SNAPSHOT_DELETE, snapshotCheck.getAccountId(), @@ -529,7 +534,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, @Override public String getSecondaryStorageURL(SnapshotVO snapshot) { - SnapshotDataStoreVO snapshotStore = this._snapshotStoreDao.findBySnapshot(snapshot.getId()); + SnapshotDataStoreVO snapshotStore = this._snapshotStoreDao.findBySnapshot(snapshot.getId(), DataStoreRole.Image); if (snapshotStore != null){ DataStore store = this.dataStoreMgr.getDataStore(snapshotStore.getDataStoreId(), DataStoreRole.Image); if ( store != null ){ @@ -904,44 +909,76 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, return null; } - @Override - public SnapshotVO allocSnapshot(Long volumeId, Long policyId) throws ResourceAllocationException { - Account caller = UserContext.current().getCaller(); + + + private boolean hostSupportSnapsthot(HostVO host) { + if (host.getHypervisorType() != HypervisorType.KVM) { + return true; + } + // Determine host capabilities + String caps = host.getCapabilities(); - VolumeVO volume = _volsDao.findById(volumeId); - if (volume == null) { - throw new InvalidParameterValueException("Creating snapshot failed due to volume:" + volumeId + " doesn't exist"); - } - DataCenter zone = _dcDao.findById(volume.getDataCenterId()); - if (zone == null) { - throw new InvalidParameterValueException("Can't find zone by id " + volume.getDataCenterId()); - } - - if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getType())) { - throw new PermissionDeniedException("Cannot perform this operation, Zone is currently disabled: " + zone.getName()); - } - - if (volume.getState() != Volume.State.Ready) { - throw new InvalidParameterValueException("VolumeId: " + volumeId + " is not in " + Volume.State.Ready + " state but " + volume.getState() + ". Cannot take snapshot."); - } - - if ( volume.getTemplateId() != null ) { - VMTemplateVO template = _templateDao.findById(volume.getTemplateId()); - if( template != null && template.getTemplateType() == Storage.TemplateType.SYSTEM ) { - throw new InvalidParameterValueException("VolumeId: " + volumeId + " is for System VM , Creating snapshot against System VM volumes is not supported"); - } - } - - StoragePoolVO storagePoolVO = _storagePoolDao.findById(volume.getPoolId()); - if (storagePoolVO == null) { - throw new InvalidParameterValueException("VolumeId: " + volumeId + " please attach this volume to a VM before create snapshot for it"); - } - - ClusterVO cluster = _clusterDao.findById(storagePoolVO.getClusterId()); + if (caps != null) { + String[] tokens = caps.split(","); + for (String token : tokens) { + if (token.contains("snapshot")) { + return true; + } + } + } + return false; + } + + private boolean supportedByHypervisor(VolumeInfo volume) { + StoragePool storagePool = (StoragePool)volume.getDataStore(); + ClusterVO cluster = _clusterDao.findById(storagePool.getClusterId()); if (cluster != null && cluster.getHypervisorType() == HypervisorType.Ovm) { throw new InvalidParameterValueException("Ovm won't support taking snapshot"); } + + if (volume.getHypervisorType().equals(HypervisorType.KVM)) { + List hosts = _resourceMgr.listAllHostsInCluster(cluster.getId()); + if (hosts != null && !hosts.isEmpty()) { + HostVO host = hosts.get(0); + if (!hostSupportSnapsthot(host)) { + throw new CloudRuntimeException("KVM Snapshot is not supported on cluster: " + host.getId()); + } + } + } + // if volume is attached to a vm in destroyed or expunging state; disallow + if (volume.getInstanceId() != null) { + UserVmVO userVm = _vmDao.findById(volume.getInstanceId()); + if (userVm != null) { + if (userVm.getState().equals(State.Destroyed) || userVm.getState().equals(State.Expunging)) { + throw new CloudRuntimeException("Creating snapshot failed due to volume:" + volume.getId() + " is associated with vm:" + userVm.getInstanceName() + " is in " + + userVm.getState().toString() + " state"); + } + + if(userVm.getHypervisorType() == HypervisorType.VMware || userVm.getHypervisorType() == HypervisorType.KVM) { + List activeSnapshots = _snapshotDao.listByInstanceId(volume.getInstanceId(), Snapshot.State.Creating, Snapshot.State.CreatedOnPrimary, Snapshot.State.BackingUp); + if(activeSnapshots.size() > 1) + throw new CloudRuntimeException("There is other active snapshot tasks on the instance to which the volume is attached, please try again later"); + } + + List activeVMSnapshots = _vmSnapshotDao.listByInstanceId(userVm.getId(), + VMSnapshot.State.Creating, VMSnapshot.State.Reverting, VMSnapshot.State.Expunging); + if (activeVMSnapshots.size() > 0) { + throw new CloudRuntimeException( + "There is other active vm snapshot tasks on the instance to which the volume is attached, please try again later"); + } + } + } + + return true; + } + @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); @@ -975,14 +1012,13 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, } String snapshotName = vmDisplayName + "_" + volume.getName() + "_" + timeString; - // Create the Snapshot object and save it so we can return it to the - // user - HypervisorType hypervisorType = this._volsDao.getHypervisorType(volumeId); - SnapshotVO snapshotVO = new SnapshotVO(volume.getDataCenterId(), volume.getAccountId(), volume.getDomainId(), volume.getId(), volume.getDiskOfferingId(), null, snapshotName, + 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: "+volumeId); + throw new CloudRuntimeException("Failed to create snapshot for volume: " + volume.getId()); } if (backup) { _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.secondary_storage, @@ -991,7 +1027,19 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.primary_storage, new Long(volume.getSize())); } - return snapshot; + SnapshotInfo snap = this.snapshotFactory.getSnapshot(snapshot.getId(), volume.getDataStore()); + boolean processed = false; + for (SnapshotStrategy strategy : snapshotStrategies) { + if (strategy.canHandle(snap)) { + processed = true; + snap = strategy.takeSnapshot(snap); + break; + } + } + if (!processed) { + throw new CloudRuntimeException("Can't find snapshot strategy to deal with snapshot:" + snapshot.getId()); + } + return snap; } @Override