diff --git a/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java b/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java index 92470edb034..a77270f7c97 100644 --- a/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java +++ b/engine/storage/image/src/org/apache/cloudstack/storage/image/TemplateServiceImpl.java @@ -73,6 +73,7 @@ import com.cloud.exception.ResourceAllocationException; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.storage.DataStoreRole; import com.cloud.storage.StoragePool; +import com.cloud.storage.Storage.TemplateType; import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.VMTemplateZoneVO; @@ -404,6 +405,11 @@ public class TemplateServiceImpl implements TemplateService { s_logger.info("Skip downloading template " + tmplt.getUniqueName() + " since no url is specified."); continue; } + // if this is private template, skip sync to a new image store + if (!tmplt.isPublicTemplate() && !tmplt.isFeatured() && tmplt.getTemplateType() != TemplateType.SYSTEM) { + s_logger.info("Skip sync downloading private template " + tmplt.getUniqueName() + " to a new image store"); + continue; + } if (availHypers.contains(tmplt.getHypervisorType())) { s_logger.info("Downloading template " + tmplt.getUniqueName() + " to image store " diff --git a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/ChildTestConfiguration.java b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/ChildTestConfiguration.java index 817262c28d7..731c1c66343 100644 --- a/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/ChildTestConfiguration.java +++ b/engine/storage/integration-test/test/org/apache/cloudstack/storage/test/ChildTestConfiguration.java @@ -185,12 +185,6 @@ public class ChildTestConfiguration extends TestConfiguration { return Mockito.mock(VirtualMachineManager.class); } - - @Bean - public SnapshotManager snapshotMgr() { - return Mockito.mock(SnapshotManager.class); - } - @Bean public ResourceManager resourceMgr() { return Mockito.mock(ResourceManager.class); 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 index b0cf5e5be6f..3cf0cb6441c 100644 --- 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 @@ -18,6 +18,10 @@ */ package org.apache.cloudstack.storage.test; +import com.cloud.storage.snapshot.SnapshotScheduler; +import com.cloud.storage.snapshot.SnapshotSchedulerImpl; +import com.cloud.user.DomainManager; +import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy; 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; @@ -35,4 +39,20 @@ public class FakeDriverTestConfiguration extends ChildTestConfiguration{ return provider; } + @Bean + public DataMotionStrategy dataMotionStrategy() { + DataMotionStrategy strategy = new MockStorageMotionStrategy(); + return strategy; + } + + @Bean + public SnapshotScheduler SnapshotScheduler() { + return Mockito.mock(SnapshotScheduler.class); + } + + @Bean + public DomainManager DomainManager() { + return Mockito.mock(DomainManager.class); + } + } 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 52ccf410c8d..2a836892ebb 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 @@ -19,7 +19,11 @@ package org.apache.cloudstack.storage.test; import java.util.Map; +import java.util.UUID; +import com.cloud.agent.api.to.DataObjectType; +import com.cloud.agent.api.to.DataTO; +import com.cloud.storage.Storage; import org.apache.cloudstack.engine.subsystem.api.storage.CopyCommandResult; import org.apache.cloudstack.engine.subsystem.api.storage.DataMotionStrategy; import org.apache.cloudstack.engine.subsystem.api.storage.DataObject; @@ -29,6 +33,9 @@ import org.apache.cloudstack.framework.async.AsyncCompletionCallback; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.host.Host; +import org.apache.cloudstack.storage.command.CopyCmdAnswer; +import org.apache.cloudstack.storage.to.SnapshotObjectTO; +import org.apache.cloudstack.storage.to.TemplateObjectTO; public class MockStorageMotionStrategy implements DataMotionStrategy { @@ -45,7 +52,22 @@ public class MockStorageMotionStrategy implements DataMotionStrategy { @Override public Void copyAsync(DataObject srcData, DataObject destData, AsyncCompletionCallback callback) { - CopyCommandResult result = new CopyCommandResult("something", null); + CopyCmdAnswer answer = null; + DataTO data = null; + if (destData.getType() == DataObjectType.SNAPSHOT) { + SnapshotObjectTO newSnapshot = new SnapshotObjectTO(); + newSnapshot.setPath(UUID.randomUUID().toString()); + data = newSnapshot; + } else if (destData.getType() == DataObjectType.TEMPLATE) { + TemplateObjectTO newTemplate = new TemplateObjectTO(); + newTemplate.setPath(UUID.randomUUID().toString()); + newTemplate.setFormat(Storage.ImageFormat.QCOW2); + newTemplate.setSize(10L); + newTemplate.setPhysicalSize(10L); + data = newTemplate; + } + answer = new CopyCmdAnswer(data); + CopyCommandResult result = new CopyCommandResult("something", answer); callback.complete(result); return null; } 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 index c98f7056662..d5465056aa6 100644 --- 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 @@ -29,6 +29,7 @@ import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.utils.component.ComponentContext; +import com.cloud.utils.db.DB; import junit.framework.Assert; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; @@ -61,10 +62,17 @@ import java.net.URISyntaxException; import java.util.HashSet; import java.util.Set; import java.util.UUID; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath:/fakeDriverTestContext.xml" }) -public class SnapshotTestWithFakeData { +public class SnapshotTestWithFakeData { @Inject SnapshotService snapshotService; @Inject @@ -85,6 +93,7 @@ public class SnapshotTestWithFakeData { VolumeService volumeService; @Inject VolumeDataFactory volumeDataFactory; + VolumeInfo vol = null; FakePrimaryDataStoreDriver driver = new FakePrimaryDataStoreDriver(); @Before @@ -191,4 +200,32 @@ public class SnapshotTestWithFakeData { Assert.assertTrue(result == null); } + @Test + public void testConcurrentSnapshot() throws URISyntaxException, InterruptedException, ExecutionException { + DataStore store = createDataStore(); + FakePrimaryDataStoreDriver dataStoreDriver = (FakePrimaryDataStoreDriver)store.getDriver(); + dataStoreDriver.makeTakeSnapshotSucceed(true); + final VolumeInfo volumeInfo = createVolume(1L, store); + Assert.assertTrue(volumeInfo.getState() == Volume.State.Ready); + vol = volumeInfo; + ExecutorService pool = Executors.newFixedThreadPool(2); + boolean result = false; + Future future = null; + for(int i = 0; i < 1; i++) { + future = pool.submit(new Callable() { + @Override + public Boolean call() throws Exception { + boolean r = false; + try { + SnapshotInfo newSnapshot = volumeService.takeSnapshot(vol); + Assert.assertTrue(newSnapshot != null); + } catch (Exception e) { + r = false; + } + return true; + } + }); + } + future.get(); + } } diff --git a/engine/storage/integration-test/test/resource/fakeDriverTestContext.xml b/engine/storage/integration-test/test/resource/fakeDriverTestContext.xml index 3abcf08090b..c3c8ef5a6bb 100644 --- a/engine/storage/integration-test/test/resource/fakeDriverTestContext.xml +++ b/engine/storage/integration-test/test/resource/fakeDriverTestContext.xml @@ -48,7 +48,6 @@ - @@ -84,4 +83,8 @@ + + + + diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java index e02d9bc83f1..bbccfcdafb3 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/datastore/PrimaryDataStoreImpl.java @@ -266,6 +266,12 @@ public class PrimaryDataStoreImpl implements PrimaryDataStore { } } else if (obj.getType() == DataObjectType.SNAPSHOT) { return objectInStoreMgr.create(obj, this); + } else if (obj.getType() == DataObjectType.VOLUME) { + VolumeVO vol = volumeDao.findById(obj.getId()); + if (vol != null) { + vol.setPoolId(this.getId()); + volumeDao.update(vol.getId(), vol); + } } return objectInStoreMgr.get(obj, this); diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index 8d00cb72a4c..404a6da6a63 100755 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -3644,10 +3644,10 @@ ServerResource { physicalDisk = secondaryStorage.getPhysicalDisk(volName); } else if (volume.getType() != Volume.Type.ISO) { PrimaryDataStoreTO store = (PrimaryDataStoreTO)data.getDataStore(); - pool = _storagePoolMgr.getStoragePool( - store.getPoolType(), - store.getUuid()); - physicalDisk = pool.getPhysicalDisk(data.getPath()); + physicalDisk = _storagePoolMgr.getPhysicalDisk( store.getPoolType(), + store.getUuid(), + data.getPath()); + pool = physicalDisk.getPool(); } String volPath = null; @@ -3720,10 +3720,9 @@ ServerResource { if (volume.getType() == Volume.Type.ROOT) { DataTO data = volume.getData(); PrimaryDataStoreTO store = (PrimaryDataStoreTO)data.getDataStore(); - KVMStoragePool pool = _storagePoolMgr.getStoragePool( - store.getPoolType(), - store.getUuid()); - KVMPhysicalDisk physicalDisk = pool.getPhysicalDisk(data.getPath()); + KVMPhysicalDisk physicalDisk = _storagePoolMgr.getPhysicalDisk( store.getPoolType(), + store.getUuid(), + data.getPath()); FilesystemDef rootFs = new FilesystemDef(physicalDisk.getPath(), "/"); vm.getDevices().addDevice(rootFs); break; @@ -3765,6 +3764,10 @@ ServerResource { // need to umount secondary storage String path = disk.getDiskPath(); String poolUuid = null; + if (path.endsWith("systemvm.iso")) { + //Don't need to clean up system vm iso, as it's stored in local + return true; + } if (path != null) { String[] token = path.split("/"); if (token.length > 3) { diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java index 40f4b5b785f..71bbc15e37c 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java @@ -31,8 +31,11 @@ import com.cloud.hypervisor.kvm.resource.KVMHAMonitor; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StorageLayer; import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.log4j.Logger; public class KVMStoragePoolManager { + private static final Logger s_logger = Logger + .getLogger(KVMStoragePoolManager.class); private class StoragePoolInformation { String name; String host; @@ -133,6 +136,36 @@ public class KVMStoragePoolManager { return createStoragePool(uuid, sourceHost, 0, sourcePath, "", protocol, false); } + public KVMPhysicalDisk getPhysicalDisk(StoragePoolType type, String poolUuid, String volName) { + int cnt = 0; + int retries = 10; + KVMPhysicalDisk vol = null; + //harden get volume, try cnt times to get volume, in case volume is created on other host + String errMsg = ""; + while (cnt < retries) { + try { + KVMStoragePool pool = getStoragePool(type, poolUuid); + vol = pool.getPhysicalDisk(volName); + if (vol != null) { + break; + } + + Thread.sleep(10000); + } catch (Exception e) { + s_logger.debug("Failed to find volume:" + volName + " due to" + e.toString() + ", retry:" + cnt); + errMsg = e.toString(); + } + cnt++; + } + + if (vol == null) { + throw new CloudRuntimeException(errMsg); + } else { + return vol; + } + + } + public KVMStoragePool createStoragePool( String name, String host, int port, String path, String userInfo, StoragePoolType type) { diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java index 95a426ac219..1d6b58fba69 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java @@ -289,8 +289,8 @@ public class KVMStorageProcessor implements StorageProcessor { if (primaryPool.getType() == StoragePoolType.CLVM) { vol = templateToPrimaryDownload(templatePath, primaryPool); } else { - BaseVol = primaryPool.getPhysicalDisk(templatePath); - vol = storagePoolMgr.createDiskFromTemplate(BaseVol, UUID.randomUUID().toString(), primaryPool); + BaseVol = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), templatePath); + vol = storagePoolMgr.createDiskFromTemplate(BaseVol, UUID.randomUUID().toString(), BaseVol.getPool()); } if (vol == null) { return new CopyCmdAnswer(" Can't create storage volume on storage pool"); @@ -386,28 +386,12 @@ public class KVMStorageProcessor implements StorageProcessor { String destVolumePath = destData.getPath(); String secondaryStorageUrl = nfsStore.getUrl(); KVMStoragePool secondaryStoragePool = null; - KVMStoragePool primaryPool = null; - try { - try { - primaryPool = storagePoolMgr.getStoragePool( - primaryStore.getPoolType(), - primaryStore.getUuid()); - } catch (CloudRuntimeException e) { - if (e.getMessage().contains("not found")) { - primaryPool = storagePoolMgr.createStoragePool(primaryStore.getUuid(), - primaryStore.getHost(), primaryStore.getPort(), - primaryStore.getPath(), null, - primaryStore.getPoolType()); - } else { - return new CopyCmdAnswer(e.getMessage()); - } - } + try { String volumeName = UUID.randomUUID().toString(); - String destVolumeName = volumeName + ".qcow2"; - KVMPhysicalDisk volume = primaryPool.getPhysicalDisk(srcVolumePath); + KVMPhysicalDisk volume = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), srcVolumePath); secondaryStoragePool = storagePoolMgr.getStoragePoolByURI( secondaryStorageUrl); secondaryStoragePool.createFolder(destVolumePath); @@ -450,18 +434,9 @@ public class KVMStorageProcessor implements StorageProcessor { secondaryStorage = storagePoolMgr.getStoragePoolByURI(nfsImageStore.getUrl()); - try { - primary = storagePoolMgr.getStoragePool(primaryStore.getPoolType(), primaryStore.getUuid()); - } catch (CloudRuntimeException e) { - if (e.getMessage().contains("not found")) { - primary = storagePoolMgr.createStoragePool(primaryStore.getUuid(), primaryStore.getHost(), - primaryStore.getPort(), primaryStore.getPath(), null, primaryStore.getPoolType()); - } else { - return new CopyCmdAnswer(e.getMessage()); - } - } + primary = storagePoolMgr.getStoragePool(primaryStore.getPoolType(), primaryStore.getUuid()); - KVMPhysicalDisk disk = primary.getPhysicalDisk(volume.getPath()); + KVMPhysicalDisk disk = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), volume.getPath()); String tmpltPath = secondaryStorage.getLocalPath() + File.separator + templateFolder; this.storageLayer.mkdirs(tmpltPath); String templateName = UUID.randomUUID().toString(); @@ -651,9 +626,9 @@ public class KVMStorageProcessor implements StorageProcessor { snapshotRelPath = destSnapshot.getPath(); snapshotDestPath = ssPmountPath + File.separator + snapshotRelPath; - KVMStoragePool primaryPool = storagePoolMgr.getStoragePool(primaryStore.getPoolType(), - primaryStore.getUuid()); - KVMPhysicalDisk snapshotDisk = primaryPool.getPhysicalDisk(volumePath); + KVMPhysicalDisk snapshotDisk = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), + primaryStore.getUuid(), volumePath); + KVMStoragePool primaryPool = snapshotDisk.getPool(); /** * RBD snapshots can't be copied using qemu-img, so we have to use @@ -788,6 +763,7 @@ public class KVMStorageProcessor implements StorageProcessor { } } + protected synchronized String attachOrDetachISO(Connect conn, String vmName, String isoPath, boolean isAttach) throws LibvirtException, URISyntaxException, InternalErrorException { String isoXml = null; @@ -957,9 +933,8 @@ public class KVMStorageProcessor implements StorageProcessor { String vmName = cmd.getVmName(); try { Connect conn = LibvirtConnection.getConnectionByVmName(vmName); - KVMStoragePool primary = storagePoolMgr.getStoragePool(primaryStore.getPoolType(), primaryStore.getUuid()); + KVMPhysicalDisk phyDisk = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), vol.getPath()); - KVMPhysicalDisk phyDisk = primary.getPhysicalDisk(vol.getPath()); attachOrDetachDisk(conn, true, vmName, phyDisk, disk.getDiskSeq().intValue()); return new AttachAnswer(disk); @@ -980,9 +955,8 @@ public class KVMStorageProcessor implements StorageProcessor { String vmName = cmd.getVmName(); try { Connect conn = LibvirtConnection.getConnectionByVmName(vmName); - KVMStoragePool primary = storagePoolMgr.getStoragePool(primaryStore.getPoolType(), primaryStore.getUuid()); + KVMPhysicalDisk phyDisk = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), vol.getPath()); - KVMPhysicalDisk phyDisk = primary.getPhysicalDisk(vol.getPath()); attachOrDetachDisk(conn, false, vmName, phyDisk, disk.getDiskSeq().intValue()); return new DettachAnswer(disk); @@ -1054,7 +1028,8 @@ public class KVMStorageProcessor implements StorageProcessor { KVMStoragePool primaryPool = storagePoolMgr.getStoragePool(primaryStore.getPoolType(), primaryStore.getUuid()); - KVMPhysicalDisk disk = primaryPool.getPhysicalDisk(volume.getPath()); + KVMPhysicalDisk disk = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), + primaryStore.getUuid(), volume.getPath()); if (state == DomainInfo.DomainState.VIR_DOMAIN_RUNNING && !primaryPool.isExternalSnapshot()) { String vmUuid = vm.getUUIDString(); Object[] args = new Object[] { snapshotName, vmUuid }; diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index 7ffb133d558..18e0ce37fb0 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -3120,7 +3120,7 @@ public class ApiResponseHelper implements ResponseGenerator { response.setIp(ApiDBUtils.findIpAddressById(result.getAddrId()).getAddress().toString()); Vpc vpc = ApiDBUtils.findVpcById(result.getVpcId()); if (vpc != null) { - response.setVpcId(result.getUuid()); + response.setVpcId(vpc.getUuid()); } response.setRemoved(result.getRemoved()); response.setObjectName("vpngateway"); diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java index 7e7026209e0..2730d258d29 100755 --- a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -139,8 +139,6 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, @Inject protected VMTemplateDao _templateDao; @Inject - protected HostDao _hostDao; - @Inject protected UserVmDao _vmDao; @Inject protected VolumeDao _volsDao; @@ -157,8 +155,6 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, @Inject protected PrimaryDataStoreDao _storagePoolDao; @Inject - protected EventDao _eventDao; - @Inject protected SnapshotPolicyDao _snapshotPolicyDao = null; @Inject protected SnapshotScheduleDao _snapshotScheduleDao;