diff --git a/api/src/com/cloud/storage/snapshot/SnapshotService.java b/api/src/com/cloud/storage/snapshot/SnapshotApiService.java similarity index 99% rename from api/src/com/cloud/storage/snapshot/SnapshotService.java rename to api/src/com/cloud/storage/snapshot/SnapshotApiService.java index b5325f52080..23e65220ff9 100644 --- a/api/src/com/cloud/storage/snapshot/SnapshotService.java +++ b/api/src/com/cloud/storage/snapshot/SnapshotApiService.java @@ -31,7 +31,7 @@ import com.cloud.storage.Volume; import com.cloud.user.Account; import com.cloud.utils.Pair; -public interface SnapshotService { +public interface SnapshotApiService { /** * List all snapshots of a disk volume. Optionally lists snapshots created by specified interval diff --git a/api/src/org/apache/cloudstack/api/BaseCmd.java b/api/src/org/apache/cloudstack/api/BaseCmd.java index b845053e470..46a9dfc1e24 100644 --- a/api/src/org/apache/cloudstack/api/BaseCmd.java +++ b/api/src/org/apache/cloudstack/api/BaseCmd.java @@ -64,7 +64,7 @@ import com.cloud.server.TaggedResourceService; import com.cloud.storage.DataStoreProviderApiService; import com.cloud.storage.StorageService; import com.cloud.storage.VolumeApiService; -import com.cloud.storage.snapshot.SnapshotService; +import com.cloud.storage.snapshot.SnapshotApiService; import com.cloud.template.TemplateApiService; import com.cloud.user.Account; import com.cloud.user.AccountService; @@ -108,7 +108,7 @@ public abstract class BaseCmd { @Inject public NetworkService _networkService; @Inject public TemplateApiService _templateService; @Inject public SecurityGroupService _securityGroupService; - @Inject public SnapshotService _snapshotService; + @Inject public SnapshotApiService _snapshotService; @Inject public ConsoleProxyService _consoleProxyService; @Inject public VpcVirtualNetworkApplianceService _routerService; @Inject public ResponseGenerator _responseGenerator; diff --git a/core/src/com/cloud/storage/resource/NfsSecondaryStorageResource.java b/core/src/com/cloud/storage/resource/NfsSecondaryStorageResource.java index e8abcbf1a67..3be9ced822c 100755 --- a/core/src/com/cloud/storage/resource/NfsSecondaryStorageResource.java +++ b/core/src/com/cloud/storage/resource/NfsSecondaryStorageResource.java @@ -48,7 +48,7 @@ import java.util.concurrent.Callable; import javax.naming.ConfigurationException; import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; -import org.apache.cloudstack.storage.command.CopyCmd; +import org.apache.cloudstack.storage.command.CopyCommand; import org.apache.log4j.Logger; import com.cloud.agent.api.Answer; @@ -215,14 +215,14 @@ SecondaryStorageResource { return execute((DeleteTemplateFromS3Command) cmd); } else if (cmd instanceof CleanupSnapshotBackupCommand){ return execute((CleanupSnapshotBackupCommand)cmd); - } else if (cmd instanceof CopyCmd) { - return execute((CopyCmd)cmd); + } else if (cmd instanceof CopyCommand) { + return execute((CopyCommand)cmd); } else { return Answer.createUnsupportedCommandAnswer(cmd); } } - protected Answer downloadFromS3ToNfs(CopyCmd cmd, DataTO srcData, S3TO s3, + protected Answer downloadFromS3ToNfs(CopyCommand cmd, DataTO srcData, S3TO s3, DataTO destData, NfsTO destImageStore) { final String storagePath = destImageStore.getUrl(); final String destPath = destData.getPath(); @@ -265,12 +265,12 @@ SecondaryStorageResource { } } - protected Answer downloadFromSwiftToNfs(CopyCmd cmd, DataTO srcData, SwiftTO srcImageStore, + protected Answer downloadFromSwiftToNfs(CopyCommand cmd, DataTO srcData, SwiftTO srcImageStore, DataTO destData, NfsTO destImageStore) { return Answer.createUnsupportedCommandAnswer(cmd); } - protected Answer execute(CopyCmd cmd) { + protected Answer execute(CopyCommand cmd) { DataTO srcData = cmd.getSrcTO(); DataTO destData = cmd.getDestTO(); DataStoreTO srcDataStore = srcData.getDataStore(); 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/SnapshotService.java similarity index 92% rename from engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotStrategy.java rename to engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotService.java index e9492c4afc6..68dc55f9a5c 100644 --- a/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotStrategy.java +++ b/engine/api/src/org/apache/cloudstack/engine/subsystem/api/storage/SnapshotService.java @@ -18,8 +18,7 @@ package org.apache.cloudstack.engine.subsystem.api.storage; -public interface SnapshotStrategy { - public boolean canHandle(SnapshotInfo snapshot); +public interface SnapshotService { public SnapshotInfo takeSnapshot(VolumeInfo volume, Long snapshotId); public SnapshotInfo backupSnapshot(SnapshotInfo snapshot); public boolean deleteSnapshot(SnapshotInfo snapshot); diff --git a/engine/api/src/org/apache/cloudstack/storage/command/CopyCmd.java b/engine/api/src/org/apache/cloudstack/storage/command/CopyCommand.java similarity index 91% rename from engine/api/src/org/apache/cloudstack/storage/command/CopyCmd.java rename to engine/api/src/org/apache/cloudstack/storage/command/CopyCommand.java index 9cb225b762f..fb280346793 100644 --- a/engine/api/src/org/apache/cloudstack/storage/command/CopyCmd.java +++ b/engine/api/src/org/apache/cloudstack/storage/command/CopyCommand.java @@ -20,7 +20,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; import com.cloud.agent.api.Command; -public class CopyCmd extends Command implements StorageSubSystemCommand { +public class CopyCommand extends Command implements StorageSubSystemCommand { private DataTO srcTO; private DataTO destTO; private int timeout; @@ -39,7 +39,7 @@ public class CopyCmd extends Command implements StorageSubSystemCommand { this.timeout = timeout; } - public CopyCmd(DataTO srcUri, DataTO destUri, int timeout) { + public CopyCommand(DataTO srcUri, DataTO destUri, int timeout) { super(); this.srcTO = srcUri; this.destTO = destUri; diff --git a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java index e72bb54ddec..8274fd75638 100644 --- a/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java +++ b/engine/storage/datamotion/src/org/apache/cloudstack/storage/motion/AncientDataMotionStrategy.java @@ -35,7 +35,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import org.apache.cloudstack.engine.subsystem.api.storage.StorageCacheManager; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; -import org.apache.cloudstack.storage.command.CopyCmd; +import org.apache.cloudstack.storage.command.CopyCommand; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.log4j.Logger; @@ -194,13 +194,13 @@ public class AncientDataMotionStrategy implements DataMotionStrategy { if (srcData.getDataStore().getRole() != DataStoreRole.ImageCache && destData.getDataStore().getRole() != DataStoreRole.ImageCache) { //need to copy it to image cache store DataObject cacheData = cacheMgr.createCacheObject(srcData, destData.getDataStore().getScope()); - CopyCmd cmd = new CopyCmd(cacheData.getTO(), destData.getTO(), _primaryStorageDownloadWait); + CopyCommand cmd = new CopyCommand(cacheData.getTO(), destData.getTO(), _primaryStorageDownloadWait); EndPoint ep = selector.select(cacheData, destData); Answer answer = ep.sendMessage(cmd); return answer; } else { //handle copy it to cache store - CopyCmd cmd = new CopyCmd(srcData.getTO(), destData.getTO(), _primaryStorageDownloadWait); + CopyCommand cmd = new CopyCommand(srcData.getTO(), destData.getTO(), _primaryStorageDownloadWait); EndPoint ep = selector.select(srcData, destData); Answer answer = ep.sendMessage(cmd); return answer; diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/DirectAgentManagerSimpleImpl.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/DirectAgentManagerSimpleImpl.java index 0725971ab08..c5b5883e9a3 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/DirectAgentManagerSimpleImpl.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/DirectAgentManagerSimpleImpl.java @@ -187,19 +187,6 @@ public class DirectAgentManagerSimpleImpl extends ManagerBase implements AgentMa return null; } - - @Override - public void sendToSecStorage(DataStore ssStore, Command cmd, Listener listener) throws AgentUnavailableException { - // TODO Auto-generated method stub - - } - - @Override - public Answer sendToSecStorage(DataStore ssStore, Command cmd) { - // TODO Auto-generated method stub - return null; - } - @Override public boolean tapLoadingAgents(Long hostId, TapAgentsAction action) { // TODO Auto-generated method stub diff --git a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/SnapshotServiceImpl.java index 1b64fd0cae3..7eee7720330 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 @@ -14,29 +14,571 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. + package org.apache.cloudstack.storage.snapshot; -import org.apache.cloudstack.engine.cloud.entity.api.SnapshotEntity; +import 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.DataMotionService; +import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; +import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; +import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; +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.SnapshotService; +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.datastore.db.PrimaryDataStoreDao; +import org.apache.log4j.Logger; import org.springframework.stereotype.Component; +import com.cloud.agent.api.BackupSnapshotAnswer; +import com.cloud.configuration.dao.ConfigurationDao; +import com.cloud.dc.ClusterVO; +import com.cloud.dc.dao.ClusterDao; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.host.HostVO; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.resource.ResourceManager; +import com.cloud.storage.DataStoreRole; +import com.cloud.storage.Snapshot; +import com.cloud.storage.SnapshotVO; +import com.cloud.storage.StoragePool; +import com.cloud.storage.VolumeManager; +import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.SnapshotDao; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.storage.snapshot.SnapshotManager; +import com.cloud.utils.NumbersUtil; +import com.cloud.utils.db.DB; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.fsm.NoTransitionException; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.VirtualMachine.State; +import com.cloud.vm.dao.UserVmDao; +import com.cloud.vm.snapshot.VMSnapshot; +import com.cloud.vm.snapshot.VMSnapshotVO; +import com.cloud.vm.snapshot.dao.VMSnapshotDao; + @Component public class SnapshotServiceImpl implements SnapshotService { - - public SnapshotServiceImpl() { - + private static final Logger s_logger = Logger.getLogger(SnapshotServiceImpl.class); + @Inject + protected VolumeDao _volsDao; + @Inject + protected UserVmDao _vmDao; + @Inject + protected PrimaryDataStoreDao _storagePoolDao; + @Inject + protected ClusterDao _clusterDao; + @Inject + protected SnapshotDao _snapshotDao; + @Inject + private ResourceManager _resourceMgr; + @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; + @Inject + VMSnapshotDao _vmSnapshotDao; + + + + + 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 = null; + if (preSnapshotVO != null) { + preSnapshotPath = preSnapshotVO.getPath(); + } + SnapshotVO snapshotVO = this._snapshotDao.findById(snapshot.getId()); + // The snapshot was successfully created + if (preSnapshotPath != null && preSnapshotPath.equals(result.getPath())) { + // empty snapshot + s_logger.debug("CreateSnapshot: this is empty snapshot "); + + snapshotVO.setPath(preSnapshotPath); + snapshotVO.setBackupSnapshotId(preSnapshotVO.getBackupSnapshotId()); + snapshotVO.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()); + try { + snapshot.processEvent(Snapshot.Event.OperationFailed); + } catch (NoTransitionException e1) { + s_logger.debug("Failed to change snapshot state: " + e1.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(); + try { + 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); + } catch (Exception e) { + s_logger.debug("Failed to take snapshot: " + snapshot.getId(), e); + try { + snapshot.processEvent(Snapshot.Event.OperationFailed); + } catch (NoTransitionException e1) { + s_logger.debug("Failed to change state for event: OperationFailed" , e); + } + throw new CloudRuntimeException("Failed to take snapshot" + snapshot.getId()); + } + + 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 SnapshotEntity getSnapshotEntity(long snapshotId) { - // TODO Auto-generated method stub + 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.getImageStoresByScope(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()); + try { + snapObj.processEvent(Snapshot.Event.OperationFailed); + } catch (NoTransitionException e1) { + s_logger.debug("Failed to change state: " + e1.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.getId(), 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 takeSnapshot(SnapshotInfo snapshot) { - // TODO Auto-generated method stub - return false; + 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 @@ -45,12 +587,4 @@ public class SnapshotServiceImpl implements SnapshotService { 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/AncientSnapshotStrategy.java b/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/strategy/AncientSnapshotStrategy.java deleted file mode 100644 index 944c477d2c7..00000000000 --- a/engine/storage/snapshot/src/org/apache/cloudstack/storage/snapshot/strategy/AncientSnapshotStrategy.java +++ /dev/null @@ -1,597 +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 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.DataMotionService; -import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectInStore; -import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType; -import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; -import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; -import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; -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.datastore.db.PrimaryDataStoreDao; -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.BackupSnapshotAnswer; -import com.cloud.configuration.dao.ConfigurationDao; -import com.cloud.dc.ClusterVO; -import com.cloud.dc.dao.ClusterDao; -import com.cloud.exception.InvalidParameterValueException; -import com.cloud.host.HostVO; -import com.cloud.hypervisor.Hypervisor.HypervisorType; -import com.cloud.resource.ResourceManager; -import com.cloud.storage.DataStoreRole; -import com.cloud.storage.Snapshot; -import com.cloud.storage.SnapshotVO; -import com.cloud.storage.StoragePool; -import com.cloud.storage.VolumeManager; -import com.cloud.storage.VolumeVO; -import com.cloud.storage.dao.SnapshotDao; -import com.cloud.storage.dao.VolumeDao; -import com.cloud.storage.snapshot.SnapshotManager; -import com.cloud.utils.NumbersUtil; -import com.cloud.utils.db.DB; -import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.utils.fsm.NoTransitionException; -import com.cloud.vm.UserVmVO; -import com.cloud.vm.VirtualMachine.State; -import com.cloud.vm.dao.UserVmDao; -import com.cloud.vm.snapshot.VMSnapshot; -import com.cloud.vm.snapshot.VMSnapshotVO; -import com.cloud.vm.snapshot.dao.VMSnapshotDao; - -@Component -public class AncientSnapshotStrategy implements SnapshotStrategy { - private static final Logger s_logger = Logger.getLogger(AncientSnapshotStrategy.class); - @Inject - protected VolumeDao _volsDao; - @Inject - protected UserVmDao _vmDao; - @Inject - protected PrimaryDataStoreDao _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; - @Inject - VMSnapshotDao _vmSnapshotDao; - - - @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 = null; - if (preSnapshotVO != null) { - preSnapshotPath = preSnapshotVO.getPath(); - } - SnapshotVO snapshotVO = this.snapshotDao.findById(snapshot.getId()); - // The snapshot was successfully created - if (preSnapshotPath != null && preSnapshotPath.equals(result.getPath())) { - // empty snapshot - s_logger.debug("CreateSnapshot: this is empty snapshot "); - - snapshotVO.setPath(preSnapshotPath); - snapshotVO.setBackupSnapshotId(preSnapshotVO.getBackupSnapshotId()); - snapshotVO.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()); - try { - snapshot.processEvent(Snapshot.Event.OperationFailed); - } catch (NoTransitionException e1) { - s_logger.debug("Failed to change snapshot state: " + e1.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(); - try { - 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); - } catch (Exception e) { - s_logger.debug("Failed to take snapshot: " + snapshot.getId(), e); - try { - snapshot.processEvent(Snapshot.Event.OperationFailed); - } catch (NoTransitionException e1) { - s_logger.debug("Failed to change state for event: OperationFailed" , e); - } - throw new CloudRuntimeException("Failed to take snapshot" + snapshot.getId()); - } - - 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.getImageStoresByScope(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()); - try { - snapObj.processEvent(Snapshot.Event.OperationFailed); - } catch (NoTransitionException e1) { - s_logger.debug("Failed to change state: " + e1.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.getId(), 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/src/org/apache/cloudstack/storage/LocalHostEndpoint.java b/engine/storage/src/org/apache/cloudstack/storage/LocalHostEndpoint.java index f08a5977b2e..9fe0c552850 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/LocalHostEndpoint.java +++ b/engine/storage/src/org/apache/cloudstack/storage/LocalHostEndpoint.java @@ -6,7 +6,7 @@ import java.util.concurrent.TimeUnit; import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; import org.apache.cloudstack.framework.async.AsyncCompletionCallback; -import org.apache.cloudstack.storage.command.CopyCmd; +import org.apache.cloudstack.storage.command.CopyCommand; import com.cloud.agent.Listener; import com.cloud.agent.api.Answer; @@ -32,7 +32,7 @@ public class LocalHostEndpoint implements EndPoint { @Override public Answer sendMessage(Command cmd) { - if (cmd instanceof CopyCmd) { + if (cmd instanceof CopyCommand) { return resource.executeRequest(cmd); } // TODO Auto-generated method stub diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageResource.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageResource.java index e7c009b00b1..300daa597a2 100644 --- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageResource.java +++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServerStorageResource.java @@ -34,7 +34,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.DataObjectType; import org.apache.cloudstack.engine.subsystem.api.storage.DataTO; import org.apache.cloudstack.storage.command.AttachPrimaryDataStoreAnswer; import org.apache.cloudstack.storage.command.AttachPrimaryDataStoreCmd; -import org.apache.cloudstack.storage.command.CopyCmd; +import org.apache.cloudstack.storage.command.CopyCommand; import org.apache.cloudstack.storage.command.CopyCmdAnswer; import org.apache.cloudstack.storage.command.CreateObjectAnswer; import org.apache.cloudstack.storage.command.CreateObjectCommand; @@ -84,8 +84,8 @@ public class XenServerStorageResource { } public Answer handleStorageCommands(StorageSubSystemCommand command) { - if (command instanceof CopyCmd) { - return this.execute((CopyCmd)command); + if (command instanceof CopyCommand) { + return this.execute((CopyCommand)command); } else if (command instanceof AttachPrimaryDataStoreCmd) { return this.execute((AttachPrimaryDataStoreCmd)command); } else if (command instanceof CreatePrimaryDataStoreCmd) { @@ -505,7 +505,7 @@ public class XenServerStorageResource { } - protected Answer directDownloadHttpTemplate(CopyCmd cmd, DecodedDataObject srcObj, DecodedDataObject destObj) { + protected Answer directDownloadHttpTemplate(CopyCommand cmd, DecodedDataObject srcObj, DecodedDataObject destObj) { Connection conn = hypervisorResource.getConnection(); SR poolsr = null; VDI vdi = null; @@ -724,7 +724,7 @@ public class XenServerStorageResource { } - protected Answer execute(CopyCmd cmd) { + protected Answer execute(CopyCommand cmd) { DataTO srcData = cmd.getSrcTO(); DataTO destData = cmd.getDestTO(); diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java index 3a453d50a72..88bcc6ac19d 100755 --- a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -34,7 +34,7 @@ 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.SnapshotService; 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.PrimaryDataStoreDao; @@ -126,8 +126,8 @@ import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.snapshot.dao.VMSnapshotDao; @Component -@Local(value = { SnapshotManager.class, SnapshotService.class }) -public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, SnapshotService { +@Local(value = { SnapshotManager.class, SnapshotApiService.class }) +public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, SnapshotApiService { private static final Logger s_logger = Logger.getLogger(SnapshotManagerImpl.class); @Inject protected VMTemplateDao _templateDao; @@ -171,9 +171,9 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, private ResourceLimitService _resourceLimitMgr; @Inject private SwiftManager _swiftMgr; - @Inject + @Inject private S3Manager _s3Mgr; - @Inject + @Inject private SecondaryStorageVmManager _ssvmMgr; @Inject private DomainManager _domainMgr; @@ -182,17 +182,17 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, @Inject private ConfigurationDao _configDao; - @Inject + @Inject private VMSnapshotDao _vmSnapshotDao; String _name; @Inject TemplateManager templateMgr; @Inject VolumeManager volumeMgr; @Inject DataStoreManager dataStoreMgr; - @Inject List snapshotStrategies; + @Inject SnapshotService snapshotSrv; @Inject VolumeDataFactory volFactory; @Inject SnapshotDataFactory snapshotFactory; - + private int _totalRetries; private int _pauseInterval; @@ -201,14 +201,14 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, protected SearchBuilder PolicySnapshotSearch; protected SearchBuilder PoliciesForSnapSearch; - + @Override public Answer sendToPool(Volume vol, Command cmd) { StoragePool pool = (StoragePool)dataStoreMgr.getPrimaryDataStore(vol.getPoolId()); long[] hostIdsToTryFirst = null; - + Long vmHostId = getHostIdForSnapshotOperation(vol); - + if (vmHostId != null) { hostIdsToTryFirst = new long[] { vmHostId }; } @@ -263,29 +263,22 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, if (volume == null) { throw new InvalidParameterValueException("No such volume exist"); } - + if (volume.getState() != Volume.State.Ready) { throw new InvalidParameterValueException("Volume is not in ready state"); } - + SnapshotInfo snapshot = null; - + boolean backedUp = false; // does the caller have the authority to act on this volume _accountMgr.checkAccess(UserContext.current().getCaller(), null, true, volume); - + SnapshotInfo snap = this.snapshotFactory.getSnapshot(snapshotId); - SnapshotStrategy strategy = null; - for (SnapshotStrategy st : snapshotStrategies) { - if (st.canHandle(snap)) { - strategy = st; - break; - } - } try { - snapshot = strategy.takeSnapshot(volume, snapshotId); + snapshot = this.snapshotSrv.takeSnapshot(volume, snapshotId); if (snapshot != null) { postCreateSnapshot(volumeId, snapshot.getId(), policyId); //Check if the snapshot was removed while backingUp. If yes, do not log snapshot create usage event @@ -344,20 +337,13 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, if (snapshot == null) { throw new CloudRuntimeException("Can't find snapshot:" + snapshotId); } - + if (snapshot.getState() == Snapshot.State.BackedUp) { return snapshot; } - - SnapshotStrategy strategy = null; - for (SnapshotStrategy st : snapshotStrategies) { - if (st.canHandle(snapshot)) { - strategy = st; - break; - } - } - - return strategy.backupSnapshot(snapshot); + + + return this.snapshotSrv.backupSnapshot(snapshot); } @Override @@ -394,7 +380,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, } catch (Exception e) { throw new CloudRuntimeException("downloadSnapshotsFromSwift failed due to " + e.toString()); } - + } private List determineBackupUuids(final SnapshotVO snapshot) { @@ -445,7 +431,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, } } - + @Override public SnapshotVO getParentSnapshot(VolumeInfo volume, Snapshot snapshot) { long preId = _snapshotDao.getLastSnapshot(volume.getId(), snapshot.getId()); @@ -454,7 +440,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, if (preId != 0 && !(volume.getLastPoolId() != null && !volume.getLastPoolId().equals(volume.getPoolId()))) { preSnapshotVO = _snapshotDao.findByIdIncludingRemoved(preId); } - + return preSnapshotVO; } @@ -475,7 +461,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, snapshotSchedule.setSnapshotId(snapshotId); _snapshotScheduleDao.update(snapshotSchedule.getId(), snapshotSchedule); } - + if (snapshot != null && snapshot.isRecursive()) { postCreateRecurringSnapshotForPolicy(userId, volumeId, snapshotId, policyId); } @@ -515,18 +501,11 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, if (snapshotCheck == null) { throw new InvalidParameterValueException("unable to find a snapshot with id " + snapshotId); } - + _accountMgr.checkAccess(caller, null, true, snapshotCheck); - - SnapshotStrategy strategy = null; - for (SnapshotStrategy st : snapshotStrategies) { - if (st.canHandle(snapshotCheck)) { - strategy = st; - break; - } - } + try { - boolean result = strategy.deleteSnapshot(snapshotCheck); + boolean result = this.snapshotSrv.deleteSnapshot(snapshotCheck); if (result) { if (snapshotCheck.getState() == Snapshot.State.BackedUp) { UsageEventUtils.publishUsageEvent(EventTypes.EVENT_SNAPSHOT_DELETE, snapshotCheck.getAccountId(), @@ -573,7 +552,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, String snapshotTypeStr = cmd.getSnapshotType(); String intervalTypeStr = cmd.getIntervalType(); Map tags = cmd.getTags(); - + Account caller = UserContext.current().getCaller(); List permittedAccounts = new ArrayList(); @@ -589,8 +568,8 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, _accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccounts, domainIdRecursiveListProject, cmd.listAll(), false); Long domainId = domainIdRecursiveListProject.first(); Boolean isRecursive = domainIdRecursiveListProject.second(); - ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third(); - + ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third(); + Filter searchFilter = new Filter(SnapshotVO.class, "created", false, cmd.getStartIndex(), cmd.getPageSizeVal()); SearchBuilder sb = _snapshotDao.createSearchBuilder(); _accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria); @@ -601,7 +580,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ); sb.and("snapshotTypeEQ", sb.entity().getsnapshotType(), SearchCriteria.Op.IN); sb.and("snapshotTypeNEQ", sb.entity().getsnapshotType(), SearchCriteria.Op.NEQ); - + if (tags != null && !tags.isEmpty()) { SearchBuilder tagSearch = _resourceTagDao.createSearchBuilder(); for (int count=0; count < tags.size(); count++) { @@ -620,7 +599,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, if (volumeId != null) { sc.setParameters("volumeId", volumeId); } - + if (tags != null && !tags.isEmpty()) { int count = 0; sc.setJoinParameters("tagSearch", "resourceType", TaggedResourceType.Snapshot.toString()); @@ -765,9 +744,9 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, if (volume == null) { throw new InvalidParameterValueException("Failed to create snapshot policy, unable to find a volume with id " + volumeId); } - + _accountMgr.checkAccess(UserContext.current().getCaller(), null, true, volume); - + if (volume.getState() != Volume.State.Ready) { throw new InvalidParameterValueException("VolumeId: " + volumeId + " is not in " + Volume.State.Ready + " state but " + volume.getState() + ". Cannot take snapshot."); } @@ -823,7 +802,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, if (owner.getType() == Account.ACCOUNT_TYPE_PROJECT) { message = "domain/project"; } - + throw new InvalidParameterValueException("Max number of snapshots shouldn't exceed the " + message + " level snapshot limit"); } @@ -869,11 +848,11 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, return new Pair, Integer>(result.first(), result.second()); } - + private List listPoliciesforVolume(long volumeId) { return _snapshotPolicyDao.listByVolumeId(volumeId); } - + private List listSnapsforVolume(long volumeId) { return _snapshotDao.listByVolumeId(volumeId); } @@ -961,7 +940,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, @Override public SnapshotVO allocSnapshot(Long volumeId, Long policyId) throws ResourceAllocationException { Account caller = UserContext.current().getCaller(); - + VolumeVO volume = _volsDao.findById(volumeId); if (volume == null) { throw new InvalidParameterValueException("Creating snapshot failed due to volume:" + volumeId + " doesn't exist"); @@ -970,11 +949,11 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, if (zone == null) { throw new InvalidParameterValueException("Can't find zone by id " + volume.getDataCenterId()); } - + if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getType())) { throw new PermissionDeniedException("Cannot perform this operation, Zone is currently disabled: " + zone.getName()); } - + if (volume.getState() != Volume.State.Ready) { throw new InvalidParameterValueException("VolumeId: " + volumeId + " is not in " + Volume.State.Ready + " state but " + volume.getState() + ". Cannot take snapshot."); } @@ -985,7 +964,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, throw new InvalidParameterValueException("VolumeId: " + volumeId + " is for System VM , Creating snapshot against System VM volumes is not supported"); } } - + StoragePoolVO storagePoolVO = _storagePoolDao.findById(volume.getPoolId()); if (storagePoolVO == null) { throw new InvalidParameterValueException("VolumeId: " + volumeId + " please attach this volume to a VM before create snapshot for it"); @@ -1030,7 +1009,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, String snapshotName = vmDisplayName + "_" + volume.getName() + "_" + timeString; // Create the Snapshot object and save it so we can return it to the - // user + // user HypervisorType hypervisorType = this._volsDao.getHypervisorType(volumeId); SnapshotVO snapshotVO = new SnapshotVO(volume.getDataCenterId(), volume.getAccountId(), volume.getDomainId(), volume.getId(), volume.getDiskOfferingId(), null, snapshotName, (short) snapshotType.ordinal(), snapshotType.name(), volume.getSize(), hypervisorType); @@ -1050,7 +1029,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, @Override public boolean configure(String name, Map params) throws ConfigurationException { - + String value = _configDao.getValue(Config.BackupSnapshotWait.toString()); _backupsnapshotwait = NumbersUtil.parseInt(value, Integer.parseInt(Config.BackupSnapshotWait.getDefaultValue())); backup = Boolean.parseBoolean(this._configDao.getValue(Config.BackupSnapshotAferTakingSnapshot.toString())); @@ -1124,7 +1103,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, return success; } - + @Override public boolean canOperateOnVolume(Volume volume) { List snapshots = _snapshotDao.listByStatus(volume.getId(), Snapshot.State.Creating,