Skip to content

Commit

Permalink
api,server,ui: granular resource limit management (apache#8362)
Browse files Browse the repository at this point in the history
Feature spec: https://cwiki.apache.org/confluence/display/CLOUDSTACK/Granular+Resource+Limit+Management

Introduces the concept of tagged resource limits for granular resource limit management. Limits can be enforced on accounts and domains for the deployment of entities for a tagged resource. Current tagged resource limits can be used for the following resource types,

Host limits
- user_vm
- cpu
- memory

Storage limits
- volume
- primary_storage

Following global settings can used to specify tags for which limit needs to be enforced,

Host: `resource.limit.host.tags`
Storage: `resource.limit.storage.tags`

Option for specifying tagged resource limits and viewing tagged resource usage are made available in the UI.

Enhances the use of templatetag for VM deployment and template creation

Adds option to list service/compute offerings that can be used with a given template. A new parameter named templateid has been added.

Adds option to list disk offering with suitability flag for a virtual machine. A new parameter named virtualmachineid has been added to the listDiskOfferings API which when passed returns suitableforvirtualmachine param in the response.
  • Loading branch information
shwstppr authored Feb 19, 2024
1 parent 6af1c25 commit 592038a
Show file tree
Hide file tree
Showing 116 changed files with 6,448 additions and 1,100 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,8 @@ jobs:
"component/test_project_usage
component/test_protocol_number_security_group
component/test_public_ip
component/test_resource_limits",
component/test_resource_limits
component/test_resource_limit_tags",
"component/test_regions_accounts
component/test_routers
component/test_snapshots
Expand Down
9 changes: 9 additions & 0 deletions api/src/main/java/com/cloud/capacity/Capacity.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
// under the License.
package com.cloud.capacity;

import java.util.List;

import org.apache.cloudstack.api.Identity;
import org.apache.cloudstack.api.InternalIdentity;

Expand All @@ -35,6 +37,11 @@ public interface Capacity extends InternalIdentity, Identity {

public static final short CAPACITY_TYPE_CPU_CORE = 90;

public static final List<Short> STORAGE_CAPACITY_TYPES = List.of(CAPACITY_TYPE_STORAGE,
CAPACITY_TYPE_STORAGE_ALLOCATED,
CAPACITY_TYPE_SECONDARY_STORAGE,
CAPACITY_TYPE_LOCAL_STORAGE);

public Long getHostOrPoolId();

public Long getDataCenterId();
Expand All @@ -54,4 +61,6 @@ public interface Capacity extends InternalIdentity, Identity {
public Float getUsedPercentage();

public Long getAllocatedCapacity();

public String getTag();
}
1 change: 1 addition & 0 deletions api/src/main/java/com/cloud/configuration/Resource.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,6 @@ public String getName() {
long getOwnerId();

ResourceOwnerType getResourceOwnerType();
String getTag();

}
2 changes: 2 additions & 0 deletions api/src/main/java/com/cloud/storage/Volume.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@

public interface Volume extends ControlledEntity, Identity, InternalIdentity, BasedOn, StateObject<Volume.State>, Displayable {

static final long DISK_OFFERING_SUITABILITY_CHECK_VOLUME_ID = -1;

// Managed storage volume parameters (specified in the compute/disk offering for PowerFlex)
String BANDWIDTH_LIMIT_IN_MBPS = "bandwidthLimitInMbps";
String IOPS_LIMIT = "iopsLimit";
Expand Down
76 changes: 55 additions & 21 deletions api/src/main/java/com/cloud/user/ResourceLimitService.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,18 @@

import java.util.List;

import org.apache.cloudstack.api.response.AccountResponse;
import org.apache.cloudstack.api.response.DomainResponse;
import org.apache.cloudstack.framework.config.ConfigKey;

import com.cloud.configuration.Resource.ResourceType;
import com.cloud.configuration.ResourceCount;
import com.cloud.configuration.ResourceLimit;
import com.cloud.domain.Domain;
import com.cloud.exception.ResourceAllocationException;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.user.ResourceReservation;
import com.cloud.offering.DiskOffering;
import com.cloud.offering.ServiceOffering;
import com.cloud.template.VirtualMachineTemplate;

public interface ResourceLimitService {

Expand All @@ -34,6 +39,13 @@ public interface ResourceLimitService {
"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",
"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", false);
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);
static final ConfigKey<String> ResourceLimitStorageTags = new ConfigKey<>("Advanced", String.class, "resource.limit.storage.tags", "",
"A comma-separated list of tags for storage resource limits", true);

static final List<ResourceType> HostTagsSupportingTypes = List.of(ResourceType.user_vm, ResourceType.cpu, ResourceType.memory);
static final List<ResourceType> StorageTagsSupportingTypes = List.of(ResourceType.volume, ResourceType.primary_storage);

/**
* Updates an existing resource limit with the specified details. If a limit doesn't exist, will create one.
Expand All @@ -46,22 +58,27 @@ public interface ResourceLimitService {
* TODO
* @param max
* TODO
* @param tag
* tag for the resource type
*
* @return the updated/created resource limit
*/
ResourceLimit updateResourceLimit(Long accountId, Long domainId, Integer resourceType, Long max);
ResourceLimit updateResourceLimit(Long accountId, Long domainId, Integer resourceType, Long max, String tag);

/**
* Updates an existing resource count details for the account/domain
*
* @param accountId
* TODO
* Id of the account for which resource recalculation to be done
* @param domainId
* TODO
* Id of the domain for which resource recalculation to be doneDO
* @param typeId
* TODO
* type of the resource for which recalculation to be done
* @param tag
* tag for the resource type for which recalculation to be done
* @return the updated/created resource counts
*/
List<? extends ResourceCount> recalculateResourceCount(Long accountId, Long domainId, Integer typeId, String tag);
List<? extends ResourceCount> recalculateResourceCount(Long accountId, Long domainId, Integer typeId);

/**
Expand All @@ -77,17 +94,18 @@ public interface ResourceLimitService {
* TODO
* @return a list of limits that match the criteria
*/
public List<? extends ResourceLimit> searchForLimits(Long id, Long accountId, Long domainId, ResourceType resourceType, Long startIndex, Long pageSizeVal);
public List<? extends ResourceLimit> searchForLimits(Long id, Long accountId, Long domainId, ResourceType resourceType, String tag, Long startIndex, Long pageSizeVal);

/**
* Finds the resource limit for a specified account and type. If the account has an infinite limit, will check
* the account's parent domain, and if that limit is also infinite, will return the ROOT domain's limit.
*
* @param account
* @param type
* @param tag
* @return resource limit
*/
public long findCorrectResourceLimitForAccount(Account account, ResourceType type);
public long findCorrectResourceLimitForAccount(Account account, ResourceType type, String tag);

/**
* This call should be used when we have already queried resource limit for an account. This is to handle
Expand All @@ -105,9 +123,10 @@ public interface ResourceLimitService {
*
* @param domain
* @param type
* @param tag
* @return resource limit
*/
public long findCorrectResourceLimitForDomain(Domain domain, ResourceType type);
public long findCorrectResourceLimitForDomain(Domain domain, ResourceType type, String tag);

/**
* Finds the default resource limit for a specified type.
Expand All @@ -122,9 +141,10 @@ public interface ResourceLimitService {
*
* @param domain
* @param type
* @param tag
* @return resource limit
*/
public long findCorrectResourceLimitForAccountAndDomain(Account account, Domain domain, ResourceType type);
public long findCorrectResourceLimitForAccountAndDomain(Account account, Domain domain, ResourceType type, String tag);

/**
* Increments the resource count
Expand All @@ -134,6 +154,7 @@ public interface ResourceLimitService {
* @param delta
*/
public void incrementResourceCount(long accountId, ResourceType type, Long... delta);
public void incrementResourceCountWithTag(long accountId, ResourceType type, String tag, Long... delta);

/**
* Decrements the resource count
Expand All @@ -143,6 +164,7 @@ public interface ResourceLimitService {
* @param delta
*/
public void decrementResourceCount(long accountId, ResourceType type, Long... delta);
public void decrementResourceCountWithTag(long accountId, ResourceType type, String tag, Long... delta);

/**
* Checks if a limit has been exceeded for an account
Expand All @@ -155,15 +177,17 @@ public interface ResourceLimitService {
* @throws ResourceAllocationException
*/
public void checkResourceLimit(Account account, ResourceCount.ResourceType type, long... count) throws ResourceAllocationException;
public void checkResourceLimitWithTag(Account account, ResourceCount.ResourceType type, String tag, long... count) throws ResourceAllocationException;

/**
* Gets the count of resources for a resource type and account
*
* @param account
* @param type
* @param tag
* @return count of resources
*/
public long getResourceCount(Account account, ResourceType type);
public long getResourceCount(Account account, ResourceType type, String tag);

/**
* Checks if a limit has been exceeded for an account if displayResource flag is on
Expand Down Expand Up @@ -208,15 +232,25 @@ public interface ResourceLimitService {
*/
void decrementResourceCount(long accountId, ResourceType type, Boolean displayResource, Long... delta);

/**
* Adds a reservation that will be counted in subsequent calls to {count}getResourceCount{code} until {code}this[code}
* is closed. It will create a reservation record that will be counted when resource limits are checked.
* @param account The account for which the reservation is.
* @param displayResource whether this resource is shown to users at all (if not it is not counted to limits)
* @param type resource type
* @param delta amount to reserve (will not be <+ 0)
* @return a {code}AutoClosable{Code} object representing the resource the user needs
*/
ResourceReservation getReservation(Account account, Boolean displayResource, ResourceType type, Long delta) throws ResourceAllocationException;
List<String> getResourceLimitHostTags();
List<String> getResourceLimitHostTags(ServiceOffering serviceOffering, VirtualMachineTemplate template);
List<String> getResourceLimitStorageTags();
List<String> getResourceLimitStorageTags(DiskOffering diskOffering);
void updateTaggedResourceLimitsAndCountsForAccounts(List<AccountResponse> responses, String tag);
void updateTaggedResourceLimitsAndCountsForDomains(List<DomainResponse> responses, String tag);
void checkVolumeResourceLimit(Account owner, Boolean display, Long size, DiskOffering diskOffering) throws ResourceAllocationException;
void incrementVolumeResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering);
void decrementVolumeResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering);
void incrementVolumePrimaryStorageResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering);
void decrementVolumePrimaryStorageResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering);
void checkVmResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template) throws ResourceAllocationException;
void incrementVmResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template);
void decrementVmResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template);
void checkVmCpuResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu) throws ResourceAllocationException;
void incrementVmCpuResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu);
void decrementVmCpuResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long cpu);
void checkVmMemoryResourceLimit(Account owner, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory) throws ResourceAllocationException;
void incrementVmMemoryResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory);
void decrementVmMemoryResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template, Long memory);

}
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ public class ApiConstants {
public static final String LAST_SERVER_STOP = "lastserverstop";
public static final String LEVEL = "level";
public static final String LENGTH = "length";
public static final String LIMIT = "limit";
public static final String LIMIT_CPU_USE = "limitcpuuse";
public static final String LIST_HOSTS = "listhosts";
public static final String LOCK = "lock";
Expand Down Expand Up @@ -380,6 +381,7 @@ public class ApiConstants {
public static final String RECONNECT = "reconnect";
public static final String RECOVER = "recover";
public static final String REQUIRES_HVM = "requireshvm";
public static final String RESOURCE_COUNT = "resourcecount";
public static final String RESOURCE_NAME = "resourcename";
public static final String RESOURCE_TYPE = "resourcetype";
public static final String RESOURCE_TYPE_NAME = "resourcetypename";
Expand Down Expand Up @@ -420,8 +422,9 @@ public class ApiConstants {
public static final String SNAPSHOT_POLICY_ID = "snapshotpolicyid";
public static final String SNAPSHOT_TYPE = "snapshottype";
public static final String SNAPSHOT_QUIESCEVM = "quiescevm";
public static final String SUPPORTS_STORAGE_SNAPSHOT = "supportsstoragesnapshot";
public static final String SOURCE_ZONE_ID = "sourcezoneid";
public static final String SUITABLE_FOR_VM = "suitableforvirtualmachine";
public static final String SUPPORTS_STORAGE_SNAPSHOT = "supportsstoragesnapshot";
public static final String START_DATE = "startdate";
public static final String START_ID = "startid";
public static final String START_IP = "startip";
Expand Down Expand Up @@ -449,6 +452,7 @@ public class ApiConstants {
public static final String TIMEOUT = "timeout";
public static final String TIMEZONE = "timezone";
public static final String TIMEZONEOFFSET = "timezoneoffset";
public static final String TOTAL = "total";
public static final String TOTAL_SUBNETS = "totalsubnets";
public static final String TYPE = "type";
public static final String TRUST_STORE = "truststore";
Expand Down Expand Up @@ -719,6 +723,7 @@ public class ApiConstants {
public static final String POLICY_UUID = "policyuuid";
public static final String RULE_UUID = "ruleuuid";
public static final String DIRECTION = "direction";
public static final String TAGGED_RESOURCES = "taggedresources";
public static final String TAG_UUID = "taguuid";
public static final String TAG_TYPE = "tagtype";
public static final String TAG_VALUE = "tagvalue";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,6 @@
import java.util.EnumSet;
import java.util.List;

import com.cloud.server.ResourceIcon;
import com.cloud.server.ResourceTag;
import org.apache.cloudstack.api.response.ResourceIconResponse;

import org.apache.cloudstack.api.APICommand;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiConstants.DomainDetails;
Expand All @@ -33,9 +29,13 @@
import org.apache.cloudstack.api.command.user.UserCmd;
import org.apache.cloudstack.api.response.DomainResponse;
import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cloudstack.api.response.ResourceIconResponse;
import org.apache.commons.collections.CollectionUtils;

import com.cloud.domain.Domain;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.server.ResourceIcon;
import com.cloud.server.ResourceTag;

@APICommand(name = "listDomains", description = "Lists domains and provides detailed information for listed domains", responseObject = DomainResponse.class, responseView = ResponseView.Restricted, entityType = {Domain.class},
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
Expand Down Expand Up @@ -71,6 +71,9 @@ public class ListDomainsCmd extends BaseListCmd implements UserCmd {
description = "flag to display the resource icon for domains")
private Boolean showIcon;

@Parameter(name = ApiConstants.TAG, type = CommandType.STRING, description = "Tag for resource type to return usage", since = "4.20.0")
private String tag;

/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
Expand Down Expand Up @@ -110,10 +113,14 @@ public EnumSet<DomainDetails> getDetails() throws InvalidParameterValueException
return dv;
}

public Boolean getShowIcon() {
public boolean getShowIcon() {
return showIcon != null ? showIcon : false;
}

public String getTag() {
return tag;
}

/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
Expand All @@ -128,12 +135,17 @@ public void execute() {
ListResponse<DomainResponse> response = _queryService.searchForDomains(this);
response.setResponseName(getCommandName());
this.setResponseObject(response);
if (response != null && response.getCount() > 0 && getShowIcon()) {
updateDomainResponse(response.getResponses());
}
updateDomainResponse(response.getResponses());
}

private void updateDomainResponse(List<DomainResponse> response) {
protected void updateDomainResponse(List<DomainResponse> response) {
if (CollectionUtils.isEmpty(response)) {
return;
}
_resourceLimitService.updateTaggedResourceLimitsAndCountsForDomains(response, getTag());
if (!getShowIcon()) {
return;
}
for (DomainResponse domainResponse : response) {
ResourceIcon resourceIcon = resourceIconManager.getByResourceTypeAndUuid(ResourceTag.ResourceObjectType.Domain, domainResponse.getId());
if (resourceIcon == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ public class ListCapacityCmd extends BaseListCmd {
@Parameter(name = ApiConstants.SORT_BY, type = CommandType.STRING, since = "3.0.0", description = "Sort the results. Available values: Usage")
private String sortBy;

@Parameter(name = ApiConstants.TAG, type = CommandType.STRING, description = "Tag for the resource type", since = "4.20.0")
private String tag;

/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
Expand Down Expand Up @@ -107,6 +110,10 @@ public String getSortBy() {
return null;
}

public String getTag() {
return tag;
}

/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
Expand Down
Loading

0 comments on commit 592038a

Please sign in to comment.