Fix create template from snapshot returning null in case of region store
Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/7b5d5648 Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/7b5d5648 Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/7b5d5648 Branch: refs/heads/master Commit: 7b5d5648d80a603d68a1232f663127819799c89a Parents: ee150aa Author: Syed <syed1.mush...@gmail.com> Authored: Thu Feb 25 16:29:58 2016 -0500 Committer: Syed <syed1.mush...@gmail.com> Committed: Mon May 9 15:59:23 2016 -0400 ---------------------------------------------------------------------- server/src/com/cloud/api/ApiResponseHelper.java | 2 +- .../com/cloud/template/TemplateManagerImpl.java | 14 +++- .../cloud/template/TemplateManagerImplTest.java | 85 ++++++++++++++++++++ 3 files changed, 99 insertions(+), 2 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7b5d5648/server/src/com/cloud/api/ApiResponseHelper.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index 1c25644..74649d7 100644 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -1388,7 +1388,7 @@ public class ApiResponseHelper implements ResponseGenerator { @Override public List<TemplateResponse> createTemplateResponses(ResponseView view, VirtualMachineTemplate result, Long zoneId, boolean readyOnly) { List<TemplateJoinVO> tvo = null; - if (zoneId == null || zoneId == -1) { + if (zoneId == null || zoneId == -1 || result.isCrossZones()) { tvo = ApiDBUtils.newTemplateView(result); } else { tvo = ApiDBUtils.newTemplateView(result, zoneId, readyOnly); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7b5d5648/server/src/com/cloud/template/TemplateManagerImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/template/TemplateManagerImpl.java b/server/src/com/cloud/template/TemplateManagerImpl.java index 6a8c3b6..17c103d 100644 --- a/server/src/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/com/cloud/template/TemplateManagerImpl.java @@ -40,6 +40,8 @@ import com.google.gson.GsonBuilder; import org.apache.cloudstack.api.command.user.template.GetUploadParamsForTemplateCmd; import org.apache.cloudstack.api.response.GetUploadParamsResponse; import org.apache.cloudstack.storage.command.TemplateOrVolumePostUploadCommand; +import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; +import org.apache.cloudstack.storage.datastore.db.ImageStoreVO; import org.apache.cloudstack.utils.imagestore.ImageStoreUtil; import org.apache.commons.collections.CollectionUtils; import org.apache.log4j.Logger; @@ -262,7 +264,8 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, private UserVmJoinDao _userVmJoinDao; @Inject private SnapshotDataStoreDao _snapshotStoreDao; - + @Inject + private ImageStoreDao _imgStoreDao; @Inject MessageBus _messageBus; @@ -276,6 +279,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, @Inject private EndPointSelector selector; + private TemplateAdapter getAdapter(HypervisorType type) { TemplateAdapter adapter = null; if (type == HypervisorType.BareMetal) { @@ -1705,6 +1709,14 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, s_logger.debug("This template is getting created from other template, setting source template Id to: " + sourceTemplateId); } } + + + // for region wide storage, set cross zones flag + List<ImageStoreVO> stores = _imgStoreDao.findRegionImageStores(); + if (!CollectionUtils.isEmpty(stores)) { + privateTemplate.setCrossZones(true); + } + privateTemplate.setSourceTemplateId(sourceTemplateId); VMTemplateVO template = _tmpltDao.persist(privateTemplate); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/7b5d5648/server/test/com/cloud/template/TemplateManagerImplTest.java ---------------------------------------------------------------------- diff --git a/server/test/com/cloud/template/TemplateManagerImplTest.java b/server/test/com/cloud/template/TemplateManagerImplTest.java index ed4ec52..6e16938 100644 --- a/server/test/com/cloud/template/TemplateManagerImplTest.java +++ b/server/test/com/cloud/template/TemplateManagerImplTest.java @@ -21,13 +21,19 @@ package com.cloud.template; import com.cloud.agent.AgentManager; import com.cloud.api.query.dao.UserVmJoinDao; +import com.cloud.configuration.Resource; import com.cloud.dc.dao.DataCenterDao; import com.cloud.domain.dao.DomainDao; import com.cloud.event.dao.UsageEventDao; import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; import com.cloud.host.Status; import com.cloud.host.dao.HostDao; +import com.cloud.hypervisor.Hypervisor; import com.cloud.projects.ProjectManager; +import com.cloud.storage.GuestOSVO; +import com.cloud.storage.Snapshot; +import com.cloud.storage.SnapshotVO; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePoolStatus; @@ -54,6 +60,7 @@ import com.cloud.utils.concurrency.NamedThreadFactory; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; +import org.apache.cloudstack.api.command.user.template.CreateTemplateCmd; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; @@ -66,6 +73,8 @@ import org.apache.cloudstack.engine.subsystem.api.storage.TemplateService; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.framework.messagebus.MessageBus; +import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; +import org.apache.cloudstack.storage.datastore.db.ImageStoreVO; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; @@ -77,6 +86,8 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @@ -102,11 +113,13 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyLong; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.eq; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(loader = AnnotationConfigContextLoader.class) @@ -133,6 +146,21 @@ public class TemplateManagerImplTest { @Inject PrimaryDataStoreDao primaryDataStoreDao; + @Inject + ResourceLimitService resourceLimitMgr; + + @Inject + ImageStoreDao imgStoreDao; + + @Inject + GuestOSDao guestOSDao; + + @Inject + VMTemplateDao tmpltDao; + + @Inject + SnapshotDao snapshotDao; + public class CustomThreadPoolExecutor extends ThreadPoolExecutor { AtomicInteger ai = new AtomicInteger(0); public CustomThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, @@ -357,6 +385,58 @@ public class TemplateManagerImplTest { assertTrue("Test template is scheduled for seeding to on pool", ((CustomThreadPoolExecutor) preloadExecutor).getCount() == 2); } + @Test + public void testCreatePrivateTemplateRecordForRegionStore() throws ResourceAllocationException { + + CreateTemplateCmd mockCreateCmd = mock(CreateTemplateCmd.class); + when(mockCreateCmd.getTemplateName()).thenReturn("test"); + when(mockCreateCmd.getTemplateTag()).thenReturn(null); + when(mockCreateCmd.getBits()).thenReturn(64); + when(mockCreateCmd.getRequiresHvm()).thenReturn(true); + when(mockCreateCmd.isPasswordEnabled()).thenReturn(false); + when(mockCreateCmd.isPublic()).thenReturn(false); + when(mockCreateCmd.isFeatured()).thenReturn(false); + when(mockCreateCmd.isDynamicallyScalable()).thenReturn(false); + when(mockCreateCmd.getVolumeId()).thenReturn(null); + when(mockCreateCmd.getSnapshotId()).thenReturn(1L); + when(mockCreateCmd.getOsTypeId()).thenReturn(1L); + when(mockCreateCmd.getEventDescription()).thenReturn("test"); + when(mockCreateCmd.getDetails()).thenReturn(null); + + Account mockTemplateOwner = mock(Account.class); + + SnapshotVO mockSnapshot = mock(SnapshotVO.class); + when(snapshotDao.findById(anyLong())).thenReturn(mockSnapshot); + + when(mockSnapshot.getVolumeId()).thenReturn(1L); + when(mockSnapshot.getState()).thenReturn(Snapshot.State.BackedUp); + when(mockSnapshot.getHypervisorType()).thenReturn(Hypervisor.HypervisorType.XenServer); + + doNothing().when(resourceLimitMgr).checkResourceLimit(any(Account.class), eq(Resource.ResourceType.template)); + doNothing().when(resourceLimitMgr).checkResourceLimit(any(Account.class), eq(Resource.ResourceType.secondary_storage), anyLong()); + + GuestOSVO mockGuestOS = mock(GuestOSVO.class); + when(guestOSDao.findById(anyLong())).thenReturn(mockGuestOS); + + when(tmpltDao.getNextInSequence(eq(Long.class), eq("id"))).thenReturn(1L); + + List<ImageStoreVO> mockRegionStores = new ArrayList<>(); + ImageStoreVO mockRegionStore = mock(ImageStoreVO.class); + mockRegionStores.add(mockRegionStore); + when(imgStoreDao.findRegionImageStores()).thenReturn(mockRegionStores); + + when(tmpltDao.persist(any(VMTemplateVO.class))).thenAnswer(new Answer<VMTemplateVO>() { + @Override + public VMTemplateVO answer(InvocationOnMock invocationOnMock) throws Throwable { + Object[] args = invocationOnMock.getArguments(); + return (VMTemplateVO)args[0]; + } + }); + + VMTemplateVO template = templateManager.createPrivateTemplateRecord(mockCreateCmd, mockTemplateOwner); + assertTrue("Template in a region store should have cross zones set", template.isCrossZones()); + } + @Configuration @ComponentScan(basePackageClasses = {TemplateManagerImpl.class}, includeFilters = {@ComponentScan.Filter(value = TestConfiguration.Library.class, type = FilterType.CUSTOM)}, @@ -524,6 +604,11 @@ public class TemplateManagerImplTest { } @Bean + public ImageStoreDao imageStoreDao() { + return Mockito.mock(ImageStoreDao.class); + } + + @Bean public MessageBus messageBus() { return Mockito.mock(MessageBus.class); }