Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -56,6 +56,13 @@ public interface TemplateManager {
+ "will validate if the provided URL is resolvable during the register of templates/ISOs before persisting them in the database.",
true);

ConfigKey<Boolean> TemplateDeleteFromPrimaryStorage = new ConfigKey<Boolean>("Advanced",
Copy link
Contributor

Choose a reason for hiding this comment

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

@abh1sar do we really need a global setting for this usecase, I think we can directly delete the template if there is no usage found.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@harikrishna-patnala I added it as a failsafe or in case someone doesn't care about how faster the space is reclaimed, but doesn't want to risk any kind of data loss. Also in case someone wants to use the setting storage.template.cleanup.enabled=false or storage.cleanup.enabled=false where the deleted template is never cleaned up.

Boolean.class,
"template.delete.from.primary.storage", "true",
"Template when deleted will be instantly deleted from the Primary Storage",
true,
ConfigKey.Scope.Global);

static final String VMWARE_TOOLS_ISO = "vmware-tools.iso";
static final String XS_TOOLS_ISO = "xs-tools.iso";

Expand Down Expand Up @@ -103,6 +110,8 @@ public interface TemplateManager {
*/
List<VMTemplateStoragePoolVO> getUnusedTemplatesInPool(StoragePoolVO pool);

void evictTemplateFromStoragePoolsForZones(Long templateId, List<Long> zoneId);

/**
* Deletes a template in the specified storage pool.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public interface VMTemplatePoolDao extends GenericDao<VMTemplateStoragePoolVO, L

List<VMTemplateStoragePoolVO> listByPoolIdAndState(long poolId, ObjectInDataStoreStateMachine.State state);

List<VMTemplateStoragePoolVO> listByPoolIdsAndTemplate(List<Long> poolIds, Long templateId);

List<VMTemplateStoragePoolVO> listByTemplateStatus(long templateId, VMTemplateStoragePoolVO.Status downloadState);

List<VMTemplateStoragePoolVO> listByTemplateStatus(long templateId, VMTemplateStoragePoolVO.Status downloadState, long poolId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,16 @@ public VMTemplateStoragePoolVO findByPoolTemplate(long poolId, long templateId,
return findOneIncludingRemovedBy(sc);
}

@Override
public List<VMTemplateStoragePoolVO> listByPoolIdsAndTemplate(List<Long> poolIds, Long templateId) {
SearchCriteria<VMTemplateStoragePoolVO> sc = PoolTemplateSearch.create();
if (poolIds != null && !poolIds.isEmpty()) {
sc.setParameters("pool_id", poolIds.toArray());
}
sc.setParameters("template_id", templateId);
return listBy(sc);
}

@Override
public List<VMTemplateStoragePoolVO> listByTemplateStatus(long templateId, VMTemplateStoragePoolVO.Status downloadState) {
SearchCriteria<VMTemplateStoragePoolVO> sc = TemplateStatusSearch.create();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,6 @@ Pair<List<Long>, Integer> searchForIdsAndCount(Long storagePoolId, String storag
String keyword, Filter searchFilter);

List<StoragePoolVO> listByIds(List<Long> ids);

List<StoragePoolVO> listByDataCenterIds(List<Long> dataCenterIds);
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public class PrimaryDataStoreDaoImpl extends GenericDaoBase<StoragePoolVO, Long>
private final GenericSearchBuilder<StoragePoolVO, Long> StatusCountSearch;
private final SearchBuilder<StoragePoolVO> ClustersSearch;
private final SearchBuilder<StoragePoolVO> IdsSearch;
private final SearchBuilder<StoragePoolVO> DcsSearch;

@Inject
private StoragePoolDetailsDao _detailsDao;
Expand Down Expand Up @@ -155,6 +156,9 @@ public PrimaryDataStoreDaoImpl() {
IdsSearch.and("ids", IdsSearch.entity().getId(), SearchCriteria.Op.IN);
IdsSearch.done();

DcsSearch = createSearchBuilder();
DcsSearch.and("dataCenterId", DcsSearch.entity().getDataCenterId(), SearchCriteria.Op.IN);
DcsSearch.done();
}

@Override
Expand Down Expand Up @@ -733,6 +737,16 @@ public List<StoragePoolVO> listByIds(List<Long> ids) {
return listBy(sc);
}

@Override
public List<StoragePoolVO> listByDataCenterIds(List<Long> dataCenterIds) {
if (CollectionUtils.isEmpty(dataCenterIds)) {
return Collections.emptyList();
}
SearchCriteria<StoragePoolVO> sc = DcsSearch.create();
sc.setParameters("dataCenterId", dataCenterIds.toArray());
return listBy(sc);
}

private SearchCriteria<StoragePoolVO> createStoragePoolSearchCriteria(Long storagePoolId, String storagePoolName,
Long zoneId, String path, Long podId, Long clusterId, String address, ScopeType scopeType,
StoragePoolStatus status, String keyword) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -693,6 +693,9 @@ public boolean delete(TemplateProfile profile) {
if (success) {
if ((imageStores != null && imageStores.size() > 1) && (profile.getZoneIdList() != null)) {
//if template is stored in more than one image stores, and the zone id is not null, then don't delete other templates.
if (templateMgr.TemplateDeleteFromPrimaryStorage.value()) {
templateMgr.evictTemplateFromStoragePoolsForZones(template.getId(), profile.getZoneIdList());
}
return cleanupTemplate(template, success);
}

Expand Down Expand Up @@ -726,6 +729,10 @@ public boolean delete(TemplateProfile profile) {

}

if (templateMgr.TemplateDeleteFromPrimaryStorage.value()) {
templateMgr.evictTemplateFromStoragePoolsForZones(template.getId(), profile.getZoneIdList());
}

// remove its related ACL permission
Pair<Class<?>, Long> templateClassForId = new Pair<>(VirtualMachineTemplate.class, template.getId());
_messageBus.publish(_name, EntityManager.MESSAGE_REMOVE_ENTITY_EVENT, PublishScope.LOCAL, templateClassForId);
Expand Down
55 changes: 39 additions & 16 deletions server/src/main/java/com/cloud/template/TemplateManagerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -1024,33 +1024,53 @@ public boolean delete(long userId, long templateId, Long zoneId) {
return adapter.delete(new TemplateProfile(userId, template, zoneId));
}

private Boolean templateIsUnusedInPool(VMTemplateStoragePoolVO templatePoolVO) {
VMTemplateVO template = _tmpltDao.findByIdIncludingRemoved(templatePoolVO.getTemplateId());

// If this is a routing template, consider it in use
if (template.getTemplateType() == TemplateType.SYSTEM) {
return false;
}

// If the template is not yet downloaded to the pool, consider it in use
if (templatePoolVO.getDownloadState() != Status.DOWNLOADED) {
return false;
}

if (template.getFormat() == ImageFormat.ISO || _volumeDao.isAnyVolumeActivelyUsingTemplateOnPool(template.getId(), templatePoolVO.getPoolId())) {
return false;
}
return true;
}

@Override
public List<VMTemplateStoragePoolVO> getUnusedTemplatesInPool(StoragePoolVO pool) {
List<VMTemplateStoragePoolVO> unusedTemplatesInPool = new ArrayList<VMTemplateStoragePoolVO>();
List<VMTemplateStoragePoolVO> allTemplatesInPool = _tmpltPoolDao.listByPoolId(pool.getId());

for (VMTemplateStoragePoolVO templatePoolVO : allTemplatesInPool) {
VMTemplateVO template = _tmpltDao.findByIdIncludingRemoved(templatePoolVO.getTemplateId());

// If this is a routing template, consider it in use
if (template.getTemplateType() == TemplateType.SYSTEM) {
continue;
}

// If the template is not yet downloaded to the pool, consider it in
// use
if (templatePoolVO.getDownloadState() != Status.DOWNLOADED) {
continue;
}

if (template.getFormat() != ImageFormat.ISO && !_volumeDao.isAnyVolumeActivelyUsingTemplateOnPool(template.getId(), pool.getId())) {
if (templateIsUnusedInPool(templatePoolVO)) {
unusedTemplatesInPool.add(templatePoolVO);
}
}

return unusedTemplatesInPool;
}

@Override
public void evictTemplateFromStoragePoolsForZones(Long templateId, List<Long> zoneIds) {
List<Long> poolIds = new ArrayList<>();
if (zoneIds != null && !zoneIds.isEmpty()) {
List<StoragePoolVO> pools = _poolDao.listByDataCenterIds(zoneIds);
poolIds = pools.stream().map(StoragePoolVO::getId).collect(Collectors.toList());
}
List<VMTemplateStoragePoolVO> templateStoragePoolVOS = _tmpltPoolDao.listByPoolIdsAndTemplate(poolIds, templateId);
for (VMTemplateStoragePoolVO templateStoragePoolVO: templateStoragePoolVOS) {
if (templateIsUnusedInPool(templateStoragePoolVO)) {
evictTemplateFromStoragePool(templateStoragePoolVO);
}
}
}

@Override
@DB
public void evictTemplateFromStoragePool(VMTemplateStoragePoolVO templatePoolVO) {
Expand Down Expand Up @@ -2368,7 +2388,10 @@ public String getConfigComponentName() {

@Override
public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] {AllowPublicUserTemplates, TemplatePreloaderPoolSize, ValidateUrlIsResolvableBeforeRegisteringTemplate};
return new ConfigKey<?>[] {AllowPublicUserTemplates,
TemplatePreloaderPoolSize,
ValidateUrlIsResolvableBeforeRegisteringTemplate,
TemplateDeleteFromPrimaryStorage};
}

public List<TemplateAdapter> getTemplateAdapters() {
Expand Down
Loading