Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
import org.apache.cloudstack.storage.command.DeleteCommand;
import org.apache.cloudstack.storage.datastore.DataObjectManager;
import org.apache.cloudstack.storage.datastore.ObjectInDataStoreManager;
import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
import org.apache.cloudstack.storage.image.datastore.ImageStoreEntity;
Expand Down Expand Up @@ -163,6 +164,8 @@ public class TemplateServiceImpl implements TemplateService {
TemplateDataFactory imageFactory;
@Inject
StorageOrchestrationService storageOrchestrator;
@Inject
ImageStoreDao dataStoreDao;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ImageStoreDao dataStoreDao;
ImageStoreDao imageStoreDao;


class TemplateOpContext<T> extends AsyncRpcContext<T> {
final TemplateObject template;
Expand Down Expand Up @@ -295,10 +298,16 @@ public void handleSysTemplateDownload(HypervisorType hostHyper, Long dcId) {
}

protected boolean shouldDownloadTemplateToStore(VMTemplateVO template, DataStore store) {
if (dataStoreDao.findById(store.getId()).isReadonly()) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (dataStoreDao.findById(store.getId()).isReadonly()) {
ImageStoreVO storeVO = imageStoreDao.findById(store.getId());
if (storeVO == null) {
// add log
return false;
}
if (storeVO.isReadonly()) {

npe for removed image store, can check store exists before isReadonly()

logger.debug("Template [{}] will not be downloaded to image store [{}] because this store is marked as read-only.", template.getUniqueName(),
store.getName());
return false;
}
Comment on lines 300 to +305

Long zoneId = store.getScope().getScopeId();
DataStore directedStore = _tmpltMgr.verifyHeuristicRulesForZone(template, zoneId);
if (directedStore != null && store.getId() != directedStore.getId()) {
logger.info("Template [{}] will not be download to image store [{}], as a heuristic rule is directing it to another store.",
logger.info("Template [{}] will not be downloaded to image store [{}], as a heuristic rule is directing it to another store.",
template.getUniqueName(), store.getName());
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
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.Scope;
import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
import org.apache.cloudstack.storage.datastore.db.ImageStoreVO;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
import org.apache.cloudstack.storage.image.store.TemplateObject;
Expand Down Expand Up @@ -104,6 +106,12 @@ public class TemplateServiceImplTest {
@Mock
DataCenterDao _dcDao;

@Mock
ImageStoreDao imageStore;

@Mock
ImageStoreVO imageMock;

Comment on lines +109 to +114
Map<String, TemplateProp> templatesInSourceStore = new HashMap<>();

@Before
Expand All @@ -119,45 +127,59 @@ public void setUp() {
Mockito.doReturn(templateInfoMock).when(templateDataFactoryMock).getTemplate(2L, sourceStoreMock);
Mockito.doReturn(3L).when(dataStoreMock).getId();
Mockito.doReturn(zoneScopeMock).when(dataStoreMock).getScope();
Mockito.when(imageStore.findById(3L)).thenReturn(imageMock);
}

@Test
public void shouldDownloadTemplateToStoreTestSkipsTemplateDirectedToAnotherStorage() {
DataStore destinedStore = Mockito.mock(DataStore.class);
Mockito.when(imageMock.isReadonly()).thenReturn(false);
Mockito.doReturn(dataStoreMock.getId() + 1L).when(destinedStore).getId();
Mockito.when(templateManagerMock.verifyHeuristicRulesForZone(tmpltMock, zoneScopeMock.getScopeId())).thenReturn(destinedStore);
Assert.assertFalse(templateService.shouldDownloadTemplateToStore(tmpltMock, dataStoreMock));
}

@Test
public void shouldDownloadTemplateToStoreTestDownloadsPublicTemplate() {
Mockito.when(imageMock.isReadonly()).thenReturn(false);
Mockito.when(tmpltMock.isPublicTemplate()).thenReturn(true);
Assert.assertTrue(templateService.shouldDownloadTemplateToStore(tmpltMock, dataStoreMock));
}

@Test
public void shouldDownloadTemplateToStoreTestDownloadsFeaturedTemplate() {
Mockito.when(imageMock.isReadonly()).thenReturn(false);
Mockito.when(tmpltMock.isFeatured()).thenReturn(true);
Assert.assertTrue(templateService.shouldDownloadTemplateToStore(tmpltMock, dataStoreMock));
}

@Test
public void shouldDownloadTemplateToStoreTestDownloadsSystemTemplate() {
Mockito.when(imageMock.isReadonly()).thenReturn(false);
Mockito.when(tmpltMock.getTemplateType()).thenReturn(Storage.TemplateType.SYSTEM);
Assert.assertTrue(templateService.shouldDownloadTemplateToStore(tmpltMock, dataStoreMock));
}

@Test
public void shouldDownloadTemplateToStoreTestDownloadsPrivateNoRefTemplate() {
Mockito.when(imageMock.isReadonly()).thenReturn(false);
Assert.assertTrue(templateService.shouldDownloadTemplateToStore(tmpltMock, dataStoreMock));
}

@Test
public void shouldDownloadTemplateToStoreTestSkipsPrivateExistingTemplate() {
Mockito.when(imageMock.isReadonly()).thenReturn(false);
Mockito.when(templateDataStoreDao.findByTemplateZone(tmpltMock.getId(), zoneScopeMock.getScopeId(), DataStoreRole.Image)).thenReturn(Mockito.mock(TemplateDataStoreVO.class));
Assert.assertFalse(templateService.shouldDownloadTemplateToStore(tmpltMock, dataStoreMock));
}

@Test
public void shouldDownloadTemplateToStoreTestSkipsWhenStorageIsReadOnly() {
Mockito.when(imageMock.isReadonly()).thenReturn(true);
Assert.assertFalse(templateService.shouldDownloadTemplateToStore(tmpltMock, dataStoreMock));

}

@Test
public void tryDownloadingTemplateToImageStoreTestDownloadsTemplateWhenUrlIsNotNull() {
Mockito.doReturn("url").when(tmpltMock).getUrl();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import org.apache.cloudstack.storage.command.CommandResult;
import org.apache.cloudstack.storage.command.CopyCommand;
import org.apache.cloudstack.storage.command.DeleteCommand;
import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
Expand Down Expand Up @@ -126,6 +127,8 @@ public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver {
AgentManager agentMgr;
@Inject
DataStoreManager dataStoreManager;
@Inject
ImageStoreDao dataStoreDao;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ImageStoreDao dataStoreDao;
ImageStoreDao imageStoreDao;


protected String _proxy = null;

Expand Down Expand Up @@ -175,10 +178,13 @@ public void createAsync(DataStore dataStore, DataObject data, AsyncCompletionCal
AsyncCallbackDispatcher<BaseImageStoreDriverImpl, DownloadAnswer> caller = AsyncCallbackDispatcher.create(this);
caller.setContext(context);
if (data.getType() == DataObjectType.TEMPLATE) {
caller.setCallback(caller.getTarget().createTemplateAsyncCallback(null, null));
if (logger.isDebugEnabled()) {
logger.debug("Downloading template to data store {}", dataStore);
if (dataStoreDao.findById(dataStore.getId()).isReadonly()) {
logger.debug("Template [{}] will not be downloaded to image store [{}] because this store is marked as read-only.", data.getName(),
dataStore.getName());
return;
Copy link
Copy Markdown
Contributor

@sureshanaparti sureshanaparti Apr 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

can have a common method to check image store exists or not and then isReadonly() ?

}
Comment on lines +181 to 185
caller.setCallback(caller.getTarget().createTemplateAsyncCallback(null, null));
logger.debug("Downloading template [{}] to data store [{}].", data.getName(), dataStore.getName());
_downloadMonitor.downloadTemplateToStorage(data, caller);
} else if (data.getType() == DataObjectType.VOLUME) {
caller.setCallback(caller.getTarget().createVolumeAsyncCallback(null, null));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,11 @@ protected boolean isZoneAndImageStoreAvailable(DataStore imageStore, Long zoneId
return false;
}

if (_imgStoreDao.findById(imageStore.getId()).isReadonly()) {
logger.info("Image store [{}] is marked as read-only. Skip downloading template to this image store.", imageStore);
return false;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use common method if possible

Comment on lines +341 to +344

if (!_statsCollector.imageStoreHasEnoughCapacity(imageStore)) {
logger.info("Image store doesn't have enough capacity. Skip downloading template to this image store [{}].", imageStore);
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@
import org.apache.cloudstack.framework.events.Event;
import org.apache.cloudstack.framework.events.EventDistributor;
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.TemplateDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
import org.apache.cloudstack.storage.heuristics.HeuristicRuleHelper;
Expand Down Expand Up @@ -137,6 +139,9 @@ public class HypervisorTemplateAdapterTest {
@Mock
StatsCollector statsCollectorMock;

@Mock
ImageStoreDao _imgStoreDao;

@Mock
Logger loggerMock;

Expand Down Expand Up @@ -458,11 +463,13 @@ public void isZoneAndImageStoreAvailableTestZoneIsDisabledShouldReturnFalse() {
@Test
public void isZoneAndImageStoreAvailableTestImageStoreDoesNotHaveEnoughCapacityShouldReturnFalse() {
DataStore dataStoreMock = Mockito.mock(DataStore.class);
ImageStoreVO ImageStoreVOMock = Mockito.mock(ImageStoreVO.class);
Long zoneId = 1L;
Set<Long> zoneSet = null;
boolean isTemplatePrivate = false;
DataCenterVO dataCenterVOMock = Mockito.mock(DataCenterVO.class);

Mockito.when(_imgStoreDao.findById(Mockito.anyLong())).thenReturn(ImageStoreVOMock);
Comment on lines +466 to +472
Mockito.when(_dcDao.findById(Mockito.anyLong())).thenReturn(dataCenterVOMock);
Mockito.when(dataCenterVOMock.getAllocationState()).thenReturn(Grouping.AllocationState.Enabled);
Mockito.when(statsCollectorMock.imageStoreHasEnoughCapacity(any(DataStore.class))).thenReturn(false);
Expand All @@ -477,11 +484,13 @@ public void isZoneAndImageStoreAvailableTestImageStoreDoesNotHaveEnoughCapacityS
@Test
public void isZoneAndImageStoreAvailableTestImageStoreHasEnoughCapacityAndZoneSetIsNullShouldReturnTrue() {
DataStore dataStoreMock = Mockito.mock(DataStore.class);
ImageStoreVO ImageStoreVOMock = Mockito.mock(ImageStoreVO.class);
Long zoneId = 1L;
Set<Long> zoneSet = null;
boolean isTemplatePrivate = false;
DataCenterVO dataCenterVOMock = Mockito.mock(DataCenterVO.class);

Mockito.when(_imgStoreDao.findById(Mockito.anyLong())).thenReturn(ImageStoreVOMock);
Mockito.when(_dcDao.findById(Mockito.anyLong())).thenReturn(dataCenterVOMock);
Mockito.when(dataCenterVOMock.getAllocationState()).thenReturn(Grouping.AllocationState.Enabled);
Mockito.when(statsCollectorMock.imageStoreHasEnoughCapacity(any(DataStore.class))).thenReturn(true);
Expand All @@ -496,11 +505,13 @@ public void isZoneAndImageStoreAvailableTestImageStoreHasEnoughCapacityAndZoneSe
@Test
public void isZoneAndImageStoreAvailableTestTemplateIsPrivateAndItIsAlreadyAllocatedToTheSameZoneShouldReturnFalse() {
DataStore dataStoreMock = Mockito.mock(DataStore.class);
ImageStoreVO ImageStoreVOMock = Mockito.mock(ImageStoreVO.class);
Long zoneId = 1L;
Set<Long> zoneSet = Set.of(1L);
boolean isTemplatePrivate = true;
DataCenterVO dataCenterVOMock = Mockito.mock(DataCenterVO.class);

Mockito.when(_imgStoreDao.findById(Mockito.anyLong())).thenReturn(ImageStoreVOMock);
Mockito.when(_dcDao.findById(Mockito.anyLong())).thenReturn(dataCenterVOMock);
Mockito.when(dataCenterVOMock.getAllocationState()).thenReturn(Grouping.AllocationState.Enabled);
Mockito.when(statsCollectorMock.imageStoreHasEnoughCapacity(any(DataStore.class))).thenReturn(true);
Expand All @@ -515,11 +526,13 @@ public void isZoneAndImageStoreAvailableTestTemplateIsPrivateAndItIsAlreadyAlloc
@Test
public void isZoneAndImageStoreAvailableTestTemplateIsPrivateAndItIsNotAlreadyAllocatedToTheSameZoneShouldReturnTrue() {
DataStore dataStoreMock = Mockito.mock(DataStore.class);
ImageStoreVO ImageStoreVOMock = Mockito.mock(ImageStoreVO.class);
Long zoneId = 1L;
Set<Long> zoneSet = new HashSet<>();
boolean isTemplatePrivate = true;
DataCenterVO dataCenterVOMock = Mockito.mock(DataCenterVO.class);

Mockito.when(_imgStoreDao.findById(Mockito.anyLong())).thenReturn(ImageStoreVOMock);
Mockito.when(_dcDao.findById(Mockito.anyLong())).thenReturn(dataCenterVOMock);
Mockito.when(dataCenterVOMock.getAllocationState()).thenReturn(Grouping.AllocationState.Enabled);
Mockito.when(statsCollectorMock.imageStoreHasEnoughCapacity(any(DataStore.class))).thenReturn(true);
Expand Down
Loading