Skip to content

Commit e8d57d1

Browse files
GutoVeroneziDaan Hoogland
authored andcommitted
Implement/fix limit validation for secondary storage
1 parent 86c9f7b commit e8d57d1

File tree

7 files changed

+687
-620
lines changed

7 files changed

+687
-620
lines changed

plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/manager/BareMetalTemplateAdapter.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,6 @@ public VMTemplateVO create(TemplateProfile profile) {
106106
}
107107
}
108108

109-
_resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template);
110109
return template;
111110
}
112111

server/src/main/java/com/cloud/storage/ImageStoreUploadMonitorImpl.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
import com.cloud.event.EventTypes;
6464
import com.cloud.event.UsageEventUtils;
6565
import com.cloud.exception.ConnectionException;
66+
import com.cloud.exception.ResourceAllocationException;
6667
import com.cloud.host.Host;
6768
import com.cloud.host.Status;
6869
import com.cloud.host.dao.HostDao;
@@ -543,6 +544,22 @@ public void doInTransactionWithoutResult(TransactionStatus status) {
543544
break;
544545
}
545546
}
547+
548+
Account owner = accountDao.findById(template.getAccountId());
549+
long templateSize = answer.getVirtualSize();
550+
551+
try (CheckedReservation secondaryStorageReservation = new CheckedReservation(owner, Resource.ResourceType.secondary_storage, null, null, templateSize, reservationDao, _resourceLimitMgr)) {
552+
_resourceLimitMgr.incrementResourceCount(owner.getId(), Resource.ResourceType.secondary_storage, templateSize);
553+
} catch (ResourceAllocationException e) {
554+
tmpTemplateDataStore.setDownloadState(VMTemplateStorageResourceAssoc.Status.UPLOAD_ERROR);
555+
tmpTemplateDataStore.setState(State.Failed);
556+
stateMachine.transitTo(tmpTemplate, VirtualMachineTemplate.Event.OperationFailed, null, _templateDao);
557+
msg = String.format("Upload of template [%s] failed because its owner [%s] does not have enough secondary storage space available.", template.getUuid(), owner.getUuid());
558+
logger.warn(msg);
559+
sendAlert = true;
560+
break;
561+
}
562+
546563
stateMachine.transitTo(tmpTemplate, VirtualMachineTemplate.Event.OperationSucceeded, null, _templateDao);
547564
//publish usage event
548565
String etype = EventTypes.EVENT_TEMPLATE_CREATE;

server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java

Lines changed: 275 additions & 276 deletions
Large diffs are not rendered by default.

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

Lines changed: 100 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,14 @@
3434
import org.apache.cloudstack.annotation.dao.AnnotationDao;
3535
import org.apache.cloudstack.api.ApiCommandResourceType;
3636
import org.apache.cloudstack.api.command.user.iso.DeleteIsoCmd;
37-
import org.apache.cloudstack.api.command.user.iso.GetUploadParamsForIsoCmd;
3837
import org.apache.cloudstack.api.command.user.iso.RegisterIsoCmd;
3938
import org.apache.cloudstack.api.command.user.template.DeleteTemplateCmd;
40-
import org.apache.cloudstack.api.command.user.template.GetUploadParamsForTemplateCmd;
4139
import org.apache.cloudstack.api.command.user.template.RegisterTemplateCmd;
4240
import org.apache.cloudstack.context.CallContext;
4341
import org.apache.cloudstack.direct.download.DirectDownloadManager;
42+
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
4443
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
44+
import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
4545
import org.apache.cloudstack.engine.subsystem.api.storage.Scope;
4646
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
4747
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateService;
@@ -57,22 +57,26 @@
5757
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
5858
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
5959
import org.apache.cloudstack.storage.image.datastore.ImageStoreEntity;
60+
import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
6061
import org.apache.cloudstack.utils.security.DigestHelper;
6162
import org.apache.commons.collections.CollectionUtils;
6263

6364
import com.cloud.agent.AgentManager;
6465
import com.cloud.agent.api.Answer;
6566
import com.cloud.alert.AlertManager;
67+
import com.cloud.configuration.Config;
6668
import com.cloud.configuration.Resource.ResourceType;
6769
import com.cloud.dc.DataCenterVO;
6870
import com.cloud.dc.dao.DataCenterDao;
71+
import com.cloud.domain.Domain;
6972
import com.cloud.deployasis.dao.TemplateDeployAsIsDetailsDao;
7073
import com.cloud.event.EventTypes;
7174
import com.cloud.event.UsageEventUtils;
7275
import com.cloud.exception.InvalidParameterValueException;
7376
import com.cloud.exception.ResourceAllocationException;
7477
import com.cloud.host.HostVO;
7578
import com.cloud.hypervisor.Hypervisor;
79+
import com.cloud.org.Grouping;
7680
import com.cloud.resource.ResourceManager;
7781
import com.cloud.storage.ScopeType;
7882
import com.cloud.storage.Storage.ImageFormat;
@@ -197,19 +201,6 @@ public TemplateProfile prepare(RegisterIsoCmd cmd) throws ResourceAllocationExce
197201
profile.setSize(templateSize);
198202
}
199203
profile.setUrl(url);
200-
// Check that the resource limit for secondary storage won't be exceeded
201-
_resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(cmd.getEntityOwnerId()),
202-
ResourceType.secondary_storage,
203-
UriUtils.getRemoteSize(url, followRedirects));
204-
return profile;
205-
}
206-
207-
@Override
208-
public TemplateProfile prepare(GetUploadParamsForIsoCmd cmd) throws ResourceAllocationException {
209-
TemplateProfile profile = super.prepare(cmd);
210-
211-
// Check that the resource limit for secondary storage won't be exceeded
212-
_resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(cmd.getEntityOwnerId()), ResourceType.secondary_storage);
213204
return profile;
214205
}
215206

@@ -228,19 +219,7 @@ public TemplateProfile prepare(RegisterTemplateCmd cmd) throws ResourceAllocatio
228219
profile.setForCks(cmd.isForCks());
229220
}
230221
profile.setUrl(url);
231-
// Check that the resource limit for secondary storage won't be exceeded
232-
_resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(cmd.getEntityOwnerId()),
233-
ResourceType.secondary_storage,
234-
UriUtils.getRemoteSize(url, followRedirects));
235-
return profile;
236-
}
237-
238-
@Override
239-
public TemplateProfile prepare(GetUploadParamsForTemplateCmd cmd) throws ResourceAllocationException {
240-
TemplateProfile profile = super.prepare(cmd);
241222

242-
// Check that the resource limit for secondary storage won't be exceeded
243-
_resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(cmd.getEntityOwnerId()), ResourceType.secondary_storage);
244223
return profile;
245224
}
246225

@@ -268,7 +247,6 @@ public VMTemplateVO create(TemplateProfile profile) {
268247
persistDirectDownloadTemplate(template.getId(), profile.getSize());
269248
}
270249

271-
_resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template);
272250
return template;
273251
}
274252

@@ -322,6 +300,44 @@ protected void validateSecondaryStorageAndCreateTemplate(List<DataStore> imageSt
322300
}
323301
}
324302

303+
protected boolean isZoneAndImageStoreAvailable(DataStore imageStore, Long zoneId, Set<Long> zoneSet, boolean isTemplatePrivate) {
304+
if (zoneId == null) {
305+
logger.warn(String.format("Zone ID is null, cannot allocate ISO/template in image store [%s].", imageStore));
306+
return false;
307+
}
308+
309+
DataCenterVO zone = _dcDao.findById(zoneId);
310+
if (zone == null) {
311+
logger.warn("Unable to find zone by id [{}], so skip downloading template to its image store [{}].", zoneId, imageStore);
312+
return false;
313+
}
314+
315+
if (Grouping.AllocationState.Disabled == zone.getAllocationState()) {
316+
logger.info("Zone [{}] is disabled. Skip downloading template to its image store [{}].", zone, imageStore);
317+
return false;
318+
}
319+
320+
if (!_statsCollector.imageStoreHasEnoughCapacity(imageStore)) {
321+
logger.info("Image store doesn't have enough capacity. Skip downloading template to this image store [{}].", imageStore);
322+
return false;
323+
}
324+
325+
if (zoneSet == null) {
326+
logger.info(String.format("Zone set is null; therefore, the ISO/template should be allocated in every secondary storage of zone [%s].", zone));
327+
return true;
328+
}
329+
330+
if (isTemplatePrivate && zoneSet.contains(zoneId)) {
331+
logger.info(String.format("The template is private and it is already allocated in a secondary storage in zone [%s]; therefore, image store [%s] will be skipped.",
332+
zone, imageStore));
333+
return false;
334+
}
335+
336+
logger.info(String.format("Private template will be allocated in image store [%s] in zone [%s].", imageStore, zone));
337+
zoneSet.add(zoneId);
338+
return true;
339+
}
340+
325341
@Override
326342
public List<TemplateOrVolumePostUploadCommand> createTemplateForPostUpload(final TemplateProfile profile) {
327343
// persist entry in vm_template, vm_template_details and template_zone_ref tables, not that entry at template_store_ref is not created here, and created in createTemplateAsync.
@@ -369,12 +385,67 @@ public List<TemplateOrVolumePostUploadCommand> doInTransaction(TransactionStatus
369385
if(payloads.isEmpty()) {
370386
throw new CloudRuntimeException("unable to find zone or an image store with enough capacity");
371387
}
372-
_resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template);
388+
373389
return payloads;
374390
}
375391
});
376392
}
377393

394+
/**
395+
* If the template/ISO is marked as private, then it is allocated to a random secondary storage; otherwise, allocates to every storage pool in every zone given by the
396+
* {@link TemplateProfile#getZoneIdList()}.
397+
*/
398+
protected void postUploadAllocation(List<DataStore> imageStores, VMTemplateVO template, List<TemplateOrVolumePostUploadCommand> payloads) {
399+
Set<Long> zoneSet = new HashSet<>();
400+
Collections.shuffle(imageStores);
401+
for (DataStore imageStore : imageStores) {
402+
Long zoneId_is = imageStore.getScope().getScopeId();
403+
404+
if (!isZoneAndImageStoreAvailable(imageStore, zoneId_is, zoneSet, isPrivateTemplate(template))) {
405+
continue;
406+
}
407+
408+
TemplateInfo tmpl = imageFactory.getTemplate(template.getId(), imageStore);
409+
410+
// persist template_store_ref entry
411+
DataObject templateOnStore = imageStore.create(tmpl);
412+
413+
// update template_store_ref and template state
414+
EndPoint ep = _epSelector.select(templateOnStore);
415+
if (ep == null) {
416+
String errMsg = String.format("There is no secondary storage VM for downloading template to image store %s", imageStore);
417+
logger.warn(errMsg);
418+
throw new CloudRuntimeException(errMsg);
419+
}
420+
421+
TemplateOrVolumePostUploadCommand payload = new TemplateOrVolumePostUploadCommand(template.getId(), template.getUuid(), tmpl.getInstallPath(), tmpl
422+
.getChecksum(), tmpl.getType().toString(), template.getUniqueName(), template.getFormat().toString(), templateOnStore.getDataStore().getUri(),
423+
templateOnStore.getDataStore().getRole().toString());
424+
//using the existing max template size configuration
425+
payload.setMaxUploadSize(_configDao.getValue(Config.MaxTemplateAndIsoSize.key()));
426+
427+
Long accountId = template.getAccountId();
428+
Account account = _accountDao.findById(accountId);
429+
Domain domain = _domainDao.findById(account.getDomainId());
430+
431+
payload.setDefaultMaxSecondaryStorageInGB(ByteScaleUtils.bytesToGibibytes(_resourceLimitMgr.findCorrectResourceLimitForAccountAndDomain(account, domain, ResourceType.secondary_storage, null)));
432+
payload.setAccountId(accountId);
433+
payload.setRemoteEndPoint(ep.getPublicAddr());
434+
payload.setRequiresHvm(template.requiresHvm());
435+
payload.setDescription(template.getDisplayText());
436+
payloads.add(payload);
437+
}
438+
}
439+
440+
protected boolean isPrivateTemplate(VMTemplateVO template){
441+
442+
// if public OR featured OR system template
443+
if(template.isPublicTemplate() || template.isFeatured() || template.getTemplateType() == TemplateType.SYSTEM)
444+
return false;
445+
else
446+
return true;
447+
}
448+
378449
private class CreateTemplateContext<T> extends AsyncRpcContext<T> {
379450
final TemplateInfo template;
380451

0 commit comments

Comments
 (0)