mirror of https://github.com/apache/cloudstack.git
[22.0] secondary storage resource limit for download
This commit is contained in:
parent
95816b44e9
commit
4855d40e6e
|
|
@ -23,9 +23,10 @@ import org.apache.cloudstack.api.InternalIdentity;
|
|||
|
||||
public interface VMTemplateStorageResourceAssoc extends InternalIdentity {
|
||||
public static enum Status {
|
||||
UNKNOWN, DOWNLOAD_ERROR, NOT_DOWNLOADED, DOWNLOAD_IN_PROGRESS, DOWNLOADED, ABANDONED, UPLOADED, NOT_UPLOADED, UPLOAD_ERROR, UPLOAD_IN_PROGRESS, CREATING, CREATED, BYPASSED
|
||||
UNKNOWN, DOWNLOAD_ERROR, NOT_DOWNLOADED, DOWNLOAD_IN_PROGRESS, DOWNLOADED, ABANDONED, LIMIT_REACHED, UPLOADED, NOT_UPLOADED, UPLOAD_ERROR, UPLOAD_IN_PROGRESS, CREATING, CREATED, BYPASSED
|
||||
}
|
||||
|
||||
List<Status> ERROR_DOWNLOAD_STATES = List.of(Status.DOWNLOAD_ERROR, Status.ABANDONED, Status.LIMIT_REACHED, Status.UNKNOWN);
|
||||
List<Status> PENDING_DOWNLOAD_STATES = List.of(Status.NOT_DOWNLOADED, Status.DOWNLOAD_IN_PROGRESS);
|
||||
|
||||
String getInstallPath();
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ public class DownloadAnswer extends Answer {
|
|||
}
|
||||
|
||||
public Long getTemplateSize() {
|
||||
return templateSize;
|
||||
return templateSize == 0 ? templatePhySicalSize : templateSize;
|
||||
}
|
||||
|
||||
public void setTemplatePhySicalSize(long templatePhySicalSize) {
|
||||
|
|
|
|||
|
|
@ -230,8 +230,10 @@ public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver {
|
|||
updateBuilder.setJobId(answer.getJobId());
|
||||
updateBuilder.setLocalDownloadPath(answer.getDownloadPath());
|
||||
updateBuilder.setInstallPath(answer.getInstallPath());
|
||||
updateBuilder.setSize(answer.getTemplateSize());
|
||||
updateBuilder.setPhysicalSize(answer.getTemplatePhySicalSize());
|
||||
if (!VMTemplateStorageResourceAssoc.ERROR_DOWNLOAD_STATES.contains(answer.getDownloadStatus())) {
|
||||
updateBuilder.setSize(answer.getTemplateSize());
|
||||
updateBuilder.setPhysicalSize(answer.getTemplatePhySicalSize());
|
||||
}
|
||||
_templateStoreDao.update(tmpltStoreVO.getId(), updateBuilder);
|
||||
// update size in vm_template table
|
||||
VMTemplateVO tmlptUpdater = _templateDao.createForUpdate();
|
||||
|
|
@ -241,8 +243,7 @@ public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver {
|
|||
|
||||
AsyncCompletionCallback<CreateCmdResult> caller = context.getParentCallback();
|
||||
|
||||
if (answer.getDownloadStatus() == VMTemplateStorageResourceAssoc.Status.DOWNLOAD_ERROR ||
|
||||
answer.getDownloadStatus() == VMTemplateStorageResourceAssoc.Status.ABANDONED || answer.getDownloadStatus() == VMTemplateStorageResourceAssoc.Status.UNKNOWN) {
|
||||
if (VMTemplateStorageResourceAssoc.ERROR_DOWNLOAD_STATES.contains(answer.getDownloadStatus())) {
|
||||
CreateCmdResult result = new CreateCmdResult(null, null);
|
||||
result.setSuccess(false);
|
||||
result.setResult(answer.getErrorString());
|
||||
|
|
|
|||
|
|
@ -269,7 +269,7 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
|
|||
|
||||
templateSizeSearch = _vmTemplateStoreDao.createSearchBuilder(SumCount.class);
|
||||
templateSizeSearch.select("sum", Func.SUM, templateSizeSearch.entity().getSize());
|
||||
templateSizeSearch.and("downloadState", templateSizeSearch.entity().getDownloadState(), Op.EQ);
|
||||
templateSizeSearch.and("downloadState", templateSizeSearch.entity().getDownloadState(), Op.IN);
|
||||
templateSizeSearch.and("destroyed", templateSizeSearch.entity().getDestroyed(), Op.EQ);
|
||||
SearchBuilder<VMTemplateVO> join1 = _vmTemplateDao.createSearchBuilder();
|
||||
join1.and("accountId", join1.entity().getAccountId(), Op.EQ);
|
||||
|
|
@ -1493,7 +1493,7 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
|
|||
long totalTemplatesSize = 0;
|
||||
|
||||
SearchCriteria<SumCount> sc = templateSizeSearch.create();
|
||||
sc.setParameters("downloadState", Status.DOWNLOADED);
|
||||
sc.setParameters("downloadState", Status.DOWNLOADED, Status.DOWNLOAD_IN_PROGRESS);
|
||||
sc.setParameters("destroyed", false);
|
||||
sc.setJoinParameters("templates", "accountId", accountId);
|
||||
List<SumCount> templates = _vmTemplateStoreDao.customSearch(sc, null);
|
||||
|
|
|
|||
|
|
@ -95,6 +95,11 @@ public abstract class DownloadActiveState extends DownloadState {
|
|||
return Status.ABANDONED.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String handleLimitReached() {
|
||||
return Status.LIMIT_REACHED.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String handleDisconnect() {
|
||||
|
||||
|
|
|
|||
|
|
@ -60,6 +60,11 @@ public class DownloadErrorState extends DownloadInactiveState {
|
|||
return Status.ABANDONED.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String handleLimitReached() {
|
||||
return Status.LIMIT_REACHED.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return Status.DOWNLOAD_ERROR.toString();
|
||||
|
|
|
|||
|
|
@ -36,6 +36,12 @@ public abstract class DownloadInactiveState extends DownloadState {
|
|||
return getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String handleLimitReached() {
|
||||
// ignore and stay put
|
||||
return getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String handleDisconnect() {
|
||||
//ignore and stay put
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
// Licensed to the Apache Software Foundation (ASF) under one
|
||||
// or more contributor license agreements. See the NOTICE file
|
||||
// distributed with this work for additional information
|
||||
// regarding copyright ownership. The ASF licenses this file
|
||||
// to you under the Apache License, Version 2.0 (the
|
||||
// "License"); you may not use this file except in compliance
|
||||
// with the License. You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing,
|
||||
// software distributed under the License is distributed on an
|
||||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations
|
||||
// under the License.
|
||||
package com.cloud.storage.download;
|
||||
|
||||
import org.apache.cloudstack.storage.command.DownloadProgressCommand.RequestType;
|
||||
import org.apache.logging.log4j.Level;
|
||||
|
||||
import com.cloud.agent.api.storage.DownloadAnswer;
|
||||
import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
|
||||
|
||||
public class DownloadLimitReachedState extends DownloadInactiveState {
|
||||
|
||||
public DownloadLimitReachedState(DownloadListener dl) {
|
||||
super(dl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return Status.LIMIT_REACHED.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String handleEvent(DownloadEvent event, Object eventObj) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
getDownloadListener().log("handleEvent, event type=" + event + ", curr state=" + getName(), Level.TRACE);
|
||||
}
|
||||
return Status.LIMIT_REACHED.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEntry(String prevState, DownloadEvent event, Object evtObj) {
|
||||
if (!prevState.equalsIgnoreCase(getName())) {
|
||||
DownloadAnswer answer = new DownloadAnswer("Storage Limit Reached", Status.LIMIT_REACHED);
|
||||
getDownloadListener().callback(answer);
|
||||
getDownloadListener().cancelStatusTask();
|
||||
getDownloadListener().cancelTimeoutTask();
|
||||
getDownloadListener().scheduleImmediateStatusCheck(RequestType.PURGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -25,6 +25,15 @@ import java.util.Timer;
|
|||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.cloud.configuration.Resource;
|
||||
import com.cloud.resourcelimit.CheckedReservation;
|
||||
import com.cloud.storage.VMTemplateVO;
|
||||
import com.cloud.storage.VolumeVO;
|
||||
import com.cloud.storage.dao.VMTemplateDao;
|
||||
import com.cloud.storage.dao.VolumeDao;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.AccountManager;
|
||||
import com.cloud.user.ResourceLimitService;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
|
||||
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
|
||||
|
|
@ -34,10 +43,13 @@ import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService;
|
|||
import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
|
||||
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
|
||||
import org.apache.cloudstack.managed.context.ManagedContextTimerTask;
|
||||
import org.apache.cloudstack.reservation.dao.ReservationDao;
|
||||
import org.apache.cloudstack.storage.command.DownloadCommand;
|
||||
import org.apache.cloudstack.storage.command.DownloadCommand.ResourceType;
|
||||
import org.apache.cloudstack.storage.command.DownloadProgressCommand;
|
||||
import org.apache.cloudstack.storage.command.DownloadProgressCommand.RequestType;
|
||||
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
|
||||
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
|
||||
import org.apache.cloudstack.utils.cache.LazyCache;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
|
@ -107,6 +119,7 @@ public class DownloadListener implements Listener {
|
|||
public static final String DOWNLOAD_ERROR = Status.DOWNLOAD_ERROR.toString();
|
||||
public static final String DOWNLOAD_IN_PROGRESS = Status.DOWNLOAD_IN_PROGRESS.toString();
|
||||
public static final String DOWNLOAD_ABANDONED = Status.ABANDONED.toString();
|
||||
public static final String DOWNLOAD_LIMIT_REACHED = Status.LIMIT_REACHED.toString();
|
||||
|
||||
private EndPoint _ssAgent;
|
||||
|
||||
|
|
@ -137,6 +150,18 @@ public class DownloadListener implements Listener {
|
|||
private DataStoreManager _storeMgr;
|
||||
@Inject
|
||||
private VolumeService _volumeSrv;
|
||||
@Inject
|
||||
private VMTemplateDao _templateDao;
|
||||
@Inject
|
||||
private TemplateDataStoreDao _templateDataStoreDao;
|
||||
@Inject
|
||||
private VolumeDao _volumeDao;
|
||||
@Inject
|
||||
private ResourceLimitService _resourceLimitMgr;
|
||||
@Inject
|
||||
private AccountManager _accountMgr;
|
||||
@Inject
|
||||
ReservationDao _reservationDao;
|
||||
|
||||
private LazyCache<Long, List<Hypervisor.HypervisorType>> zoneHypervisorsCache;
|
||||
|
||||
|
|
@ -160,7 +185,7 @@ public class DownloadListener implements Listener {
|
|||
_downloadMonitor = downloadMonitor;
|
||||
_cmd = cmd;
|
||||
initStateMachine();
|
||||
_currState = getState(Status.NOT_DOWNLOADED.toString());
|
||||
_currState = getState(NOT_DOWNLOADED);
|
||||
this._timer = timer;
|
||||
_timeoutTask = new TimeoutTask(this);
|
||||
this._timer.schedule(_timeoutTask, 3 * STATUS_POLL_INTERVAL);
|
||||
|
|
@ -184,11 +209,12 @@ public class DownloadListener implements Listener {
|
|||
}
|
||||
|
||||
private void initStateMachine() {
|
||||
_stateMap.put(Status.NOT_DOWNLOADED.toString(), new NotDownloadedState(this));
|
||||
_stateMap.put(Status.DOWNLOADED.toString(), new DownloadCompleteState(this));
|
||||
_stateMap.put(Status.DOWNLOAD_ERROR.toString(), new DownloadErrorState(this));
|
||||
_stateMap.put(Status.DOWNLOAD_IN_PROGRESS.toString(), new DownloadInProgressState(this));
|
||||
_stateMap.put(Status.ABANDONED.toString(), new DownloadAbandonedState(this));
|
||||
_stateMap.put(NOT_DOWNLOADED, new NotDownloadedState(this));
|
||||
_stateMap.put(DOWNLOADED, new DownloadCompleteState(this));
|
||||
_stateMap.put(DOWNLOAD_ERROR, new DownloadErrorState(this));
|
||||
_stateMap.put(DOWNLOAD_IN_PROGRESS, new DownloadInProgressState(this));
|
||||
_stateMap.put(DOWNLOAD_ABANDONED, new DownloadAbandonedState(this));
|
||||
_stateMap.put(DOWNLOAD_LIMIT_REACHED, new DownloadLimitReachedState(this));
|
||||
}
|
||||
|
||||
private DownloadState getState(String stateName) {
|
||||
|
|
@ -239,10 +265,53 @@ public class DownloadListener implements Listener {
|
|||
return false;
|
||||
}
|
||||
|
||||
private Long getAccountIdForDataObject() {
|
||||
if (object == null) {
|
||||
return null;
|
||||
}
|
||||
if (DataObjectType.TEMPLATE.equals(object.getType())) {
|
||||
VMTemplateVO t = _templateDao.findById(object.getId());
|
||||
return t != null ? t.getAccountId() : null;
|
||||
} else if (DataObjectType.VOLUME.equals(object.getType())) {
|
||||
VolumeVO v = _volumeDao.findById(object.getId());
|
||||
return v != null ? v.getAccountId() : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Long getSizeFromDB() {
|
||||
Long lastSize = 0L;
|
||||
if (DataObjectType.TEMPLATE.equals(object.getType())) {
|
||||
TemplateDataStoreVO t = _templateDataStoreDao.findByStoreTemplate(object.getDataStore().getId(), object.getId());
|
||||
lastSize = t.getSize();
|
||||
} else if (DataObjectType.VOLUME.equals(object.getType())) {
|
||||
VolumeVO v = _volumeDao.findById(object.getId());
|
||||
lastSize = v.getSize();
|
||||
}
|
||||
return lastSize;
|
||||
}
|
||||
|
||||
private Boolean checkAndUpdateResourceLimits(DownloadAnswer answer) {
|
||||
Long lastSize = getSizeFromDB();
|
||||
Long currentSize = answer.getTemplateSize();
|
||||
|
||||
if (currentSize > lastSize) {
|
||||
Long accountId = getAccountIdForDataObject();
|
||||
Account account = _accountMgr.getAccount(accountId);
|
||||
Long usage = currentSize - lastSize;
|
||||
try (CheckedReservation secStorageReservation = new CheckedReservation(account, Resource.ResourceType.secondary_storage, usage, _reservationDao, _resourceLimitMgr)) {
|
||||
_resourceLimitMgr.incrementResourceCount(accountId, Resource.ResourceType.secondary_storage, usage);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean processAnswers(long agentId, long seq, Answer[] answers) {
|
||||
boolean processed = false;
|
||||
if (answers != null & answers.length > 0) {
|
||||
if (answers != null && answers.length > 0) {
|
||||
if (answers[0] instanceof DownloadAnswer) {
|
||||
final DownloadAnswer answer = (DownloadAnswer)answers[0];
|
||||
if (getJobId() == null) {
|
||||
|
|
@ -250,7 +319,11 @@ public class DownloadListener implements Listener {
|
|||
} else if (!getJobId().equalsIgnoreCase(answer.getJobId())) {
|
||||
return false;//TODO
|
||||
}
|
||||
transition(DownloadEvent.DOWNLOAD_ANSWER, answer);
|
||||
if (!checkAndUpdateResourceLimits(answer)) {
|
||||
transition(DownloadEvent.LIMIT_REACHED, answer);
|
||||
} else {
|
||||
transition(DownloadEvent.DOWNLOAD_ANSWER, answer);
|
||||
}
|
||||
processed = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import com.cloud.agent.api.storage.DownloadAnswer;
|
|||
|
||||
public abstract class DownloadState {
|
||||
public static enum DownloadEvent {
|
||||
DOWNLOAD_ANSWER, ABANDON_DOWNLOAD, TIMEOUT_CHECK, DISCONNECT
|
||||
DOWNLOAD_ANSWER, ABANDON_DOWNLOAD, LIMIT_REACHED, TIMEOUT_CHECK, DISCONNECT
|
||||
};
|
||||
|
||||
protected Logger logger = LogManager.getLogger(getClass());
|
||||
|
|
@ -51,6 +51,8 @@ public abstract class DownloadState {
|
|||
return handleAnswer(answer);
|
||||
case ABANDON_DOWNLOAD:
|
||||
return handleAbort();
|
||||
case LIMIT_REACHED:
|
||||
return handleLimitReached();
|
||||
case TIMEOUT_CHECK:
|
||||
Date now = new Date();
|
||||
long update = now.getTime() - dl.getLastUpdated().getTime();
|
||||
|
|
@ -78,6 +80,8 @@ public abstract class DownloadState {
|
|||
|
||||
public abstract String handleAbort();
|
||||
|
||||
public abstract String handleLimitReached();
|
||||
|
||||
public abstract String handleDisconnect();
|
||||
|
||||
public abstract String handleAnswer(DownloadAnswer answer);
|
||||
|
|
|
|||
|
|
@ -388,13 +388,12 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
|
|||
CreateTemplateContext<TemplateApiResult> context) {
|
||||
TemplateApiResult result = callback.getResult();
|
||||
TemplateInfo template = context.template;
|
||||
VMTemplateVO tmplt = _tmpltDao.findById(template.getId());
|
||||
if (result.isSuccess()) {
|
||||
VMTemplateVO tmplt = _tmpltDao.findById(template.getId());
|
||||
// need to grant permission for public templates
|
||||
if (tmplt.isPublicTemplate()) {
|
||||
_messageBus.publish(_name, TemplateManager.MESSAGE_REGISTER_PUBLIC_TEMPLATE_EVENT, PublishScope.LOCAL, tmplt.getId());
|
||||
}
|
||||
long accountId = tmplt.getAccountId();
|
||||
if (template.getSize() != null) {
|
||||
// publish usage event
|
||||
String etype = EventTypes.EVENT_TEMPLATE_CREATE;
|
||||
|
|
@ -423,9 +422,13 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
|
|||
UsageEventUtils.publishUsageEvent(etype, template.getAccountId(), -1, template.getId(), template.getName(), null, null, physicalSize,
|
||||
template.getSize(), VirtualMachineTemplate.class.getName(), template.getUuid());
|
||||
}
|
||||
_resourceLimitMgr.incrementResourceCount(accountId, ResourceType.secondary_storage, template.getSize());
|
||||
}
|
||||
}
|
||||
if (tmplt != null) {
|
||||
long accountId = tmplt.getAccountId();
|
||||
Account account = _accountDao.findById(accountId);
|
||||
_resourceLimitMgr.recalculateResourceCount(accountId, account.getDomainId(), ResourceType.secondary_storage.getOrdinal());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
@ -554,7 +557,7 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase {
|
|||
}
|
||||
} else {
|
||||
logger.warn("Template: {} won't be deleted from image store: {} " +
|
||||
"because deletion of one of the Datadisk templates that belonged to the template failed", template, imageStore);
|
||||
"because deletion of one of the Datadisk Templates that belonged to the Template failed", template, imageStore);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -999,7 +999,7 @@ public class DownloadManagerImpl extends ManagerBase implements DownloadManager
|
|||
break; // TODO
|
||||
}
|
||||
return new DownloadAnswer(jobId, getDownloadPct(jobId), getDownloadError(jobId), getDownloadStatus2(jobId), getDownloadLocalPath(jobId), getInstallPath(jobId),
|
||||
getDownloadTemplateSize(jobId), getDownloadTemplatePhysicalSize(jobId), getDownloadCheckSum(jobId));
|
||||
getDownloadTemplateSize(jobId), td.getDownloadedBytes(), getDownloadCheckSum(jobId));
|
||||
}
|
||||
|
||||
private String getInstallPath(String jobId) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue