diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakeDriverTestConfiguration.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakeDriverTestConfiguration.java new file mode 100644 index 00000000000..b0cf5e5be6f --- /dev/null +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakeDriverTestConfiguration.java @@ -0,0 +1,38 @@ +/* + * 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.test; + +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider; +import org.apache.cloudstack.storage.datastore.provider.CloudStackPrimaryDataStoreProviderImpl; +import org.apache.cloudstack.storage.datastore.type.DataStoreType; +import org.mockito.Mockito; +import org.springframework.context.annotation.Bean; + +import java.util.HashSet; +import java.util.Set; + +public class FakeDriverTestConfiguration extends ChildTestConfiguration{ + @Bean + public CloudStackPrimaryDataStoreProviderImpl dataStoreProvider() { + CloudStackPrimaryDataStoreProviderImpl provider = Mockito.mock(CloudStackPrimaryDataStoreProviderImpl.class); + + return provider; + } + +} diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakePrimaryDataStoreDriver.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakePrimaryDataStoreDriver.java new file mode 100644 index 00000000000..810afd11577 --- /dev/null +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/FakePrimaryDataStoreDriver.java @@ -0,0 +1,107 @@ +/* + * 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.test; + +import com.cloud.agent.api.to.DataStoreTO; +import com.cloud.agent.api.to.DataTO; +import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo; +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.DataStore; +import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; +import org.apache.cloudstack.framework.async.AsyncCompletionCallback; +import org.apache.cloudstack.storage.command.CommandResult; +import org.apache.cloudstack.storage.command.CreateObjectAnswer; +import org.apache.cloudstack.storage.to.SnapshotObjectTO; + +import java.util.UUID; + +public class FakePrimaryDataStoreDriver implements PrimaryDataStoreDriver { + boolean snapshotResult = true; + @Override + public ChapInfo getChapInfo(VolumeInfo volumeInfo) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback callback) { + CreateCmdResult result = new CreateCmdResult(null, null); + if (snapshotResult) { + SnapshotObjectTO newSnap = new SnapshotObjectTO(); + newSnap.setPath(UUID.randomUUID().toString()); + + CreateObjectAnswer answer = new CreateObjectAnswer(newSnap); + result.setAnswer(answer); + } else { + result.setResult("Failed to create snapshot"); + } + callback.complete(result); + return; + } + + public void makeTakeSnapshotSucceed(boolean success) { + snapshotResult = success; + } + + @Override + public void revertSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback callback) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public DataTO getTO(DataObject data) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public DataStoreTO getStoreTO(DataStore store) { + return null; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void createAsync(DataStore store, DataObject data, AsyncCompletionCallback callback) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void deleteAsync(DataStore store, DataObject data, AsyncCompletionCallback callback) { + CommandResult result = new CommandResult(); + result.setSuccess(true); + callback.complete(result); + return; + } + + @Override + public void copyAsync(DataObject srcdata, DataObject destData, AsyncCompletionCallback callback) { + //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public boolean canCopy(DataObject srcData, DataObject destData) { + return false; //To change body of implemented methods use File | Settings | File Templates. + } + + @Override + public void resize(DataObject data, AsyncCompletionCallback callback) { + //To change body of implemented methods use File | Settings | File Templates. + } +} diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTestWithFakeData.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTestWithFakeData.java new file mode 100644 index 00000000000..c98f7056662 --- /dev/null +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/SnapshotTestWithFakeData.java @@ -0,0 +1,194 @@ +/* + * 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.test; + +import com.cloud.hypervisor.Hypervisor; +import com.cloud.storage.ScopeType; +import com.cloud.storage.Snapshot; +import com.cloud.storage.SnapshotVO; +import com.cloud.storage.Storage; +import com.cloud.storage.StoragePoolStatus; +import com.cloud.storage.Volume; +import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.SnapshotDao; +import com.cloud.storage.dao.VolumeDao; +import com.cloud.utils.component.ComponentContext; +import junit.framework.Assert; +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.DataStoreProvider; +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.PrimaryDataStoreProvider; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotResult; +import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotService; +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.VolumeService; +import org.apache.cloudstack.storage.datastore.PrimaryDataStore; +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import javax.inject.Inject; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = { "classpath:/fakeDriverTestContext.xml" }) +public class SnapshotTestWithFakeData { + @Inject + SnapshotService snapshotService; + @Inject + SnapshotDao snapshotDao; + @Inject + PrimaryDataStoreDao primaryDataStoreDao; + @Inject + DataStoreManager dataStoreManager; + @Inject + SnapshotDataFactory snapshotDataFactory; + @Inject + PrimaryDataStoreProvider primaryDataStoreProvider; + @Inject + SnapshotDataStoreDao snapshotDataStoreDao; + @Inject + VolumeDao volumeDao; + @Inject + VolumeService volumeService; + @Inject + VolumeDataFactory volumeDataFactory; + FakePrimaryDataStoreDriver driver = new FakePrimaryDataStoreDriver(); + + @Before + public void setUp() { + Mockito.when(primaryDataStoreProvider.configure(Mockito.anyMap())).thenReturn(true); + Set types = new HashSet(); + types.add(DataStoreProvider.DataStoreProviderType.PRIMARY); + + Mockito.when(primaryDataStoreProvider.getTypes()).thenReturn(types); + Mockito.when(primaryDataStoreProvider.getName()).thenReturn(DataStoreProvider.DEFAULT_PRIMARY); + Mockito.when(primaryDataStoreProvider.getDataStoreDriver()).thenReturn(driver); + + ComponentContext.initComponentsLifeCycle(); + } + private SnapshotVO createSnapshotInDb() { + Snapshot.Type snapshotType = Snapshot.Type.MANUAL; + SnapshotVO snapshotVO = new SnapshotVO(1, 2, 1, 1L, 1L, UUID.randomUUID() + .toString(), (short) snapshotType.ordinal(), snapshotType.name(), 100, + Hypervisor.HypervisorType.XenServer); + return this.snapshotDao.persist(snapshotVO); + } + + private VolumeInfo createVolume(Long templateId, DataStore store) { + VolumeVO volume = new VolumeVO(Volume.Type.DATADISK, UUID.randomUUID().toString(), 1L, 1L, 1L, 1L, 1000, 0L, 0L, ""); + ; + volume.setPoolId(store.getId()); + + volume = volumeDao.persist(volume); + VolumeInfo volumeInfo = volumeDataFactory.getVolume(volume.getId(), store); + volumeInfo.stateTransit(Volume.Event.CreateRequested); + volumeInfo.stateTransit(Volume.Event.OperationSucceeded); + return volumeInfo; + } + private DataStore createDataStore() throws URISyntaxException { + StoragePoolVO pool = new StoragePoolVO(); + pool.setClusterId(1L); + pool.setDataCenterId(1); + URI uri = new URI("nfs://jfkdkf/fjdkfj"); + pool.setHostAddress(uri.getHost()); + pool.setPath(uri.getPath()); + pool.setPort(0); + pool.setName(UUID.randomUUID().toString()); + pool.setUuid(UUID.randomUUID().toString()); + pool.setStatus(StoragePoolStatus.Up); + pool.setPoolType(Storage.StoragePoolType.NetworkFilesystem); + pool.setPodId(1L); + pool.setScope(ScopeType.CLUSTER); + pool.setStorageProviderName(DataStoreProvider.DEFAULT_PRIMARY); + pool = this.primaryDataStoreDao.persist(pool); + DataStore store = this.dataStoreManager.getPrimaryDataStore(pool.getId()); + return store; + } + @Test + public void testTakeSnapshot() throws URISyntaxException { + SnapshotVO snapshotVO = createSnapshotInDb(); + DataStore store = createDataStore(); + try { + SnapshotInfo snapshotInfo = snapshotDataFactory.getSnapshot(snapshotVO.getId(), store); + SnapshotResult result = snapshotService.takeSnapshot(snapshotInfo); + Assert.assertTrue(result.isSuccess()); + SnapshotDataStoreVO storeRef = snapshotDataStoreDao.findByStoreSnapshot(store.getRole(), store.getId(), snapshotVO.getId()); + Assert.assertTrue(storeRef != null); + Assert.assertTrue(storeRef.getState() == ObjectInDataStoreStateMachine.State.Ready); + snapshotInfo = result.getSnashot(); + boolean deletResult = snapshotService.deleteSnapshot(snapshotInfo); + Assert.assertTrue(deletResult); + snapshotDataStoreDao.expunge(storeRef.getId()); + } finally { + snapshotDao.expunge(snapshotVO.getId()); + primaryDataStoreDao.remove(store.getId()); + } + } + + @Test + public void testTakeSnapshotWithFailed() throws URISyntaxException { + SnapshotVO snapshotVO = createSnapshotInDb(); + DataStore store = null; + try { + store = createDataStore(); + FakePrimaryDataStoreDriver dataStoreDriver = (FakePrimaryDataStoreDriver)store.getDriver(); + dataStoreDriver.makeTakeSnapshotSucceed(false); + SnapshotInfo snapshotInfo = snapshotDataFactory.getSnapshot(snapshotVO.getId(), store); + SnapshotResult result = snapshotService.takeSnapshot(snapshotInfo); + Assert.assertFalse(result.isSuccess()); + SnapshotDataStoreVO storeRef = snapshotDataStoreDao.findByStoreSnapshot(store.getRole(), store.getId(), snapshotVO.getId()); + Assert.assertTrue(storeRef == null); + } finally { + snapshotDao.expunge(snapshotVO.getId()); + if (store != null) { + primaryDataStoreDao.remove(store.getId()); + } + } + } + + @Test + public void testTakeSnapshotFromVolume() throws URISyntaxException { + DataStore store = createDataStore(); + FakePrimaryDataStoreDriver dataStoreDriver = (FakePrimaryDataStoreDriver)store.getDriver(); + dataStoreDriver.makeTakeSnapshotSucceed(false); + VolumeInfo volumeInfo = createVolume(1L, store); + Assert.assertTrue(volumeInfo.getState() == Volume.State.Ready); + SnapshotInfo result = volumeService.takeSnapshot(volumeInfo); + Assert.assertTrue(volumeInfo.getState() == Volume.State.Ready); + Assert.assertTrue(result == null); + } + +} diff --git a/engine/storage/integration-test/test/resource/fakeDriverTestContext.xml b/engine/storage/integration-test/test/resource/fakeDriverTestContext.xml new file mode 100644 index 00000000000..3abcf08090b --- /dev/null +++ b/engine/storage/integration-test/test/resource/fakeDriverTestContext.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 ab785d7eb24..66cfa4641c2 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 @@ -213,11 +213,6 @@ public class SnapshotServiceImpl implements SnapshotService { try { result = future.get(); - if (result.isFailed()) { - snapshot.processEvent(Snapshot.Event.OperationFailed); - snapshot.processEvent(Event.OperationFailed); - throw new CloudRuntimeException(result.getResult()); - } return result; } catch (InterruptedException e) { s_logger.debug("Failed to create snapshot", e); @@ -225,9 +220,6 @@ public class SnapshotServiceImpl implements SnapshotService { } catch (ExecutionException e) { s_logger.debug("Failed to create snapshot", e); throw new CloudRuntimeException("Failed to create snapshot", e); - } catch (NoTransitionException e) { - s_logger.debug("Failed to create snapshot", e); - throw new CloudRuntimeException("Failed to create snapshot", e); } } diff --git a/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java b/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java index 6b925ce3797..652df43c785 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java +++ b/engine/storage/src/org/apache/cloudstack/storage/datastore/ObjectInDataStoreManagerImpl.java @@ -245,6 +245,12 @@ public class ObjectInDataStoreManagerImpl implements ObjectInDataStoreManager { s_logger.warn("Template " + objId + " is not found on storage pool " + dataStore.getId() + ", so no need to delete"); return true; } + } else if (dataObj.getType() == DataObjectType.SNAPSHOT) { + SnapshotDataStoreVO destSnapshotStore = snapshotDataStoreDao.findByStoreSnapshot(dataStore.getRole(), dataStore.getId(), objId); + if (destSnapshotStore != null && destSnapshotStore.getState() != ObjectInDataStoreStateMachine.State.Ready) { + snapshotDataStoreDao.remove(destSnapshotStore.getId()); + } + return true; } } else { // Image store 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 e8304dddd02..07ae6a9f0e3 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 @@ -1266,7 +1266,7 @@ public class VolumeServiceImpl implements VolumeService { try { snapshot = snapshotMgr.takeSnapshot(volume); } catch (Exception e) { - s_logger.debug("Take snapshot: " + volume.getId() + " failed: " + e.toString()); + s_logger.debug("Take snapshot: " + volume.getId() + " failed", e); } finally { if (snapshot != null) { vol.stateTransit(Volume.Event.OperationSucceeded);