Skip to content

Commit 3e3545e

Browse files
committed
Implement enforcement of secondary storage copy limits for templates based on zone configuration
1 parent c1f7db2 commit 3e3545e

4 files changed

Lines changed: 76 additions & 11 deletions

File tree

engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/TemplateService.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ public TemplateInfo getTemplate() {
6767

6868
void handleTemplateSync(DataStore store);
6969

70+
void enforceSecStorageCopyLimit(long templateId, long zoneId);
71+
7072
void downloadBootstrapSysTemplate(DataStore store);
7173

7274
void addSystemVMTemplatesToSecondary(DataStore store);

engine/components-api/src/main/java/com/cloud/template/TemplateManager.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -153,11 +153,8 @@ public interface TemplateManager {
153153
List<DataStore> getImageStoreByTemplate(long templateId, Long zoneId);
154154

155155
/**
156-
* Returns the maximum number of secondary storage pools a template should be copied to in the given zone,
157-
* based on {@link #PublicTemplateSecStorageCopy} / {@link #PrivateTemplateSecStorageCopy}.
158-
* A return value of {@code 0} (or less) means "no limit" (copy to all secondary storage pools).
159-
* System, routing and builtin templates are always exempt from the limit (returns {@code 0}), as they
160-
* must be available on every secondary storage pool.
156+
* Max number of secondary storage copies for the template in this zone; {@code 0} means no limit.
157+
* SYSTEM/ROUTING/BUILTIN templates are always exempt (returns {@code 0}).
161158
*/
162159
int getSecStorageCopyLimit(VMTemplateVO template, long zoneId);
163160

engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/TemplateServiceImpl.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,63 @@ private boolean hasReachedSecStorageCopyLimit(VMTemplateVO template, long zoneId
325325
return count >= copyLimit;
326326
}
327327

328+
@Override
329+
public void enforceSecStorageCopyLimit(long templateId, long zoneId) {
330+
VMTemplateVO template = _templateDao.findById(templateId);
331+
if (template == null) {
332+
return;
333+
}
334+
int copyLimit = _tmpltMgr.getSecStorageCopyLimit(template, zoneId);
335+
if (copyLimit <= 0) {
336+
return;
337+
}
338+
if (_tmpltMgr.verifyHeuristicRulesForZone(template, zoneId) != null) {
339+
return;
340+
}
341+
GlobalLock lock = GlobalLock.getInternLock("template.copy.limit." + templateId + "." + zoneId);
342+
try {
343+
if (!lock.lock(30)) {
344+
logger.warn("Could not acquire lock to enforce secondary storage copy limit for template [{}] in zone [{}].",
345+
template.getUniqueName(), zoneId);
346+
return;
347+
}
348+
List<DataStore> stores = _storeMgr.getImageStoresByScope(new ZoneScope(zoneId));
349+
if (stores == null) {
350+
return;
351+
}
352+
List<TemplateDataStoreVO> removable = new ArrayList<>();
353+
for (DataStore ds : stores) {
354+
TemplateDataStoreVO ref = _vmTemplateStoreDao.findByStoreTemplate(ds.getId(), templateId);
355+
if (ref != null
356+
&& ref.getState() == State.Ready
357+
&& ref.getDownloadState() == Status.DOWNLOADED
358+
&& (ref.getRefCnt() == null || ref.getRefCnt() == 0)) {
359+
removable.add(ref);
360+
}
361+
}
362+
int excess = removable.size() - copyLimit;
363+
if (excess <= 0) {
364+
return;
365+
}
366+
logger.info("Template [{}] has [{}] removable secondary storage copies in zone [{}], limit is [{}]; removing [{}] excess copies.",
367+
template.getUniqueName(), removable.size(), zoneId, copyLimit, excess);
368+
for (int i = 0; i < excess; i++) {
369+
DataStore ds = _storeMgr.getDataStore(removable.get(i).getDataStoreId(), DataStoreRole.Image);
370+
try {
371+
deleteTemplateAsync(_templateFactory.getTemplate(templateId, ds));
372+
logger.info("Removed excess copy of template [{}] from image store [{}] to honor the secondary storage copy limit.",
373+
template.getUniqueName(), ds.getName());
374+
} catch (Exception e) {
375+
logger.warn("Failed to remove excess copy of template [{}] from image store [{}]: {}",
376+
template.getUniqueName(), ds, e.getMessage());
377+
}
378+
}
379+
} finally {
380+
lock.unlock();
381+
lock.releaseRef();
382+
}
383+
}
384+
328385
protected boolean shouldDownloadTemplateToStore(VMTemplateVO template, DataStore store) {
329386
Long zoneId = store.getScope().getScopeId();
330387
DataStore directedStore = _tmpltMgr.verifyHeuristicRulesForZone(template, zoneId);
@@ -619,6 +676,12 @@ public void handleTemplateSync(DataStore store) {
619676
}
620677
}
621678

679+
if (zoneId != null) {
680+
for (VMTemplateVO tmplt : allTemplates) {
681+
enforceSecStorageCopyLimit(tmplt.getId(), zoneId);
682+
}
683+
}
684+
622685
for (String uniqueName : templateInfos.keySet()) {
623686
TemplateProp tInfo = templateInfos.get(uniqueName);
624687
if (_tmpltMgr.templateIsDeleteable(tInfo.getId())) {

server/src/main/java/com/cloud/template/TemplateManagerImpl.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1693,6 +1693,12 @@ public void doInTransactionWithoutResult(TransactionStatus status) {
16931693
_launchPermissionDao.removeAllPermissions(id);
16941694
_messageBus.publish(_name, TemplateManager.MESSAGE_RESET_TEMPLATE_PERMISSION_EVENT, PublishScope.LOCAL, template.getId());
16951695
}
1696+
1697+
if (isPublic != null || isFeatured != null || "reset".equalsIgnoreCase(operation)) {
1698+
for (VMTemplateZoneVO templateZone : _tmpltZoneDao.listByTemplateId(template.getId())) {
1699+
_tmpltSvr.enforceSecStorageCopyLimit(template.getId(), templateZone.getZoneId());
1700+
}
1701+
}
16961702
return true;
16971703
}
16981704

@@ -2183,15 +2189,12 @@ public int getSecStorageCopyLimit(VMTemplateVO template, long zoneId) {
21832189
return 0;
21842190
}
21852191
TemplateType type = template.getTemplateType();
2186-
// System, routing and builtin templates must be available on every secondary storage pool,
2187-
// so they are never subject to the configured copy limit.
21882192
if (type == TemplateType.SYSTEM || type == TemplateType.ROUTING || type == TemplateType.BUILTIN) {
21892193
return 0;
21902194
}
2191-
boolean isPrivate = !template.isPublicTemplate() && !template.isFeatured();
2192-
return isPrivate
2193-
? PrivateTemplateSecStorageCopy.valueIn(zoneId)
2194-
: PublicTemplateSecStorageCopy.valueIn(zoneId);
2195+
return template.isPublicTemplate()
2196+
? PublicTemplateSecStorageCopy.valueIn(zoneId)
2197+
: PrivateTemplateSecStorageCopy.valueIn(zoneId);
21952198
}
21962199

21972200
@Override

0 commit comments

Comments
 (0)