mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
server: fix resource reservation leakage (#9169)
Co-authored-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
This commit is contained in:
parent
be552fdce9
commit
cc8dc84f64
@ -38,7 +38,10 @@ public interface ResourceLimitService {
|
|||||||
static final ConfigKey<Long> MaxProjectSecondaryStorage = new ConfigKey<>("Project Defaults", Long.class, "max.project.secondary.storage", "400",
|
static final ConfigKey<Long> MaxProjectSecondaryStorage = new ConfigKey<>("Project Defaults", Long.class, "max.project.secondary.storage", "400",
|
||||||
"The default maximum secondary storage space (in GiB) that can be used for a project", false);
|
"The default maximum secondary storage space (in GiB) that can be used for a project", false);
|
||||||
static final ConfigKey<Long> ResourceCountCheckInterval = new ConfigKey<>("Advanced", Long.class, "resourcecount.check.interval", "300",
|
static final ConfigKey<Long> ResourceCountCheckInterval = new ConfigKey<>("Advanced", Long.class, "resourcecount.check.interval", "300",
|
||||||
"Time (in seconds) to wait before running resource recalculation and fixing task. Default is 300 seconds, Setting this to 0 disables execution of the task", true);
|
"Time (in seconds) to wait before running resource recalculation and fixing tasks like stale resource reservation cleanup" +
|
||||||
|
". Default is 300 seconds, Setting this to 0 disables execution of the task", true);
|
||||||
|
static final ConfigKey<Long> ResourceReservationCleanupDelay = new ConfigKey<>("Advanced", Long.class, "resource.reservation.cleanup.delay", "3600",
|
||||||
|
"Time (in seconds) after which a resource reservation gets deleted. Default is 3600 seconds, Setting this to 0 disables execution of the task", true);
|
||||||
static final ConfigKey<String> ResourceLimitHostTags = new ConfigKey<>("Advanced", String.class, "resource.limit.host.tags", "",
|
static final ConfigKey<String> ResourceLimitHostTags = new ConfigKey<>("Advanced", String.class, "resource.limit.host.tags", "",
|
||||||
"A comma-separated list of tags for host resource limits", true);
|
"A comma-separated list of tags for host resource limits", true);
|
||||||
static final ConfigKey<String> ResourceLimitStorageTags = new ConfigKey<>("Advanced", String.class, "resource.limit.storage.tags", "",
|
static final ConfigKey<String> ResourceLimitStorageTags = new ConfigKey<>("Advanced", String.class, "resource.limit.storage.tags", "",
|
||||||
|
|||||||
@ -34,8 +34,11 @@ import org.apache.cloudstack.context.CallContext;
|
|||||||
import com.cloud.configuration.ResourceCount;
|
import com.cloud.configuration.ResourceCount;
|
||||||
import com.cloud.user.Account;
|
import com.cloud.user.Account;
|
||||||
|
|
||||||
@APICommand(name = "updateResourceCount", description = "Recalculate and update resource count for an account or domain.", responseObject = ResourceCountResponse.class,
|
@APICommand(name = "updateResourceCount",
|
||||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
|
description = "Recalculate and update resource count for an account or domain. " +
|
||||||
|
"This also executes some cleanup tasks before calculating resource counts.",
|
||||||
|
responseObject = ResourceCountResponse.class,
|
||||||
|
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
|
||||||
public class UpdateResourceCountCmd extends BaseCmd {
|
public class UpdateResourceCountCmd extends BaseCmd {
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -22,6 +22,8 @@ import org.apache.cloudstack.api.InternalIdentity;
|
|||||||
|
|
||||||
import com.cloud.configuration.Resource;
|
import com.cloud.configuration.Resource;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* an interface defining an {code}AutoClosable{code} reservation object
|
* an interface defining an {code}AutoClosable{code} reservation object
|
||||||
*/
|
*/
|
||||||
@ -39,4 +41,6 @@ ResourceReservation extends InternalIdentity {
|
|||||||
String getTag();
|
String getTag();
|
||||||
|
|
||||||
Long getReservedAmount();
|
Long getReservedAmount();
|
||||||
|
|
||||||
|
Date getCreated();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,10 +25,14 @@ import javax.persistence.GenerationType;
|
|||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
import com.cloud.utils.db.GenericDao;
|
||||||
import org.apache.cloudstack.user.ResourceReservation;
|
import org.apache.cloudstack.user.ResourceReservation;
|
||||||
|
|
||||||
import com.cloud.configuration.Resource;
|
import com.cloud.configuration.Resource;
|
||||||
import com.cloud.utils.exception.CloudRuntimeException;
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
|
import org.apache.cloudstack.utils.identity.ManagementServerNode;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "resource_reservation")
|
@Table(name = "resource_reservation")
|
||||||
@ -57,6 +61,12 @@ public class ReservationVO implements ResourceReservation {
|
|||||||
@Column(name = "amount")
|
@Column(name = "amount")
|
||||||
long amount;
|
long amount;
|
||||||
|
|
||||||
|
@Column(name = "mgmt_server_id")
|
||||||
|
Long managementServerId;
|
||||||
|
|
||||||
|
@Column(name = GenericDao.CREATED_COLUMN)
|
||||||
|
private Date created;
|
||||||
|
|
||||||
protected ReservationVO() {
|
protected ReservationVO() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,6 +79,7 @@ public class ReservationVO implements ResourceReservation {
|
|||||||
this.resourceType = resourceType;
|
this.resourceType = resourceType;
|
||||||
this.tag = tag;
|
this.tag = tag;
|
||||||
this.amount = delta;
|
this.amount = delta;
|
||||||
|
this.managementServerId = ManagementServerNode.getManagementServerId();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReservationVO(Long accountId, Long domainId, Resource.ResourceType resourceType, Long delta) {
|
public ReservationVO(Long accountId, Long domainId, Resource.ResourceType resourceType, Long delta) {
|
||||||
@ -114,4 +125,16 @@ public class ReservationVO implements ResourceReservation {
|
|||||||
this.resourceId = resourceId;
|
this.resourceId = resourceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Date getCreated() {
|
||||||
|
return created;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreated(Date created) {
|
||||||
|
this.created = created;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getManagementServerId() {
|
||||||
|
return managementServerId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,13 +23,17 @@ import org.apache.cloudstack.reservation.ReservationVO;
|
|||||||
import com.cloud.configuration.Resource;
|
import com.cloud.configuration.Resource;
|
||||||
import com.cloud.utils.db.GenericDao;
|
import com.cloud.utils.db.GenericDao;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface ReservationDao extends GenericDao<ReservationVO, Long> {
|
public interface ReservationDao extends GenericDao<ReservationVO, Long> {
|
||||||
long getAccountReservation(Long account, Resource.ResourceType resourceType, String tag);
|
long getAccountReservation(Long account, Resource.ResourceType resourceType, String tag);
|
||||||
long getDomainReservation(Long domain, Resource.ResourceType resourceType, String tag);
|
long getDomainReservation(Long domain, Resource.ResourceType resourceType, String tag);
|
||||||
void setResourceId(Resource.ResourceType type, Long resourceId);
|
void setResourceId(Resource.ResourceType type, Long resourceId);
|
||||||
List<Long> getResourceIds(long accountId, Resource.ResourceType type);
|
|
||||||
List<ReservationVO> getReservationsForAccount(long accountId, Resource.ResourceType type, String tag);
|
List<ReservationVO> getReservationsForAccount(long accountId, Resource.ResourceType type, String tag);
|
||||||
void removeByIds(List<Long> reservationIds);
|
void removeByIds(List<Long> reservationIds);
|
||||||
|
|
||||||
|
int removeByMsId(long managementServerId);
|
||||||
|
|
||||||
|
int removeStaleReservations(Long accountId, Resource.ResourceType resourceType, String tag, Date createdBefore);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,8 +18,8 @@
|
|||||||
//
|
//
|
||||||
package org.apache.cloudstack.reservation.dao;
|
package org.apache.cloudstack.reservation.dao;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import org.apache.cloudstack.context.CallContext;
|
import org.apache.cloudstack.context.CallContext;
|
||||||
import org.apache.cloudstack.reservation.ReservationVO;
|
import org.apache.cloudstack.reservation.ReservationVO;
|
||||||
@ -42,6 +42,8 @@ public class ReservationDaoImpl extends GenericDaoBase<ReservationVO, Long> impl
|
|||||||
private static final String ACCOUNT_ID = "accountId";
|
private static final String ACCOUNT_ID = "accountId";
|
||||||
private static final String DOMAIN_ID = "domainId";
|
private static final String DOMAIN_ID = "domainId";
|
||||||
private static final String IDS = "ids";
|
private static final String IDS = "ids";
|
||||||
|
private static final String MS_ID = "managementServerId";
|
||||||
|
private static final String CREATED = "created";
|
||||||
private final SearchBuilder<ReservationVO> listResourceByAccountAndTypeSearch;
|
private final SearchBuilder<ReservationVO> listResourceByAccountAndTypeSearch;
|
||||||
private final SearchBuilder<ReservationVO> listAccountAndTypeSearch;
|
private final SearchBuilder<ReservationVO> listAccountAndTypeSearch;
|
||||||
private final SearchBuilder<ReservationVO> listAccountAndTypeAndNoTagSearch;
|
private final SearchBuilder<ReservationVO> listAccountAndTypeAndNoTagSearch;
|
||||||
@ -50,6 +52,7 @@ public class ReservationDaoImpl extends GenericDaoBase<ReservationVO, Long> impl
|
|||||||
private final SearchBuilder<ReservationVO> listDomainAndTypeAndNoTagSearch;
|
private final SearchBuilder<ReservationVO> listDomainAndTypeAndNoTagSearch;
|
||||||
private final SearchBuilder<ReservationVO> listResourceByAccountAndTypeAndNoTagSearch;
|
private final SearchBuilder<ReservationVO> listResourceByAccountAndTypeAndNoTagSearch;
|
||||||
private final SearchBuilder<ReservationVO> listIdsSearch;
|
private final SearchBuilder<ReservationVO> listIdsSearch;
|
||||||
|
private final SearchBuilder<ReservationVO> listMsIdSearch;
|
||||||
|
|
||||||
public ReservationDaoImpl() {
|
public ReservationDaoImpl() {
|
||||||
|
|
||||||
@ -71,12 +74,14 @@ public class ReservationDaoImpl extends GenericDaoBase<ReservationVO, Long> impl
|
|||||||
listAccountAndTypeSearch.and(ACCOUNT_ID, listAccountAndTypeSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
|
listAccountAndTypeSearch.and(ACCOUNT_ID, listAccountAndTypeSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
|
||||||
listAccountAndTypeSearch.and(RESOURCE_TYPE, listAccountAndTypeSearch.entity().getResourceType(), SearchCriteria.Op.EQ);
|
listAccountAndTypeSearch.and(RESOURCE_TYPE, listAccountAndTypeSearch.entity().getResourceType(), SearchCriteria.Op.EQ);
|
||||||
listAccountAndTypeSearch.and(RESOURCE_TAG, listAccountAndTypeSearch.entity().getTag(), SearchCriteria.Op.EQ);
|
listAccountAndTypeSearch.and(RESOURCE_TAG, listAccountAndTypeSearch.entity().getTag(), SearchCriteria.Op.EQ);
|
||||||
|
listAccountAndTypeSearch.and(CREATED, listAccountAndTypeSearch.entity().getCreated(), SearchCriteria.Op.LT);
|
||||||
listAccountAndTypeSearch.done();
|
listAccountAndTypeSearch.done();
|
||||||
|
|
||||||
listAccountAndTypeAndNoTagSearch = createSearchBuilder();
|
listAccountAndTypeAndNoTagSearch = createSearchBuilder();
|
||||||
listAccountAndTypeAndNoTagSearch.and(ACCOUNT_ID, listAccountAndTypeAndNoTagSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
|
listAccountAndTypeAndNoTagSearch.and(ACCOUNT_ID, listAccountAndTypeAndNoTagSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
|
||||||
listAccountAndTypeAndNoTagSearch.and(RESOURCE_TYPE, listAccountAndTypeAndNoTagSearch.entity().getResourceType(), SearchCriteria.Op.EQ);
|
listAccountAndTypeAndNoTagSearch.and(RESOURCE_TYPE, listAccountAndTypeAndNoTagSearch.entity().getResourceType(), SearchCriteria.Op.EQ);
|
||||||
listAccountAndTypeAndNoTagSearch.and(RESOURCE_TAG, listAccountAndTypeAndNoTagSearch.entity().getTag(), SearchCriteria.Op.NULL);
|
listAccountAndTypeAndNoTagSearch.and(RESOURCE_TAG, listAccountAndTypeAndNoTagSearch.entity().getTag(), SearchCriteria.Op.NULL);
|
||||||
|
listAccountAndTypeAndNoTagSearch.and(CREATED, listAccountAndTypeAndNoTagSearch.entity().getCreated(), SearchCriteria.Op.LT);
|
||||||
listAccountAndTypeAndNoTagSearch.done();
|
listAccountAndTypeAndNoTagSearch.done();
|
||||||
|
|
||||||
listDomainAndTypeSearch = createSearchBuilder();
|
listDomainAndTypeSearch = createSearchBuilder();
|
||||||
@ -94,18 +99,24 @@ public class ReservationDaoImpl extends GenericDaoBase<ReservationVO, Long> impl
|
|||||||
listIdsSearch = createSearchBuilder();
|
listIdsSearch = createSearchBuilder();
|
||||||
listIdsSearch.and(IDS, listIdsSearch.entity().getId(), SearchCriteria.Op.IN);
|
listIdsSearch.and(IDS, listIdsSearch.entity().getId(), SearchCriteria.Op.IN);
|
||||||
listIdsSearch.done();
|
listIdsSearch.done();
|
||||||
|
|
||||||
|
listMsIdSearch = createSearchBuilder();
|
||||||
|
listMsIdSearch.and(MS_ID, listMsIdSearch.entity().getManagementServerId(), SearchCriteria.Op.EQ);
|
||||||
|
listMsIdSearch.done();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getAccountReservation(Long accountId, Resource.ResourceType resourceType, String tag) {
|
public long getAccountReservation(Long accountId, Resource.ResourceType resourceType, String tag) {
|
||||||
long total = 0;
|
long total = 0;
|
||||||
SearchCriteria<ReservationVO> sc = tag == null ?
|
SearchCriteria<ReservationVO> sc;
|
||||||
listAccountAndTypeAndNoTagSearch.create() : listAccountAndTypeSearch.create();
|
if (tag == null) {
|
||||||
sc.setParameters(ACCOUNT_ID, accountId);
|
sc = listAccountAndTypeAndNoTagSearch.create();
|
||||||
sc.setParameters(RESOURCE_TYPE, resourceType);
|
} else {
|
||||||
if (tag != null) {
|
sc = listAccountAndTypeSearch.create();
|
||||||
sc.setParameters(RESOURCE_TAG, tag);
|
sc.setParameters(RESOURCE_TAG, tag);
|
||||||
}
|
}
|
||||||
|
sc.setParameters(ACCOUNT_ID, accountId);
|
||||||
|
sc.setParameters(RESOURCE_TYPE, resourceType);
|
||||||
List<ReservationVO> reservations = listBy(sc);
|
List<ReservationVO> reservations = listBy(sc);
|
||||||
for (ReservationVO reservation : reservations) {
|
for (ReservationVO reservation : reservations) {
|
||||||
total += reservation.getReservedAmount();
|
total += reservation.getReservedAmount();
|
||||||
@ -116,13 +127,15 @@ public class ReservationDaoImpl extends GenericDaoBase<ReservationVO, Long> impl
|
|||||||
@Override
|
@Override
|
||||||
public long getDomainReservation(Long domainId, Resource.ResourceType resourceType, String tag) {
|
public long getDomainReservation(Long domainId, Resource.ResourceType resourceType, String tag) {
|
||||||
long total = 0;
|
long total = 0;
|
||||||
SearchCriteria<ReservationVO> sc = tag == null ?
|
SearchCriteria<ReservationVO> sc;
|
||||||
listDomainAndTypeAndNoTagSearch.create() : listDomainAndTypeSearch.create();
|
if (tag == null) {
|
||||||
sc.setParameters(DOMAIN_ID, domainId);
|
sc = listDomainAndTypeAndNoTagSearch.create();
|
||||||
sc.setParameters(RESOURCE_TYPE, resourceType);
|
} else {
|
||||||
if (tag != null) {
|
sc = listDomainAndTypeSearch.create();
|
||||||
sc.setParameters(RESOURCE_TAG, tag);
|
sc.setParameters(RESOURCE_TAG, tag);
|
||||||
}
|
}
|
||||||
|
sc.setParameters(DOMAIN_ID, domainId);
|
||||||
|
sc.setParameters(RESOURCE_TYPE, resourceType);
|
||||||
List<ReservationVO> reservations = listBy(sc);
|
List<ReservationVO> reservations = listBy(sc);
|
||||||
for (ReservationVO reservation : reservations) {
|
for (ReservationVO reservation : reservations) {
|
||||||
total += reservation.getReservedAmount();
|
total += reservation.getReservedAmount();
|
||||||
@ -149,23 +162,17 @@ public class ReservationDaoImpl extends GenericDaoBase<ReservationVO, Long> impl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Long> getResourceIds(long accountId, Resource.ResourceType type) {
|
|
||||||
SearchCriteria<ReservationVO> sc = listResourceByAccountAndTypeSearch.create();
|
|
||||||
sc.setParameters(ACCOUNT_ID, accountId);
|
|
||||||
sc.setParameters(RESOURCE_TYPE, type);
|
|
||||||
return listBy(sc).stream().map(ReservationVO::getResourceId).collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ReservationVO> getReservationsForAccount(long accountId, Resource.ResourceType type, String tag) {
|
public List<ReservationVO> getReservationsForAccount(long accountId, Resource.ResourceType type, String tag) {
|
||||||
SearchCriteria<ReservationVO> sc = tag == null ?
|
SearchCriteria<ReservationVO> sc;
|
||||||
listResourceByAccountAndTypeAndNoTagSearch.create() : listResourceByAccountAndTypeSearch.create();
|
if (tag == null) {
|
||||||
sc.setParameters(ACCOUNT_ID, accountId);
|
sc = listResourceByAccountAndTypeAndNoTagSearch.create();
|
||||||
sc.setParameters(RESOURCE_TYPE, type);
|
} else {
|
||||||
if (tag != null) {
|
sc = listResourceByAccountAndTypeSearch.create();
|
||||||
sc.setParameters(RESOURCE_TAG, tag);
|
sc.setParameters(RESOURCE_TAG, tag);
|
||||||
}
|
}
|
||||||
|
sc.setParameters(ACCOUNT_ID, accountId);
|
||||||
|
sc.setParameters(RESOURCE_TYPE, type);
|
||||||
return listBy(sc);
|
return listBy(sc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,4 +184,28 @@ public class ReservationDaoImpl extends GenericDaoBase<ReservationVO, Long> impl
|
|||||||
remove(sc);
|
remove(sc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int removeByMsId(long managementServerId) {
|
||||||
|
SearchCriteria<ReservationVO> sc = listMsIdSearch.create();
|
||||||
|
sc.setParameters(MS_ID, managementServerId);
|
||||||
|
return remove(sc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int removeStaleReservations(Long accountId, Resource.ResourceType resourceType, String tag,
|
||||||
|
Date createdBefore) {
|
||||||
|
SearchCriteria<ReservationVO> sc;
|
||||||
|
if (tag == null) {
|
||||||
|
sc = listAccountAndTypeAndNoTagSearch.create();
|
||||||
|
} else {
|
||||||
|
sc = listAccountAndTypeSearch.create();
|
||||||
|
sc.setParameters(RESOURCE_TAG, tag);
|
||||||
|
}
|
||||||
|
sc.setParameters(ACCOUNT_ID, accountId);
|
||||||
|
sc.setParameters(RESOURCE_TYPE, resourceType);
|
||||||
|
sc.setParameters(CREATED, createdBefore);
|
||||||
|
return remove(sc);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,13 +29,15 @@ DROP INDEX `i_resource_count__type_domaintId`,
|
|||||||
ADD UNIQUE INDEX `i_resource_count__type_tag_accountId` (`type`,`tag`,`account_id`),
|
ADD UNIQUE INDEX `i_resource_count__type_tag_accountId` (`type`,`tag`,`account_id`),
|
||||||
ADD UNIQUE INDEX `i_resource_count__type_tag_domaintId` (`type`,`tag`,`domain_id`);
|
ADD UNIQUE INDEX `i_resource_count__type_tag_domaintId` (`type`,`tag`,`domain_id`);
|
||||||
|
|
||||||
|
|
||||||
ALTER TABLE `cloud`.`resource_reservation`
|
|
||||||
ADD COLUMN `resource_id` bigint unsigned NULL;
|
|
||||||
|
|
||||||
ALTER TABLE `cloud`.`resource_reservation`
|
ALTER TABLE `cloud`.`resource_reservation`
|
||||||
MODIFY COLUMN `amount` bigint NOT NULL;
|
MODIFY COLUMN `amount` bigint NOT NULL;
|
||||||
|
|
||||||
|
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.resource_reservation', 'resource_id', 'bigint unsigned NULL COMMENT "id of the resource" ');
|
||||||
|
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.resource_reservation', 'mgmt_server_id', 'bigint unsigned NULL COMMENT "management server id" ');
|
||||||
|
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.resource_reservation', 'created', 'datetime DEFAULT NULL COMMENT "date when the reservation was created" ');
|
||||||
|
|
||||||
|
UPDATE `cloud`.`resource_reservation` SET `created` = now() WHERE created IS NULL;
|
||||||
|
|
||||||
|
|
||||||
-- Update Default System offering for Router to 512MiB
|
-- Update Default System offering for Router to 512MiB
|
||||||
UPDATE `cloud`.`service_offering` SET ram_size = 512 WHERE unique_name IN ("Cloud.Com-SoftwareRouter", "Cloud.Com-SoftwareRouter-Local",
|
UPDATE `cloud`.`service_offering` SET ram_size = 512 WHERE unique_name IN ("Cloud.Com-SoftwareRouter", "Cloud.Com-SoftwareRouter-Local",
|
||||||
|
|||||||
@ -62,12 +62,28 @@ public class CheckedReservation implements AutoCloseable {
|
|||||||
return String.format("%s-%s", ResourceReservation.class.getSimpleName(), type.getName());
|
return String.format("%s-%s", ResourceReservation.class.getSimpleName(), type.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void removeAllReservations() {
|
||||||
|
if (CollectionUtils.isEmpty(reservations)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CallContext.current().removeContextParameter(getContextParameterKey());
|
||||||
|
for (ResourceReservation reservation : reservations) {
|
||||||
|
reservationDao.remove(reservation.getId());
|
||||||
|
}
|
||||||
|
this.reservations = null;
|
||||||
|
}
|
||||||
|
|
||||||
protected void checkLimitAndPersistReservations(Account account, ResourceType resourceType, Long resourceId, List<String> resourceLimitTags, Long amount) throws ResourceAllocationException {
|
protected void checkLimitAndPersistReservations(Account account, ResourceType resourceType, Long resourceId, List<String> resourceLimitTags, Long amount) throws ResourceAllocationException {
|
||||||
checkLimitAndPersistReservation(account, resourceType, resourceId, null, amount);
|
try {
|
||||||
if (CollectionUtils.isNotEmpty(resourceLimitTags)) {
|
checkLimitAndPersistReservation(account, resourceType, resourceId, null, amount);
|
||||||
for (String tag : resourceLimitTags) {
|
if (CollectionUtils.isNotEmpty(resourceLimitTags)) {
|
||||||
checkLimitAndPersistReservation(account, resourceType, resourceId, tag, amount);
|
for (String tag : resourceLimitTags) {
|
||||||
|
checkLimitAndPersistReservation(account, resourceType, resourceId, tag, amount);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (ResourceAllocationException rae) {
|
||||||
|
removeAllReservations();
|
||||||
|
throw rae;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,14 +163,7 @@ public class CheckedReservation implements AutoCloseable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws Exception {
|
public void close() throws Exception {
|
||||||
if (CollectionUtils.isEmpty(reservations)) {
|
removeAllReservations();
|
||||||
return;
|
|
||||||
}
|
|
||||||
CallContext.current().removeContextParameter(getContextParameterKey());
|
|
||||||
for (ResourceReservation reservation : reservations) {
|
|
||||||
reservationDao.remove(reservation.getId());
|
|
||||||
}
|
|
||||||
reservations = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Account getAccount() {
|
public Account getAccount() {
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@ -219,8 +220,16 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void cleanupResourceReservationsForMs() {
|
||||||
|
int reservationsRemoved = reservationDao.removeByMsId(ManagementServerNode.getManagementServerId());
|
||||||
|
if (reservationsRemoved > 0) {
|
||||||
|
logger.warn("Removed {} resource reservations for management server id {}", reservationsRemoved, ManagementServerNode.getManagementServerId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean start() {
|
public boolean start() {
|
||||||
|
cleanupResourceReservationsForMs();
|
||||||
if (ResourceCountCheckInterval.value() >= 0) {
|
if (ResourceCountCheckInterval.value() >= 0) {
|
||||||
ConfigKeyScheduledExecutionWrapper runner = new ConfigKeyScheduledExecutionWrapper(_rcExecutor, new ResourceCountCheckTask(), ResourceCountCheckInterval, TimeUnit.SECONDS);
|
ConfigKeyScheduledExecutionWrapper runner = new ConfigKeyScheduledExecutionWrapper(_rcExecutor, new ResourceCountCheckTask(), ResourceCountCheckInterval, TimeUnit.SECONDS);
|
||||||
runner.start();
|
runner.start();
|
||||||
@ -230,6 +239,10 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean stop() {
|
public boolean stop() {
|
||||||
|
if (_rcExecutor != null) {
|
||||||
|
_rcExecutor.shutdown();
|
||||||
|
}
|
||||||
|
cleanupResourceReservationsForMs();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1200,8 +1213,22 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void cleanupStaleResourceReservations(final long accountId, final ResourceType type, String tag) {
|
||||||
|
Long delay = ResourceReservationCleanupDelay.value();
|
||||||
|
if (delay == null || delay <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Date cleanupBefore = new Date(System.currentTimeMillis() - delay * 1000);
|
||||||
|
int rowsRemoved = reservationDao.removeStaleReservations(accountId, type, tag, cleanupBefore);
|
||||||
|
if (rowsRemoved > 0) {
|
||||||
|
logger.warn("Removed {} stale resource reservations for account {} of type {} and tag {}",
|
||||||
|
rowsRemoved, accountId, type, tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@DB
|
@DB
|
||||||
protected long recalculateAccountResourceCount(final long accountId, final ResourceType type, String tag) {
|
protected long recalculateAccountResourceCount(final long accountId, final ResourceType type, String tag) {
|
||||||
|
cleanupStaleResourceReservations(accountId, type, tag);
|
||||||
final Long newCount;
|
final Long newCount;
|
||||||
if (type == Resource.ResourceType.user_vm) {
|
if (type == Resource.ResourceType.user_vm) {
|
||||||
newCount = calculateVmCountForAccount(accountId, tag);
|
newCount = calculateVmCountForAccount(accountId, tag);
|
||||||
@ -2106,6 +2133,7 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
|
|||||||
public ConfigKey<?>[] getConfigKeys() {
|
public ConfigKey<?>[] getConfigKeys() {
|
||||||
return new ConfigKey<?>[] {
|
return new ConfigKey<?>[] {
|
||||||
ResourceCountCheckInterval,
|
ResourceCountCheckInterval,
|
||||||
|
ResourceReservationCleanupDelay,
|
||||||
MaxAccountSecondaryStorage,
|
MaxAccountSecondaryStorage,
|
||||||
MaxProjectSecondaryStorage,
|
MaxProjectSecondaryStorage,
|
||||||
ResourceLimitHostTags,
|
ResourceLimitHostTags,
|
||||||
|
|||||||
@ -24,7 +24,9 @@ import static org.mockito.Mockito.lenient;
|
|||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.cloudstack.context.CallContext;
|
import org.apache.cloudstack.context.CallContext;
|
||||||
import org.apache.cloudstack.reservation.ReservationVO;
|
import org.apache.cloudstack.reservation.ReservationVO;
|
||||||
@ -143,4 +145,41 @@ public class CheckedReservationTest {
|
|||||||
Assert.fail("Exception faced: " + e.getMessage());
|
Assert.fail("Exception faced: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultipleReservationsWithOneFailing() {
|
||||||
|
List<String> tags = List.of("abc", "xyz");
|
||||||
|
when(account.getAccountId()).thenReturn(1L);
|
||||||
|
when(account.getDomainId()).thenReturn(4L);
|
||||||
|
Map<Long, ReservationVO> persistedReservations = new HashMap<>();
|
||||||
|
Mockito.when(reservationDao.persist(Mockito.any(ReservationVO.class))).thenAnswer((Answer<ReservationVO>) invocation -> {
|
||||||
|
ReservationVO reservationVO = (ReservationVO) invocation.getArguments()[0];
|
||||||
|
Long id = (long) (persistedReservations.size() + 1);
|
||||||
|
ReflectionTestUtils.setField(reservationVO, "id", id);
|
||||||
|
persistedReservations.put(id, reservationVO);
|
||||||
|
return reservationVO;
|
||||||
|
});
|
||||||
|
Mockito.when(reservationDao.remove(Mockito.anyLong())).thenAnswer((Answer<Boolean>) invocation -> {
|
||||||
|
Long id = (Long) invocation.getArguments()[0];
|
||||||
|
persistedReservations.remove(id);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
Mockito.doThrow(ResourceAllocationException.class).when(resourceLimitService).checkResourceLimitWithTag(account, Resource.ResourceType.cpu, "xyz", 1L);
|
||||||
|
try (CheckedReservation vmReservation = new CheckedReservation(account, Resource.ResourceType.user_vm, tags, 1L, reservationDao, resourceLimitService);
|
||||||
|
CheckedReservation cpuReservation = new CheckedReservation(account, Resource.ResourceType.cpu, tags, 1L, reservationDao, resourceLimitService);
|
||||||
|
CheckedReservation memReservation = new CheckedReservation(account, Resource.ResourceType.memory, tags, 256L, reservationDao, resourceLimitService);
|
||||||
|
) {
|
||||||
|
Assert.fail("Exception should have occurred but all reservations successful!");
|
||||||
|
} catch (Exception ex) {
|
||||||
|
if (!(ex instanceof ResourceAllocationException)) {
|
||||||
|
Assert.fail(String.format("Expected ResourceAllocationException but %s occurred!", ex.getClass().getSimpleName()));
|
||||||
|
}
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
} catch (Exception rae) {
|
||||||
|
// Check if all persisted reservations are removed
|
||||||
|
Assert.assertTrue("All persisted reservations are not removed", persistedReservations.isEmpty());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user