mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Merge pull request #1424 from syed/create-template-api-bug
[CLOUDSTACK-8973] Fix create template from snapshot returning null in case of region storeThis PR fixes a case where when we create a template from a snpashot in a region wide store, we don't set the cross zone flag which causes a null response to be returned Tests: Before fix ``` (local) > create template snapshotid=33aa3f3b-5a47-4d2a-8d27-12952c01ebed displaytext=t2 ostypeid=20c8ead6-d750-11e5-9f8c-06524200007c name=t9 accountid = 1b13d7c2-d750-11e5-9f8c-06524200007c cmd = org.apache.cloudstack.api.command.admin.template.CreateTemplateCmdByAdmin created = 2016-02-23T16:09:24+0000 jobid = 4f9f5ff9-e7f0-4af6-999c-799431fd47de jobinstanceid = a08a9711-bd31-43bb-80a2-49cf9d722a19 jobinstancetype = Template jobprocstatus = 0 jobresult: null: crossZones = False isfeatured = False ispublic = False isready = False tags: jobresultcode = 0 jobresulttype = object jobstatus = 1 userid = 1b140f08-d750-11e5-9f8c-06524200007c ``` See the *null* in response After fix: ``` (local) > create template snapshotid=33aa3f3b-5a47-4d2a-8d27-12952c01ebed displaytext=t2 ostypeid=20c8ead6-d750-11e5-9f8c-06524200007c name=t11 accountid = 1b13d7c2-d750-11e5-9f8c-06524200007c cmd = org.apache.cloudstack.api.command.admin.template.CreateTemplateCmdByAdmin created = 2016-02-25T21:47:03+0000 jobid = 1b74209b-b3c1-4168-a243-f559aa0c081b jobinstanceid = 06ecee5a-b1f2-4e67-80fb-f0f44b0aa198 jobinstancetype = Template jobprocstatus = 0 jobresult: template: id = 06ecee5a-b1f2-4e67-80fb-f0f44b0aa198 name = t11 account = admin created = 2016-02-25T21:47:03+0000 crossZones = True displaytext = t2 domain = ROOT domainid = 1b13ab80-d750-11e5-9f8c-06524200007c format = VHD hypervisor = XenServer isdynamicallyscalable = False isextractable = True isfeatured = False ispublic = False isready = True ostypeid = 20c8ead6-d750-11e5-9f8c-06524200007c ostypename = CentOS 5 (64-bit) passwordenabled = False size = 21474836480 sourcetemplateid = 1af0f0cc-d750-11e5-9f8c-06524200007c sshkeyenabled = False status = Download Complete tags: templatetype = USER jobresultcode = 0 jobresulttype = object jobstatus = 1 userid = 1b140f08-d750-11e5-9f8c-06524200007c ``` Works correctly * pr/1424: Fix create template from snapshot returning null in case of region store Signed-off-by: Will Stevens <williamstevens@gmail.com>
This commit is contained in:
commit
978184bccb
@ -1392,7 +1392,7 @@ public class ApiResponseHelper implements ResponseGenerator {
|
|||||||
@Override
|
@Override
|
||||||
public List<TemplateResponse> createTemplateResponses(ResponseView view, VirtualMachineTemplate result, Long zoneId, boolean readyOnly) {
|
public List<TemplateResponse> createTemplateResponses(ResponseView view, VirtualMachineTemplate result, Long zoneId, boolean readyOnly) {
|
||||||
List<TemplateJoinVO> tvo = null;
|
List<TemplateJoinVO> tvo = null;
|
||||||
if (zoneId == null || zoneId == -1) {
|
if (zoneId == null || zoneId == -1 || result.isCrossZones()) {
|
||||||
tvo = ApiDBUtils.newTemplateView(result);
|
tvo = ApiDBUtils.newTemplateView(result);
|
||||||
} else {
|
} else {
|
||||||
tvo = ApiDBUtils.newTemplateView(result, zoneId, readyOnly);
|
tvo = ApiDBUtils.newTemplateView(result, zoneId, readyOnly);
|
||||||
|
|||||||
@ -40,6 +40,8 @@ import com.google.gson.GsonBuilder;
|
|||||||
import org.apache.cloudstack.api.command.user.template.GetUploadParamsForTemplateCmd;
|
import org.apache.cloudstack.api.command.user.template.GetUploadParamsForTemplateCmd;
|
||||||
import org.apache.cloudstack.framework.async.AsyncCallFuture;
|
import org.apache.cloudstack.framework.async.AsyncCallFuture;
|
||||||
import org.apache.cloudstack.storage.command.TemplateOrVolumePostUploadCommand;
|
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.cloudstack.utils.imagestore.ImageStoreUtil;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
@ -263,7 +265,8 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
|
|||||||
private UserVmJoinDao _userVmJoinDao;
|
private UserVmJoinDao _userVmJoinDao;
|
||||||
@Inject
|
@Inject
|
||||||
private SnapshotDataStoreDao _snapshotStoreDao;
|
private SnapshotDataStoreDao _snapshotStoreDao;
|
||||||
|
@Inject
|
||||||
|
private ImageStoreDao _imgStoreDao;
|
||||||
@Inject
|
@Inject
|
||||||
MessageBus _messageBus;
|
MessageBus _messageBus;
|
||||||
|
|
||||||
@ -277,6 +280,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
|
|||||||
@Inject
|
@Inject
|
||||||
private EndPointSelector selector;
|
private EndPointSelector selector;
|
||||||
|
|
||||||
|
|
||||||
private TemplateAdapter getAdapter(HypervisorType type) {
|
private TemplateAdapter getAdapter(HypervisorType type) {
|
||||||
TemplateAdapter adapter = null;
|
TemplateAdapter adapter = null;
|
||||||
if (type == HypervisorType.BareMetal) {
|
if (type == HypervisorType.BareMetal) {
|
||||||
@ -1719,6 +1723,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);
|
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);
|
privateTemplate.setSourceTemplateId(sourceTemplateId);
|
||||||
|
|
||||||
VMTemplateVO template = _tmpltDao.persist(privateTemplate);
|
VMTemplateVO template = _tmpltDao.persist(privateTemplate);
|
||||||
|
|||||||
@ -21,13 +21,19 @@ package com.cloud.template;
|
|||||||
|
|
||||||
import com.cloud.agent.AgentManager;
|
import com.cloud.agent.AgentManager;
|
||||||
import com.cloud.api.query.dao.UserVmJoinDao;
|
import com.cloud.api.query.dao.UserVmJoinDao;
|
||||||
|
import com.cloud.configuration.Resource;
|
||||||
import com.cloud.dc.dao.DataCenterDao;
|
import com.cloud.dc.dao.DataCenterDao;
|
||||||
import com.cloud.domain.dao.DomainDao;
|
import com.cloud.domain.dao.DomainDao;
|
||||||
import com.cloud.event.dao.UsageEventDao;
|
import com.cloud.event.dao.UsageEventDao;
|
||||||
import com.cloud.exception.InvalidParameterValueException;
|
import com.cloud.exception.InvalidParameterValueException;
|
||||||
|
import com.cloud.exception.ResourceAllocationException;
|
||||||
import com.cloud.host.Status;
|
import com.cloud.host.Status;
|
||||||
import com.cloud.host.dao.HostDao;
|
import com.cloud.host.dao.HostDao;
|
||||||
|
import com.cloud.hypervisor.Hypervisor;
|
||||||
import com.cloud.projects.ProjectManager;
|
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.StorageManager;
|
||||||
import com.cloud.storage.StoragePool;
|
import com.cloud.storage.StoragePool;
|
||||||
import com.cloud.storage.StoragePoolStatus;
|
import com.cloud.storage.StoragePoolStatus;
|
||||||
@ -54,6 +60,7 @@ import com.cloud.utils.concurrency.NamedThreadFactory;
|
|||||||
import com.cloud.utils.exception.CloudRuntimeException;
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
import com.cloud.vm.dao.UserVmDao;
|
import com.cloud.vm.dao.UserVmDao;
|
||||||
import com.cloud.vm.dao.VMInstanceDao;
|
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.context.CallContext;
|
||||||
import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
|
import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
|
||||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
|
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.engine.subsystem.api.storage.VolumeDataFactory;
|
||||||
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
|
||||||
import org.apache.cloudstack.framework.messagebus.MessageBus;
|
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.PrimaryDataStoreDao;
|
||||||
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
|
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
|
||||||
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
|
||||||
@ -77,6 +86,8 @@ import org.junit.Before;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mockito;
|
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.Bean;
|
||||||
import org.springframework.context.annotation.ComponentScan;
|
import org.springframework.context.annotation.ComponentScan;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@ -102,11 +113,13 @@ import java.util.concurrent.TimeUnit;
|
|||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.Matchers.any;
|
||||||
import static org.mockito.Matchers.anyBoolean;
|
import static org.mockito.Matchers.anyBoolean;
|
||||||
import static org.mockito.Matchers.anyLong;
|
import static org.mockito.Matchers.anyLong;
|
||||||
import static org.mockito.Mockito.doNothing;
|
import static org.mockito.Mockito.doNothing;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.mockito.Mockito.eq;
|
||||||
|
|
||||||
@RunWith(SpringJUnit4ClassRunner.class)
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
|
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
|
||||||
@ -133,6 +146,21 @@ public class TemplateManagerImplTest {
|
|||||||
@Inject
|
@Inject
|
||||||
PrimaryDataStoreDao primaryDataStoreDao;
|
PrimaryDataStoreDao primaryDataStoreDao;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ResourceLimitService resourceLimitMgr;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ImageStoreDao imgStoreDao;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
GuestOSDao guestOSDao;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
VMTemplateDao tmpltDao;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
SnapshotDao snapshotDao;
|
||||||
|
|
||||||
public class CustomThreadPoolExecutor extends ThreadPoolExecutor {
|
public class CustomThreadPoolExecutor extends ThreadPoolExecutor {
|
||||||
AtomicInteger ai = new AtomicInteger(0);
|
AtomicInteger ai = new AtomicInteger(0);
|
||||||
public CustomThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
|
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);
|
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
|
@Configuration
|
||||||
@ComponentScan(basePackageClasses = {TemplateManagerImpl.class},
|
@ComponentScan(basePackageClasses = {TemplateManagerImpl.class},
|
||||||
includeFilters = {@ComponentScan.Filter(value = TestConfiguration.Library.class, type = FilterType.CUSTOM)},
|
includeFilters = {@ComponentScan.Filter(value = TestConfiguration.Library.class, type = FilterType.CUSTOM)},
|
||||||
@ -523,6 +603,11 @@ public class TemplateManagerImplTest {
|
|||||||
return Mockito.mock(SnapshotDataStoreDao.class);
|
return Mockito.mock(SnapshotDataStoreDao.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ImageStoreDao imageStoreDao() {
|
||||||
|
return Mockito.mock(ImageStoreDao.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public MessageBus messageBus() {
|
public MessageBus messageBus() {
|
||||||
return Mockito.mock(MessageBus.class);
|
return Mockito.mock(MessageBus.class);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user