This is an automated email from the ASF dual-hosted git repository.
dahn pushed a commit to branch 4.20
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/4.20 by this push:
new 4d95f08a3ab Delete template from storage pool instantly if no volume
is using it (#11782)
4d95f08a3ab is described below
commit 4d95f08a3abc181ae050018f5dd7741ab34f8ca1
Author: Abhisar Sinha <[email protected]>
AuthorDate: Thu Oct 9 13:11:18 2025 +0530
Delete template from storage pool instantly if no volume is using it
(#11782)
---
.../java/com/cloud/template/TemplateManager.java | 9 ++++
.../com/cloud/storage/dao/VMTemplatePoolDao.java | 2 +
.../cloud/storage/dao/VMTemplatePoolDaoImpl.java | 10 ++++
.../storage/datastore/db/PrimaryDataStoreDao.java | 2 +
.../datastore/db/PrimaryDataStoreDaoImpl.java | 14 ++++++
.../cloud/template/HypervisorTemplateAdapter.java | 11 ++++-
.../com/cloud/template/TemplateManagerImpl.java | 55 +++++++++++++++-------
7 files changed, 85 insertions(+), 18 deletions(-)
diff --git
a/engine/components-api/src/main/java/com/cloud/template/TemplateManager.java
b/engine/components-api/src/main/java/com/cloud/template/TemplateManager.java
index b8912526fdf..a0f91882c4d 100644
---
a/engine/components-api/src/main/java/com/cloud/template/TemplateManager.java
+++
b/engine/components-api/src/main/java/com/cloud/template/TemplateManager.java
@@ -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",
+ 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";
@@ -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.
*
diff --git
a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplatePoolDao.java
b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplatePoolDao.java
index a3ce03a74c3..a208590e23a 100644
--- a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplatePoolDao.java
+++ b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplatePoolDao.java
@@ -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);
diff --git
a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplatePoolDaoImpl.java
b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplatePoolDaoImpl.java
index 5a2ec1163fb..5dfc138d8e1 100644
---
a/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplatePoolDaoImpl.java
+++
b/engine/schema/src/main/java/com/cloud/storage/dao/VMTemplatePoolDaoImpl.java
@@ -150,6 +150,16 @@ public class VMTemplatePoolDaoImpl extends
GenericDaoBase<VMTemplateStoragePoolV
return findOneIncludingRemovedBy(sc);
}
+ @Override
+ public List<VMTemplateStoragePoolVO> listByPoolIdsAndTemplate(List<Long>
poolIds, Long templateId) {
+ SearchCriteria<VMTemplateStoragePoolVO> sc =
PoolTemplateSearch.create();
+ if (CollectionUtils.isNotEmpty(poolIds)) {
+ 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();
diff --git
a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java
b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java
index f0c235e842c..102df6848f8 100644
---
a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java
+++
b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java
@@ -154,4 +154,6 @@ public interface PrimaryDataStoreDao extends
GenericDao<StoragePoolVO, Long> {
String keyword, Filter searchFilter);
List<StoragePoolVO> listByIds(List<Long> ids);
+
+ List<StoragePoolVO> listByDataCenterIds(List<Long> dataCenterIds);
}
diff --git
a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java
b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java
index cb7313954dc..0d901c82306 100644
---
a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java
+++
b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDaoImpl.java
@@ -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;
@@ -155,6 +156,9 @@ public class PrimaryDataStoreDaoImpl extends
GenericDaoBase<StoragePoolVO, Long>
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
@@ -733,6 +737,16 @@ public class PrimaryDataStoreDaoImpl extends
GenericDaoBase<StoragePoolVO, Long>
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) {
diff --git
a/server/src/main/java/com/cloud/template/HypervisorTemplateAdapter.java
b/server/src/main/java/com/cloud/template/HypervisorTemplateAdapter.java
index f7eb654141d..8d38aba0e7e 100644
--- a/server/src/main/java/com/cloud/template/HypervisorTemplateAdapter.java
+++ b/server/src/main/java/com/cloud/template/HypervisorTemplateAdapter.java
@@ -627,7 +627,7 @@ public class HypervisorTemplateAdapter extends
TemplateAdapterBase {
boolean dataDiskDeletetionResult = true;
List<VMTemplateVO> dataDiskTemplates =
templateDao.listByParentTemplatetId(template.getId());
- if (dataDiskTemplates != null && dataDiskTemplates.size() > 0)
{
+ if (CollectionUtils.isNotEmpty(dataDiskTemplates)) {
logger.info("Template: {} has Datadisk template(s)
associated with it. Delete Datadisk templates before deleting the template",
template);
for (VMTemplateVO dataDiskTemplate : dataDiskTemplates) {
logger.info("Delete Datadisk template: {} from image
store: {}", dataDiskTemplate, imageStore);
@@ -693,6 +693,9 @@ public class HypervisorTemplateAdapter extends
TemplateAdapterBase {
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);
}
@@ -705,7 +708,7 @@ public class HypervisorTemplateAdapter extends
TemplateAdapterBase {
// find all eligible image stores for this template
List<DataStore> iStores =
templateMgr.getImageStoreByTemplate(template.getId(), null);
- if (iStores == null || iStores.size() == 0) {
+ if (CollectionUtils.isEmpty(iStores)) {
// remove any references from template_zone_ref
List<VMTemplateZoneVO> templateZones =
templateZoneDao.listByTemplateId(template.getId());
if (templateZones != null) {
@@ -726,6 +729,10 @@ public class HypervisorTemplateAdapter extends
TemplateAdapterBase {
}
+ 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);
diff --git a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java
b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java
index cf88ccec919..5aa31eff170 100755
--- a/server/src/main/java/com/cloud/template/TemplateManagerImpl.java
+++ b/server/src/main/java/com/cloud/template/TemplateManagerImpl.java
@@ -1024,33 +1024,53 @@ public class TemplateManagerImpl extends ManagerBase
implements TemplateManager,
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 (CollectionUtils.isNotEmpty(zoneIds)) {
+ 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) {
@@ -2368,7 +2388,10 @@ public class TemplateManagerImpl extends ManagerBase
implements TemplateManager,
@Override
public ConfigKey<?>[] getConfigKeys() {
- return new ConfigKey<?>[] {AllowPublicUserTemplates,
TemplatePreloaderPoolSize, ValidateUrlIsResolvableBeforeRegisteringTemplate};
+ return new ConfigKey<?>[] {AllowPublicUserTemplates,
+ TemplatePreloaderPoolSize,
+ ValidateUrlIsResolvableBeforeRegisteringTemplate,
+ TemplateDeleteFromPrimaryStorage};
}
public List<TemplateAdapter> getTemplateAdapters() {