diff --git a/api/src/com/cloud/storage/Snapshot.java b/api/src/com/cloud/storage/Snapshot.java index 3f6b8f5a8e4..9c2217e0972 100644 --- a/api/src/com/cloud/storage/Snapshot.java +++ b/api/src/com/cloud/storage/Snapshot.java @@ -61,22 +61,6 @@ public interface Snapshot extends ControlledEntity, Identity, InternalIdentity, BackedUp, Error; - private final static StateMachine2 s_fsm = new StateMachine2(); - - public static StateMachine2 getStateMachine() { - return s_fsm; - } - - static { - s_fsm.addTransition(null, Event.CreateRequested, Creating); - s_fsm.addTransition(Creating, Event.OperationSucceeded, CreatedOnPrimary); - s_fsm.addTransition(Creating, Event.OperationNotPerformed, BackedUp); - s_fsm.addTransition(Creating, Event.OperationFailed, Error); - s_fsm.addTransition(CreatedOnPrimary, Event.BackupToSecondary, BackingUp); - s_fsm.addTransition(BackingUp, Event.OperationSucceeded, BackedUp); - s_fsm.addTransition(BackingUp, Event.OperationFailed, Error); - } - public String toString() { return this.name(); } @@ -107,7 +91,7 @@ public interface Snapshot extends ControlledEntity, Identity, InternalIdentity, Date getCreated(); - Type getType(); + Type getRecurringType(); State getState(); diff --git a/core/src/com/cloud/storage/SnapshotVO.java b/core/src/com/cloud/storage/SnapshotVO.java index f44212ff396..1bb0854e006 100644 --- a/core/src/com/cloud/storage/SnapshotVO.java +++ b/core/src/com/cloud/storage/SnapshotVO.java @@ -175,7 +175,7 @@ public class SnapshotVO implements Snapshot { } @Override - public Type getType() { + public Type getRecurringType() { if (snapshotType < 0 || snapshotType >= Type.values().length) { return null; } diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/CopyCommandResult.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/CopyCommandResult.java index 100fd4edba3..571a77c3786 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/CopyCommandResult.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/CopyCommandResult.java @@ -18,14 +18,22 @@ */ package org.apache.cloudstack.engine.subsystem.api.storage; +import com.cloud.agent.api.Answer; + public class CopyCommandResult extends CommandResult { private final String path; - public CopyCommandResult(String path) { + private final Answer answer; + public CopyCommandResult(String path, Answer answer) { super(); this.path = path; + this.answer = answer; } public String getPath() { return this.path; } + + public Answer getAnswer() { + return this.answer; + } } 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 60dfb9fb71f..32ea996e638 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 @@ -23,4 +23,5 @@ import com.cloud.utils.fsm.StateObject; public interface DataObjectInStore extends StateObject { public String getInstallPath(); + public void setInstallPath(String path); } diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreDriver.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreDriver.java index 4aba9bfdbff..cf5759b2924 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreDriver.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreDriver.java @@ -30,4 +30,5 @@ public interface DataStoreDriver { public void deleteAsync(DataObject data, AsyncCompletionCallback callback); public void copyAsync(DataObject srcdata, DataObject destData, AsyncCompletionCallback callback); public boolean canCopy(DataObject srcData, DataObject destData); + public void resize(DataObject data, AsyncCompletionCallback callback); } diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/ObjectInDataStoreStateMachine.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/ObjectInDataStoreStateMachine.java index af9974e1118..726ce0821c5 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/ObjectInDataStoreStateMachine.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/ObjectInDataStoreStateMachine.java @@ -49,6 +49,7 @@ public interface ObjectInDataStoreStateMachine extends StateObject callback); + public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback callback); public void revertSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback callback); } diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotInfo.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotInfo.java index 30cf182e5c9..b90404c5667 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotInfo.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotInfo.java @@ -16,9 +16,13 @@ // under the License. package org.apache.cloudstack.engine.subsystem.api.storage; +import com.cloud.storage.Snapshot; -public interface SnapshotInfo extends DataObject { + +public interface SnapshotInfo extends DataObject, Snapshot { public SnapshotInfo getParent(); public SnapshotInfo getChild(); public VolumeInfo getBaseVolume(); + Long getDataCenterId(); + public Long getPrevSnapshotId(); } 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 new file mode 100644 index 00000000000..f854f6bb5d4 --- /dev/null +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotStrategy.java @@ -0,0 +1,10 @@ +package org.apache.cloudstack.engine.subsystem.api.storage; + + +public interface SnapshotStrategy { + public boolean canHandle(SnapshotInfo snapshot); + public SnapshotInfo takeSnapshot(VolumeInfo volume, Long snapshotId); + public SnapshotInfo backupSnapshot(SnapshotInfo snapshot); + public boolean deleteSnapshot(SnapshotInfo snapshot); + public boolean revertSnapshot(SnapshotInfo snapshot); +} 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 f2a3d5e8749..349325af45d 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 @@ -18,10 +18,13 @@ */ package org.apache.cloudstack.engine.subsystem.api.storage; +import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.Volume; public interface VolumeInfo extends DataObject, Volume { public boolean isAttachedVM(); public void addPayload(Object data); public Object getpayload(); + public HypervisorType getHypervisorType(); + public Long getLastPoolId(); } diff --git a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java index 58258ebd659..102c47174b1 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java @@ -74,5 +74,7 @@ public interface VolumeService { boolean destroyVolume(long volumeId) throws ConcurrentOperationException; AsyncCallFuture registerVolume(VolumeInfo volume, DataStore store); + + AsyncCallFuture resize(VolumeInfo volume); } diff --git a/engine/storage/image/src/org/apache/cloudstack/storage/image/driver/AncientImageDataStoreDriverImpl.java b/engine/storage/image/src/org/apache/cloudstack/storage/image/driver/AncientImageDataStoreDriverImpl.java index 2c19c7fc039..97ea6c48c79 100644 --- a/engine/storage/image/src/org/apache/cloudstack/storage/image/driver/AncientImageDataStoreDriverImpl.java +++ b/engine/storage/image/src/org/apache/cloudstack/storage/image/driver/AncientImageDataStoreDriverImpl.java @@ -38,22 +38,30 @@ import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; +import com.cloud.agent.api.DeleteSnapshotBackupCommand; import com.cloud.agent.api.storage.DeleteVolumeCommand; +import com.cloud.agent.api.to.S3TO; +import com.cloud.agent.api.to.SwiftTO; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; import com.cloud.storage.RegisterVolumePayload; import com.cloud.storage.Storage.ImageFormat; +import com.cloud.storage.SnapshotVO; import com.cloud.storage.VMTemplateStorageResourceAssoc; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.VMTemplateZoneVO; import com.cloud.storage.VolumeHostVO; import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VMTemplateHostDao; import com.cloud.storage.dao.VMTemplateZoneDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.dao.VolumeHostDao; import com.cloud.storage.download.DownloadMonitor; +import com.cloud.storage.s3.S3Manager; +import com.cloud.storage.snapshot.SnapshotManager; +import com.cloud.storage.swift.SwiftManager; import com.cloud.utils.exception.CloudRuntimeException; public class AncientImageDataStoreDriverImpl implements ImageDataStoreDriver { @@ -69,7 +77,13 @@ public class AncientImageDataStoreDriverImpl implements ImageDataStoreDriver { @Inject VolumeDao volumeDao; @Inject VolumeHostDao volumeHostDao; @Inject HostDao hostDao; + @Inject SnapshotDao snapshotDao; @Inject AgentManager agentMgr; + @Inject SnapshotManager snapshotMgr; + @Inject + private SwiftManager _swiftMgr; + @Inject + private S3Manager _s3Mgr; @Override public String grantAccess(DataObject data, EndPoint ep) { // TODO Auto-generated method stub @@ -158,6 +172,49 @@ public class AncientImageDataStoreDriverImpl implements ImageDataStoreDriver { } + private void deleteSnapshot(DataObject data, AsyncCompletionCallback callback) { + Long snapshotId = data.getId(); + SnapshotVO snapshot = this.snapshotDao.findByIdIncludingRemoved(snapshotId); + CommandResult result = new CommandResult(); + if (snapshot == null) { + s_logger.debug("Destroying snapshot " + snapshotId + " backup failed due to unable to find snapshot "); + result.setResult("Unable to find snapshot: " + snapshotId); + callback.complete(result); + return; + } + + try { + String secondaryStoragePoolUrl = this.snapshotMgr.getSecondaryStorageURL(snapshot); + Long dcId = snapshot.getDataCenterId(); + Long accountId = snapshot.getAccountId(); + Long volumeId = snapshot.getVolumeId(); + + String backupOfSnapshot = snapshot.getBackupSnapshotId(); + if (backupOfSnapshot == null) { + callback.complete(result); + return; + } + SwiftTO swift = _swiftMgr.getSwiftTO(snapshot.getSwiftId()); + S3TO s3 = _s3Mgr.getS3TO(); + + DeleteSnapshotBackupCommand cmd = new DeleteSnapshotBackupCommand( + swift, s3, secondaryStoragePoolUrl, dcId, accountId, volumeId, + backupOfSnapshot, false); + Answer answer = agentMgr.sendToSSVM(dcId, cmd); + + if ((answer != null) && answer.getResult()) { + snapshot.setBackupSnapshotId(null); + snapshotDao.update(snapshotId, snapshot); + } else if (answer != null) { + result.setResult(answer.getDetails()); + } + } catch (Exception e) { + s_logger.debug("failed to delete snapshot: " + snapshotId + ": " + e.toString()); + result.setResult(e.toString()); + } + callback.complete(result); + } + @Override public void deleteAsync(DataObject data, AsyncCompletionCallback callback) { @@ -165,10 +222,9 @@ public class AncientImageDataStoreDriverImpl implements ImageDataStoreDriver { deleteVolume(data, callback); } else if (data.getType() == DataObjectType.TEMPLATE) { deleteTemplate(data, callback); + } else if (data.getType() == DataObjectType.SNAPSHOT) { + deleteSnapshot(data, callback); } - - - } @Override @@ -184,4 +240,11 @@ public class AncientImageDataStoreDriverImpl implements ImageDataStoreDriver { return false; } + @Override + public void resize(DataObject data, + AsyncCompletionCallback callback) { + // TODO Auto-generated method stub + + } + } diff --git a/engine/storage/image/src/org/apache/cloudstack/storage/image/driver/DefaultImageDataStoreDriverImpl.java b/engine/storage/image/src/org/apache/cloudstack/storage/image/driver/DefaultImageDataStoreDriverImpl.java index 1a506fa782b..3d46c73cde2 100644 --- a/engine/storage/image/src/org/apache/cloudstack/storage/image/driver/DefaultImageDataStoreDriverImpl.java +++ b/engine/storage/image/src/org/apache/cloudstack/storage/image/driver/DefaultImageDataStoreDriverImpl.java @@ -116,4 +116,11 @@ public class DefaultImageDataStoreDriverImpl implements ImageDataStoreDriver { // TODO Auto-generated method stub } + + @Override + public void resize(DataObject data, + AsyncCompletionCallback callback) { + // TODO Auto-generated method stub + + } } diff --git a/engine/storage/imagemotion/src/org/apache/cloudstack/storage/image/motion/DefaultImageMotionStrategy.java b/engine/storage/imagemotion/src/org/apache/cloudstack/storage/image/motion/DefaultImageMotionStrategy.java index 561c1cb288f..c49a521a3ca 100644 --- a/engine/storage/imagemotion/src/org/apache/cloudstack/storage/image/motion/DefaultImageMotionStrategy.java +++ b/engine/storage/imagemotion/src/org/apache/cloudstack/storage/image/motion/DefaultImageMotionStrategy.java @@ -101,7 +101,7 @@ public class DefaultImageMotionStrategy implements ImageMotionStrategy { DataStore destStore = destData.getDataStore(); DataStore srcStore = srcData.getDataStore(); EndPoint ep = selector.select(srcData, destData); - CopyCommandResult result = new CopyCommandResult(""); + CopyCommandResult result = new CopyCommandResult("", null); if (ep == null) { result.setResult("can't find end point"); callback.complete(result); @@ -125,12 +125,12 @@ public class DefaultImageMotionStrategy implements ImageMotionStrategy { AsyncCompletionCallback parentCall = context.getParentCallback(); Answer answer = (Answer)callback.getResult(); if (!answer.getResult()) { - CopyCommandResult result = new CopyCommandResult(""); + CopyCommandResult result = new CopyCommandResult("", null); result.setResult(answer.getDetails()); parentCall.complete(result); } else { CopyCmdAnswer ans = (CopyCmdAnswer)answer; - CopyCommandResult result = new CopyCommandResult(ans.getPath()); + CopyCommandResult result = new CopyCommandResult(ans.getPath(), null); parentCall.complete(result); } return null; diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockStorageMotionStrategy.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockStorageMotionStrategy.java index e2e8f9439c5..b619ee9240f 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockStorageMotionStrategy.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/MockStorageMotionStrategy.java @@ -34,7 +34,7 @@ public class MockStorageMotionStrategy implements DataMotionStrategy { @Override public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback) { - CopyCommandResult result = new CopyCommandResult("something"); + CopyCommandResult result = new CopyCommandResult("something", null); callback.complete(result); return null; } 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 095320907c7..5af5260c340 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 @@ -25,39 +25,55 @@ 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.DataStoreRole; 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.VolumeDataFactory; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.storage.datastore.ObjectInDataStoreManager; -import org.apache.cloudstack.storage.snapshot.db.SnapshotDao2; -import org.apache.cloudstack.storage.snapshot.db.SnapshotVO; import org.springframework.stereotype.Component; +import com.cloud.storage.Snapshot; +import com.cloud.storage.SnapshotVO; +import com.cloud.storage.dao.SnapshotDao; +import com.cloud.utils.exception.CloudRuntimeException; + @Component public class SnapshotDataFactoryImpl implements SnapshotDataFactory { @Inject - SnapshotDao2 snapshotDao; + SnapshotDao snapshotDao; @Inject ObjectInDataStoreManager objMap; @Inject DataStoreManager storeMgr; + @Inject + VolumeDataFactory volumeFactory; @Override public SnapshotInfo getSnapshot(long snapshotId, DataStore store) { - SnapshotVO snapshot = snapshotDao.findById(snapshotId); + SnapshotVO snapshot = snapshotDao.findByIdIncludingRemoved(snapshotId); DataObjectInStore obj = objMap.findObject(snapshot.getUuid(), DataObjectType.SNAPSHOT, store.getUuid(), store.getRole()); if (obj == null) { return null; } - SnapshotObject so = new SnapshotObject(snapshot, store); + SnapshotObject so = SnapshotObject.getSnapshotObject(snapshot, store); return so; } @Override public SnapshotInfo getSnapshot(long snapshotId) { - // TODO Auto-generated method stub - return null; + SnapshotVO snapshot = snapshotDao.findByIdIncludingRemoved(snapshotId); + SnapshotObject so = null; + if (snapshot.getState() == Snapshot.State.BackedUp) { + DataStore store = objMap.findStore(snapshot.getUuid(), DataObjectType.SNAPSHOT, 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) { - // TODO Auto-generated method stub - return null; + throw new CloudRuntimeException("not implemented yet"); } } 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 d9fc8aabfe8..a82be6de01d 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 @@ -18,21 +18,54 @@ */ package org.apache.cloudstack.storage.snapshot; +import java.util.Date; + +import javax.inject.Inject; + 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.ObjectInDataStoreStateMachine.Event; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; 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.snapshot.db.SnapshotVO; +import org.apache.cloudstack.storage.datastore.ObjectInDataStoreManager; +import org.apache.log4j.Logger; + +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.storage.Snapshot; +import com.cloud.storage.SnapshotVO; +import com.cloud.storage.dao.SnapshotDao; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.utils.component.ComponentContext; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.fsm.NoTransitionException; public class SnapshotObject implements SnapshotInfo { + private static final Logger s_logger = Logger.getLogger(SnapshotObject.class); private SnapshotVO snapshot; private DataStore store; - - public SnapshotObject(SnapshotVO snapshot, DataStore store) { - this.snapshot = snapshot; - this.store = store; + @Inject + protected SnapshotDao snapshotDao; + @Inject + protected VolumeDao volumeDao; + @Inject protected VolumeDataFactory volFactory; + @Inject protected SnapshotStateMachineManager stateMachineMgr; + @Inject + ObjectInDataStoreManager ojbectInStoreMgr; + protected SnapshotObject() { + + } + + protected void configure(SnapshotVO snapshot, DataStore store) { + this.snapshot = snapshot; + this.store = store; + } + + public static SnapshotObject getSnapshotObject(SnapshotVO snapshot, DataStore store) { + SnapshotObject snapObj = ComponentContext.inject(SnapshotObject.class); + snapObj.configure(snapshot, store); + return snapObj; } public DataStore getStore() { @@ -53,56 +86,138 @@ public class SnapshotObject implements SnapshotInfo { @Override public VolumeInfo getBaseVolume() { - // TODO Auto-generated method stub - return null; + return volFactory.getVolume(this.snapshot.getVolumeId()); } @Override public long getId() { - // TODO Auto-generated method stub - return 0; + return this.snapshot.getId(); } @Override public String getUri() { - // TODO Auto-generated method stub - return null; + return this.snapshot.getUuid(); } @Override public DataStore getDataStore() { - // TODO Auto-generated method stub - return null; + return this.store; } @Override public Long getSize() { - // TODO Auto-generated method stub - return 0L; + return this.getSize(); } @Override public DataObjectType getType() { - // TODO Auto-generated method stub - return null; + return DataObjectType.SNAPSHOT; } @Override public DiskFormat getFormat() { - // TODO Auto-generated method stub return null; } @Override public String getUuid() { - // TODO Auto-generated method stub - return null; + return this.snapshot.getUuid(); } - @Override - public void processEvent(Event event) { - // TODO Auto-generated method stub - - } + @Override + public void processEvent( + ObjectInDataStoreStateMachine.Event event) { + try { + ojbectInStoreMgr.update(this, event); + } catch (Exception e) { + s_logger.debug("Failed to update state:" + e.toString()); + throw new CloudRuntimeException("Failed to update state: " + e.toString()); + } + } + @Override + public long getAccountId() { + return this.snapshot.getAccountId(); + } + + @Override + public long getVolumeId() { + return this.snapshot.getVolumeId(); + } + + @Override + public String getPath() { + return this.snapshot.getPath(); + } + + public void setPath(String path) { + this.snapshot.setPath(path); + } + + @Override + public String getName() { + return this.snapshot.getName(); + } + + @Override + public Date getCreated() { + return this.snapshot.getCreated(); + } + + @Override + public Type getRecurringType() { + return this.snapshot.getRecurringType(); + } + + @Override + public State getState() { + return this.snapshot.getState(); + } + + @Override + public HypervisorType getHypervisorType() { + return this.snapshot.getHypervisorType(); + } + + @Override + public boolean isRecursive() { + return this.snapshot.isRecursive(); + } + + @Override + public short getsnapshotType() { + return this.snapshot.getsnapshotType(); + } + + @Override + public long getDomainId() { + return this.snapshot.getDomainId(); + } + + public void setPrevSnapshotId(Long id) { + this.snapshot.setPrevSnapshotId(id); + } + + @Override + public Long getDataCenterId() { + return this.snapshot.getDataCenterId(); + } + + public void processEvent(Snapshot.Event event) + throws NoTransitionException { + stateMachineMgr.processEvent(this.snapshot, event); + } + + @Override + public Long getPrevSnapshotId() { + return this.snapshot.getPrevSnapshotId(); + } + + public void setBackupSnapshotId(String id) { + this.snapshot.setBackupSnapshotId(id); + } + + public String getBackupSnapshotId() { + return this.snapshot.getBackupSnapshotId(); + } } 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 bd3caf4c0bc..1b64fd0cae3 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 @@ -22,6 +22,10 @@ import org.springframework.stereotype.Component; @Component public class SnapshotServiceImpl implements SnapshotService { + + public SnapshotServiceImpl() { + + } @Override public SnapshotEntity getSnapshotEntity(long snapshotId) { @@ -46,5 +50,7 @@ public class SnapshotServiceImpl implements SnapshotService { // TODO Auto-generated method stub return false; } + + } diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotStateMachineManager.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotStateMachineManager.java new file mode 100644 index 00000000000..1c3ac28d2f7 --- /dev/null +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotStateMachineManager.java @@ -0,0 +1,9 @@ +package org.apache.cloudstack.storage.snapshot; + +import com.cloud.storage.Snapshot.Event; +import com.cloud.storage.SnapshotVO; +import com.cloud.utils.fsm.NoTransitionException; + +public interface SnapshotStateMachineManager { + public void processEvent(SnapshotVO snapshot, Event event) throws NoTransitionException; +} 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 new file mode 100644 index 00000000000..a20a2c8b876 --- /dev/null +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotStateMachineManagerImpl.java @@ -0,0 +1,37 @@ +package org.apache.cloudstack.storage.snapshot; + +import javax.inject.Inject; + +import org.springframework.stereotype.Component; + +import com.cloud.storage.Snapshot; +import com.cloud.storage.Snapshot.Event; +import com.cloud.storage.Snapshot.State; +import com.cloud.storage.SnapshotVO; +import com.cloud.storage.dao.SnapshotDao; +import com.cloud.storage.listener.SnapshotStateListener; +import com.cloud.utils.fsm.NoTransitionException; +import com.cloud.utils.fsm.StateMachine2; + +@Component +public class SnapshotStateMachineManagerImpl implements +SnapshotStateMachineManager { + private StateMachine2 stateMachine = new StateMachine2(); + @Inject + protected SnapshotDao snapshotDao; + public SnapshotStateMachineManagerImpl() { + stateMachine.addTransition(null, Event.CreateRequested, Snapshot.State.Creating); + stateMachine.addTransition(Snapshot.State.Creating, Event.OperationSucceeded, Snapshot.State.CreatedOnPrimary); + stateMachine.addTransition(Snapshot.State.Creating, Event.OperationNotPerformed, Snapshot.State.BackedUp); + stateMachine.addTransition(Snapshot.State.Creating, Event.OperationFailed, Snapshot.State.Error); + 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.Error); + + stateMachine.registerListener(new SnapshotStateListener()); + } + + public void processEvent(SnapshotVO snapshot, Event event) throws NoTransitionException { + stateMachine.transitTo(snapshot, event, null, snapshotDao); + } +} diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/strategy/AncientSnasphotStrategy.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/strategy/AncientSnasphotStrategy.java new file mode 100644 index 00000000000..2e3b90f0490 --- /dev/null +++ b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/strategy/AncientSnasphotStrategy.java @@ -0,0 +1,562 @@ +package org.apache.cloudstack.storage.snapshot.strategy; + +import java.util.List; +import java.util.concurrent.ExecutionException; + +import javax.inject.Inject; + +import org.apache.cloudstack.engine.subsystem.api.storage.CommandResult; +import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; +import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult; +import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; +import org.apache.cloudstack.engine.subsystem.api.storage.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.DataStoreRole; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; +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.SnapshotStrategy; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope; +import org.apache.cloudstack.framework.async.AsyncCallFuture; +import org.apache.cloudstack.framework.async.AsyncCallbackDispatcher; +import org.apache.cloudstack.framework.async.AsyncCompletionCallback; +import org.apache.cloudstack.framework.async.AsyncRpcConext; +import org.apache.cloudstack.storage.datastore.ObjectInDataStoreManager; +import org.apache.cloudstack.storage.motion.DataMotionService; +import org.apache.cloudstack.storage.snapshot.SnapshotObject; +import org.apache.cloudstack.storage.snapshot.SnapshotStateMachineManager; +import org.apache.log4j.Logger; +import org.springframework.stereotype.Component; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.BackupSnapshotAnswer; +import com.cloud.agent.api.DeleteSnapshotBackupCommand; +import com.cloud.agent.api.to.S3TO; +import com.cloud.agent.api.to.SwiftTO; +import com.cloud.configuration.Resource.ResourceType; +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.dc.ClusterVO; +import com.cloud.dc.dao.ClusterDao; +import com.cloud.event.EventTypes; +import com.cloud.event.UsageEventUtils; +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.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.StoragePoolDao; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.storage.s3.S3Manager; +import com.cloud.storage.snapshot.SnapshotManager; +import com.cloud.storage.swift.SwiftManager; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.Transaction; +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; + +@Component +public class AncientSnasphotStrategy implements SnapshotStrategy { + private static final Logger s_logger = Logger.getLogger(AncientSnasphotStrategy.class); + @Inject + protected VolumeDao _volsDao; + @Inject + protected UserVmDao _vmDao; + @Inject + protected StoragePoolDao _storagePoolDao; + @Inject + protected ClusterDao _clusterDao; + @Inject + protected SnapshotDao snapshotDao; + @Inject + private ResourceManager _resourceMgr; + @Inject + protected SnapshotDao _snapshotDao; + @Inject + protected SnapshotManager snapshotMgr; + @Inject + protected VolumeManager volumeMgr; + @Inject + private ConfigurationDao _configDao; + @Inject + protected SnapshotStateMachineManager stateMachineManager; + @Inject + private VolumeDao volumeDao; + @Inject + SnapshotDataFactory snapshotfactory; + @Inject + DataStoreManager dataStoreMgr; + @Inject + DataMotionService motionSrv; + @Inject + ObjectInDataStoreManager objInStoreMgr; + + + @Override + public boolean canHandle(SnapshotInfo snapshot) { + return true; + } + + static private class CreateSnapshotContext extends AsyncRpcConext { + final VolumeInfo volume; + final SnapshotInfo snapshot; + final AsyncCallFuture future; + public CreateSnapshotContext(AsyncCompletionCallback callback, VolumeInfo volume, + SnapshotInfo snapshot, + AsyncCallFuture future) { + super(callback); + this.volume = volume; + this.snapshot = snapshot; + this.future = future; + } + } + + static private class DeleteSnapshotContext extends AsyncRpcConext { + final SnapshotInfo snapshot; + final AsyncCallFuture future; + public DeleteSnapshotContext(AsyncCompletionCallback callback, SnapshotInfo snapshot, + AsyncCallFuture future) { + super(callback); + this.snapshot = snapshot; + this.future = future; + } + + } + + static private class CopySnapshotContext extends AsyncRpcConext { + final SnapshotInfo srcSnapshot; + final SnapshotInfo destSnapshot; + final AsyncCallFuture future; + public CopySnapshotContext(AsyncCompletionCallback callback, + SnapshotInfo srcSnapshot, + SnapshotInfo destSnapshot, + AsyncCallFuture future) { + super(callback); + this.srcSnapshot = srcSnapshot; + this.destSnapshot = destSnapshot; + this.future = future; + } + + } + + protected Void createSnapshotAsyncCallback(AsyncCallbackDispatcher callback, + CreateSnapshotContext context) { + CreateCmdResult result = callback.getResult(); + SnapshotObject snapshot = (SnapshotObject)context.snapshot; + VolumeInfo volume = context.volume; + AsyncCallFuture future = context.future; + SnapshotResult snapResult = new SnapshotResult(snapshot); + 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()); + } + + + snapResult.setResult(result.getResult()); + future.complete(snapResult); + return null; + } + + try { + SnapshotVO preSnapshotVO = this.snapshotMgr.getParentSnapshot(volume, snapshot); + String 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.setSwiftId(preSnapshotVO.getSwiftId()); + snapshotVO.setPrevSnapshotId(preSnapshotVO.getId()); + snapshotVO.setSecHostId(preSnapshotVO.getSecHostId()); + 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())); + } + } catch (Exception e) { + s_logger.debug("Failed to create snapshot: ", e); + snapResult.setResult(e.toString()); + } + + future.complete(snapResult); + 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); + } + + 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()); + } + AsyncCallFuture future = new AsyncCallFuture(); + + CreateSnapshotContext context = new CreateSnapshotContext( + null, volume, snapshot, future); + AsyncCallbackDispatcher caller = AsyncCallbackDispatcher + .create(this); + caller.setCallback( + caller.getTarget().createSnapshotAsyncCallback(null, null)) + .setContext(context); + PrimaryDataStoreDriver primaryStore = (PrimaryDataStoreDriver)volume.getDataStore().getDriver(); + + primaryStore.takeSnapshot(snapshot, caller); + SnapshotResult result; + try { + result = future.get(); + if (result.isFailed()) { + s_logger.debug("Failed to create snapshot:" + result.getResult()); + throw new CloudRuntimeException(result.getResult()); + } + return result.snashot; + } catch (InterruptedException e) { + s_logger.debug("Failed to create snapshot", e); + throw new CloudRuntimeException("Failed to create snapshot", e); + } catch (ExecutionException e) { + s_logger.debug("Failed to create snapshot", e); + throw new CloudRuntimeException("Failed to create snapshot", e); + } + + } + + 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); + try { + + snapObj.processEvent(Snapshot.Event.BackupToSecondary); + + ZoneScope scope = new ZoneScope(snapshot.getDataCenterId()); + List stores = this.dataStoreMgr.getImageStores(scope); + if (stores.size() != 1) { + throw new CloudRuntimeException("find out more than one image stores"); + } + + DataStore imageStore = stores.get(0); + SnapshotInfo snapshotOnImageStore = (SnapshotInfo)imageStore.create(snapshot); + + snapshotOnImageStore.processEvent(Event.CreateOnlyRequested); + CopySnapshotContext context = new CopySnapshotContext(null, snapshot, + snapshotOnImageStore, future); + AsyncCallbackDispatcher caller = AsyncCallbackDispatcher + .create(this); + caller.setCallback( + caller.getTarget().copySnapshotAsyncCallback(null, null)) + .setContext(context); + this.motionSrv.copyAsync(snapshot, snapshotOnImageStore, caller); + } catch (Exception e) { + s_logger.debug("Failed to copy snapshot", e); + result.setResult("Failed to copy snapshot:" +e.toString()); + future.complete(result); + } + + try { + SnapshotResult res = future.get(); + SnapshotInfo destSnapshot = res.snashot; + return destSnapshot; + } catch (InterruptedException e) { + s_logger.debug("failed copy snapshot", e); + throw new CloudRuntimeException("Failed to copy snapshot" , e); + } catch (ExecutionException e) { + s_logger.debug("Failed to copy snapshot", e); + throw new CloudRuntimeException("Failed to copy snapshot" , e); + } + + } + + protected Void copySnapshotAsyncCallback(AsyncCallbackDispatcher callback, + CopySnapshotContext context) { + CopyCommandResult result = callback.getResult(); + SnapshotInfo destSnapshot = context.destSnapshot; + SnapshotObject srcSnapshot = (SnapshotObject)context.srcSnapshot; + AsyncCallFuture future = context.future; + SnapshotResult snapResult = new SnapshotResult(destSnapshot); + if (result.isFailed()) { + snapResult.setResult(result.getResult()); + future.complete(snapResult); + return null; + } + + try { + BackupSnapshotAnswer answer = (BackupSnapshotAnswer)result.getAnswer(); + + DataObjectInStore dataInStore = objInStoreMgr.findObject(destSnapshot, destSnapshot.getDataStore()); + dataInStore.setInstallPath(answer.getBackupSnapshotName()); + objInStoreMgr.update(destSnapshot, Event.OperationSuccessed); + + srcSnapshot.processEvent(Snapshot.Event.OperationSucceeded); + snapResult = new SnapshotResult(this.snapshotfactory.getSnapshot(destSnapshot.getId())); + future.complete(snapResult); + } catch (Exception e) { + s_logger.debug("Failed to update snapshot state", e); + snapResult.setResult(e.toString()); + future.complete(snapResult); + } + return null; + } + + @DB + protected boolean destroySnapshotBackUp(SnapshotVO snapshot) { + DataStore store = objInStoreMgr.findStore(snapshot.getUuid(), DataObjectType.SNAPSHOT, DataStoreRole.Image); + if (store == null) { + s_logger.debug("Can't find snapshot" + snapshot.getId() + " backed up into image store"); + return false; + } + + try { + SnapshotInfo snapshotInfo = this.snapshotfactory.getSnapshot(snapshot.getId(), store); + snapshotInfo.processEvent(ObjectInDataStoreStateMachine.Event.DestroyRequested); + + AsyncCallFuture future = new AsyncCallFuture(); + DeleteSnapshotContext context = new DeleteSnapshotContext(null, + snapshotInfo, future); + AsyncCallbackDispatcher caller = AsyncCallbackDispatcher + .create(this); + caller.setCallback( + caller.getTarget().deleteSnapshotCallback(null, null)) + .setContext(context); + + store.getDriver().deleteAsync(snapshotInfo, caller); + + SnapshotResult result = future.get(); + if (result.isFailed()) { + s_logger.debug("Failed to delete snapsoht: " + result.getResult()); + } + return result.isSuccess(); + } catch (Exception e) { + s_logger.debug("Failed to delete snapshot", e); + return false; + } + } + + protected Void deleteSnapshotCallback(AsyncCallbackDispatcher callback, + DeleteSnapshotContext context) { + CommandResult result = callback.getResult(); + AsyncCallFuture future = context.future; + SnapshotInfo snapshot = context.snapshot; + if (result.isFailed()) { + s_logger.debug("delete snapshot failed" + result.getResult()); + snapshot.processEvent(ObjectInDataStoreStateMachine.Event.OperationFailed); + SnapshotResult res = new SnapshotResult(context.snapshot); + future.complete(res); + return null; + } + snapshot.processEvent(ObjectInDataStoreStateMachine.Event.OperationSuccessed); + SnapshotResult res = new SnapshotResult(context.snapshot); + 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; + + } + + @Override + public boolean revertSnapshot(SnapshotInfo snapshot) { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/strategy/HypervisorBasedSnapshot.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/strategy/HypervisorBasedSnapshot.java deleted file mode 100644 index 8ef09275bcd..00000000000 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/strategy/HypervisorBasedSnapshot.java +++ /dev/null @@ -1,44 +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.strategy; - -import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; -import org.apache.cloudstack.storage.snapshot.SnapshotStrategy; -import org.springframework.stereotype.Component; - -@Component -public class HypervisorBasedSnapshot implements SnapshotStrategy { - - @Override - public boolean takeSnapshot(SnapshotInfo snapshot) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean revertSnapshot(SnapshotInfo snapshot) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean deleteSnapshot(SnapshotInfo snapshot) { - // TODO Auto-generated method stub - return false; - } - -} diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/strategy/StorageBasedSnapshot.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/strategy/StorageBasedSnapshot.java deleted file mode 100644 index 7af395acb96..00000000000 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/strategy/StorageBasedSnapshot.java +++ /dev/null @@ -1,42 +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.strategy; - -import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; -import org.apache.cloudstack.storage.snapshot.SnapshotStrategy; - -public class StorageBasedSnapshot implements SnapshotStrategy { - - @Override - public boolean takeSnapshot(SnapshotInfo snapshot) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean revertSnapshot(SnapshotInfo snapshot) { - // TODO Auto-generated method stub - return false; - } - - @Override - public boolean deleteSnapshot(SnapshotInfo snapshot) { - // TODO Auto-generated method stub - return false; - } - -} diff --git a/engine/storage/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreProviderManager.java b/engine/storage/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreProviderManager.java index 664c2d1c216..d1c26e1a272 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreProviderManager.java +++ b/engine/storage/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreProviderManager.java @@ -19,7 +19,7 @@ package org.apache.cloudstack.storage.datastore; import org.apache.cloudstack.engine.subsystem.api.storage.HypervisorHostListener; -import org.apache.cloudstack.storage.volume.PrimaryDataStoreDriver; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; public interface PrimaryDataStoreProviderManager { diff --git a/engine/storage/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java b/engine/storage/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java index ed3ca6aa8d9..c067a1b651c 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java +++ b/engine/storage/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java @@ -28,6 +28,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreRole; +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.datastore.db.PrimaryDataStoreDao; @@ -36,6 +37,8 @@ import org.apache.log4j.Logger; import org.springframework.stereotype.Component; import com.cloud.agent.api.Answer; +import com.cloud.agent.api.BackupSnapshotAnswer; +import com.cloud.agent.api.BackupSnapshotCommand; import com.cloud.agent.api.Command; import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand; import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand; @@ -47,7 +50,9 @@ import com.cloud.agent.api.storage.CopyVolumeCommand; import com.cloud.agent.api.storage.CreateAnswer; import com.cloud.agent.api.storage.CreateCommand; import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer; +import com.cloud.agent.api.to.S3TO; import com.cloud.agent.api.to.StorageFilerTO; +import com.cloud.agent.api.to.SwiftTO; import com.cloud.configuration.Config; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.exception.StorageUnavailableException; @@ -72,7 +77,9 @@ import com.cloud.storage.dao.VMTemplateHostDao; import com.cloud.storage.dao.VMTemplatePoolDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.dao.VolumeHostDao; +import com.cloud.storage.s3.S3Manager; import com.cloud.storage.snapshot.SnapshotManager; +import com.cloud.storage.swift.SwiftManager; import com.cloud.template.TemplateManager; import com.cloud.utils.NumbersUtil; import com.cloud.utils.db.DB; @@ -112,6 +119,10 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { @Inject VMTemplatePoolDao templatePoolDao; @Inject VolumeManager volumeMgr; + @Inject + private SwiftManager _swiftMgr; + @Inject + private S3Manager _s3Mgr; @Override public boolean canHandle(DataObject srcData, DataObject destData) { @@ -120,7 +131,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { } @DB - protected String copyVolumeFromImage(DataObject srcData, DataObject destData) { + protected Answer copyVolumeFromImage(DataObject srcData, DataObject destData) { String value = configDao.getValue(Config.RecreateSystemVmEnabled.key()); int _copyvolumewait = NumbersUtil.parseInt(value, Integer.parseInt(Config.CopyVolumeWait.getDefaultValue())); @@ -162,16 +173,17 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { this.volDao.update(vol.getId(), vol); volumeHostDao.remove(volumeHostVO.getId()); txn.commit(); - return errMsg; + return cvAnswer; } - private void copyTemplate(DataObject srcData, DataObject destData) { + private Answer copyTemplate(DataObject srcData, DataObject destData) { VMTemplateVO template = this.templateDao.findById(srcData.getId()); templateMgr.prepareTemplateForCreate(template, (StoragePool) destData.getDataStore()); + return null; } - protected String copyFromSnapshot(DataObject snapObj, DataObject volObj) { + protected Answer copyFromSnapshot(DataObject snapObj, DataObject volObj) { SnapshotVO snapshot = this.snapshotDao.findById(snapObj.getId()); StoragePool pool = (StoragePool) volObj.getDataStore(); String vdiUUID = null; @@ -227,8 +239,8 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { if ((answer != null) && answer.getResult()) { snapshotDao.updateSnapshotVersion(volumeId, "2.1", "2.2"); } else { - return "Unable to upgrade snapshot from 2.1 to 2.2 for " - + snapshot.getId(); + throw new CloudRuntimeException("Unable to upgrade snapshot from 2.1 to 2.2 for " + + snapshot.getId()); } } } @@ -277,11 +289,10 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { snapshotMgr.deleteSnapshotsDirForVolume( secondaryStoragePoolUrl, dcId, accountId, volumeId); } - snapshotDao.unlockFromLockTable(snapshotId.toString()); } } - protected String cloneVolume(DataObject template, DataObject volume) { + protected Answer cloneVolume(DataObject template, DataObject volume) { VolumeInfo volInfo = (VolumeInfo)volume; DiskOfferingVO offering = diskOfferingDao.findById(volInfo.getDiskOfferingId()); VMTemplateStoragePoolVO tmpltStoredOn = templatePoolDao.findByPoolTemplate(template.getDataStore().getId(), template.getId()); @@ -298,8 +309,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { answer = storagMgr.sendToPool(pool, null, cmd); } catch (StorageUnavailableException e) { s_logger.debug("Failed to send to storage pool", e); - errMsg = e.toString(); - return errMsg; + throw new CloudRuntimeException("Failed to send to storage pool", e); } if (answer.getResult()) { @@ -327,10 +337,10 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { errMsg = answer.getDetails(); } - return errMsg; + return answer; } - protected String copyVolumeBetweenPools(DataObject srcData, DataObject destData) { + protected Answer copyVolumeBetweenPools(DataObject srcData, DataObject destData) { VolumeInfo volume = (VolumeInfo)srcData; VolumeInfo destVolume = (VolumeInfo)destData; String secondaryStorageURL = this.templateMgr.getSecondaryStorageURL(volume @@ -380,41 +390,45 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { VolumeVO destVol = this.volDao.findById(destVolume.getId()); destVol.setPath(cvAnswer.getVolumePath()); this.volDao.update(destVol.getId(), destVol); - return null; + return cvAnswer; } @Override public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback) { + Answer answer = null; String errMsg = null; try { if (destData.getType() == DataObjectType.VOLUME && srcData.getType() == DataObjectType.VOLUME && srcData.getDataStore().getRole() == DataStoreRole.Image) { - errMsg = copyVolumeFromImage(srcData, destData); + answer = copyVolumeFromImage(srcData, destData); } else if (destData.getType() == DataObjectType.TEMPLATE && srcData.getType() == DataObjectType.TEMPLATE) { - copyTemplate(srcData, destData); + answer = copyTemplate(srcData, destData); } else if (srcData.getType() == DataObjectType.SNAPSHOT && destData.getType() == DataObjectType.VOLUME) { - errMsg = copyFromSnapshot(srcData, destData); + answer = copyFromSnapshot(srcData, destData); } else if (srcData.getType() == DataObjectType.SNAPSHOT && destData.getType() == DataObjectType.TEMPLATE) { - errMsg = createTemplateFromSnashot(srcData, destData); + answer = createTemplateFromSnashot(srcData, destData); } else if (srcData.getType() == DataObjectType.VOLUME && destData.getType() == DataObjectType.TEMPLATE) { - errMsg = createTemplateFromVolume(srcData, destData); + answer = createTemplateFromVolume(srcData, destData); } else if (srcData.getType() == DataObjectType.TEMPLATE && destData.getType() == DataObjectType.VOLUME) { - errMsg = cloneVolume(srcData, destData); + answer = cloneVolume(srcData, destData); } else if (destData.getType() == DataObjectType.VOLUME && srcData.getType() == DataObjectType.VOLUME && srcData.getDataStore().getRole() == DataStoreRole.Primary) { - errMsg = copyVolumeBetweenPools(srcData, destData); + answer = copyVolumeBetweenPools(srcData, destData); + } else if (srcData.getType() == DataObjectType.SNAPSHOT && + destData.getType() == DataObjectType.SNAPSHOT) { + answer = copySnapshot(srcData, destData); } } catch (Exception e) { s_logger.debug("copy failed", e); errMsg = e.toString(); } - CopyCommandResult result = new CopyCommandResult(null); + CopyCommandResult result = new CopyCommandResult(null, answer); result.setResult(errMsg); callback.complete(result); @@ -422,7 +436,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { } @DB - protected String createTemplateFromSnashot(DataObject srcData, + protected Answer createTemplateFromSnashot(DataObject srcData, DataObject destData) { long snapshotId = srcData.getId(); SnapshotVO snapshot = snapshotDao.findById(snapshotId); @@ -538,7 +552,7 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { } @DB - protected String sendCommand(Command cmd, StoragePool pool, + protected Answer sendCommand(Command cmd, StoragePool pool, long templateId, long zoneId, long hostId) { CreatePrivateTemplateAnswer answer = null; @@ -551,11 +565,8 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { e); } - if (answer == null) { - return "Failed to execute CreatePrivateTemplateFromSnapshotCommand"; - } else if (!answer.getResult()) { - return "Failed to execute CreatePrivateTemplateFromSnapshotCommand" - + answer.getDetails(); + if (answer == null || !answer.getResult()) { + return answer; } VMTemplateVO privateTemplate = templateDao.findById(templateId); @@ -594,10 +605,10 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { templateHostVO.setPhysicalSize(answer.getphysicalSize()); templateHostDao.persist(templateHostVO); txn.close(); - return null; + return answer; } - private String createTemplateFromVolume(DataObject srcObj, + private Answer createTemplateFromVolume(DataObject srcObj, DataObject destObj) { long volumeId = srcObj.getId(); VolumeVO volume = this.volDao.findById(volumeId); @@ -633,5 +644,82 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { return sendCommand(cmd, pool, template.getId(), zoneId, secondaryStorageHost.getId()); } + + private HostVO getSecHost(long volumeId, long dcId) { + Long id = snapshotDao.getSecHostId(volumeId); + if ( id != null) { + return hostDao.findById(id); + } + return this.templateMgr.getSecondaryStorageHost(dcId); + } + + protected Answer copySnapshot(DataObject srcObject, DataObject destObject) { + SnapshotInfo srcSnapshot = (SnapshotInfo)srcObject; + VolumeInfo baseVolume = srcSnapshot.getBaseVolume(); + Long dcId = baseVolume.getDataCenterId(); + Long accountId = baseVolume.getAccountId(); + + HostVO secHost = getSecHost(baseVolume.getId(), baseVolume.getDataCenterId()); + + String secondaryStoragePoolUrl = secHost.getStorageUrl(); + 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(), 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()); + if (backupSnapshotCommand.getSwift() != null ) { + snapshotVO.setSwiftId(swift.getId()); + snapshotVO.setBackupSnapshotId(answer.getBackupSnapshotName()); + } else if (backupSnapshotCommand.getS3() != null) { + snapshotVO.setS3Id(s3.getId()); + snapshotVO.setBackupSnapshotId(answer.getBackupSnapshotName()); + } else { + snapshotVO.setSecHostId(secHost.getId()); + snapshotVO.setBackupSnapshotId(answer.getBackupSnapshotName()); + } + if (answer.isFull()) { + snapshotVO.setPrevSnapshotId(0L); + } + this.snapshotDao.update(srcSnapshot.getId(), snapshotVO); + } + return answer; + } } diff --git a/engine/storage/src/org/apache/cloudstack/storage/snapshot/SnapshotEntityImpl.java b/engine/storage/src/org/apache/cloudstack/storage/snapshot/SnapshotEntityImpl.java index 6a7d78a972a..0a91186aaab 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/snapshot/SnapshotEntityImpl.java +++ b/engine/storage/src/org/apache/cloudstack/storage/snapshot/SnapshotEntityImpl.java @@ -105,13 +105,6 @@ public class SnapshotEntityImpl implements SnapshotEntity { return null; } - @Override - public Type getType() { - // TODO Auto-generated method stub - return null; - } - - @Override public HypervisorType getHypervisorType() { // TODO Auto-generated method stub @@ -190,4 +183,10 @@ public class SnapshotEntityImpl implements SnapshotEntity { return null; } + @Override + public Type getRecurringType() { + // TODO Auto-generated method stub + return null; + } + } diff --git a/engine/storage/src/org/apache/cloudstack/storage/snapshot/SnapshotStrategy.java b/engine/storage/src/org/apache/cloudstack/storage/snapshot/SnapshotStrategy.java deleted file mode 100644 index 8c4c815eb7d..00000000000 --- a/engine/storage/src/org/apache/cloudstack/storage/snapshot/SnapshotStrategy.java +++ /dev/null @@ -1,25 +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.subsystem.api.storage.SnapshotInfo; - -public interface SnapshotStrategy { - 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/DefaultPrimaryDataStore.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/DefaultPrimaryDataStore.java index 72c1843da42..f2a999330ad 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/DefaultPrimaryDataStore.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/DefaultPrimaryDataStore.java @@ -31,6 +31,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreRole; import org.apache.cloudstack.engine.subsystem.api.storage.ImageDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreLifeCycle; import org.apache.cloudstack.engine.subsystem.api.storage.Scope; import org.apache.cloudstack.engine.subsystem.api.storage.ScopeType; @@ -42,7 +43,6 @@ import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope; import org.apache.cloudstack.engine.subsystem.api.storage.disktype.DiskFormat; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; -import org.apache.cloudstack.storage.volume.PrimaryDataStoreDriver; import org.apache.cloudstack.storage.volume.VolumeObject; import org.apache.log4j.Logger; diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/driver/AncientPrimaryDataStoreDriverImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/driver/AncientPrimaryDataStoreDriverImpl.java index 9946fba5f63..657ba80e971 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/driver/AncientPrimaryDataStoreDriverImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/driver/AncientPrimaryDataStoreDriverImpl.java @@ -29,261 +29,325 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; 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.EndPoint; +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.datastore.db.PrimaryDataStoreDao; -import org.apache.cloudstack.storage.volume.PrimaryDataStoreDriver; +import org.apache.cloudstack.storage.volume.VolumeObject; import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; +import com.cloud.agent.api.ManageSnapshotAnswer; +import com.cloud.agent.api.ManageSnapshotCommand; import com.cloud.agent.api.storage.CreateAnswer; import com.cloud.agent.api.storage.CreateCommand; import com.cloud.agent.api.storage.DestroyCommand; +import com.cloud.agent.api.storage.ResizeVolumeAnswer; +import com.cloud.agent.api.storage.ResizeVolumeCommand; import com.cloud.agent.api.to.StorageFilerTO; import com.cloud.exception.StorageUnavailableException; import com.cloud.host.HostVO; import com.cloud.host.dao.HostDao; import com.cloud.storage.DiskOfferingVO; +import com.cloud.storage.ResizeVolumePayload; import com.cloud.storage.Storage; import com.cloud.storage.Storage.StoragePoolType; +import com.cloud.storage.SnapshotVO; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; import com.cloud.storage.VMTemplateHostVO; import com.cloud.storage.VMTemplateStoragePoolVO; import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.VolumeManager; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.DiskOfferingDao; +import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VolumeDao; +import com.cloud.storage.snapshot.SnapshotManager; import com.cloud.template.TemplateManager; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.DiskProfile; import com.cloud.vm.dao.VMInstanceDao; public class AncientPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver { - private static final Logger s_logger = Logger - .getLogger(AncientPrimaryDataStoreDriverImpl.class); - @Inject DiskOfferingDao diskOfferingDao; - @Inject VMTemplateDao templateDao; - @Inject VolumeDao volumeDao; - @Inject TemplateManager templateMgr; - @Inject HostDao hostDao; - @Inject StorageManager storageMgr; - @Inject VMInstanceDao vmDao; - @Inject PrimaryDataStoreDao primaryStoreDao; - @Override - public String grantAccess(DataObject data, EndPoint ep) { - // TODO Auto-generated method stub - return null; - } + private static final Logger s_logger = Logger + .getLogger(AncientPrimaryDataStoreDriverImpl.class); + @Inject DiskOfferingDao diskOfferingDao; + @Inject VMTemplateDao templateDao; + @Inject VolumeDao volumeDao; + @Inject TemplateManager templateMgr; + @Inject HostDao hostDao; + @Inject StorageManager storageMgr; + @Inject VolumeManager volumeMgr; + @Inject VMInstanceDao vmDao; + @Inject SnapshotDao snapshotDao; + @Inject PrimaryDataStoreDao primaryStoreDao; + @Inject SnapshotManager snapshotMgr; + @Override + public String grantAccess(DataObject data, EndPoint ep) { + // TODO Auto-generated method stub + return null; + } - @Override - public boolean revokeAccess(DataObject data, EndPoint ep) { - // TODO Auto-generated method stub - return false; - } + @Override + public boolean revokeAccess(DataObject data, EndPoint ep) { + // TODO Auto-generated method stub + return false; + } - @Override - public Set listObjects(DataStore store) { - // TODO Auto-generated method stub - return null; - } + @Override + public Set listObjects(DataStore store) { + // TODO Auto-generated method stub + return null; + } - public boolean createVolume( - VolumeInfo volume) throws StorageUnavailableException { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Creating volume: " + volume); - } + public boolean 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); + + VMTemplateVO template = null; + if (volume.getTemplateId() != null) { + template = templateDao.findById(volume.getTemplateId()); + } + + 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 = null; + VMTemplateStoragePoolVO tmpltStoredOn = null; + + for (int i = 0; i < 2; i++) { + if (template != null + && template.getFormat() != Storage.ImageFormat.ISO) { + if (pool.getPoolType() == StoragePoolType.CLVM) { + // prepareISOForCreate does what we need, which is to + // tell us where the template is + VMTemplateHostVO tmpltHostOn = templateMgr + .prepareISOForCreate(template, pool); + if (tmpltHostOn == null) { + s_logger.debug("cannot find template " + + template.getId() + " " + + template.getName()); + throw new CloudRuntimeException("cannot find template" + + template.getId() + + template.getName()); + } + HostVO secondaryStorageHost = hostDao + .findById(tmpltHostOn.getHostId()); + String tmpltHostUrl = secondaryStorageHost + .getStorageUrl(); + String fullTmpltUrl = tmpltHostUrl + "/" + + tmpltHostOn.getInstallPath(); + cmd = new CreateCommand(diskProfile, fullTmpltUrl, + new StorageFilerTO(pool)); + } else { + tmpltStoredOn = templateMgr.prepareTemplateForCreate( + template, pool); + if (tmpltStoredOn == null) { + s_logger.debug("Cannot use this pool " + pool + + " because we can't propagate template " + + template); + throw new CloudRuntimeException("Cannot use this pool " + pool + + " because we can't propagate template " + + template); + } + cmd = new CreateCommand(diskProfile, + tmpltStoredOn.getLocalDownloadPath(), + new StorageFilerTO(pool)); + } + } else { + if (template != null + && Storage.ImageFormat.ISO == template.getFormat()) { + VMTemplateHostVO tmpltHostOn = templateMgr + .prepareISOForCreate(template, pool); + if (tmpltHostOn == null) { + throw new CloudRuntimeException( + "Did not find ISO in secondry storage in zone " + + pool.getDataCenterId()); + } + } + 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; + } else { + if (tmpltStoredOn != null + && (answer instanceof CreateAnswer) + && ((CreateAnswer) answer) + .templateReloadRequested()) { + if (!templateMgr + .resetTemplateDownloadStateOnPool(tmpltStoredOn + .getId())) { + break; // break out of template-redeploy retry loop + } + } else { + break; + } + } + } + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Unable to create volume " + volume.getId()); + } + return false; + } + + @Override + public void createAsync(DataObject data, + AsyncCompletionCallback callback) { + // TODO Auto-generated method stub + String errMsg = null; + if (data.getType() == DataObjectType.VOLUME) { + try { + createVolume((VolumeInfo)data); + } catch (StorageUnavailableException e) { + s_logger.debug("failed to create volume", e); + errMsg = e.toString(); + } catch (Exception e) { + s_logger.debug("failed to create volume", e); + errMsg = e.toString(); + } + } + CreateCmdResult result = new CreateCmdResult(null, null); + if (errMsg != null) { + result.setResult(errMsg); + } + + callback.complete(result); + + } + + @Override + public void deleteAsync(DataObject data, + AsyncCompletionCallback callback) { + + String vmName = null; + VolumeVO vol = this.volumeDao.findById(data.getId()); + + + StoragePool pool = (StoragePool)data.getDataStore(); + + DestroyCommand cmd = new DestroyCommand(pool, vol, vmName); + + CommandResult result = new CommandResult(); + try { + Answer answer = this.storageMgr.sendToPool(pool, cmd); + if (answer != null && !answer.getResult()) { + result.setResult(answer.getDetails()); + s_logger.info("Will retry delete of " + vol + " from " + pool.getId()); + } + } catch (StorageUnavailableException e) { + s_logger.error("Storage is unavailable currently. Will retry delete of " + + vol + " from " + pool.getId(), e); + result.setResult(e.toString()); + } catch (Exception ex) { + s_logger.debug("Unable to destoy volume" + vol + " from " + pool.getId(), ex); + result.setResult(ex.toString()); + } + callback.complete(result); + } + + @Override + public void copyAsync(DataObject srcdata, DataObject destData, + AsyncCompletionCallback callback) { + // TODO Auto-generated method stub + + } + + @Override + public boolean canCopy(DataObject srcData, DataObject destData) { + // TODO Auto-generated method stub + return false; + } + + @Override + public void takeSnapshot(SnapshotInfo snapshot, + AsyncCompletionCallback callback) { + VolumeInfo volume = snapshot.getBaseVolume(); + String vmName = this.volumeMgr.getVmNameOnVolume(volume); + SnapshotVO preSnapshotVO = this.snapshotMgr.getParentSnapshot(volume, snapshot); + StoragePool srcPool = (StoragePool)volume.getDataStore(); + + ManageSnapshotCommand cmd = new ManageSnapshotCommand(snapshot.getId(), volume.getPath(), srcPool, preSnapshotVO.getPath(), snapshot.getName(), vmName); + + ManageSnapshotAnswer answer = (ManageSnapshotAnswer) this.snapshotMgr.sendToPool(volume, cmd); - DiskOfferingVO offering = diskOfferingDao.findById(volume.getDiskOfferingId()); - DiskProfile diskProfile = new DiskProfile(volume, offering, - null); - - VMTemplateVO template = null; - if (volume.getTemplateId() != null) { - template = templateDao.findById(volume.getTemplateId()); - } - - 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 = null; - VMTemplateStoragePoolVO tmpltStoredOn = null; - - for (int i = 0; i < 2; i++) { - if (template != null - && template.getFormat() != Storage.ImageFormat.ISO) { - if (pool.getPoolType() == StoragePoolType.CLVM) { - // prepareISOForCreate does what we need, which is to - // tell us where the template is - VMTemplateHostVO tmpltHostOn = templateMgr - .prepareISOForCreate(template, pool); - if (tmpltHostOn == null) { - s_logger.debug("cannot find template " - + template.getId() + " " - + template.getName()); - throw new CloudRuntimeException("cannot find template" - + template.getId() - + template.getName()); - } - HostVO secondaryStorageHost = hostDao - .findById(tmpltHostOn.getHostId()); - String tmpltHostUrl = secondaryStorageHost - .getStorageUrl(); - String fullTmpltUrl = tmpltHostUrl + "/" - + tmpltHostOn.getInstallPath(); - cmd = new CreateCommand(diskProfile, fullTmpltUrl, - new StorageFilerTO(pool)); - } else { - tmpltStoredOn = templateMgr.prepareTemplateForCreate( - template, pool); - if (tmpltStoredOn == null) { - s_logger.debug("Cannot use this pool " + pool - + " because we can't propagate template " - + template); - throw new CloudRuntimeException("Cannot use this pool " + pool - + " because we can't propagate template " - + template); - } - cmd = new CreateCommand(diskProfile, - tmpltStoredOn.getLocalDownloadPath(), - new StorageFilerTO(pool)); - } - } else { - if (template != null - && Storage.ImageFormat.ISO == template.getFormat()) { - VMTemplateHostVO tmpltHostOn = templateMgr - .prepareISOForCreate(template, pool); - if (tmpltHostOn == null) { - throw new CloudRuntimeException( - "Did not find ISO in secondry storage in zone " - + pool.getDataCenterId()); - } - } - 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; - } else { - if (tmpltStoredOn != null - && (answer instanceof CreateAnswer) - && ((CreateAnswer) answer) - .templateReloadRequested()) { - if (!templateMgr - .resetTemplateDownloadStateOnPool(tmpltStoredOn - .getId())) { - break; // break out of template-redeploy retry loop - } - } else { - break; - } - } - } - } - - if (s_logger.isDebugEnabled()) { - s_logger.debug("Unable to create volume " + volume.getId()); - } - return false; - } - - @Override - public void createAsync(DataObject data, - AsyncCompletionCallback callback) { - // TODO Auto-generated method stub - String errMsg = null; - if (data.getType() == DataObjectType.VOLUME) { - try { - createVolume((VolumeInfo)data); - } catch (StorageUnavailableException e) { - s_logger.debug("failed to create volume", e); - errMsg = e.toString(); - } catch (Exception e) { - s_logger.debug("failed to create volume", e); - errMsg = e.toString(); - } - } - CreateCmdResult result = new CreateCmdResult(null, null); - if (errMsg != null) { - result.setResult(errMsg); + CreateCmdResult result = null; + if ((answer != null) && answer.getResult()) { + result = new CreateCmdResult(answer.getSnapshotPath(), null); + } else { + result = new CreateCmdResult(null, null); } callback.complete(result); - - } + } - @Override - public void deleteAsync(DataObject data, - AsyncCompletionCallback callback) { + @Override + public void revertSnapshot(SnapshotInfo snapshot, + AsyncCompletionCallback callback) { + // TODO Auto-generated method stub - String vmName = null; - VolumeVO vol = this.volumeDao.findById(data.getId()); + } + @Override + public void resize(DataObject data, + AsyncCompletionCallback callback) { + VolumeObject vol = (VolumeObject)data; + StoragePool pool = (StoragePool)data.getDataStore(); + ResizeVolumePayload resizeParameter = (ResizeVolumePayload)vol.getpayload(); - StoragePool pool = (StoragePool)data.getDataStore(); + ResizeVolumeCommand resizeCmd = new ResizeVolumeCommand( + vol.getPath(), new StorageFilerTO(pool), vol.getSize(), + resizeParameter.newSize, resizeParameter.shrinkOk, resizeParameter.instanceName); + CreateCmdResult result = new CreateCmdResult(null, null); + try { + ResizeVolumeAnswer answer = (ResizeVolumeAnswer) this.storageMgr.sendToPool(pool, + resizeParameter.hosts, resizeCmd); + if (answer != null && answer.getResult()) { + long finalSize = answer.getNewSize(); + s_logger.debug("Resize: volume started at size " + vol.getSize() + + " and ended at size " + finalSize); - DestroyCommand cmd = new DestroyCommand(pool, vol, vmName); + vol.setSize(finalSize); + vol.update(); + } else if (answer != null) { + result.setResult(answer.getDetails()); + } else { + s_logger.debug("return a null answer, mark it as failed for unknown reason"); + result.setResult("return a null answer, mark it as failed for unknown reason"); + } - CommandResult result = new CommandResult(); - try { - Answer answer = this.storageMgr.sendToPool(pool, cmd); - if (answer != null && !answer.getResult()) { - result.setResult(answer.getDetails()); - s_logger.info("Will retry delete of " + vol + " from " + pool.getId()); - } - } catch (StorageUnavailableException e) { - s_logger.error("Storage is unavailable currently. Will retry delete of " - + vol + " from " + pool.getId(), e); - result.setResult(e.toString()); - } catch (Exception ex) { - s_logger.debug("Unable to destoy volume" + vol + " from " + pool.getId(), ex); - result.setResult(ex.toString()); - } - callback.complete(result); - } + } catch (Exception e) { + s_logger.debug("sending resize command failed", e); + result.setResult(e.toString()); + } - @Override - public void copyAsync(DataObject srcdata, DataObject destData, - AsyncCompletionCallback callback) { - // TODO Auto-generated method stub - - } - - @Override - public boolean canCopy(DataObject srcData, DataObject destData) { - // TODO Auto-generated method stub - return false; - } - - @Override - public void takeSnapshot(SnapshotInfo snapshot, - AsyncCompletionCallback callback) { - // TODO Auto-generated method stub - - } - - @Override - public void revertSnapshot(SnapshotInfo snapshot, - AsyncCompletionCallback callback) { - // TODO Auto-generated method stub - - } + callback.complete(result); + } } diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/driver/DefaultPrimaryDataStoreDriverImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/driver/DefaultPrimaryDataStoreDriverImpl.java index efd04d18294..6d0c2c6862b 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/driver/DefaultPrimaryDataStoreDriverImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/driver/DefaultPrimaryDataStoreDriverImpl.java @@ -27,6 +27,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import org.apache.cloudstack.framework.async.AsyncCallbackDispatcher; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; @@ -36,7 +37,6 @@ import org.apache.cloudstack.storage.command.CreateObjectCommand; import org.apache.cloudstack.storage.command.DeleteCommand; import org.apache.cloudstack.storage.datastore.DataObjectManager; import org.apache.cloudstack.storage.endpoint.EndPointSelector; -import org.apache.cloudstack.storage.volume.PrimaryDataStoreDriver; import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; @@ -210,13 +210,6 @@ public class DefaultPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver return null; } - @Override - public void takeSnapshot(SnapshotInfo snapshot, - AsyncCompletionCallback callback) { - // TODO Auto-generated method stub - - } - @Override public void revertSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback callback) { @@ -238,5 +231,19 @@ public class DefaultPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver // TODO Auto-generated method stub } + + @Override + public void resize(DataObject data, + AsyncCompletionCallback callback) { + // TODO Auto-generated method stub + + } + + @Override + public void takeSnapshot(SnapshotInfo snapshot, + AsyncCompletionCallback callback) { + // TODO Auto-generated method stub + + } } diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/manager/DefaultPrimaryDataStoreProviderManagerImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/manager/DefaultPrimaryDataStoreProviderManagerImpl.java index fdbe4b47c1e..f395061d633 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/manager/DefaultPrimaryDataStoreProviderManagerImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/manager/DefaultPrimaryDataStoreProviderManagerImpl.java @@ -27,13 +27,13 @@ import javax.inject.Inject; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProviderManager; import org.apache.cloudstack.engine.subsystem.api.storage.HypervisorHostListener; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; import org.apache.cloudstack.storage.datastore.DefaultPrimaryDataStore; import org.apache.cloudstack.storage.datastore.PrimaryDataStore; import org.apache.cloudstack.storage.datastore.PrimaryDataStoreProviderManager; import org.apache.cloudstack.storage.datastore.db.DataStoreProviderDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; -import org.apache.cloudstack.storage.volume.PrimaryDataStoreDriver; import org.springframework.stereotype.Component; @Component diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/AncientPrimaryDataStoreProviderImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/AncientPrimaryDataStoreProviderImpl.java index 702ab238ba8..0ef17040a52 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/AncientPrimaryDataStoreProviderImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/AncientPrimaryDataStoreProviderImpl.java @@ -23,10 +23,10 @@ import java.util.Map; import javax.inject.Inject; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreLifeCycle; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; import org.apache.cloudstack.storage.datastore.PrimaryDataStoreProviderManager; import org.apache.cloudstack.storage.datastore.driver.AncientPrimaryDataStoreDriverImpl; import org.apache.cloudstack.storage.datastore.lifecycle.AncientPrimaryDataStoreLifeCyclImpl; -import org.apache.cloudstack.storage.volume.PrimaryDataStoreDriver; import org.springframework.stereotype.Component; import com.cloud.utils.component.ComponentContext; diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/DefaultPrimaryDatastoreProviderImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/DefaultPrimaryDatastoreProviderImpl.java index 85a5d0226d7..a1402c13b3d 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/DefaultPrimaryDatastoreProviderImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/provider/DefaultPrimaryDatastoreProviderImpl.java @@ -22,10 +22,10 @@ import javax.inject.Inject; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreLifeCycle; import org.apache.cloudstack.engine.subsystem.api.storage.HypervisorHostListener; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; import org.apache.cloudstack.storage.datastore.PrimaryDataStoreProviderManager; import org.apache.cloudstack.storage.datastore.driver.DefaultPrimaryDataStoreDriverImpl; import org.apache.cloudstack.storage.datastore.lifecycle.DefaultPrimaryDataStoreLifeCycleImpl; -import org.apache.cloudstack.storage.volume.PrimaryDataStoreDriver; import org.springframework.stereotype.Component; import com.cloud.utils.component.ComponentContext; diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java index 87951ceeb64..6ad6cc9486f 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java @@ -30,6 +30,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.disktype.DiskFormat; import org.apache.cloudstack.storage.datastore.ObjectInDataStoreManager; import org.apache.log4j.Logger; +import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.Volume; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.VolumeDao; @@ -73,6 +74,10 @@ public class VolumeObject implements VolumeInfo { public void setPath(String uuid) { volumeVO.setPath(uuid); } + + public void setSize(Long size) { + volumeVO.setSize(size); + } public Volume.State getState() { return volumeVO.getState(); @@ -182,6 +187,8 @@ public class VolumeObject implements VolumeInfo { volEvent = Volume.Event.OperationSucceeded; } else if (event == ObjectInDataStoreStateMachine.Event.OperationFailed) { volEvent = Volume.Event.OperationFailed; + } else if (event == ObjectInDataStoreStateMachine.Event.ResizeRequested) { + volEvent = Volume.Event.ResizeRequested; } this.stateTransit(volEvent); } catch (Exception e) { @@ -310,4 +317,14 @@ public class VolumeObject implements VolumeInfo { public Object getpayload() { return this.payload; } + + @Override + public HypervisorType getHypervisorType() { + return this.volumeDao.getHypervisorType(this.volumeVO.getId()); + } + + @Override + public Long getLastPoolId() { + return this.volumeVO.getLastPoolId(); + } } 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 ef99a49b809..c019374d9b9 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 @@ -56,9 +56,6 @@ import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.VirtualMachine; import com.cloud.vm.dao.VMInstanceDao; -//1. change volume state -//2. orchestrator of volume, control most of the information of volume, storage pool id, voluem state, scope etc. - @Component public class VolumeServiceImpl implements VolumeService { private static final Logger s_logger = Logger @@ -423,8 +420,49 @@ public class VolumeServiceImpl implements VolumeService { public AsyncCallFuture createVolumeFromSnapshot( VolumeInfo volume, DataStore store, SnapshotInfo snapshot) { AsyncCallFuture future = new AsyncCallFuture(); - VolumeApiResult result = new VolumeApiResult(volume); - return null; + + try { + DataObject volumeOnStore = store.create(volume); + volume.processEvent(Event.CreateOnlyRequested); + CreateVolumeFromBaseImageContext context = new CreateVolumeFromBaseImageContext(null, + (VolumeObject)volume, store, volumeOnStore, future); + AsyncCallbackDispatcher caller = AsyncCallbackDispatcher.create(this); + caller.setCallback(caller.getTarget().createVolumeFromSnapshotCallback(null, null)) + .setContext(context); + this.motionSrv.copyAsync(snapshot, volumeOnStore, caller); + } catch (Exception e) { + s_logger.debug("create volume from snapshot failed", e); + VolumeApiResult result = new VolumeApiResult(volume); + result.setResult(e.toString()); + future.complete(result); + } + + return future; + } + + protected Void createVolumeFromSnapshotCallback(AsyncCallbackDispatcher callback, + CreateVolumeFromBaseImageContext context) { + CopyCommandResult result = callback.getResult(); + VolumeInfo volume = context.vo; + VolumeApiResult apiResult = new VolumeApiResult(volume); + Event event = null; + if (result.isFailed()) { + apiResult.setResult(result.getResult()); + event = Event.OperationFailed; + } else { + event = Event.OperationSuccessed; + } + + try { + volume.processEvent(event); + } catch (Exception e) { + s_logger.debug("create volume from snapshot failed", e); + apiResult.setResult(e.toString()); + } + + AsyncCallFuture future = context.future; + future.complete(apiResult); + return null; } protected VolumeVO duplicateVolumeOnAnotherStorage(Volume volume, StoragePool pool) { @@ -552,5 +590,61 @@ public class VolumeServiceImpl implements VolumeService { context.future.complete(res); return null; } + + + @Override + public AsyncCallFuture resize(VolumeInfo volume) { + AsyncCallFuture future = new AsyncCallFuture(); + VolumeApiResult result = new VolumeApiResult(volume); + try { + volume.processEvent(Event.ResizeRequested); + } catch (Exception e) { + s_logger.debug("Failed to change state to resize", e); + result.setResult(e.toString()); + future.complete(result); + return future; + } + CreateVolumeContext context = new CreateVolumeContext(null, volume, future); + AsyncCallbackDispatcher caller = AsyncCallbackDispatcher.create(this); + caller.setCallback(caller.getTarget().registerVolumeCallback(null, null)) + .setContext(context); + volume.getDataStore().getDriver().resize(volume, caller); + return future; + } + + protected Void resizeVolumeCallback(AsyncCallbackDispatcher callback, CreateVolumeContext context) { + CreateCmdResult result = callback.getResult(); + AsyncCallFuture future = context.future; + VolumeInfo volume = (VolumeInfo)context.volume; + + if (result.isFailed()) { + try { + volume.processEvent(Event.OperationFailed); + } catch (Exception e) { + s_logger.debug("Failed to change state", e); + } + VolumeApiResult res = new VolumeApiResult(volume); + res.setResult(result.getResult()); + future.complete(res); + return null; + } + + try { + volume.processEvent(Event.OperationSuccessed); + } catch(Exception e) { + s_logger.debug("Failed to change state", e); + VolumeApiResult res = new VolumeApiResult(volume); + res.setResult(result.getResult()); + future.complete(res); + return null; + } + + VolumeApiResult res = new VolumeApiResult(volume); + future.complete(res); + + return null; + } + + } diff --git a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java index 88c53740f32..f31126c2aeb 100644 --- a/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java +++ b/plugins/storage/volume/solidfire/src/org/apache/cloudstack/storage/datastore/driver/SolidfirePrimaryDataStoreDriver.java @@ -24,9 +24,9 @@ import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; -import org.apache.cloudstack.storage.volume.PrimaryDataStoreDriver; public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { @@ -72,17 +72,25 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { return false; } - @Override - public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback callback) { - // TODO Auto-generated method stub - - } - @Override public void revertSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback callback) { // TODO Auto-generated method stub } + @Override + public void resize(DataObject data, + AsyncCompletionCallback callback) { + // TODO Auto-generated method stub + + } + + @Override + public void takeSnapshot(SnapshotInfo snapshot, + AsyncCompletionCallback callback) { + // TODO Auto-generated method stub + + } + } diff --git a/server/src/com/cloud/api/ApiDBUtils.java b/server/src/com/cloud/api/ApiDBUtils.java index 0a203528f85..c28daefa69a 100755 --- a/server/src/com/cloud/api/ApiDBUtils.java +++ b/server/src/com/cloud/api/ApiDBUtils.java @@ -618,7 +618,7 @@ public class ApiDBUtils { public static String getSnapshotIntervalTypes(long snapshotId) { SnapshotVO snapshot = _snapshotDao.findById(snapshotId); - return snapshot.getType().name(); + return snapshot.getRecurringType().name(); } public static String getStoragePoolTags(long poolId) { diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index 3da31689d1d..845b242d573 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -341,7 +341,7 @@ public class ApiResponseHelper implements ResponseGenerator { populateOwner(snapshotResponse, snapshot); VolumeVO volume = findVolumeById(snapshot.getVolumeId()); - String snapshotTypeStr = snapshot.getType().name(); + String snapshotTypeStr = snapshot.getRecurringType().name(); snapshotResponse.setSnapshotType(snapshotTypeStr); if (volume != null) { snapshotResponse.setVolumeId(volume.getUuid()); diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index c0c23b6b641..8c77715c0cf 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -137,7 +137,8 @@ public enum Config { SnapshotMonthlyMax("Snapshots", SnapshotManager.class, Integer.class, "snapshot.max.monthly", "8", "Maximum monthly snapshots for a volume", null), SnapshotPollInterval("Snapshots", SnapshotManager.class, Integer.class, "snapshot.poll.interval", "300", "The time interval in seconds when the management server polls for snapshots to be scheduled.", null), SnapshotDeltaMax("Snapshots", SnapshotManager.class, Integer.class, "snapshot.delta.max", "16", "max delta snapshots between two full snapshots.", null), - + BackupSnapshotAferTakingSnapshot("Snapshots", SnapshotManager.class, Boolean.class, "snapshot.backup.rightafter", "true", "backup snapshot right after snapshot is taken", null), + // Advanced JobExpireMinutes("Advanced", ManagementServer.class, String.class, "job.expire.minutes", "1440", "Time (in minutes) for async-jobs to be kept in system", null), JobCancelThresholdMinutes("Advanced", ManagementServer.class, String.class, "job.cancel.threshold.minutes", "60", "Time (in minutes) for async-jobs to be forcely cancelled if it has been in process for long", null), diff --git a/server/src/com/cloud/storage/ResizeVolumePayload.java b/server/src/com/cloud/storage/ResizeVolumePayload.java new file mode 100644 index 00000000000..472d627dd4a --- /dev/null +++ b/server/src/com/cloud/storage/ResizeVolumePayload.java @@ -0,0 +1,14 @@ +package com.cloud.storage; + +public class ResizeVolumePayload { + public final Long newSize; + public final boolean shrinkOk; + public final String instanceName; + public final long[] hosts; + public ResizeVolumePayload(Long newSize, boolean shrinkOk, String instanceName, long[] hosts) { + this.newSize = newSize; + this.shrinkOk = shrinkOk; + this.instanceName = instanceName; + this.hosts = hosts; + } +} diff --git a/server/src/com/cloud/storage/VolumeManager.java b/server/src/com/cloud/storage/VolumeManager.java index ebb9e54cd35..af3cbbfbae5 100644 --- a/server/src/com/cloud/storage/VolumeManager.java +++ b/server/src/com/cloud/storage/VolumeManager.java @@ -52,9 +52,9 @@ public interface VolumeManager extends VolumeApiService { boolean volumeOnSharedStoragePool(VolumeVO volume); - boolean volumeInactive(VolumeVO volume); + boolean volumeInactive(Volume volume); - String getVmNameOnVolume(VolumeVO volume); + String getVmNameOnVolume(Volume volume); VolumeVO allocVolume(CreateVolumeCmd cmd) throws ResourceAllocationException; diff --git a/server/src/com/cloud/storage/VolumeManagerImpl.java b/server/src/com/cloud/storage/VolumeManagerImpl.java index 573b8e90e12..a69607f1f3f 100644 --- a/server/src/com/cloud/storage/VolumeManagerImpl.java +++ b/server/src/com/cloud/storage/VolumeManagerImpl.java @@ -66,12 +66,6 @@ import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; import com.cloud.agent.api.AttachVolumeAnswer; import com.cloud.agent.api.AttachVolumeCommand; -import com.cloud.agent.api.storage.CopyVolumeAnswer; -import com.cloud.agent.api.storage.CopyVolumeCommand; -import com.cloud.agent.api.storage.DestroyCommand; -import com.cloud.agent.api.storage.ResizeVolumeAnswer; -import com.cloud.agent.api.storage.ResizeVolumeCommand; -import com.cloud.agent.api.to.StorageFilerTO; import com.cloud.agent.api.to.VolumeTO; import com.cloud.alert.AlertManager; import com.cloud.api.ApiDBUtils; @@ -763,7 +757,7 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { } @Override - public boolean volumeInactive(VolumeVO volume) { + public boolean volumeInactive(Volume volume) { Long vmId = volume.getInstanceId(); if (vmId != null) { UserVm vm = _userVmDao.findById(vmId); @@ -779,7 +773,7 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { } @Override - public String getVmNameOnVolume(VolumeVO volume) { + public String getVmNameOnVolume(Volume volume) { Long vmId = volume.getInstanceId(); if (vmId != null) { VMInstanceVO vm = _vmInstanceDao.findById(vmId); @@ -1013,7 +1007,6 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { public VolumeVO resizeVolume(ResizeVolumeCmd cmd) { Long newSize = null; boolean shrinkOk = cmd.getShrinkOk(); - boolean success = false; VolumeVO volume = _volsDao.findById(cmd.getEntityId()); if (volume == null) { @@ -1170,64 +1163,31 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { "VM must be stopped or disk detached in order to resize with the Xen HV"); } } - + + ResizeVolumePayload payload = new ResizeVolumePayload(newSize, shrinkOk, instanceName, hosts); + try { - try { - stateTransitTo(volume, Volume.Event.ResizeRequested); - } catch (NoTransitionException etrans) { - throw new CloudRuntimeException( - "Unable to change volume state for resize: " - + etrans.toString()); - } + VolumeInfo vol = this.volFactory.getVolume(volume.getId()); + vol.addPayload(payload); + + AsyncCallFuture future = this.volService.resize(vol); + future.get(); + volume = _volsDao.findById(volume.getId()); - ResizeVolumeCommand resizeCmd = new ResizeVolumeCommand( - volume.getPath(), new StorageFilerTO(pool), currentSize, - newSize, shrinkOk, instanceName); - ResizeVolumeAnswer answer = (ResizeVolumeAnswer) this.storageMgr.sendToPool(pool, - hosts, resizeCmd); + if (newDiskOffering != null) { + volume.setDiskOfferingId(cmd.getNewDiskOfferingId()); + } + _volsDao.update(volume.getId(), volume); - /* - * need to fetch/store new volume size in database. This value comes - * from hypervisor rather than trusting that a success means we have - * a volume of the size we requested - */ - if (answer != null && answer.getResult()) { - long finalSize = answer.getNewSize(); - s_logger.debug("Resize: volume started at size " + currentSize - + " and ended at size " + finalSize); - volume.setSize(finalSize); - if (newDiskOffering != null) { - volume.setDiskOfferingId(cmd.getNewDiskOfferingId()); - } - _volsDao.update(volume.getId(), volume); - - success = true; - return volume; - } else if (answer != null) { - s_logger.debug("Resize: returned '" + answer.getDetails() + "'"); - } - } catch (StorageUnavailableException e) { - s_logger.debug("volume failed to resize: " + e); - return null; - } finally { - if (success) { - try { - stateTransitTo(volume, Volume.Event.OperationSucceeded); - } catch (NoTransitionException etrans) { - throw new CloudRuntimeException( - "Failed to change volume state: " - + etrans.toString()); - } - } else { - try { - stateTransitTo(volume, Volume.Event.OperationFailed); - } catch (NoTransitionException etrans) { - throw new CloudRuntimeException( - "Failed to change volume state: " - + etrans.toString()); - } - } - } + return volume; + } catch (InterruptedException e) { + s_logger.debug("failed get resize volume result", e); + } catch (ExecutionException e) { + s_logger.debug("failed get resize volume result", e); + } catch (Exception e) { + s_logger.debug("failed get resize volume result", e); + } + return null; } diff --git a/server/src/com/cloud/storage/dao/SnapshotDao.java b/server/src/com/cloud/storage/dao/SnapshotDao.java index 3b961f6fa89..0e378a724b4 100644 --- a/server/src/com/cloud/storage/dao/SnapshotDao.java +++ b/server/src/com/cloud/storage/dao/SnapshotDao.java @@ -25,7 +25,7 @@ import com.cloud.utils.fsm.StateDao; import java.util.List; -public interface SnapshotDao extends GenericDao, StateDao { +public interface SnapshotDao extends GenericDao, StateDao { List listByVolumeId(long volumeId); List listByVolumeId(Filter filter, long volumeId); SnapshotVO findNextSnapshot(long parentSnapId); diff --git a/server/src/com/cloud/storage/dao/SnapshotDaoImpl.java b/server/src/com/cloud/storage/dao/SnapshotDaoImpl.java index a8a07dcc3a6..5b3f2732f99 100644 --- a/server/src/com/cloud/storage/dao/SnapshotDaoImpl.java +++ b/server/src/com/cloud/storage/dao/SnapshotDaoImpl.java @@ -324,7 +324,7 @@ public class SnapshotDaoImpl extends GenericDaoBase implements } @Override - public boolean updateState(State currentState, Event event, State nextState, Snapshot snapshot, Object data) { + public boolean updateState(State currentState, Event event, State nextState, SnapshotVO snapshot, Object data) { Transaction txn = Transaction.currentTxn(); txn.start(); SnapshotVO snapshotVO = (SnapshotVO)snapshot; diff --git a/server/src/com/cloud/storage/listener/SnapshotStateListener.java b/server/src/com/cloud/storage/listener/SnapshotStateListener.java index 17ccce54c82..8f94f23a27c 100644 --- a/server/src/com/cloud/storage/listener/SnapshotStateListener.java +++ b/server/src/com/cloud/storage/listener/SnapshotStateListener.java @@ -17,24 +17,24 @@ package com.cloud.storage.listener; -import com.cloud.event.EventCategory; -import com.cloud.storage.Snapshot; -import com.cloud.storage.Snapshot.Event; -import com.cloud.storage.Snapshot.State; -import com.cloud.server.ManagementServer; -import com.cloud.utils.fsm.StateListener; - -import org.apache.cloudstack.framework.events.EventBus; -import org.apache.cloudstack.framework.events.EventBusException; -import org.apache.log4j.Logger; - -import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import javax.inject.Inject; -public class SnapshotStateListener implements StateListener { +import org.apache.cloudstack.framework.events.EventBus; +import org.apache.cloudstack.framework.events.EventBusException; +import org.apache.log4j.Logger; + +import com.cloud.event.EventCategory; +import com.cloud.server.ManagementServer; +import com.cloud.storage.Snapshot; +import com.cloud.storage.Snapshot.Event; +import com.cloud.storage.Snapshot.State; +import com.cloud.storage.SnapshotVO; +import com.cloud.utils.fsm.StateListener; + +public class SnapshotStateListener implements StateListener { // get the event bus provider if configured @Inject protected EventBus _eventBus; @@ -46,13 +46,13 @@ public class SnapshotStateListener implements StateListener listSnapsforPolicy(long policyId, Filter filter); - */ - /** - * List all policies which are assigned to the specified volume - */ - List listPoliciesforVolume(long volumeId); - - /** - * List all policies to which a specified snapshot belongs. For ex: A snapshot may belong to a hourly snapshot and a daily - * snapshot run at the same time - */ - /* - * List listPoliciesforSnapshot(long snapshotId); - */ - /** - * List all snapshots for a specified volume irrespective of the policy which created the snapshot - */ - List listSnapsforVolume(long volumeId); - void deletePoliciesForVolume(Long volumeId); /** @@ -109,35 +55,20 @@ public interface SnapshotManager { * The account which is to be deleted. */ boolean deleteSnapshotDirsForAccount(long accountId); - - SnapshotPolicyVO getPolicyForVolume(long volumeId); - - boolean destroySnapshotBackUp(long snapshotId); - - /** - * Create a snapshot of a volume - * - * @param cmd - * the API command wrapping the parameters for creating the snapshot (mainly volumeId) - * @return the Snapshot that was created - */ - SnapshotVO createSnapshotOnPrimary(VolumeVO volume, Long polocyId, Long snapshotId) throws ResourceAllocationException; - - List listPoliciesforSnapshot(long snapshotId); - - List listSnapsforPolicy(long policyId, Filter filter); - + void downloadSnapshotsFromSwift(SnapshotVO ss); void downloadSnapshotsFromS3(SnapshotVO snapshot); - HostVO getSecondaryStorageHost(SnapshotVO snapshot); - String getSecondaryStorageURL(SnapshotVO snapshot); - void deleteSnapshotsForVolume (String secondaryStoragePoolUrl, Long dcId, Long accountId, Long volumeId ); - void deleteSnapshotsDirForVolume(String secondaryStoragePoolUrl, Long dcId, Long accountId, Long volumeId); boolean canOperateOnVolume(Volume volume); + + Answer sendToPool(Volume vol, Command cmd); + + SnapshotVO getParentSnapshot(VolumeInfo volume, Snapshot snapshot); + + Snapshot backupSnapshot(Long snapshotId); } diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java index 58ca9a41cfa..7df99d67be2 100755 --- a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -32,20 +32,21 @@ import org.apache.cloudstack.api.command.user.snapshot.DeleteSnapshotPoliciesCmd import org.apache.cloudstack.api.command.user.snapshot.ListSnapshotPoliciesCmd; import org.apache.cloudstack.api.command.user.snapshot.ListSnapshotsCmd; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; +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.SnapshotStrategy; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; -import com.cloud.agent.api.BackupSnapshotAnswer; -import com.cloud.agent.api.BackupSnapshotCommand; import com.cloud.agent.api.Command; import com.cloud.agent.api.DeleteSnapshotBackupCommand; import com.cloud.agent.api.DeleteSnapshotsDirCommand; import com.cloud.agent.api.DownloadSnapshotFromS3Command; -import com.cloud.agent.api.ManageSnapshotAnswer; -import com.cloud.agent.api.ManageSnapshotCommand; import com.cloud.agent.api.downloadSnapshotFromSwiftCommand; import com.cloud.agent.api.to.S3TO; import com.cloud.agent.api.to.SwiftTO; @@ -65,7 +66,6 @@ import com.cloud.event.EventTypes; import com.cloud.event.EventVO; import com.cloud.event.UsageEventUtils; import com.cloud.event.dao.EventDao; -import com.cloud.event.dao.UsageEventDao; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceAllocationException; @@ -75,7 +75,6 @@ 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.Snapshot; import com.cloud.storage.Snapshot.Type; @@ -83,7 +82,6 @@ import com.cloud.storage.SnapshotPolicyVO; import com.cloud.storage.SnapshotScheduleVO; import com.cloud.storage.SnapshotVO; import com.cloud.storage.Storage; -import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; import com.cloud.storage.VMTemplateVO; @@ -97,7 +95,6 @@ import com.cloud.storage.dao.SnapshotScheduleDao; import com.cloud.storage.dao.StoragePoolDao; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VolumeDao; -import com.cloud.storage.listener.SnapshotStateListener; import com.cloud.storage.s3.S3Manager; import com.cloud.storage.secondary.SecondaryStorageVmManager; import com.cloud.storage.swift.SwiftManager; @@ -123,14 +120,8 @@ import com.cloud.utils.db.Filter; import com.cloud.utils.db.JoinBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; -import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.utils.fsm.NoTransitionException; -import com.cloud.utils.fsm.StateMachine2; -import com.cloud.vm.UserVmVO; import com.cloud.vm.VMInstanceVO; -import com.cloud.vm.VirtualMachine; -import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.dao.UserVmDao; import org.apache.cloudstack.api.command.user.snapshot.CreateSnapshotPolicyCmd; @@ -189,8 +180,6 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, @Inject protected ClusterDao _clusterDao; @Inject - private UsageEventDao _usageEventDao; - @Inject private ResourceLimitService _resourceLimitMgr; @Inject private SwiftManager _swiftMgr; @@ -199,12 +188,8 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, @Inject private SecondaryStorageVmManager _ssvmMgr; @Inject - private ResourceManager _resourceMgr; - @Inject private DomainManager _domainMgr; @Inject - private VolumeDao _volumeDao; - @Inject private ResourceTagDao _resourceTagDao; @Inject private ConfigurationDao _configDao; @@ -216,21 +201,20 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, @Inject TemplateManager templateMgr; @Inject VolumeManager volumeMgr; @Inject DataStoreManager dataStoreMgr; + @Inject List snapshotStrategies; + @Inject VolumeDataFactory volFactory; + @Inject SnapshotDataFactory snapshotFactory; private int _totalRetries; private int _pauseInterval; - private int _deltaSnapshotMax; private int _backupsnapshotwait; - private StateMachine2 _snapshotFsm; - protected SearchBuilder PolicySnapshotSearch; protected SearchBuilder PoliciesForSnapSearch; - - - protected Answer sendToPool(Volume vol, Command cmd) { + @Override + public Answer sendToPool(Volume vol, Command cmd) { StoragePool pool = (StoragePool)dataStoreMgr.getPrimaryDataStore(vol.getPoolId()); long[] hostIdsToTryFirst = null; @@ -282,127 +266,11 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, return null; } - @Override - public SnapshotVO createSnapshotOnPrimary(VolumeVO volume, Long policyId, Long snapshotId) { - SnapshotVO snapshot = _snapshotDao.findById(snapshotId); - if (snapshot == null) { - throw new CloudRuntimeException("Can not find snapshot " + snapshotId); - } - - try { - stateTransitTo(snapshot, Snapshot.Event.CreateRequested); - } catch (NoTransitionException nte) { - s_logger.debug("Failed to update snapshot state due to " + nte.getMessage()); - } - - // Send a ManageSnapshotCommand to the agent - String vmName = this.volumeMgr.getVmNameOnVolume(volume); - long volumeId = volume.getId(); - long preId = _snapshotDao.getLastSnapshot(volumeId, snapshotId); - - String preSnapshotPath = null; - SnapshotVO preSnapshotVO = null; - if (preId != 0 && !(volume.getLastPoolId() != null && !volume.getLastPoolId().equals(volume.getPoolId()))) { - preSnapshotVO = _snapshotDao.findByIdIncludingRemoved(preId); - if (preSnapshotVO != null && preSnapshotVO.getBackupSnapshotId() != null) { - preSnapshotPath = preSnapshotVO.getPath(); - } - } - StoragePool srcPool = (StoragePool)dataStoreMgr.getPrimaryDataStore(volume.getPoolId()); - // RBD volumes do not support snapshotting in the way CloudStack does it. - // For now we leave the snapshot feature disabled for RBD volumes - if (srcPool.getPoolType() == StoragePoolType.RBD) { - throw new CloudRuntimeException("RBD volumes do not support snapshotting"); - } - - ManageSnapshotCommand cmd = new ManageSnapshotCommand(snapshotId, volume.getPath(), srcPool, preSnapshotPath, snapshot.getName(), vmName); - - ManageSnapshotAnswer answer = (ManageSnapshotAnswer) sendToPool(volume, cmd); - // Update the snapshot in the database - if ((answer != null) && answer.getResult()) { - // The snapshot was successfully created - if (preSnapshotPath != null && preSnapshotPath.equals(answer.getSnapshotPath())) { - // empty snapshot - s_logger.debug("CreateSnapshot: this is empty snapshot "); - try { - snapshot.setPath(preSnapshotPath); - snapshot.setBackupSnapshotId(preSnapshotVO.getBackupSnapshotId()); - snapshot.setSwiftId(preSnapshotVO.getSwiftId()); - snapshot.setPrevSnapshotId(preId); - snapshot.setSecHostId(preSnapshotVO.getSecHostId()); - stateTransitTo(snapshot, Snapshot.Event.OperationNotPerformed); - } catch (NoTransitionException nte) { - s_logger.debug("CreateSnapshot: failed to update state of snapshot due to " + nte.getMessage()); - } - } else { - long preSnapshotId = 0; - - if (preSnapshotVO != null && preSnapshotVO.getBackupSnapshotId() != null) { - preSnapshotId = preId; - // default delta snap number is 16 - 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; - volume.setLastPoolId(volume.getPoolId()); - _volumeDao.update(volume.getId(), volume); - } - snapshot = updateDBOnCreate(snapshotId, answer.getSnapshotPath(), preSnapshotId); - } - // Get the snapshot_schedule table entry for this snapshot and - // policy id. - // Set the snapshotId to retrieve it back later. - if (policyId != Snapshot.MANUAL_POLICY_ID) { - SnapshotScheduleVO snapshotSchedule = _snapshotScheduleDao.getCurrentSchedule(volumeId, policyId, true); - assert snapshotSchedule != null; - snapshotSchedule.setSnapshotId(snapshotId); - _snapshotScheduleDao.update(snapshotSchedule.getId(), snapshotSchedule); - } - - } else { - if (answer != null) { - s_logger.error(answer.getDetails()); - } - try { - stateTransitTo(snapshot, Snapshot.Event.OperationFailed); - } catch (NoTransitionException nte) { - s_logger.debug("Failed to update snapshot state due to " + nte.getMessage()); - } - throw new CloudRuntimeException("Creating snapshot for volume " + volumeId + " on primary storage failed."); - } - - return snapshot; - } - - public SnapshotVO createSnapshotImpl(long volumeId, long policyId) throws ResourceAllocationException { - return null; - } - @Override @DB @ActionEvent(eventType = EventTypes.EVENT_SNAPSHOT_CREATE, eventDescription = "creating snapshot", async = true) - public SnapshotVO createSnapshot(Long volumeId, Long policyId, Long snapshotId, Account snapshotOwner) { - VolumeVO volume = _volsDao.findById(volumeId); + public Snapshot createSnapshot(Long volumeId, Long policyId, Long snapshotId, Account snapshotOwner) { + VolumeInfo volume = this.volFactory.getVolume(volumeId); if (volume == null) { throw new InvalidParameterValueException("No such volume exist"); } @@ -411,120 +279,50 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, throw new InvalidParameterValueException("Volume is not in ready state"); } - SnapshotVO snapshot = null; + SnapshotInfo snapshot = null; boolean backedUp = false; - UserVmVO uservm = null; // 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); + SnapshotStrategy strategy = null; + for (SnapshotStrategy st : snapshotStrategies) { + if (st.canHandle(snap)) { + strategy = st; + break; + } + } + try { - - Long poolId = volume.getPoolId(); - if (poolId == null) { - throw new CloudRuntimeException("You cannot take a snapshot of a volume until it has been attached to an instance"); - } - - if (_volsDao.getHypervisorType(volume.getId()).equals(HypervisorType.KVM)) { - uservm = _vmDao.findById(volume.getInstanceId()); - if (uservm != null && uservm.getType() != VirtualMachine.Type.User) { - throw new CloudRuntimeException("Can't take a snapshot on system vm "); - } - - StoragePoolVO storagePool = _storagePoolDao.findById(volume.getPoolId()); - 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 is attached to a vm in taking vm snapshot; 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:" + volumeId + " 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"); - } */ - } - } - - snapshot = createSnapshotOnPrimary(volume, policyId, snapshotId); - if (snapshot != null) { - if (snapshot.getState() == Snapshot.State.CreatedOnPrimary) { - backedUp = backupSnapshotToSecondaryStorage(snapshot); - } else if (snapshot.getState() == Snapshot.State.BackedUp) { - // For empty snapshot we set status to BackedUp in createSnapshotOnPrimary - backedUp = true; - } else { - throw new CloudRuntimeException("Failed to create snapshot: " + snapshot + " on primary storage"); - } - if (!backedUp) { - throw new CloudRuntimeException("Created snapshot: " + snapshot + " on primary but failed to backup on secondary"); - } - } else { - throw new CloudRuntimeException("Failed to create snapshot: " + snapshot + " on primary storage"); - } - } finally { - // Cleanup jobs to do after the snapshot has been created; decrement resource count - if (snapshot != null) { - postCreateSnapshot(volumeId, snapshot.getId(), policyId, backedUp); - //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()); - } - if( !backedUp ) { - - } else { - _resourceLimitMgr.incrementResourceCount(snapshotOwner.getId(), ResourceType.snapshot); - } - } - - /* - try { - _storageMgr.stateTransitTo(volume, Volume.Event.OperationSucceeded); - } catch (NoTransitionException e) { - s_logger.debug("Failed to transit volume state: " + e.toString()); - }*/ + snapshot = strategy.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()); + } + _resourceLimitMgr.incrementResourceCount(snapshotOwner.getId(), ResourceType.snapshot); + } + + Boolean backup = Boolean.parseBoolean(this._configDao.getValue(Config.BackupSnapshotAferTakingSnapshot.toString())); + if (backup) { + this.backupSnapshot(snapshotId); + } + } catch(Exception e) { + s_logger.debug("Failed to create snapshot", e); + throw new CloudRuntimeException("Failed to create snapshot", e); } return snapshot; } - private SnapshotVO updateDBOnCreate(Long id, String snapshotPath, long preSnapshotId) { - SnapshotVO createdSnapshot = _snapshotDao.findByIdIncludingRemoved(id); - createdSnapshot.setPath(snapshotPath); - createdSnapshot.setPrevSnapshotId(preSnapshotId); - try { - stateTransitTo(createdSnapshot, Snapshot.Event.OperationSucceeded); - } catch (NoTransitionException nte) { - s_logger.debug("Faile to update state of snapshot due to " + nte.getMessage()); - } - return createdSnapshot; - } - - private static void checkObjectStorageConfiguration(SwiftTO swift, S3TO s3) { + private void checkObjectStorageConfiguration(SwiftTO swift, S3TO s3) { if (swift != null && s3 != null) { throw new CloudRuntimeException( @@ -533,26 +331,6 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, } - @Override - public void deleteSnapshotsForVolume (String secondaryStoragePoolUrl, Long dcId, Long accountId, Long volumeId ){ - SwiftTO swift = _swiftMgr.getSwiftTO(); - S3TO s3 = _s3Mgr.getS3TO(); - - checkObjectStorageConfiguration(swift, s3); - - DeleteSnapshotBackupCommand cmd = new DeleteSnapshotBackupCommand( - swift, s3, secondaryStoragePoolUrl, dcId, accountId, volumeId, - null, true); - try { - Answer ans = _agentMgr.sendToSSVM(dcId, cmd); - if ( ans == null || !ans.getResult() ) { - s_logger.warn("DeleteSnapshotBackupCommand failed due to " + ans.getDetails() + " volume id: " + volumeId); - } - } catch (Exception e) { - s_logger.warn("DeleteSnapshotBackupCommand failed due to" + e.toString() + " volume id: " + volumeId); - } - } - @Override public void deleteSnapshotsDirForVolume(String secondaryStoragePoolUrl, Long dcId, Long accountId, Long volumeId) { DeleteSnapshotsDirCommand cmd = new DeleteSnapshotsDirCommand(secondaryStoragePoolUrl, dcId, accountId, volumeId); @@ -566,6 +344,23 @@ 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); + } + + SnapshotStrategy strategy = null; + for (SnapshotStrategy st : snapshotStrategies) { + if (st.canHandle(snapshot)) { + strategy = st; + break; + } + } + + return strategy.backupSnapshot(snapshot); + } @Override public void downloadSnapshotsFromSwift(SnapshotVO ss) { @@ -652,133 +447,17 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, } } - + @Override - @DB - public boolean backupSnapshotToSecondaryStorage(SnapshotVO ss) { - long snapshotId = ss.getId(); - SnapshotVO snapshot = _snapshotDao.acquireInLockTable(snapshotId); - if (snapshot == null) { - throw new CloudRuntimeException("Can not acquire lock for snapshot: " + ss); - } - try { - try { - stateTransitTo(snapshot, Snapshot.Event.BackupToSecondary); - } catch (NoTransitionException nte) { - s_logger.debug("Failed to update the state of snapshot while backing up snapshot"); - } + public SnapshotVO getParentSnapshot(VolumeInfo volume, Snapshot snapshot) { + long preId = _snapshotDao.getLastSnapshot(volume.getId(), snapshot.getId()); - long volumeId = snapshot.getVolumeId(); - VolumeVO volume = _volsDao.lockRow(volumeId, true); - - Long dcId = volume.getDataCenterId(); - Long accountId = volume.getAccountId(); - - HostVO secHost = getSecHost(volumeId, volume.getDataCenterId()); - - String secondaryStoragePoolUrl = secHost.getStorageUrl(); - String snapshotUuid = snapshot.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(); - - checkObjectStorageConfiguration(swift, s3); - - long prevSnapshotId = snapshot.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(volume); - String vmName = this.volumeMgr.getVmNameOnVolume(volume); - StoragePool srcPool = (StoragePool)dataStoreMgr.getPrimaryDataStore(volume.getPoolId()); - BackupSnapshotCommand backupSnapshotCommand = new BackupSnapshotCommand(secondaryStoragePoolUrl, dcId, accountId, volumeId, snapshot.getId(), volume.getPath(), srcPool, snapshotUuid, - snapshot.getName(), prevSnapshotUuid, prevBackupUuid, isVolumeInactive, vmName, _backupsnapshotwait); - - if ( swift != null ) { - backupSnapshotCommand.setSwift(swift); - } else if (s3 != null) { - backupSnapshotCommand.setS3(s3); - } - - String backedUpSnapshotUuid = null; - // By default, assume failed. - boolean backedUp = false; - BackupSnapshotAnswer answer = (BackupSnapshotAnswer) sendToPool(volume, backupSnapshotCommand); - if (answer != null && answer.getResult()) { - backedUpSnapshotUuid = answer.getBackupSnapshotName(); - if (backedUpSnapshotUuid != null) { - backedUp = true; - } - } else if (answer != null) { - s_logger.error(answer.getDetails()); - } - // Update the status in all cases. - Transaction txn = Transaction.currentTxn(); - txn.start(); - - if (backedUp) { - if (backupSnapshotCommand.getSwift() != null ) { - snapshot.setSwiftId(swift.getId()); - snapshot.setBackupSnapshotId(backedUpSnapshotUuid); - } else if (backupSnapshotCommand.getS3() != null) { - snapshot.setS3Id(s3.getId()); - snapshot.setBackupSnapshotId(backedUpSnapshotUuid); - } else { - snapshot.setSecHostId(secHost.getId()); - snapshot.setBackupSnapshotId(backedUpSnapshotUuid); - } - if (answer.isFull()) { - snapshot.setPrevSnapshotId(0); - } - try { - stateTransitTo(snapshot, Snapshot.Event.OperationSucceeded); - } catch (NoTransitionException nte) { - s_logger.debug("Failed to update the state of snapshot while backing up snapshot"); - } - - } else { - try { - stateTransitTo(snapshot, Snapshot.Event.OperationFailed); - } catch (NoTransitionException nte) { - s_logger.debug("Failed to update the state of snapshot while backing up snapshot"); - } - s_logger.warn("Failed to back up snapshot on secondary storage, deleting the record from the DB"); - _snapshotDao.remove(snapshotId); - } - txn.commit(); - - return backedUp; - } finally { - if (snapshot != null) { - _snapshotDao.releaseFromLockTable(snapshotId); - } - } - - } - - private HostVO getSecHost(long volumeId, long dcId) { - Long id = _snapshotDao.getSecHostId(volumeId); - if ( id != null) { - return _hostDao.findById(id); - } - return this.templateMgr.getSecondaryStorageHost(dcId); + SnapshotVO preSnapshotVO = null; + if (preId != 0 && !(volume.getLastPoolId() != null && !volume.getLastPoolId().equals(volume.getPoolId()))) { + preSnapshotVO = _snapshotDao.findByIdIncludingRemoved(preId); + } + + return preSnapshotVO; } private Long getSnapshotUserId() { @@ -789,11 +468,15 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, return userId; } - @Override - @DB - public void postCreateSnapshot(Long volumeId, Long snapshotId, Long policyId, boolean backedUp) { + private void postCreateSnapshot(Long volumeId, Long snapshotId, Long policyId) { Long userId = getSnapshotUserId(); SnapshotVO snapshot = _snapshotDao.findById(snapshotId); + if (policyId != Snapshot.MANUAL_POLICY_ID) { + SnapshotScheduleVO snapshotSchedule = _snapshotScheduleDao.getCurrentSchedule(volumeId, policyId, true); + assert snapshotSchedule != null; + snapshotSchedule.setSnapshotId(snapshotId); + _snapshotScheduleDao.update(snapshotSchedule.getId(), snapshotSchedule); + } if (snapshot != null && snapshot.isRecursive()) { postCreateRecurringSnapshotForPolicy(userId, volumeId, snapshotId, policyId); @@ -803,7 +486,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, private void postCreateRecurringSnapshotForPolicy(long userId, long volumeId, long snapshotId, long policyId) { // Use count query SnapshotVO spstVO = _snapshotDao.findById(snapshotId); - Type type = spstVO.getType(); + Type type = spstVO.getRecurringType(); int maxSnaps = type.getMax(); List snaps = listSnapsforVolumeType(volumeId, type); @@ -815,7 +498,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, SnapshotVO oldestSnapshot = snaps.get(0); long oldSnapId = oldestSnapshot.getId(); s_logger.debug("Max snaps: " + policy.getMaxSnaps() + " exceeded for snapshot policy with Id: " + policyId + ". Deleting oldest snapshot: " + oldSnapId); - if(deleteSnapshotInternal(oldSnapId)){ + if(deleteSnapshot(oldSnapId)){ //log Snapshot delete event ActionEventUtils.onCompletedActionEvent(User.UID_SYSTEM, oldestSnapshot.getAccountId(), EventVO.LEVEL_INFO, EventTypes.EVENT_SNAPSHOT_DELETE, "Successfully deleted oldest snapshot: " + oldSnapId, 0); } @@ -830,98 +513,38 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, Account caller = UserContext.current().getCaller(); // Verify parameters - Snapshot snapshotCheck = _snapshotDao.findById(snapshotId); + SnapshotInfo snapshotCheck = this.snapshotFactory.getSnapshot(snapshotId); if (snapshotCheck == null) { throw new InvalidParameterValueException("unable to find a snapshot with id " + snapshotId); } _accountMgr.checkAccess(caller, null, true, snapshotCheck); - if( !Snapshot.State.BackedUp.equals(snapshotCheck.getState() ) ) { - throw new InvalidParameterValueException("Can't delete snapshotshot " + snapshotId + " due to it is not in BackedUp Status"); + SnapshotStrategy strategy = null; + for (SnapshotStrategy st : snapshotStrategies) { + if (st.canHandle(snapshotCheck)) { + strategy = st; + break; + } + } + try { + boolean result = strategy.deleteSnapshot(snapshotCheck); + if (result) { + if (snapshotCheck.getState() == Snapshot.State.BackedUp) { + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SNAPSHOT_DELETE, snapshotCheck.getAccountId(), + snapshotCheck.getDataCenterId(), snapshotId, snapshotCheck.getName(), null, null, 0L, + snapshotCheck.getClass().getName(), snapshotCheck.getUuid()); + } + _resourceLimitMgr.decrementResourceCount(snapshotCheck.getAccountId(), ResourceType.snapshot); + } + return result; + } catch (Exception e) { + s_logger.debug("Failed to delete snapshot: " + snapshotCheck.getId() + ":" + e.toString()); + throw new CloudRuntimeException("Failed to delete snapshot:" + e.toString()); } - - return deleteSnapshotInternal(snapshotId); } - @DB - private boolean deleteSnapshotInternal(Long snapshotId) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Calling deleteSnapshot for snapshotId: " + snapshotId); - } - SnapshotVO lastSnapshot = null; - SnapshotVO snapshot = _snapshotDao.findById(snapshotId); - if (snapshot.getBackupSnapshotId() != null) { - List snaps = _snapshotDao.listByBackupUuid(snapshot.getVolumeId(), snapshot.getBackupSnapshotId()); - if (snaps != null && snaps.size() > 1) { - snapshot.setBackupSnapshotId(null); - _snapshotDao.update(snapshot.getId(), snapshot); - } - } - - Transaction txn = Transaction.currentTxn(); - txn.start(); - _snapshotDao.remove(snapshotId); - if (snapshot.getState() == Snapshot.State.BackedUp) { - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SNAPSHOT_DELETE, snapshot.getAccountId(), - snapshot.getDataCenterId(), snapshotId, snapshot.getName(), null, null, 0L, - snapshot.getClass().getName(), snapshot.getUuid()); - } - _resourceLimitMgr.decrementResourceCount(snapshot.getAccountId(), ResourceType.snapshot); - txn.commit(); - - 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(lastId)) { - - } else { - s_logger.debug("Destroying snapshot backup failed " + lastSnapshot); - break; - } - } - } - lastId = lastSnapshot.getPrevSnapshotId(); - if (lastId == 0) { - break; - } - lastSnapshot = _snapshotDao.findByIdIncludingRemoved(lastId); - } - } - return true; - } - - @Override - @DB - public boolean destroySnapshot(long userId, long snapshotId, long policyId) { - return true; - } - - - @Override - public HostVO getSecondaryStorageHost(SnapshotVO snapshot) { + private HostVO getSecondaryStorageHost(SnapshotVO snapshot) { HostVO secHost = null; if( snapshot.getSwiftId() == null || snapshot.getSwiftId() == 0) { secHost = _hostDao.findById(snapshot.getSecHostId()); @@ -941,51 +564,6 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, throw new CloudRuntimeException("Can not find secondary storage"); } - @Override - @DB - public boolean destroySnapshotBackUp(long snapshotId) { - boolean success = false; - String details; - SnapshotVO snapshot = _snapshotDao.findByIdIncludingRemoved(snapshotId); - if (snapshot == null) { - throw new CloudRuntimeException("Destroying snapshot " + snapshotId + " backup failed due to unable to find snapshot "); - } - String secondaryStoragePoolUrl = getSecondaryStorageURL(snapshot); - Long dcId = snapshot.getDataCenterId(); - Long accountId = snapshot.getAccountId(); - Long volumeId = snapshot.getVolumeId(); - - String backupOfSnapshot = snapshot.getBackupSnapshotId(); - if (backupOfSnapshot == null) { - return true; - } - SwiftTO swift = _swiftMgr.getSwiftTO(snapshot.getSwiftId()); - S3TO s3 = _s3Mgr.getS3TO(); - - checkObjectStorageConfiguration(swift, s3); - - DeleteSnapshotBackupCommand cmd = new DeleteSnapshotBackupCommand( - swift, s3, secondaryStoragePoolUrl, dcId, accountId, volumeId, - backupOfSnapshot, false); - Answer answer = _agentMgr.sendToSSVM(dcId, cmd); - - if ((answer != null) && answer.getResult()) { - snapshot.setBackupSnapshotId(null); - _snapshotDao.update(snapshotId, snapshot); - success = true; - details = "Successfully deleted snapshot " + snapshotId + " for volumeId: " + volumeId; - s_logger.debug(details); - } else if (answer != null) { - details = "Failed to destroy snapshot id:" + snapshotId + " for volume: " + volumeId + " due to "; - if (answer.getDetails() != null) { - details += answer.getDetails(); - } - s_logger.error(details); - } - return success; - - } - @Override public Pair, Integer> listSnapshots(ListSnapshotsCmd cmd) { Long volumeId = cmd.getVolumeId(); @@ -1161,7 +739,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, List snapshots = listSnapsforVolume(volumeId); for (SnapshotVO snapshot : snapshots) { if (_snapshotDao.expunge(snapshot.getId())) { - if (snapshot.getType() == Type.MANUAL) { + if (snapshot.getRecurringType() == Type.MANUAL) { _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.snapshot); } @@ -1271,8 +849,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, return policy; } - @Override - public boolean deletePolicy(long userId, Long policyId) { + protected boolean deletePolicy(long userId, Long policyId) { SnapshotPolicyVO snapshotPolicy = _snapshotPolicyDao.findById(policyId); _snapSchedMgr.removeSchedule(snapshotPolicy.getVolumeId(), snapshotPolicy.getId()); return _snapshotPolicyDao.remove(policyId); @@ -1290,31 +867,16 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, return new Pair, Integer>(result.first(), result.second()); } - @Override - public List listPoliciesforVolume(long volumeId) { + + private List listPoliciesforVolume(long volumeId) { return _snapshotPolicyDao.listByVolumeId(volumeId); } - - @Override - public List listPoliciesforSnapshot(long snapshotId) { - SearchCriteria sc = PoliciesForSnapSearch.create(); - sc.setJoinParameters("policyRef", "snapshotId", snapshotId); - return _snapshotPolicyDao.search(sc, null); - } - - @Override - public List listSnapsforPolicy(long policyId, Filter filter) { - SearchCriteria sc = PolicySnapshotSearch.create(); - sc.setJoinParameters("policy", "policyId", policyId); - return _snapshotDao.search(sc, filter); - } - - @Override - public List listSnapsforVolume(long volumeId) { + + private List listSnapsforVolume(long volumeId) { return _snapshotDao.listByVolumeId(volumeId); } - public List listSnapsforVolumeType(long volumeId, Type type) { + private List listSnapsforVolumeType(long volumeId, Type type) { return _snapshotDao.listByVolumeIdType(volumeId, type); } @@ -1333,9 +895,6 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, } } - /** - * {@inheritDoc} - */ @Override public List findRecurringSnapshotSchedule(ListRecurringSnapshotScheduleCmd cmd) { Long volumeId = cmd.getVolumeId(); @@ -1374,12 +933,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, return snapshotSchedules; } - @Override - public SnapshotPolicyVO getPolicyForVolume(long volumeId) { - return _snapshotPolicyDao.findOneByVolume(volumeId); - } - - public Type getSnapshotType(Long policyId) { + private Type getSnapshotType(Long policyId) { if (policyId.equals(Snapshot.MANUAL_POLICY_ID)) { return Type.MANUAL; } else { @@ -1389,7 +943,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, } } - public Type getSnapshotType(IntervalType intvType) { + private Type getSnapshotType(IntervalType intvType) { if (intvType.equals(IntervalType.HOURLY)) { return Type.HOURLY; } else if (intvType.equals(IntervalType.DAILY)) { @@ -1489,15 +1043,11 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, Type.DAILY.setMax(NumbersUtil.parseInt(_configDao.getValue("snapshot.max.daily"), DAILYMAX)); Type.WEEKLY.setMax(NumbersUtil.parseInt(_configDao.getValue("snapshot.max.weekly"), WEEKLYMAX)); Type.MONTHLY.setMax(NumbersUtil.parseInt(_configDao.getValue("snapshot.max.monthly"), MONTHLYMAX)); - _deltaSnapshotMax = NumbersUtil.parseInt(_configDao.getValue("snapshot.delta.max"), DELTAMAX); _totalRetries = NumbersUtil.parseInt(_configDao.getValue("total.retries"), 4); _pauseInterval = 2 * NumbersUtil.parseInt(_configDao.getValue("ping.interval"), 60); s_logger.info("Snapshot Manager is configured."); - _snapshotFsm = Snapshot.State.getStateMachine(); - _snapshotFsm.registerListener(new SnapshotStateListener()); - return true; } @@ -1558,24 +1108,6 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, return success; } - - 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; - } @Override public boolean canOperateOnVolume(Volume volume) { @@ -1586,8 +1118,4 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, } return true; } - - protected boolean stateTransitTo(Snapshot snapshot, Snapshot.Event e) throws NoTransitionException { - return _snapshotFsm.transitTo(snapshot, e, null, _snapshotDao); - } }