From 18695276f79a4cf90279f6ff25ebc7c66b6a9c49 Mon Sep 17 00:00:00 2001 From: Sanjay Tripathi Date: Fri, 29 Mar 2013 11:21:10 +0530 Subject: [PATCH] CLOUDSTACK-1156: Limit Primary and Secondary storage for domain/accounts Addition of two new resource types i.e. Primary and Secondary storage space in the existing pool of resource types. Added methods to set the limits on these resources using updateResourceLimit API command and to get a count using updateResourceCount. Also added calls in the Templates, Volumes, Snapshots life cycle to check these limits and to increment/decrement the new resource types Resource Name :: Resource type number Primary Storage 10 Secondary Storage 11 Also added jUnit Tests for the same. Reviewed by : nitin mehta --- api/src/com/cloud/configuration/Resource.java | 7 +- .../com/cloud/storage/VolumeApiService.java | 5 +- .../user/resource/UpdateResourceCountCmd.java | 6 +- .../user/resource/UpdateResourceLimitCmd.java | 6 +- .../command/user/volume/ResizeVolumeCmd.java | 3 +- .../api/response/AccountResponse.java | 54 ++++++- .../api/response/ResourceCountResponse.java | 2 +- .../api/response/ResourceLimitResponse.java | 2 +- .../manager/BareMetalTemplateAdapter.java | 15 +- .../src/com/cloud/alert/AlertManagerImpl.java | 3 +- .../src/com/cloud/api/ApiResponseHelper.java | 149 ++++++++++++----- .../api/query/dao/AccountJoinDaoImpl.java | 18 +++ .../com/cloud/api/query/vo/AccountJoinVO.java | 54 +++++++ .../src/com/cloud/configuration/Config.java | 5 +- .../ResourceLimitManagerImpl.java | 54 +++++++ .../src/com/cloud/storage/VolumeManager.java | 3 +- .../com/cloud/storage/VolumeManagerImpl.java | 91 ++++++++--- .../com/cloud/storage/dao/SnapshotDao.java | 12 +- .../cloud/storage/dao/SnapshotDaoImpl.java | 65 +++++--- .../src/com/cloud/storage/dao/VolumeDao.java | 66 +++++--- .../com/cloud/storage/dao/VolumeDaoImpl.java | 48 +++++- .../storage/download/DownloadListener.java | 62 ++++++- .../storage/download/DownloadMonitorImpl.java | 56 ++++++- .../storage/snapshot/SnapshotManagerImpl.java | 70 ++++---- .../template/HypervisorTemplateAdapter.java | 29 ++-- .../cloud/template/TemplateManagerImpl.java | 47 +++--- .../src/com/cloud/vm/UserVmManagerImpl.java | 15 +- .../ResourceLimitManagerImplTest.java | 18 ++- .../vpc/MockResourceLimitManagerImpl.java | 8 + setup/db/db/schema-40to410.sql | 24 --- setup/db/db/schema-410to420.sql | 153 +++++++++++++++++- utils/src/com/cloud/utils/UriUtils.java | 33 ++++ 32 files changed, 934 insertions(+), 249 deletions(-) diff --git a/api/src/com/cloud/configuration/Resource.java b/api/src/com/cloud/configuration/Resource.java index 7614c8a4b43..5550e29eca4 100644 --- a/api/src/com/cloud/configuration/Resource.java +++ b/api/src/com/cloud/configuration/Resource.java @@ -20,7 +20,7 @@ public interface Resource { public static final short RESOURCE_UNLIMITED = -1; - public enum ResourceType { + public enum ResourceType { // Primary and Secondary storage are allocated_storage and not the physical storage. user_vm("user_vm", 0, ResourceOwnerType.Account, ResourceOwnerType.Domain), public_ip("public_ip", 1, ResourceOwnerType.Account, ResourceOwnerType.Domain), volume("volume", 2, ResourceOwnerType.Account, ResourceOwnerType.Domain), @@ -30,11 +30,14 @@ public interface Resource { network("network", 6, ResourceOwnerType.Account, ResourceOwnerType.Domain), vpc("vpc", 7, ResourceOwnerType.Account, ResourceOwnerType.Domain), cpu("cpu", 8, ResourceOwnerType.Account, ResourceOwnerType.Domain), - memory("memory", 9, ResourceOwnerType.Account, ResourceOwnerType.Domain); + memory("memory", 9, ResourceOwnerType.Account, ResourceOwnerType.Domain), + primary_storage("primary_storage", 10, ResourceOwnerType.Account, ResourceOwnerType.Domain), + secondary_storage("secondary_storage", 11, ResourceOwnerType.Account, ResourceOwnerType.Domain); private String name; private ResourceOwnerType[] supportedOwners; private int ordinal; + public static final long bytesToGiB = 1024 * 1024 * 1024; ResourceType(String name, int ordinal, ResourceOwnerType... supportedOwners) { this.name = name; diff --git a/api/src/com/cloud/storage/VolumeApiService.java b/api/src/com/cloud/storage/VolumeApiService.java index 8517988dfc6..09a07d4be13 100644 --- a/api/src/com/cloud/storage/VolumeApiService.java +++ b/api/src/com/cloud/storage/VolumeApiService.java @@ -59,9 +59,10 @@ public interface VolumeApiService { * @param cmd * the API command wrapping the criteria * @return the volume object + * @throws ResourceAllocationException */ - Volume resizeVolume(ResizeVolumeCmd cmd); - + Volume resizeVolume(ResizeVolumeCmd cmd) throws ResourceAllocationException; + Volume migrateVolume(MigrateVolumeCmd cmd) throws ConcurrentOperationException; /** diff --git a/api/src/org/apache/cloudstack/api/command/user/resource/UpdateResourceCountCmd.java b/api/src/org/apache/cloudstack/api/command/user/resource/UpdateResourceCountCmd.java index f6d3a98a05d..0f4c932827d 100644 --- a/api/src/org/apache/cloudstack/api/command/user/resource/UpdateResourceCountCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/resource/UpdateResourceCountCmd.java @@ -53,7 +53,7 @@ public class UpdateResourceCountCmd extends BaseCmd { required=true, description="If account parameter specified then updates resource counts for a specified account in this domain else update resource counts for all accounts & child domains in specified domain.") private Long domainId; - @Parameter(name=ApiConstants.RESOURCE_TYPE, type=CommandType.INTEGER, description= "Type of resource to update. If specifies valid values are 0, 1, 2, 3, 4, 5, 6, 7, 8 and 9. If not specified will update all resource counts" + + @Parameter(name=ApiConstants.RESOURCE_TYPE, type=CommandType.INTEGER, description= "Type of resource to update. If specifies valid values are 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 and 11. If not specified will update all resource counts" + "0 - Instance. Number of instances a user can create. " + "1 - IP. Number of public IP addresses a user can own. " + "2 - Volume. Number of disk volumes a user can create." + @@ -63,7 +63,9 @@ public class UpdateResourceCountCmd extends BaseCmd { "6 - Network. Number of guest network a user can create." + "7 - VPC. Number of VPC a user can create." + "8 - CPU. Total number of CPU cores a user can use." + - "9 - Memory. Total Memory (in MB) a user can use." ) + "9 - Memory. Total Memory (in MB) a user can use." + + "10 - PrimaryStorage. Total primary storage space (in GiB) a user can use." + + "11 - SecondaryStorage. Total secondary storage space (in GiB) a user can use." ) private Integer resourceType; @Parameter(name=ApiConstants.PROJECT_ID, type=CommandType.UUID, entityType = ProjectResponse.class, diff --git a/api/src/org/apache/cloudstack/api/command/user/resource/UpdateResourceLimitCmd.java b/api/src/org/apache/cloudstack/api/command/user/resource/UpdateResourceLimitCmd.java index 0039f6293f7..826596c4c45 100644 --- a/api/src/org/apache/cloudstack/api/command/user/resource/UpdateResourceLimitCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/resource/UpdateResourceLimitCmd.java @@ -54,7 +54,7 @@ public class UpdateResourceLimitCmd extends BaseCmd { @Parameter(name=ApiConstants.MAX, type=CommandType.LONG, description=" Maximum resource limit.") private Long max; - @Parameter(name=ApiConstants.RESOURCE_TYPE, type=CommandType.INTEGER, required=true, description="Type of resource to update. Values are 0, 1, 2, 3, 4, 6, 7, 8 and 9. 0 - Instance. Number of instances a user can create. " + + @Parameter(name=ApiConstants.RESOURCE_TYPE, type=CommandType.INTEGER, required=true, description="Type of resource to update. Values are 0, 1, 2, 3, 4, 6, 7, 8, 9, 10 and 11. 0 - Instance. Number of instances a user can create. " + "1 - IP. Number of public IP addresses a user can own. " + "2 - Volume. Number of disk volumes a user can create." + "3 - Snapshot. Number of snapshots a user can create." + @@ -62,7 +62,9 @@ public class UpdateResourceLimitCmd extends BaseCmd { "6 - Network. Number of guest network a user can create." + "7 - VPC. Number of VPC a user can create." + "8 - CPU. Total number of CPU cores a user can use." + - "9 - Memory. Total Memory (in MB) a user can use." ) + "9 - Memory. Total Memory (in MB) a user can use." + + "10 - PrimaryStorage. Total primary storage space (in GiB) a user can use." + + "11 - SecondaryStorage. Total secondary storage space (in GiB) a user can use." ) private Integer resourceType; ///////////////////////////////////////////////////// diff --git a/api/src/org/apache/cloudstack/api/command/user/volume/ResizeVolumeCmd.java b/api/src/org/apache/cloudstack/api/command/user/volume/ResizeVolumeCmd.java index 955727a7d82..871bf455529 100644 --- a/api/src/org/apache/cloudstack/api/command/user/volume/ResizeVolumeCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/volume/ResizeVolumeCmd.java @@ -30,6 +30,7 @@ import com.cloud.async.AsyncJob; import com.cloud.event.EventTypes; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.PermissionDeniedException; +import com.cloud.exception.ResourceAllocationException; import com.cloud.projects.Project; import com.cloud.storage.Volume; import com.cloud.user.Account; @@ -131,7 +132,7 @@ public class ResizeVolumeCmd extends BaseAsyncCmd { } @Override - public void execute(){ + public void execute() throws ResourceAllocationException{ UserContext.current().setEventDetails("Volume Id: " + getEntityId() + " to size " + getSize() + "G"); Volume volume = _volumeService.resizeVolume(this); if (volume != null) { diff --git a/api/src/org/apache/cloudstack/api/response/AccountResponse.java b/api/src/org/apache/cloudstack/api/response/AccountResponse.java index 9a98a356492..c109a6325c8 100644 --- a/api/src/org/apache/cloudstack/api/response/AccountResponse.java +++ b/api/src/org/apache/cloudstack/api/response/AccountResponse.java @@ -132,24 +132,42 @@ public class AccountResponse extends BaseResponse { @SerializedName("vpcavailable") @Param(description="the total number of vpcs available to be created for this account", since="4.0.0") private String vpcAvailable; - @SerializedName("cpulimit") @Param(description="the total number of cpu cores the account can own", since="4.1.0") + @SerializedName("cpulimit") @Param(description="the total number of cpu cores the account can own", since="4.2.0") private String cpuLimit; - @SerializedName("cputotal") @Param(description="the total number of cpu cores owned by account", since="4.1.0") + @SerializedName("cputotal") @Param(description="the total number of cpu cores owned by account", since="4.2.0") private Long cpuTotal; - @SerializedName("cpuavailable") @Param(description="the total number of cpu cores available to be created for this account", since="4.1.0") + @SerializedName("cpuavailable") @Param(description="the total number of cpu cores available to be created for this account", since="4.2.0") private String cpuAvailable; - @SerializedName("memorylimit") @Param(description="the total memory (in MB) the account can own", since="4.1.0") + @SerializedName("memorylimit") @Param(description="the total memory (in MB) the account can own", since="4.2.0") private String memoryLimit; - @SerializedName("memorytotal") @Param(description="the total memory (in MB) owned by account", since="4.1.0") + @SerializedName("memorytotal") @Param(description="the total memory (in MB) owned by account", since="4.2.0") private Long memoryTotal; - @SerializedName("memoryavailable") @Param(description="the total memory (in MB) available to be created for this account", since="4.1.0") + @SerializedName("memoryavailable") @Param(description="the total memory (in MB) available to be created for this account", since="4.2.0") private String memoryAvailable; + @SerializedName("primarystoragelimit") @Param(description="the total primary storage space (in GiB) the account can own", since="4.2.0") + private String primaryStorageLimit; + + @SerializedName("primarystoragetotal") @Param(description="the total primary storage space (in GiB) owned by account", since="4.2.0") + private Long primaryStorageTotal; + + @SerializedName("primarystorageavailable") @Param(description="the total primary storage space (in GiB) available to be used for this account", since="4.2.0") + private String primaryStorageAvailable; + + @SerializedName("secondarystoragelimit") @Param(description="the total secondary storage space (in GiB) the account can own", since="4.2.0") + private String secondaryStorageLimit; + + @SerializedName("secondarystoragetotal") @Param(description="the total secondary storage space (in GiB) owned by account", since="4.2.0") + private Long secondaryStorageTotal; + + @SerializedName("secondarystorageavailable") @Param(description="the total secondary storage space (in GiB) available to be used for this account", since="4.2.0") + private String secondaryStorageAvailable; + @SerializedName(ApiConstants.STATE) @Param(description="the state of the account") private String state; @@ -336,6 +354,30 @@ public class AccountResponse extends BaseResponse { this.memoryAvailable = memoryAvailable; } + public void setPrimaryStorageLimit(String primaryStorageLimit) { + this.primaryStorageLimit = primaryStorageLimit; + } + + public void setPrimaryStorageTotal(Long primaryStorageTotal) { + this.primaryStorageTotal = primaryStorageTotal; + } + + public void setPrimaryStorageAvailable(String primaryStorageAvailable) { + this.primaryStorageAvailable = primaryStorageAvailable; + } + + public void setSecondaryStorageLimit(String secondaryStorageLimit) { + this.secondaryStorageLimit = secondaryStorageLimit; + } + + public void setSecondaryStorageTotal(Long secondaryStorageTotal) { + this.secondaryStorageTotal = secondaryStorageTotal; + } + + public void setSecondaryStorageAvailable(String secondaryStorageAvailable) { + this.secondaryStorageAvailable = secondaryStorageAvailable; + } + public void setDefaultZone(String defaultZoneId) { this.defaultZoneId = defaultZoneId; } diff --git a/api/src/org/apache/cloudstack/api/response/ResourceCountResponse.java b/api/src/org/apache/cloudstack/api/response/ResourceCountResponse.java index a7fbbf2630b..842eaa670dd 100644 --- a/api/src/org/apache/cloudstack/api/response/ResourceCountResponse.java +++ b/api/src/org/apache/cloudstack/api/response/ResourceCountResponse.java @@ -40,7 +40,7 @@ public class ResourceCountResponse extends BaseResponse implements ControlledEnt @SerializedName(ApiConstants.DOMAIN) @Param(description="the domain name for which resource count's are updated") private String domainName; - @SerializedName(ApiConstants.RESOURCE_TYPE) @Param(description="resource type. Values include 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. See the resourceType parameter for more information on these values.") + @SerializedName(ApiConstants.RESOURCE_TYPE) @Param(description="resource type. Values include 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11. See the resourceType parameter for more information on these values.") private String resourceType; @SerializedName("resourcecount") @Param(description="resource count") diff --git a/api/src/org/apache/cloudstack/api/response/ResourceLimitResponse.java b/api/src/org/apache/cloudstack/api/response/ResourceLimitResponse.java index b444e7a68a7..e7735f5c020 100644 --- a/api/src/org/apache/cloudstack/api/response/ResourceLimitResponse.java +++ b/api/src/org/apache/cloudstack/api/response/ResourceLimitResponse.java @@ -36,7 +36,7 @@ public class ResourceLimitResponse extends BaseResponse implements ControlledEnt @SerializedName(ApiConstants.DOMAIN) @Param(description="the domain name of the resource limit") private String domainName; - @SerializedName(ApiConstants.RESOURCE_TYPE) @Param(description="resource type. Values include 0, 1, 2, 3, 4, 6, 7, 8, 9. See the resourceType parameter for more information on these values.") + @SerializedName(ApiConstants.RESOURCE_TYPE) @Param(description="resource type. Values include 0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11. See the resourceType parameter for more information on these values.") private String resourceType; @SerializedName("max") @Param(description="the maximum number of the resource. A -1 means the resource currently has no limit.") diff --git a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BareMetalTemplateAdapter.java b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BareMetalTemplateAdapter.java index 33725f63b1c..928183ba3df 100755 --- a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BareMetalTemplateAdapter.java +++ b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/manager/BareMetalTemplateAdapter.java @@ -50,6 +50,7 @@ import com.cloud.storage.VMTemplateZoneVO; import com.cloud.template.TemplateAdapter; import com.cloud.template.TemplateAdapterBase; import com.cloud.user.Account; +import com.cloud.utils.UriUtils; import com.cloud.utils.db.DB; import com.cloud.utils.exception.CloudRuntimeException; @@ -82,7 +83,11 @@ public class BareMetalTemplateAdapter extends TemplateAdapterBase implements Tem throw new CloudRuntimeException("Please add PXE server before adding baremetal template in zone " + profile.getZoneId()); } } - + + // Check that the resource limit for secondary storage won't be exceeded + _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(cmd.getEntityOwnerId()), + ResourceType.secondary_storage, UriUtils.getRemoteSize(profile.getUrl())); + return profile; } @@ -133,6 +138,8 @@ public class BareMetalTemplateAdapter extends TemplateAdapterBase implements Tem } _resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template); + _resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.secondary_storage, + UriUtils.getRemoteSize(profile.getUrl())); return template; } @@ -205,8 +212,10 @@ public class BareMetalTemplateAdapter extends TemplateAdapterBase implements Tem s_logger.debug("Failed to acquire lock when deleting template with ID: " + templateId); success = false; } else if (_tmpltDao.remove(templateId)) { - // Decrement the number of templates - _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.template); + // Decrement the number of templates and total secondary storage space used by the account. + _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.template); + _resourceLimitMgr.recalculateResourceCount(accountId, template.getDomainId(), + ResourceType.secondary_storage.getOrdinal()); } } finally { diff --git a/server/src/com/cloud/alert/AlertManagerImpl.java b/server/src/com/cloud/alert/AlertManagerImpl.java index a45482fd4ef..655ed98ea60 100755 --- a/server/src/com/cloud/alert/AlertManagerImpl.java +++ b/server/src/com/cloud/alert/AlertManagerImpl.java @@ -802,7 +802,8 @@ public class AlertManagerImpl extends ManagerBase implements AlertManager { (alertType != AlertManager.ALERT_TYPE_CONSOLE_PROXY) && (alertType != AlertManager.ALERT_TYPE_SSVM) && (alertType != AlertManager.ALERT_TYPE_STORAGE_MISC) && - (alertType != AlertManager.ALERT_TYPE_MANAGMENT_NODE)) { + (alertType != AlertManager.ALERT_TYPE_MANAGMENT_NODE) && + (alertType != AlertManager.ALERT_TYPE_RESOURCE_LIMIT_EXCEEDED)) { alert = _alertDao.getLastAlert(alertType, dataCenterId, podId, clusterId); } diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index f33601f76ec..ec809091f23 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -16,10 +16,32 @@ // under the License. package com.cloud.api; -import com.cloud.api.query.ViewResponseHelper; -import com.cloud.api.query.vo.*; -import com.cloud.api.response.ApiResponseSerializer; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.TimeZone; + +import javax.inject.Inject; + +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.acl.ControlledEntity.ACLType; +import org.apache.cloudstack.api.ApiConstants.HostDetails; +import org.apache.cloudstack.api.ApiConstants.VMDetails; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.ResponseGenerator; +import org.apache.cloudstack.api.command.user.job.QueryAsyncJobResultCmd; +import org.apache.cloudstack.api.response.AccountResponse; import org.apache.cloudstack.api.response.AsyncJobResponse; import org.apache.cloudstack.api.response.AutoScalePolicyResponse; import org.apache.cloudstack.api.response.AutoScaleVmGroupResponse; @@ -30,6 +52,7 @@ import org.apache.cloudstack.api.response.ClusterResponse; import org.apache.cloudstack.api.response.ConditionResponse; import org.apache.cloudstack.api.response.ConfigurationResponse; import org.apache.cloudstack.api.response.ControlledEntityResponse; +import org.apache.cloudstack.api.response.ControlledViewEntityResponse; import org.apache.cloudstack.api.response.CounterResponse; import org.apache.cloudstack.api.response.CreateCmdResponse; import org.apache.cloudstack.api.response.DiskOfferingResponse; @@ -39,10 +62,10 @@ import org.apache.cloudstack.api.response.EventResponse; import org.apache.cloudstack.api.response.ExtractResponse; import org.apache.cloudstack.api.response.FirewallResponse; import org.apache.cloudstack.api.response.FirewallRuleResponse; +import org.apache.cloudstack.api.response.GlobalLoadBalancerResponse; import org.apache.cloudstack.api.response.GuestOSResponse; import org.apache.cloudstack.api.response.HostResponse; import org.apache.cloudstack.api.response.HypervisorCapabilitiesResponse; -import org.apache.cloudstack.api.response.ControlledViewEntityResponse; import org.apache.cloudstack.api.response.IPAddressResponse; import org.apache.cloudstack.api.response.InstanceGroupResponse; import org.apache.cloudstack.api.response.IpForwardingRuleResponse; @@ -56,6 +79,7 @@ import org.apache.cloudstack.api.response.NetworkACLResponse; import org.apache.cloudstack.api.response.NetworkOfferingResponse; import org.apache.cloudstack.api.response.NetworkResponse; import org.apache.cloudstack.api.response.NicResponse; +import org.apache.cloudstack.api.response.NicSecondaryIpResponse; import org.apache.cloudstack.api.response.PhysicalNetworkResponse; import org.apache.cloudstack.api.response.PodResponse; import org.apache.cloudstack.api.response.PrivateGatewayResponse; @@ -63,10 +87,12 @@ import org.apache.cloudstack.api.response.ProjectAccountResponse; import org.apache.cloudstack.api.response.ProjectInvitationResponse; import org.apache.cloudstack.api.response.ProjectResponse; import org.apache.cloudstack.api.response.ProviderResponse; +import org.apache.cloudstack.api.response.RegionResponse; import org.apache.cloudstack.api.response.RemoteAccessVpnResponse; import org.apache.cloudstack.api.response.ResourceCountResponse; import org.apache.cloudstack.api.response.ResourceLimitResponse; import org.apache.cloudstack.api.response.ResourceTagResponse; +import org.apache.cloudstack.api.response.S3Response; import org.apache.cloudstack.api.response.SecurityGroupResponse; import org.apache.cloudstack.api.response.SecurityGroupRuleResponse; import org.apache.cloudstack.api.response.ServiceOfferingResponse; @@ -90,6 +116,7 @@ import org.apache.cloudstack.api.response.TrafficTypeResponse; import org.apache.cloudstack.api.response.UsageRecordResponse; import org.apache.cloudstack.api.response.UserResponse; import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.api.response.VMSnapshotResponse; import org.apache.cloudstack.api.response.VirtualRouterProviderResponse; import org.apache.cloudstack.api.response.VlanIpRangeResponse; import org.apache.cloudstack.api.response.VolumeResponse; @@ -97,37 +124,86 @@ import org.apache.cloudstack.api.response.VpcOfferingResponse; import org.apache.cloudstack.api.response.VpcResponse; import org.apache.cloudstack.api.response.VpnUsersResponse; import org.apache.cloudstack.api.response.ZoneResponse; -import org.apache.cloudstack.api.response.S3Response; +import org.apache.cloudstack.region.Region; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; +import org.apache.cloudstack.usage.Usage; +import org.apache.cloudstack.usage.UsageService; +import org.apache.cloudstack.usage.UsageTypes; +import org.apache.log4j.Logger; import org.springframework.stereotype.Component; +import com.cloud.api.query.ViewResponseHelper; +import com.cloud.api.query.vo.AccountJoinVO; +import com.cloud.api.query.vo.AsyncJobJoinVO; +import com.cloud.api.query.vo.ControlledViewEntity; +import com.cloud.api.query.vo.DataCenterJoinVO; +import com.cloud.api.query.vo.DiskOfferingJoinVO; +import com.cloud.api.query.vo.DomainRouterJoinVO; +import com.cloud.api.query.vo.EventJoinVO; +import com.cloud.api.query.vo.HostJoinVO; +import com.cloud.api.query.vo.InstanceGroupJoinVO; +import com.cloud.api.query.vo.ProjectAccountJoinVO; +import com.cloud.api.query.vo.ProjectInvitationJoinVO; +import com.cloud.api.query.vo.ProjectJoinVO; +import com.cloud.api.query.vo.ResourceTagJoinVO; +import com.cloud.api.query.vo.SecurityGroupJoinVO; +import com.cloud.api.query.vo.ServiceOfferingJoinVO; +import com.cloud.api.query.vo.StoragePoolJoinVO; +import com.cloud.api.query.vo.UserAccountJoinVO; +import com.cloud.api.query.vo.UserVmJoinVO; +import com.cloud.api.query.vo.VolumeJoinVO; +import com.cloud.api.response.ApiResponseSerializer; import com.cloud.async.AsyncJob; import com.cloud.capacity.Capacity; import com.cloud.capacity.CapacityVO; import com.cloud.capacity.dao.CapacityDaoImpl.SummedCapacity; import com.cloud.configuration.Configuration; import com.cloud.configuration.Resource.ResourceOwnerType; +import com.cloud.configuration.Resource.ResourceType; import com.cloud.configuration.ResourceCount; import com.cloud.configuration.ResourceLimit; import com.cloud.dao.EntityManager; -import com.cloud.dc.*; +import com.cloud.dc.ClusterVO; +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.HostPodVO; +import com.cloud.dc.Pod; +import com.cloud.dc.StorageNetworkIpRange; +import com.cloud.dc.Vlan; import com.cloud.dc.Vlan.VlanType; +import com.cloud.dc.VlanVO; import com.cloud.domain.Domain; import com.cloud.event.Event; import com.cloud.host.Host; import com.cloud.host.HostVO; import com.cloud.hypervisor.HypervisorCapabilities; -import com.cloud.network.*; +import com.cloud.network.IpAddress; +import com.cloud.network.Network; import com.cloud.network.Network.Capability; import com.cloud.network.Network.Provider; import com.cloud.network.Network.Service; +import com.cloud.network.NetworkProfile; import com.cloud.network.Networks.TrafficType; -import com.cloud.network.as.*; +import com.cloud.network.PhysicalNetwork; +import com.cloud.network.PhysicalNetworkServiceProvider; +import com.cloud.network.PhysicalNetworkTrafficType; +import com.cloud.network.RemoteAccessVpn; +import com.cloud.network.Site2SiteCustomerGateway; +import com.cloud.network.Site2SiteVpnConnection; +import com.cloud.network.Site2SiteVpnGateway; +import com.cloud.network.VirtualRouterProvider; +import com.cloud.network.VpnUser; +import com.cloud.network.as.AutoScalePolicy; +import com.cloud.network.as.AutoScaleVmGroup; +import com.cloud.network.as.AutoScaleVmProfile; +import com.cloud.network.as.AutoScaleVmProfileVO; +import com.cloud.network.as.Condition; +import com.cloud.network.as.ConditionVO; +import com.cloud.network.as.Counter; import com.cloud.network.dao.IPAddressVO; import com.cloud.network.dao.NetworkVO; import com.cloud.network.dao.PhysicalNetworkVO; import com.cloud.network.router.VirtualRouter; -import com.cloud.network.rules.*; import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.FirewallRuleVO; import com.cloud.network.rules.HealthCheckPolicy; @@ -156,12 +232,25 @@ import com.cloud.server.Criteria; import com.cloud.server.ResourceTag; import com.cloud.server.ResourceTag.TaggedResourceType; import com.cloud.service.ServiceOfferingVO; -import com.cloud.storage.*; +import com.cloud.storage.DiskOfferingVO; +import com.cloud.storage.GuestOS; +import com.cloud.storage.GuestOSCategoryVO; +import com.cloud.storage.S3; +import com.cloud.storage.Snapshot; +import com.cloud.storage.SnapshotVO; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.Storage.TemplateType; +import com.cloud.storage.StoragePool; +import com.cloud.storage.Swift; +import com.cloud.storage.UploadVO; +import com.cloud.storage.VMTemplateHostVO; +import com.cloud.storage.VMTemplateS3VO; import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; - +import com.cloud.storage.VMTemplateSwiftVO; +import com.cloud.storage.VMTemplateVO; +import com.cloud.storage.Volume; +import com.cloud.storage.VolumeVO; import com.cloud.storage.snapshot.SnapshotPolicy; import com.cloud.storage.snapshot.SnapshotSchedule; import com.cloud.template.VirtualMachineTemplate; @@ -179,40 +268,10 @@ import com.cloud.vm.Nic; import com.cloud.vm.NicProfile; import com.cloud.vm.NicVO; import com.cloud.vm.VMInstanceVO; -import com.cloud.vm.NicSecondaryIp; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.Type; import com.cloud.vm.dao.NicSecondaryIpVO; - -import org.apache.cloudstack.acl.ControlledEntity; -import org.apache.cloudstack.acl.ControlledEntity.ACLType; -import org.apache.cloudstack.api.ApiConstants.HostDetails; -import org.apache.cloudstack.api.ApiConstants.VMDetails; -import org.apache.cloudstack.api.BaseCmd; -import org.apache.cloudstack.api.ResponseGenerator; -import org.apache.cloudstack.api.command.user.job.QueryAsyncJobResultCmd; -import org.apache.cloudstack.api.response.*; -import org.apache.cloudstack.region.Region; -import org.apache.cloudstack.usage.Usage; -import org.apache.cloudstack.usage.UsageService; -import org.apache.cloudstack.usage.UsageTypes; -import com.cloud.vm.VmStats; -import com.cloud.vm.dao.UserVmData; -import com.cloud.vm.dao.UserVmData.NicData; -import com.cloud.vm.dao.UserVmData.SecurityGroupData; -import com.cloud.vm.snapshot.VMSnapshot; -import org.apache.cloudstack.api.ResponseGenerator; -import org.apache.cloudstack.api.response.VMSnapshotResponse; -import org.apache.log4j.Logger; -import org.springframework.stereotype.Component; - -import java.text.DecimalFormat; -import java.util.*; - -import javax.inject.Inject; - -import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; +import com.cloud.vm.snapshot.VMSnapshot; @Component public class ApiResponseHelper implements ResponseGenerator { @@ -290,7 +349,11 @@ public class ApiResponseHelper implements ResponseGenerator { populateDomain(resourceLimitResponse, accountTemp.getDomainId()); } resourceLimitResponse.setResourceType(Integer.valueOf(limit.getType().getOrdinal()).toString()); - resourceLimitResponse.setMax(limit.getMax()); + if(limit.getType() == ResourceType.primary_storage || limit.getType() == ResourceType.secondary_storage) { + resourceLimitResponse.setMax((long) Math.ceil(limit.getMax()/ResourceType.bytesToGiB)); + } else { + resourceLimitResponse.setMax(limit.getMax()); + } resourceLimitResponse.setObjectName("resourcelimit"); return resourceLimitResponse; diff --git a/server/src/com/cloud/api/query/dao/AccountJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/AccountJoinDaoImpl.java index 898bafc47cd..51ac5e61edb 100644 --- a/server/src/com/cloud/api/query/dao/AccountJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/AccountJoinDaoImpl.java @@ -175,6 +175,24 @@ public class AccountJoinDaoImpl extends GenericDaoBase impl accountResponse.setMemoryTotal(memoryTotal); accountResponse.setMemoryAvailable(memoryAvail); + //get resource limits for primary storage space and convert it from Bytes to GiB + long primaryStorageLimit = ApiDBUtils.findCorrectResourceLimit(account.getPrimaryStorageLimit(), account.getType(), ResourceType.primary_storage); + String primaryStorageLimitDisplay = (accountIsAdmin || primaryStorageLimit == -1) ? "Unlimited" : String.valueOf(primaryStorageLimit / ResourceType.bytesToGiB); + long primaryStorageTotal = (account.getPrimaryStorageTotal() == null) ? 0 : (account.getPrimaryStorageTotal() / ResourceType.bytesToGiB); + String primaryStorageAvail = (accountIsAdmin || primaryStorageLimit == -1) ? "Unlimited" : String.valueOf((primaryStorageLimit / ResourceType.bytesToGiB) - primaryStorageTotal); + accountResponse.setPrimaryStorageLimit(primaryStorageLimitDisplay); + accountResponse.setPrimaryStorageTotal(primaryStorageTotal); + accountResponse.setPrimaryStorageAvailable(primaryStorageAvail); + + //get resource limits for secondary storage space and convert it from Bytes to GiB + long secondaryStorageLimit = ApiDBUtils.findCorrectResourceLimit(account.getSecondaryStorageLimit(), account.getType(), ResourceType.secondary_storage); + String secondaryStorageLimitDisplay = (accountIsAdmin || secondaryStorageLimit == -1) ? "Unlimited" : String.valueOf(secondaryStorageLimit / ResourceType.bytesToGiB); + long secondaryStorageTotal = (account.getSecondaryStorageTotal() == null) ? 0 : (account.getSecondaryStorageTotal() / ResourceType.bytesToGiB); + String secondaryStorageAvail = (accountIsAdmin || secondaryStorageLimit == -1) ? "Unlimited" : String.valueOf((secondaryStorageLimit / ResourceType.bytesToGiB) - secondaryStorageTotal); + accountResponse.setSecondaryStorageLimit(secondaryStorageLimitDisplay); + accountResponse.setSecondaryStorageTotal(secondaryStorageTotal); + accountResponse.setSecondaryStorageAvailable(secondaryStorageAvail); + // adding all the users for an account as part of the response obj List usersForAccount = ApiDBUtils.findUserViewByAccountId(account.getId()); List userResponses = ViewResponseHelper.createUserResponse(usersForAccount.toArray(new UserAccountJoinVO[usersForAccount.size()])); diff --git a/server/src/com/cloud/api/query/vo/AccountJoinVO.java b/server/src/com/cloud/api/query/vo/AccountJoinVO.java index cd7231cc8ea..1cb17ef688b 100644 --- a/server/src/com/cloud/api/query/vo/AccountJoinVO.java +++ b/server/src/com/cloud/api/query/vo/AccountJoinVO.java @@ -162,6 +162,20 @@ public class AccountJoinVO extends BaseViewVO implements InternalIdentity, Ident @Column(name="memoryTotal") private Long memoryTotal; + + @Column(name="primaryStorageLimit") + private Long primaryStorageLimit; + + @Column(name="primaryStorageTotal") + private Long primaryStorageTotal; + + + @Column(name="secondaryStorageLimit") + private Long secondaryStorageLimit; + + @Column(name="secondaryStorageTotal") + private Long secondaryStorageTotal; + @Column(name="job_id") private long jobId; @@ -478,6 +492,7 @@ public class AccountJoinVO extends BaseViewVO implements InternalIdentity, Ident this.cpuTotal = cpuTotal; } + public Long getMemoryTotal() { return memoryTotal; } @@ -488,6 +503,25 @@ public class AccountJoinVO extends BaseViewVO implements InternalIdentity, Ident } + public Long getPrimaryStorageTotal() { + return primaryStorageTotal; + } + + + public void setPrimaryStorageTotal(Long primaryStorageTotal) { + this.primaryStorageTotal = primaryStorageTotal; + } + + public Long getSecondaryStorageTotal() { + return secondaryStorageTotal; + } + + + public void setSecondaryStorageTotal(Long secondaryStorageTotal) { + this.secondaryStorageTotal = secondaryStorageTotal; + } + + public Long getVmLimit() { return vmLimit; } @@ -588,6 +622,26 @@ public class AccountJoinVO extends BaseViewVO implements InternalIdentity, Ident } + public Long getPrimaryStorageLimit() { + return primaryStorageLimit; + } + + + public void setPrimaryStorageLimit(Long primaryStorageLimit) { + this.primaryStorageLimit = primaryStorageLimit; + } + + + public Long getSecondaryStorageLimit() { + return secondaryStorageLimit; + } + + + public void setSecondaryStorageLimit(Long secondaryStorageLimit) { + this.secondaryStorageLimit = secondaryStorageLimit; + } + + public long getJobId() { return jobId; } diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index ba4ae623abe..5b2616f2d70 100755 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -323,7 +323,8 @@ public enum Config { DefaultMaxAccountVpcs("Account Defaults", ManagementServer.class, Long.class, "max.account.vpcs", "20", "The default maximum number of vpcs that can be created for an account", null), DefaultMaxAccountCpus("Account Defaults", ManagementServer.class, Long.class, "max.account.cpus", "40", "The default maximum number of cpu cores that can be used for an account", null), DefaultMaxAccountMemory("Account Defaults", ManagementServer.class, Long.class, "max.account.memory", "40960", "The default maximum memory (in MB) that can be used for an account", null), - + DefaultMaxAccountPrimayStorage("Account Defaults", ManagementServer.class, Long.class, "max.account.primary.storage", "200", "The default maximum primary storage space (in GiB) that can be used for an account", null), + DefaultMaxAccountSecondaryStorage("Account Defaults", ManagementServer.class, Long.class, "max.account.secondary.storage", "400", "The default maximum secondary storage space (in GiB) that can be used for an account", null), ResourceCountCheckInterval("Advanced", ManagementServer.class, Long.class, "resourcecount.check.interval", "0", "Time (in seconds) to wait before retrying resource count check task. Default is 0 which is to never run the task", "Seconds"), DirectAgentLoadSize("Advanced", ManagementServer.class, Integer.class, "direct.agent.load.size", "16", "The number of direct agents to load each time", null), @@ -349,6 +350,8 @@ public enum Config { DefaultMaxProjectVpcs("Project Defaults", ManagementServer.class, Long.class, "max.project.vpcs", "20", "The default maximum number of vpcs that can be created for a project", null), DefaultMaxProjectCpus("Project Defaults", ManagementServer.class, Long.class, "max.project.cpus", "40", "The default maximum number of cpu cores that can be used for a project", null), DefaultMaxProjectMemory("Project Defaults", ManagementServer.class, Long.class, "max.project.memory", "40960", "The default maximum memory (in MB) that can be used for a project", null), + DefaultMaxProjectPrimayStorage("Project Defaults", ManagementServer.class, Long.class, "max.project.primary.storage", "200", "The default maximum primary storage space (in GiB) that can be used for an project", null), + DefaultMaxProjectSecondaryStorage("Project Defaults", ManagementServer.class, Long.class, "max.project.secondary.storage", "400", "The default maximum secondary storage space (in GiB) that can be used for an project", null), ProjectInviteRequired("Project Defaults", ManagementServer.class, Boolean.class, "project.invite.required", "false", "If invitation confirmation is required when add account to project. Default value is false", null), ProjectInvitationExpirationTime("Project Defaults", ManagementServer.class, Long.class, "project.invite.timeout", "86400", "Invitation expiration time (in seconds). Default is 1 day - 86400 seconds", null), diff --git a/server/src/com/cloud/resourcelimit/ResourceLimitManagerImpl.java b/server/src/com/cloud/resourcelimit/ResourceLimitManagerImpl.java index 23c079679b1..bbe0190e3ae 100755 --- a/server/src/com/cloud/resourcelimit/ResourceLimitManagerImpl.java +++ b/server/src/com/cloud/resourcelimit/ResourceLimitManagerImpl.java @@ -61,8 +61,12 @@ import com.cloud.projects.dao.ProjectAccountDao; import com.cloud.projects.dao.ProjectDao; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.storage.VMTemplateHostVO; +import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; +import com.cloud.storage.VMTemplateVO; import com.cloud.storage.dao.SnapshotDao; import com.cloud.storage.dao.VMTemplateDao; +import com.cloud.storage.dao.VMTemplateHostDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.dao.VolumeDaoImpl.SumCount; import com.cloud.user.Account; @@ -135,6 +139,10 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim private VpcDao _vpcDao; @Inject private ServiceOfferingDao _serviceOfferingDao; + @Inject + private VMTemplateHostDao _vmTemplateHostDao; + + protected GenericSearchBuilder templateSizeSearch; protected SearchBuilder ResourceCountSearch; ScheduledExecutorService _rcExecutor; @@ -164,6 +172,15 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim ResourceCountSearch.and("domainId", ResourceCountSearch.entity().getDomainId(), SearchCriteria.Op.EQ); ResourceCountSearch.done(); + templateSizeSearch = _vmTemplateHostDao.createSearchBuilder(SumCount.class); + templateSizeSearch.select("sum", Func.SUM, templateSizeSearch.entity().getSize()); + templateSizeSearch.and("downloadState", templateSizeSearch.entity().getDownloadState(), Op.EQ); + templateSizeSearch.and("destroyed", templateSizeSearch.entity().getDestroyed(), Op.EQ); + SearchBuilder join1 = _vmTemplateDao.createSearchBuilder(); + join1.and("accountId", join1.entity().getAccountId(), Op.EQ); + templateSizeSearch.join("templates", join1, templateSizeSearch.entity().getTemplateId(), join1.entity().getId(), JoinBuilder.JoinType.INNER); + templateSizeSearch.done(); + _resourceCountCheckInterval = NumbersUtil.parseInt(_configDao.getValue(Config.ResourceCountCheckInterval.key()), 0); if (_resourceCountCheckInterval > 0) { _rcExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("ResourceCountChecker")); @@ -178,6 +195,8 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim projectResourceLimitMap.put(Resource.ResourceType.vpc, Long.parseLong(_configDao.getValue(Config.DefaultMaxProjectVpcs.key()))); projectResourceLimitMap.put(Resource.ResourceType.cpu, Long.parseLong(_configDao.getValue(Config.DefaultMaxProjectCpus.key()))); projectResourceLimitMap.put(Resource.ResourceType.memory, Long.parseLong(_configDao.getValue(Config.DefaultMaxProjectMemory.key()))); + projectResourceLimitMap.put(Resource.ResourceType.primary_storage, Long.parseLong(_configDao.getValue(Config.DefaultMaxProjectPrimayStorage.key()))); + projectResourceLimitMap.put(Resource.ResourceType.secondary_storage, Long.parseLong(_configDao.getValue(Config.DefaultMaxProjectSecondaryStorage.key()))); accountResourceLimitMap.put(Resource.ResourceType.public_ip, Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountPublicIPs.key()))); accountResourceLimitMap.put(Resource.ResourceType.snapshot, Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountSnapshots.key()))); @@ -188,6 +207,8 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim accountResourceLimitMap.put(Resource.ResourceType.vpc, Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountVpcs.key()))); accountResourceLimitMap.put(Resource.ResourceType.cpu, Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountCpus.key()))); accountResourceLimitMap.put(Resource.ResourceType.memory, Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountMemory.key()))); + accountResourceLimitMap.put(Resource.ResourceType.primary_storage, Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountPrimayStorage.key()))); + accountResourceLimitMap.put(Resource.ResourceType.secondary_storage, Long.parseLong(_configDao.getValue(Config.DefaultMaxAccountSecondaryStorage.key()))); return true; } @@ -246,6 +267,10 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim value = accountResourceLimitMap.get(type); } if (value != null) { + // convert the value from GiB to bytes in case of primary or secondary storage. + if (type == ResourceType.primary_storage || type == ResourceType.secondary_storage) { + value = value * ResourceType.bytesToGiB; + } return value; } } @@ -276,6 +301,9 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim value = accountResourceLimitMap.get(type); } if (value != null) { + if (type == ResourceType.primary_storage || type == ResourceType.secondary_storage) { + value = value * ResourceType.bytesToGiB; + } return value; } } @@ -548,6 +576,11 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim } } + //Convert max storage size from GiB to bytes + if (resourceType == ResourceType.primary_storage || resourceType == ResourceType.secondary_storage) { + max = max * ResourceType.bytesToGiB; + } + ResourceOwnerType ownerType = null; Long ownerId = null; @@ -789,6 +822,10 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim newCount = countCpusForAccount(accountId); } else if (type == Resource.ResourceType.memory) { newCount = calculateMemoryForAccount(accountId); + } else if (type == Resource.ResourceType.primary_storage) { + newCount = _volumeDao.primaryStorageUsedForAccount(accountId); + } else if (type == Resource.ResourceType.secondary_storage) { + newCount = calculateSecondaryStorageForAccount(accountId); } else { throw new InvalidParameterValueException("Unsupported resource type " + type); } @@ -847,6 +884,23 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim } } + public long calculateSecondaryStorageForAccount(long accountId) { + long totalVolumesSize = _volumeDao.secondaryStorageUsedForAccount(accountId); + long totalSnapshotsSize = _snapshotDao.secondaryStorageUsedForAccount(accountId); + long totalTemplatesSize = 0; + + SearchCriteria sc = templateSizeSearch.create(); + sc.setParameters("downloadState", Status.DOWNLOADED); + sc.setParameters("destroyed", false); + sc.setJoinParameters("templates", "accountId", accountId); + List templates = _vmTemplateHostDao.customSearch(sc, null); + if (templates != null) { + totalTemplatesSize = templates.get(0).sum; + } + + return totalVolumesSize + totalSnapshotsSize + totalTemplatesSize; + } + @Override public long getResourceCount(Account account, ResourceType type) { return _resourceCountDao.getResourceCount(account.getId(), ResourceOwnerType.Account, type); diff --git a/server/src/com/cloud/storage/VolumeManager.java b/server/src/com/cloud/storage/VolumeManager.java index af3cbbfbae5..2101038f8f3 100644 --- a/server/src/com/cloud/storage/VolumeManager.java +++ b/server/src/com/cloud/storage/VolumeManager.java @@ -61,7 +61,8 @@ public interface VolumeManager extends VolumeApiService { VolumeVO createVolume(CreateVolumeCmd cmd); - VolumeVO resizeVolume(ResizeVolumeCmd cmd); + VolumeVO resizeVolume(ResizeVolumeCmd cmd) + throws ResourceAllocationException; boolean deleteVolume(long volumeId, Account caller) throws ConcurrentOperationException; diff --git a/server/src/com/cloud/storage/VolumeManagerImpl.java b/server/src/com/cloud/storage/VolumeManagerImpl.java index 737ed0a3bac..ff0235f018a 100644 --- a/server/src/com/cloud/storage/VolumeManagerImpl.java +++ b/server/src/com/cloud/storage/VolumeManagerImpl.java @@ -146,6 +146,7 @@ import com.cloud.uservm.UserVm; import com.cloud.utils.EnumUtils; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; +import com.cloud.utils.UriUtils; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.db.DB; import com.cloud.utils.db.JoinBuilder; @@ -469,6 +470,10 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { } validateUrl(url); + // Check that the resource limit for secondary storage won't be exceeded + _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(ownerId), ResourceType.secondary_storage, + UriUtils.getRemoteSize(url)); + return false; } @@ -736,6 +741,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { // decrement it _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.volume); + _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.secondary_storage, + UriUtils.getRemoteSize(url)); txn.commit(); return volume; @@ -906,6 +913,10 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { _accountMgr.checkAccess(caller, null, true, snapshotCheck); } + // Check that the resource limit for primary storage won't be exceeded + _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(ownerId), ResourceType.primary_storage, + new Long(size)); + // Verify that zone exists DataCenterVO zone = _dcDao.findById(zoneId); if (zone == null) { @@ -974,6 +985,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { // decrement it _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.volume); + _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.primary_storage, + new Long(volume.getSize())); txn.commit(); @@ -1006,6 +1019,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { + " as volume failed to create on the backend"); _resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.volume); + _resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.primary_storage, + new Long(volume.getSize())); } } } @@ -1013,7 +1028,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { @Override @DB @ActionEvent(eventType = EventTypes.EVENT_VOLUME_RESIZE, eventDescription = "resizing volume", async = true) - public VolumeVO resizeVolume(ResizeVolumeCmd cmd) { + public VolumeVO resizeVolume(ResizeVolumeCmd cmd) + throws ResourceAllocationException { Long newSize = null; boolean shrinkOk = cmd.getShrinkOk(); @@ -1149,6 +1165,12 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { + " would shrink the volume, need to sign off by supplying the shrinkok parameter with value of true"); } + if (!shrinkOk) { + /* Check resource limit for this account on primary storage resource */ + _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(volume.getAccountId()), + ResourceType.primary_storage, new Long(newSize - currentSize)); + } + /* * get a list of hosts to send the commands to, try the system the * associated vm is running on first, then the last known place it ran. @@ -1176,27 +1198,35 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { ResizeVolumePayload payload = new ResizeVolumePayload(newSize, shrinkOk, instanceName, hosts); try { - VolumeInfo vol = this.volFactory.getVolume(volume.getId()); + VolumeInfo vol = this.volFactory.getVolume(volume.getId()); vol.addPayload(payload); - - AsyncCallFuture future = this.volService.resize(vol); - future.get(); - volume = _volsDao.findById(volume.getId()); - if (newDiskOffering != null) { - volume.setDiskOfferingId(cmd.getNewDiskOfferingId()); - } - _volsDao.update(volume.getId(), volume); + AsyncCallFuture future = this.volService.resize(vol); + future.get(); + volume = _volsDao.findById(volume.getId()); + + if (newDiskOffering != null) { + volume.setDiskOfferingId(cmd.getNewDiskOfferingId()); + } + _volsDao.update(volume.getId(), volume); + + /* Update resource count for the account on primary storage resource */ + if (!shrinkOk) { + _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.primary_storage, + new Long(newSize - currentSize)); + } else { + _resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.primary_storage, + new Long(currentSize - newSize)); + } + return volume; + } catch (InterruptedException e) { + s_logger.debug("failed get resize volume result", e); + } catch (ExecutionException e) { + s_logger.debug("failed get resize volume result", e); + } catch (Exception e) { + s_logger.debug("failed get resize volume result", e); + } - return volume; - } catch (InterruptedException e) { - s_logger.debug("failed get resize volume result", e); - } catch (ExecutionException e) { - s_logger.debug("failed get resize volume result", e); - } catch (Exception e) { - s_logger.debug("failed get resize volume result", e); - } - return null; } @@ -1243,9 +1273,19 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { VMInstanceVO vmInstance = this._vmInstanceDao.findById(instanceId); if (instanceId == null || (vmInstance.getType().equals(VirtualMachine.Type.User))) { - // Decrement the resource count for volumes belonging user VM's only + // Decrement the resource count for volumes and primary storage belonging user VM's only _resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.volume); + /* If volume is in primary storage, decrement primary storage count else decrement secondary + storage count (in case of upload volume). */ + if (volume.getFolder() != null) { + _resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.primary_storage, + new Long(volume.getSize())); + } else { + _resourceLimitMgr.recalculateResourceCount(volume.getAccountId(), volume.getDomainId(), + ResourceType.secondary_storage.getOrdinal()); + } + // Log usage event for volumes belonging user VM's only UsageEventVO usageEvent = new UsageEventVO( EventTypes.EVENT_VOLUME_DELETE, volume.getAccountId(), @@ -1317,6 +1357,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { _resourceLimitMgr.incrementResourceCount(vm.getAccountId(), ResourceType.volume); + _resourceLimitMgr.incrementResourceCount(vm.getAccountId(), ResourceType.primary_storage, + new Long(vol.getSize())); } return toDiskProfile(vol, offering); } @@ -1364,6 +1406,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { _resourceLimitMgr.incrementResourceCount(vm.getAccountId(), ResourceType.volume); + _resourceLimitMgr.incrementResourceCount(vm.getAccountId(), ResourceType.primary_storage, + new Long(vol.getSize())); } return toDiskProfile(vol, offering); } @@ -1435,6 +1479,13 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager { vol = copyVolume(rootDiskPool , volume, vm, rootDiskTmplt, dcVO, pod, diskVO, svo, rootDiskHyperType); + if (vol != null) { + // Moving of Volume is successful, decrement the volume resource count from secondary for an account and increment it into primary storage under same account. + _resourceLimitMgr.decrementResourceCount(volume.getAccountId(), + ResourceType.secondary_storage, new Long(volume.getSize())); + _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), + ResourceType.primary_storage, new Long(volume.getSize())); + } } return vol; } diff --git a/server/src/com/cloud/storage/dao/SnapshotDao.java b/server/src/com/cloud/storage/dao/SnapshotDao.java index 0e378a724b4..3ac9e77fb5d 100644 --- a/server/src/com/cloud/storage/dao/SnapshotDao.java +++ b/server/src/com/cloud/storage/dao/SnapshotDao.java @@ -40,7 +40,15 @@ public interface SnapshotDao extends GenericDao, StateDao listByHostId(Filter filter, long hostId); List listByHostId(long hostId); public Long countSnapshotsForAccount(long accountId); - List listByInstanceId(long instanceId, Snapshot.State... status); - List listByStatus(long volumeId, Snapshot.State... status); + List listByInstanceId(long instanceId, Snapshot.State... status); + List listByStatus(long volumeId, Snapshot.State... status); List listAllByStatus(Snapshot.State... status); + /** + * Gets the Total Secondary Storage space (in bytes) used by snapshots allocated for an account + * + * @param account + * @return total Secondary Storage space allocated + */ + long secondaryStorageUsedForAccount(long accountId); + } diff --git a/server/src/com/cloud/storage/dao/SnapshotDaoImpl.java b/server/src/com/cloud/storage/dao/SnapshotDaoImpl.java index 825b6d56e00..f55663fe204 100644 --- a/server/src/com/cloud/storage/dao/SnapshotDaoImpl.java +++ b/server/src/com/cloud/storage/dao/SnapshotDaoImpl.java @@ -35,15 +35,20 @@ import com.cloud.storage.Snapshot.Type; import com.cloud.storage.SnapshotVO; import com.cloud.storage.Volume; import com.cloud.storage.VolumeVO; +import com.cloud.storage.dao.VolumeDaoImpl.SumCount; import com.cloud.tags.dao.ResourceTagDao; -import com.cloud.tags.dao.ResourceTagsDaoImpl; - -import com.cloud.utils.db.*; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.Filter; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.GenericSearchBuilder; import com.cloud.utils.db.JoinBuilder.JoinType; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Func; +import com.cloud.utils.db.SearchCriteria.Op; +import com.cloud.utils.db.Transaction; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.dao.VMInstanceDao; -import com.cloud.vm.dao.VMInstanceDaoImpl; @Component @Local (value={SnapshotDao.class}) @@ -53,19 +58,19 @@ public class SnapshotDaoImpl extends GenericDaoBase implements private static final String UPDATE_SNAPSHOT_VERSION = "UPDATE snapshots SET version = ? WHERE volume_id = ? AND version = ?"; private static final String GET_SECHOST_ID = "SELECT sechost_id FROM snapshots where volume_id = ? AND backup_snap_id IS NOT NULL AND sechost_id IS NOT NULL LIMIT 1"; private static final String UPDATE_SECHOST_ID = "UPDATE snapshots SET sechost_id = ? WHERE data_center_id = ?"; - + private SearchBuilder VolumeIdSearch; private SearchBuilder VolumeIdTypeSearch; private SearchBuilder ParentIdSearch; - private SearchBuilder backupUuidSearch; + private SearchBuilder backupUuidSearch; private SearchBuilder VolumeIdVersionSearch; private SearchBuilder HostIdSearch; private SearchBuilder AccountIdSearch; private SearchBuilder InstanceIdSearch; private SearchBuilder StatusSearch; private GenericSearchBuilder CountSnapshotsByAccount; + private GenericSearchBuilder secondaryStorageSearch; @Inject ResourceTagDao _tagsDao; - @Inject protected VMInstanceDao _instanceDao; @Inject protected VolumeDao _volumeDao; @@ -187,23 +192,29 @@ public class SnapshotDaoImpl extends GenericDaoBase implements CountSnapshotsByAccount.and("removed", CountSnapshotsByAccount.entity().getRemoved(), SearchCriteria.Op.NULL); CountSnapshotsByAccount.done(); - InstanceIdSearch = createSearchBuilder(); + InstanceIdSearch = createSearchBuilder(); InstanceIdSearch.and("status", InstanceIdSearch.entity().getState(), SearchCriteria.Op.IN); - - SearchBuilder instanceSearch = _instanceDao.createSearchBuilder(); - instanceSearch.and("instanceId", instanceSearch.entity().getId(), SearchCriteria.Op.EQ); - - SearchBuilder volumeSearch = _volumeDao.createSearchBuilder(); - volumeSearch.and("state", volumeSearch.entity().getState(), SearchCriteria.Op.EQ); - volumeSearch.join("instanceVolumes", instanceSearch, instanceSearch.entity().getId(), volumeSearch.entity().getInstanceId(), JoinType.INNER); - - InstanceIdSearch.join("instanceSnapshots", volumeSearch, volumeSearch.entity().getId(), InstanceIdSearch.entity().getVolumeId(), JoinType.INNER); - InstanceIdSearch.done(); + + SearchBuilder instanceSearch = _instanceDao.createSearchBuilder(); + instanceSearch.and("instanceId", instanceSearch.entity().getId(), SearchCriteria.Op.EQ); + + SearchBuilder volumeSearch = _volumeDao.createSearchBuilder(); + volumeSearch.and("state", volumeSearch.entity().getState(), SearchCriteria.Op.EQ); + volumeSearch.join("instanceVolumes", instanceSearch, instanceSearch.entity().getId(), volumeSearch.entity().getInstanceId(), JoinType.INNER); + + InstanceIdSearch.join("instanceSnapshots", volumeSearch, volumeSearch.entity().getId(), InstanceIdSearch.entity().getVolumeId(), JoinType.INNER); + InstanceIdSearch.done(); + + secondaryStorageSearch = createSearchBuilder(SumCount.class); + secondaryStorageSearch.select("sum", Func.SUM, secondaryStorageSearch.entity().getSize()); + secondaryStorageSearch.and("accountId", secondaryStorageSearch.entity().getAccountId(), Op.EQ); + secondaryStorageSearch.and("isRemoved", secondaryStorageSearch.entity().getRemoved(), Op.NULL); + secondaryStorageSearch.done(); } - - @Override + + @Override public Long getSecHostId(long volumeId) { - + Transaction txn = Transaction.currentTxn(); PreparedStatement pstmt = null; String sql = GET_SECHOST_ID; @@ -331,4 +342,16 @@ public class SnapshotDaoImpl extends GenericDaoBase implements txn.commit(); return true; } + + @Override + public long secondaryStorageUsedForAccount(long accountId) { + SearchCriteria sc = secondaryStorageSearch.create(); + sc.setParameters("accountId", accountId); + List storageSpace = customSearch(sc, null); + if (storageSpace != null) { + return storageSpace.get(0).sum; + } else { + return 0; + } + } } diff --git a/server/src/com/cloud/storage/dao/VolumeDao.java b/server/src/com/cloud/storage/dao/VolumeDao.java index d7a2667edb9..251318108d8 100755 --- a/server/src/com/cloud/storage/dao/VolumeDao.java +++ b/server/src/com/cloud/storage/dao/VolumeDao.java @@ -27,50 +27,66 @@ import com.cloud.utils.db.GenericDao; import com.cloud.utils.fsm.StateDao; public interface VolumeDao extends GenericDao, StateDao { - - List findDetachedByAccount(long accountId); - + + List findDetachedByAccount(long accountId); + List findByAccount(long accountId); - + Pair getCountAndTotalByPool(long poolId); - + Pair getNonDestroyedCountAndTotalByPool(long poolId); - + List findByInstance(long id); - + List findByInstanceAndType(long id, Volume.Type vType); - + List findByInstanceIdDestroyed(long vmId); - + List findByAccountAndPod(long accountId, long podId); - + List findByTemplateAndZone(long templateId, long zoneId); - + void deleteVolumesByInstance(long instanceId); - + void attachVolume(long volumeId, long vmId, long deviceId); - + void detachVolume(long volumeId); - + boolean isAnyVolumeActivelyUsingTemplateOnPool(long templateId, long poolId); - + List findCreatedByInstance(long id); - + List findByPoolId(long poolId); - - List findByInstanceAndDeviceId(long instanceId, long deviceId); - + + List findByInstanceAndDeviceId(long instanceId, long deviceId); + List findUsableVolumesForInstance(long instanceId); - + Long countAllocatedVolumesForAccount(long accountId); - + HypervisorType getHypervisorType(long volumeId); - + List listVolumesToBeDestroyed(); - + ImageFormat getImageFormat(Long volumeId); - + List findReadyRootVolumesByInstance(long instanceId); - + List listPoolIdsByVolumeCount(long dcId, Long podId, Long clusterId, long accountId); + + /** + * Gets the Total Primary Storage space allocated for an account + * + * @param account + * @return total Primary Storage space (in bytes) used + */ + long primaryStorageUsedForAccount(long accountId); + + /** + * Gets the Total Secondary Storage space used by volumes allocated for an account + * + * @param account + * @return total Secondary Storage space (in bytes) used + */ + long secondaryStorageUsedForAccount(long accountId); } diff --git a/server/src/com/cloud/storage/dao/VolumeDaoImpl.java b/server/src/com/cloud/storage/dao/VolumeDaoImpl.java index 40ed875caab..12ca3c7e98e 100755 --- a/server/src/com/cloud/storage/dao/VolumeDaoImpl.java +++ b/server/src/com/cloud/storage/dao/VolumeDaoImpl.java @@ -38,7 +38,6 @@ import com.cloud.storage.Volume.State; import com.cloud.storage.Volume.Type; import com.cloud.storage.VolumeVO; import com.cloud.tags.dao.ResourceTagDao; -import com.cloud.tags.dao.ResourceTagsDaoImpl; import com.cloud.utils.Pair; import com.cloud.utils.db.DB; import com.cloud.utils.db.GenericDaoBase; @@ -62,8 +61,10 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol protected final SearchBuilder InstanceStatesSearch; protected final SearchBuilder AllFieldsSearch; protected GenericSearchBuilder CountByAccount; + protected GenericSearchBuilder primaryStorageSearch; + protected GenericSearchBuilder secondaryStorageSearch; @Inject ResourceTagDao _tagsDao; - + protected static final String SELECT_VM_SQL = "SELECT DISTINCT instance_id from volumes v where v.host_id = ? and v.mirror_state = ?"; protected static final String SELECT_HYPERTYPE_FROM_VOLUME = "SELECT c.hypervisor_type from volumes v, storage_pool s, cluster c where v.pool_id = s.id and s.cluster_id = c.id and v.id = ?"; @@ -298,8 +299,22 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol CountByAccount = createSearchBuilder(Long.class); CountByAccount.select(null, Func.COUNT, null); CountByAccount.and("account", CountByAccount.entity().getAccountId(), SearchCriteria.Op.EQ); - CountByAccount.and("state", CountByAccount.entity().getState(), SearchCriteria.Op.NIN); + CountByAccount.and("state", CountByAccount.entity().getState(), SearchCriteria.Op.NIN); CountByAccount.done(); + + primaryStorageSearch = createSearchBuilder(SumCount.class); + primaryStorageSearch.select("sum", Func.SUM, primaryStorageSearch.entity().getSize()); + primaryStorageSearch.and("accountId", primaryStorageSearch.entity().getAccountId(), Op.EQ); + primaryStorageSearch.and("path", primaryStorageSearch.entity().getPath(), Op.NNULL); + primaryStorageSearch.and("isRemoved", primaryStorageSearch.entity().getRemoved(), Op.NULL); + primaryStorageSearch.done(); + + secondaryStorageSearch = createSearchBuilder(SumCount.class); + secondaryStorageSearch.select("sum", Func.SUM, secondaryStorageSearch.entity().getSize()); + secondaryStorageSearch.and("accountId", secondaryStorageSearch.entity().getAccountId(), Op.EQ); + secondaryStorageSearch.and("path", secondaryStorageSearch.entity().getPath(), Op.NULL); + secondaryStorageSearch.and("isRemoved", secondaryStorageSearch.entity().getRemoved(), Op.NULL); + secondaryStorageSearch.done(); } @Override @DB(txn=false) @@ -316,9 +331,34 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol SearchCriteria sc = CountByAccount.create(); sc.setParameters("account", accountId); sc.setParameters("state", Volume.State.Destroy); - return customSearch(sc, null).get(0); + return customSearch(sc, null).get(0); } + @Override + public long primaryStorageUsedForAccount(long accountId) { + SearchCriteria sc = primaryStorageSearch.create(); + sc.setParameters("accountId", accountId); + List storageSpace = customSearch(sc, null); + if (storageSpace != null) { + return storageSpace.get(0).sum; + } else { + return 0; + } + } + + @Override + public long secondaryStorageUsedForAccount(long accountId) { + SearchCriteria sc = secondaryStorageSearch.create(); + sc.setParameters("accountId", accountId); + List storageSpace = customSearch(sc, null); + if (storageSpace != null) { + return storageSpace.get(0).sum; + } else { + return 0; + } + } + + public static class SumCount { public long sum; public long count; diff --git a/server/src/com/cloud/storage/download/DownloadListener.java b/server/src/com/cloud/storage/download/DownloadListener.java index d4c20cf4989..1d488034c5a 100755 --- a/server/src/com/cloud/storage/download/DownloadListener.java +++ b/server/src/com/cloud/storage/download/DownloadListener.java @@ -22,6 +22,8 @@ import java.util.Map; import java.util.Timer; import java.util.TimerTask; +import javax.inject.Inject; + import org.apache.log4j.Level; import org.apache.log4j.Logger; @@ -36,28 +38,31 @@ import com.cloud.agent.api.StartupSecondaryStorageCommand; import com.cloud.agent.api.StartupStorageCommand; import com.cloud.agent.api.storage.DownloadAnswer; import com.cloud.agent.api.storage.DownloadCommand; -import com.cloud.agent.api.storage.DownloadProgressCommand; import com.cloud.agent.api.storage.DownloadCommand.ResourceType; +import com.cloud.agent.api.storage.DownloadProgressCommand; import com.cloud.agent.api.storage.DownloadProgressCommand.RequestType; +import com.cloud.alert.AlertManager; import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.ConnectionException; +import com.cloud.exception.ResourceAllocationException; import com.cloud.host.HostVO; - +import com.cloud.host.dao.HostDao; import com.cloud.storage.Storage; import com.cloud.storage.StorageManager; import com.cloud.storage.VMTemplateHostVO; +import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.VolumeHostVO; import com.cloud.storage.VolumeVO; -import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; -import com.cloud.storage.Volume.Event; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VMTemplateHostDao; import com.cloud.storage.dao.VolumeDao; import com.cloud.storage.dao.VolumeHostDao; import com.cloud.storage.download.DownloadState.DownloadEvent; +import com.cloud.user.AccountManager; +import com.cloud.user.ResourceLimitService; +import com.cloud.utils.UriUtils; import com.cloud.utils.exception.CloudRuntimeException; -import com.cloud.utils.fsm.NoTransitionException; /** * Monitor progress of template download to a single storage server @@ -119,6 +124,9 @@ public class DownloadListener implements Listener { private StorageManager _storageMgr; private VMTemplateHostDao vmTemplateHostDao; private VMTemplateDao _vmTemplateDao; + private ResourceLimitService _resourceLimitMgr; + private AccountManager _accountMgr; + private AlertManager _alertMgr; private final DownloadMonitorImpl downloadMonitor; @@ -137,7 +145,7 @@ public class DownloadListener implements Listener { private Long templateHostId; private Long volumeHostId; - public DownloadListener(HostVO ssAgent, HostVO host, VMTemplateVO template, Timer _timer, VMTemplateHostDao dao, Long templHostId, DownloadMonitorImpl downloadMonitor, DownloadCommand cmd, VMTemplateDao templateDao) { + public DownloadListener(HostVO ssAgent, HostVO host, VMTemplateVO template, Timer _timer, VMTemplateHostDao dao, Long templHostId, DownloadMonitorImpl downloadMonitor, DownloadCommand cmd, VMTemplateDao templateDao, ResourceLimitService _resourceLimitMgr, AlertManager _alertMgr, AccountManager _accountMgr) { this.ssAgent = ssAgent; this.sserver = host; this.template = template; @@ -151,10 +159,13 @@ public class DownloadListener implements Listener { this.timeoutTask = new TimeoutTask(this); this.timer.schedule(timeoutTask, 3*STATUS_POLL_INTERVAL); this._vmTemplateDao = templateDao; + this._resourceLimitMgr = _resourceLimitMgr; + this._accountMgr = _accountMgr; + this._alertMgr = _alertMgr; updateDatabase(Status.NOT_DOWNLOADED, ""); } - public DownloadListener(HostVO ssAgent, HostVO host, VolumeVO volume, Timer _timer, VolumeHostDao dao, Long volHostId, DownloadMonitorImpl downloadMonitor, DownloadCommand cmd, VolumeDao volumeDao, StorageManager storageMgr) { + public DownloadListener(HostVO ssAgent, HostVO host, VolumeVO volume, Timer _timer, VolumeHostDao dao, Long volHostId, DownloadMonitorImpl downloadMonitor, DownloadCommand cmd, VolumeDao volumeDao, StorageManager storageMgr, ResourceLimitService _resourceLimitMgr, AlertManager _alertMgr, AccountManager _accountMgr) { this.ssAgent = ssAgent; this.sserver = host; this.volume = volume; @@ -169,6 +180,9 @@ public class DownloadListener implements Listener { this.timer.schedule(timeoutTask, 3*STATUS_POLL_INTERVAL); this._volumeDao = volumeDao; this._storageMgr = storageMgr; + this._resourceLimitMgr = _resourceLimitMgr; + this._accountMgr = _accountMgr; + this._alertMgr = _alertMgr; updateDatabase(Status.NOT_DOWNLOADED, ""); } @@ -332,6 +346,24 @@ public class DownloadListener implements Listener { templateDaoBuilder.setChecksum(answer.getCheckSum()); _vmTemplateDao.update(template.getId(), templateDaoBuilder); } + + if (answer.getTemplateSize() > 0) { + //long hostId = vmTemplateHostDao.findByTemplateId(template.getId()).getHostId(); + long accountId = template.getAccountId(); + try { + _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(accountId), + com.cloud.configuration.Resource.ResourceType.secondary_storage, + answer.getTemplateSize() - UriUtils.getRemoteSize(template.getUrl())); + } catch (ResourceAllocationException e) { + s_logger.warn(e.getMessage()); + _alertMgr.sendAlert(_alertMgr.ALERT_TYPE_RESOURCE_LIMIT_EXCEEDED, sserver.getDataCenterId(), + null, e.getMessage(), e.getMessage()); + } finally { + _resourceLimitMgr.recalculateResourceCount(accountId, _accountMgr.getAccount(accountId).getDomainId(), + com.cloud.configuration.Resource.ResourceType.secondary_storage.getOrdinal()); + } + } + } else { VolumeHostVO updateBuilder = volumeHostDao.createForUpdate(); updateBuilder.setDownloadPercent(answer.getDownloadPct()); @@ -351,6 +383,22 @@ public class DownloadListener implements Listener { updateVolume.setSize(answer.getTemplateSize()); _volumeDao.update(volume.getId(), updateVolume); + if (answer.getTemplateSize() > 0) { + try { + String url = volumeHostDao.findByVolumeId(volume.getId()).getDownloadUrl(); + _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(volume.getAccountId()), + com.cloud.configuration.Resource.ResourceType.secondary_storage, + answer.getTemplateSize() - UriUtils.getRemoteSize(url)); + } catch (ResourceAllocationException e) { + s_logger.warn(e.getMessage()); + _alertMgr.sendAlert(_alertMgr.ALERT_TYPE_RESOURCE_LIMIT_EXCEEDED, volume.getDataCenterId(), + volume.getPodId(), e.getMessage(), e.getMessage()); + } finally { + _resourceLimitMgr.recalculateResourceCount(volume.getAccountId(), volume.getDomainId(), + com.cloud.configuration.Resource.ResourceType.secondary_storage.getOrdinal()); + } + } + /*if (answer.getCheckSum() != null) { VMTemplateVO templateDaoBuilder = _vmTemplateDao.createForUpdate(); templateDaoBuilder.setChecksum(answer.getCheckSum()); diff --git a/server/src/com/cloud/storage/download/DownloadMonitorImpl.java b/server/src/com/cloud/storage/download/DownloadMonitorImpl.java index 0bc89e31f66..7ed6f3f27d6 100755 --- a/server/src/com/cloud/storage/download/DownloadMonitorImpl.java +++ b/server/src/com/cloud/storage/download/DownloadMonitorImpl.java @@ -64,6 +64,7 @@ import com.cloud.event.UsageEventUtils; import com.cloud.event.dao.UsageEventDao; import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.StorageUnavailableException; import com.cloud.host.Host; import com.cloud.host.HostVO; @@ -97,7 +98,9 @@ import com.cloud.storage.template.TemplateConstants; import com.cloud.storage.template.TemplateInfo; import com.cloud.template.TemplateManager; import com.cloud.user.Account; +import com.cloud.user.AccountManager; import com.cloud.user.ResourceLimitService; +import com.cloud.utils.UriUtils; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.db.DB; import com.cloud.utils.db.JoinBuilder; @@ -177,6 +180,8 @@ public class DownloadMonitorImpl extends ManagerBase implements DownloadMonitor protected ResourceLimitService _resourceLimitMgr; @Inject protected UserVmDao _userVmDao; + @Inject + protected AccountManager _accountMgr; private Boolean _sslCopy = new Boolean(false); private String _copyAuthPasswd; @@ -289,7 +294,7 @@ public class DownloadMonitorImpl extends ManagerBase implements DownloadMonitor s_logger.warn("There is no secondary storage VM for secondary storage host " + destServer.getName()); return false; } - DownloadListener dl = new DownloadListener(ssAhost, destServer, template, _timer, _vmTemplateHostDao, destTmpltHost.getId(), this, dcmd, _templateDao); + DownloadListener dl = new DownloadListener(ssAhost, destServer, template, _timer, _vmTemplateHostDao, destTmpltHost.getId(), this, dcmd, _templateDao, _resourceLimitMgr, _alertMgr, _accountMgr); if (downloadJobExists) { dl.setCurrState(destTmpltHost.getDownloadState()); } @@ -376,7 +381,7 @@ public class DownloadMonitorImpl extends ManagerBase implements DownloadMonitor s_logger.warn("There is no secondary storage VM for secondary storage host " + sserver.getName()); return; } - DownloadListener dl = new DownloadListener(ssAhost, sserver, template, _timer, _vmTemplateHostDao, vmTemplateHost.getId(), this, dcmd, _templateDao); + DownloadListener dl = new DownloadListener(ssAhost, sserver, template, _timer, _vmTemplateHostDao, vmTemplateHost.getId(), this, dcmd, _templateDao, _resourceLimitMgr, _alertMgr, _accountMgr); if (downloadJobExists) { dl.setCurrState(vmTemplateHost.getDownloadState()); } @@ -465,7 +470,7 @@ public class DownloadMonitorImpl extends ManagerBase implements DownloadMonitor return; } DownloadListener dl = new DownloadListener(ssvm, sserver, volume, _timer, _volumeHostDao, volumeHost.getId(), - this, dcmd, _volumeDao, _storageMgr); + this, dcmd, _volumeDao, _storageMgr, _resourceLimitMgr, _alertMgr, _accountMgr); if (downloadJobExists) { dl.setCurrState(volumeHost.getDownloadState()); @@ -561,18 +566,21 @@ public class DownloadMonitorImpl extends ManagerBase implements DownloadMonitor else{ s_logger.warn("Failed to get size for volume" + volume.getName()); } - String eventType = EventTypes.EVENT_VOLUME_UPLOAD; + String eventType = EventTypes.EVENT_VOLUME_UPLOAD; if(volume.getAccountId() != Account.ACCOUNT_ID_SYSTEM){ UsageEventUtils.publishUsageEvent(eventType, volume.getAccountId(), host.getDataCenterId(), volume.getId(), volume.getName(), null, 0l, size, volume.getClass().getName(), volume.getUuid()); } }else if (dnldStatus == Status.DOWNLOAD_ERROR || dnldStatus == Status.ABANDONED || dnldStatus == Status.UNKNOWN){ - //Decrement the volume count - _resourceLimitMgr.decrementResourceCount(volume.getAccountId(), com.cloud.configuration.Resource.ResourceType.volume); + //Decrement the volume and secondary storage space count + _resourceLimitMgr.decrementResourceCount(volume.getAccountId(), + com.cloud.configuration.Resource.ResourceType.volume); + _resourceLimitMgr.recalculateResourceCount(volume.getAccountId(), volume.getDomainId(), + com.cloud.configuration.Resource.ResourceType.secondary_storage.getOrdinal()); } - txn.commit(); + txn.commit(); } - + @Override public void handleSysTemplateDownload(HostVO host) { List hypers = _resourceMgr.listAvailHypervisorInZone(host.getId(), host.getDataCenterId()); @@ -748,6 +756,22 @@ public class DownloadMonitorImpl extends ManagerBase implements DownloadMonitor volume.setSize(volInfo.getSize()); _volumeDao.update(volumeHost.getVolumeId(), volume); } + + if (volInfo.getSize() > 0) { + try { + String url = _volumeHostDao.findByVolumeId(volume.getId()).getDownloadUrl(); + _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(volume.getAccountId()), + com.cloud.configuration.Resource.ResourceType.secondary_storage, + volInfo.getSize() - UriUtils.getRemoteSize(url)); + } catch (ResourceAllocationException e) { + s_logger.warn(e.getMessage()); + _alertMgr.sendAlert(_alertMgr.ALERT_TYPE_RESOURCE_LIMIT_EXCEEDED, volume.getDataCenterId(), + volume.getPodId(), e.getMessage(), e.getMessage()); + } finally { + _resourceLimitMgr.recalculateResourceCount(volume.getAccountId(), volume.getDomainId(), + com.cloud.configuration.Resource.ResourceType.secondary_storage.getOrdinal()); + } + } } continue; } @@ -857,6 +881,22 @@ public class DownloadMonitorImpl extends ManagerBase implements DownloadMonitor tmpltHost.setSize(tmpltInfo.getSize()); tmpltHost.setPhysicalSize(tmpltInfo.getPhysicalSize()); tmpltHost.setLastUpdated(new Date()); + + if (tmpltInfo.getSize() > 0) { + long accountId = tmplt.getAccountId(); + try { + _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(accountId), + com.cloud.configuration.Resource.ResourceType.secondary_storage, + tmpltInfo.getSize() - UriUtils.getRemoteSize(tmplt.getUrl())); + } catch (ResourceAllocationException e) { + s_logger.warn(e.getMessage()); + _alertMgr.sendAlert(_alertMgr.ALERT_TYPE_RESOURCE_LIMIT_EXCEEDED, ssHost.getDataCenterId(), + null, e.getMessage(), e.getMessage()); + } finally { + _resourceLimitMgr.recalculateResourceCount(accountId, _accountMgr.getAccount(accountId).getDomainId(), + com.cloud.configuration.Resource.ResourceType.secondary_storage.getOrdinal()); + } + } } _vmTemplateHostDao.update(tmpltHost.getId(), tmpltHost); } else { diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java index bacca019294..3a453d50a72 100755 --- a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java +++ b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java @@ -123,20 +123,8 @@ import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.dao.UserVmDao; - -import org.apache.cloudstack.api.command.user.snapshot.CreateSnapshotPolicyCmd; -import org.apache.cloudstack.api.command.user.snapshot.DeleteSnapshotPoliciesCmd; -import org.apache.cloudstack.api.command.user.snapshot.ListSnapshotPoliciesCmd; -import org.apache.cloudstack.api.command.user.snapshot.ListSnapshotsCmd; -import org.apache.log4j.Logger; - -import javax.ejb.Local; -import javax.naming.ConfigurationException; -import java.util.*; -import com.cloud.vm.snapshot.VMSnapshot; import com.cloud.vm.snapshot.dao.VMSnapshotDao; - @Component @Local(value = { SnapshotManager.class, SnapshotService.class }) public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, SnapshotService { @@ -209,6 +197,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, private int _totalRetries; private int _pauseInterval; private int _backupsnapshotwait; + Boolean backup; protected SearchBuilder PolicySnapshotSearch; protected SearchBuilder PoliciesForSnapSearch; @@ -307,16 +296,21 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, volume.getSize(), snapshot.getClass().getName(), snapshot.getUuid()); } - _resourceLimitMgr.incrementResourceCount(snapshotOwner.getId(), ResourceType.snapshot); + _resourceLimitMgr.incrementResourceCount(snapshotOwner.getId(), ResourceType.snapshot); } - - Boolean backup = Boolean.parseBoolean(this._configDao.getValue(Config.BackupSnapshotAferTakingSnapshot.toString())); if (backup) { this.backupSnapshot(snapshotId); } } catch(Exception e) { - s_logger.debug("Failed to create snapshot", e); - throw new CloudRuntimeException("Failed to create snapshot", e); + s_logger.debug("Failed to create snapshot", e); + if (backup) { + _resourceLimitMgr.decrementResourceCount(snapshotOwner.getId(), ResourceType.secondary_storage, + new Long(volume.getSize())); + } else { + _resourceLimitMgr.decrementResourceCount(snapshotOwner.getId(), ResourceType.primary_storage, + new Long(volume.getSize())); + } + throw new CloudRuntimeException("Failed to create snapshot", e); } return snapshot; @@ -539,7 +533,9 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, snapshotCheck.getDataCenterId(), snapshotId, snapshotCheck.getName(), null, null, 0L, snapshotCheck.getClass().getName(), snapshotCheck.getUuid()); } - _resourceLimitMgr.decrementResourceCount(snapshotCheck.getAccountId(), ResourceType.snapshot); + _resourceLimitMgr.decrementResourceCount(snapshotCheck.getAccountId(), ResourceType.snapshot); + _resourceLimitMgr.decrementResourceCount(snapshotCheck.getAccountId(), ResourceType.secondary_storage, + new Long(snapshotCheck.getSize())); } return result; } catch (Exception e) { @@ -745,6 +741,8 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, if (_snapshotDao.expunge(snapshot.getId())) { if (snapshot.getRecurringType() == Type.MANUAL) { _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.snapshot); + _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.secondary_storage, + new Long(snapshot.getSize())); } // Log event after successful deletion @@ -995,23 +993,29 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, ClusterVO cluster = _clusterDao.findById(storagePoolVO.getClusterId()); if (cluster != null && cluster.getHypervisorType() == HypervisorType.Ovm) { - throw new InvalidParameterValueException("Ovm won't support taking snapshot"); + throw new InvalidParameterValueException("Ovm won't support taking snapshot"); } - + // Verify permissions _accountMgr.checkAccess(caller, null, true, volume); Type snapshotType = getSnapshotType(policyId); Account owner = _accountMgr.getAccount(volume.getAccountId()); + try{ - _resourceLimitMgr.checkResourceLimit(owner, ResourceType.snapshot); - } catch (ResourceAllocationException e){ - if (snapshotType != Type.MANUAL){ - String msg = "Snapshot resource limit exceeded for account id : " + owner.getId() + ". Failed to create recurring snapshots"; - s_logger.warn(msg); - _alertMgr.sendAlert(AlertManager.ALERT_TYPE_UPDATE_RESOURCE_COUNT, 0L, 0L, msg, - "Snapshot resource limit exceeded for account id : " + owner.getId() + ". Failed to create recurring snapshots; please use updateResourceLimit to increase the limit"); - } - throw e; + _resourceLimitMgr.checkResourceLimit(owner, ResourceType.snapshot); + if (backup) { + _resourceLimitMgr.checkResourceLimit(owner, ResourceType.secondary_storage, new Long(volume.getSize())); + } else { + _resourceLimitMgr.checkResourceLimit(owner, ResourceType.primary_storage, new Long(volume.getSize())); + } + } catch (ResourceAllocationException e) { + if (snapshotType != Type.MANUAL){ + String msg = "Snapshot resource limit exceeded for account id : " + owner.getId() + ". Failed to create recurring snapshots"; + s_logger.warn(msg); + _alertMgr.sendAlert(AlertManager.ALERT_TYPE_UPDATE_RESOURCE_COUNT, 0L, 0L, msg, + "Snapshot resource limit exceeded for account id : " + owner.getId() + ". Failed to create recurring snapshots; please use updateResourceLimit to increase the limit"); + } + throw e; } // Determine the name for this snapshot @@ -1034,6 +1038,13 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, if (snapshot == null) { throw new CloudRuntimeException("Failed to create snapshot for volume: "+volumeId); } + if (backup) { + _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.secondary_storage, + new Long(volume.getSize())); + } else { + _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.primary_storage, + new Long(volume.getSize())); + } return snapshot; } @@ -1042,6 +1053,7 @@ public class SnapshotManagerImpl extends ManagerBase implements SnapshotManager, String value = _configDao.getValue(Config.BackupSnapshotWait.toString()); _backupsnapshotwait = NumbersUtil.parseInt(value, Integer.parseInt(Config.BackupSnapshotWait.getDefaultValue())); + backup = Boolean.parseBoolean(this._configDao.getValue(Config.BackupSnapshotAferTakingSnapshot.toString())); Type.HOURLY.setMax(NumbersUtil.parseInt(_configDao.getValue("snapshot.max.hourly"), HOURLYMAX)); Type.DAILY.setMax(NumbersUtil.parseInt(_configDao.getValue("snapshot.max.daily"), DAILYMAX)); diff --git a/server/src/com/cloud/template/HypervisorTemplateAdapter.java b/server/src/com/cloud/template/HypervisorTemplateAdapter.java index 142642193b6..a0444e5e989 100755 --- a/server/src/com/cloud/template/HypervisorTemplateAdapter.java +++ b/server/src/com/cloud/template/HypervisorTemplateAdapter.java @@ -39,7 +39,6 @@ import org.apache.cloudstack.engine.subsystem.api.storage.ImageDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.ImageService; import org.apache.cloudstack.framework.async.AsyncCallFuture; import org.apache.log4j.Logger; -import org.springframework.stereotype.Component; import com.cloud.agent.AgentManager; import com.cloud.agent.api.Answer; @@ -61,20 +60,11 @@ import com.cloud.storage.VMTemplateZoneVO; import com.cloud.storage.download.DownloadMonitor; import com.cloud.storage.secondary.SecondaryStorageVmManager; import com.cloud.user.Account; +import com.cloud.utils.UriUtils; import com.cloud.utils.db.DB; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.UserVmVO; -import org.apache.cloudstack.api.command.user.iso.DeleteIsoCmd; -import org.apache.cloudstack.api.command.user.iso.RegisterIsoCmd; -import org.apache.cloudstack.api.command.user.template.DeleteTemplateCmd; -import org.apache.cloudstack.api.command.user.template.RegisterTemplateCmd; -import org.apache.log4j.Logger; - -import javax.ejb.Local; -import java.net.*; -import java.util.List; - @Local(value=TemplateAdapter.class) public class HypervisorTemplateAdapter extends TemplateAdapterBase implements TemplateAdapter { private final static Logger s_logger = Logger.getLogger(HypervisorTemplateAdapter.class); @@ -134,6 +124,9 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase implements Te } profile.setUrl(validateUrl(url)); + // Check that the resource limit for secondary storage won't be exceeded + _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(cmd.getEntityOwnerId()), + ResourceType.secondary_storage, UriUtils.getRemoteSize(url)); return profile; } @@ -160,6 +153,9 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase implements Te } profile.setUrl(validateUrl(url)); + // Check that the resource limit for secondary storage won't be exceeded + _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(cmd.getEntityOwnerId()), + ResourceType.secondary_storage, UriUtils.getRemoteSize(url)); return profile; } @@ -183,10 +179,11 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase implements Te s_logger.debug("create template Failed", e); throw new CloudRuntimeException("create template Failed", e); } - _resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template); - + _resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.template); + _resourceLimitMgr.incrementResourceCount(profile.getAccountId(), ResourceType.secondary_storage, + UriUtils.getRemoteSize(profile.getUrl())); return template; - } + } @Override @DB public boolean delete(TemplateProfile profile) { @@ -294,8 +291,10 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase implements Te s_logger.debug("Failed to acquire lock when deleting template with ID: " + templateId); success = false; } else if (_tmpltDao.remove(templateId)) { - // Decrement the number of templates + // Decrement the number of templates and total secondary storage space used by the account _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.template); + _resourceLimitMgr.recalculateResourceCount(accountId, account.getDomainId(), + ResourceType.secondary_storage.getOrdinal()); } } finally { diff --git a/server/src/com/cloud/template/TemplateManagerImpl.java b/server/src/com/cloud/template/TemplateManagerImpl.java index d843dbc9b8d..2892e0081c2 100755 --- a/server/src/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/com/cloud/template/TemplateManagerImpl.java @@ -893,16 +893,19 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, @Override @DB public boolean copy(long userId, VMTemplateVO template, HostVO srcSecHost, DataCenterVO srcZone, DataCenterVO dstZone) throws StorageUnavailableException, ResourceAllocationException { - List dstSecHosts = _ssvmMgr.listSecondaryStorageHostsInOneZone(dstZone.getId()); - long tmpltId = template.getId(); + List dstSecHosts = _ssvmMgr.listSecondaryStorageHostsInOneZone(dstZone.getId()); + long tmpltId = template.getId(); long dstZoneId = dstZone.getId(); - if (dstSecHosts == null || dstSecHosts.isEmpty() ) { - throw new StorageUnavailableException("Destination zone is not ready", DataCenter.class, dstZone.getId()); - } + if (dstSecHosts == null || dstSecHosts.isEmpty() ) { + throw new StorageUnavailableException("Destination zone is not ready", DataCenter.class, dstZone.getId()); + } AccountVO account = _accountDao.findById(template.getAccountId()); + VMTemplateHostVO srcTmpltHost = _tmpltHostDao.findByHostTemplate(srcSecHost.getId(), tmpltId); + _resourceLimitMgr.checkResourceLimit(account, ResourceType.template); - - // Event details + _resourceLimitMgr.checkResourceLimit(account, ResourceType.secondary_storage, new Long(srcTmpltHost.getSize())); + + // Event details String copyEventType; String createEventType; if (template.getFormat().equals(ImageFormat.ISO)){ @@ -912,12 +915,10 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, copyEventType = EventTypes.EVENT_TEMPLATE_COPY; createEventType = EventTypes.EVENT_TEMPLATE_CREATE; } - - + Transaction txn = Transaction.currentTxn(); txn.start(); - - VMTemplateHostVO srcTmpltHost = _tmpltHostDao.findByHostTemplate(srcSecHost.getId(), tmpltId); + for ( HostVO dstSecHost : dstSecHosts ) { VMTemplateHostVO dstTmpltHost = null; try { @@ -948,7 +949,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, } finally { txn.commit(); } - + if(_downloadMonitor.copyTemplate(template, srcSecHost, dstSecHost) ) { _tmpltDao.addTemplateToZone(template, dstZoneId); @@ -1711,6 +1712,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, VMTemplateVO privateTemplate = null; Long accountId = null; SnapshotVO snapshot = null; + VolumeVO volume = null; try { TemplateInfo tmplInfo = this.tmplFactory.getTemplate(templateId); @@ -1725,6 +1727,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, SnapshotInfo snapInfo = this.snapshotFactory.getSnapshot(snapshotId); future = this.imageSvr.createTemplateFromSnapshotAsync(snapInfo, tmplInfo, store.get(0)); } else if (volumeId != null) { + volume = _volumeDao.findById(volumeId); VolumeInfo volInfo = this.volFactory.getVolume(volumeId); future = this.imageSvr.createTemplateFromVolumeAsync(volInfo, tmplInfo, store.get(0)); } else { @@ -1757,7 +1760,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, s_logger.debug("Failed to create template", e); throw new CloudRuntimeException("Failed to create template", e); } - + } finally { /*if (snapshot != null && snapshot.getSwiftId() != null && secondaryStorageURL != null && zoneId != null @@ -1773,8 +1776,9 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, // decrement resource count if (accountId != null) { - _resourceLimitMgr.decrementResourceCount(accountId, - ResourceType.template); + _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.template); + _resourceLimitMgr.decrementResourceCount(accountId, ResourceType.secondary_storage, + new Long(volume != null ? volume.getSize() : snapshot.getSize())); } txn.commit(); } @@ -1854,6 +1858,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, HypervisorType hyperType; VolumeVO volume = null; + SnapshotVO snapshot = null; VMTemplateVO privateTemplate = null; if (volumeId != null) { // create template from volume volume = this._volumeDao.findById(volumeId); @@ -1879,7 +1884,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, } hyperType = this._volumeDao.getHypervisorType(volumeId); } else { // create template from snapshot - SnapshotVO snapshot = _snapshotDao.findById(snapshotId); + snapshot = _snapshotDao.findById(snapshotId); if (snapshot == null) { throw new InvalidParameterValueException( "Failed to create private template record, unable to find snapshot " @@ -1911,8 +1916,9 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, hyperType = snapshot.getHypervisorType(); } - _resourceLimitMgr.checkResourceLimit(templateOwner, - ResourceType.template); + _resourceLimitMgr.checkResourceLimit(templateOwner, ResourceType.template); + _resourceLimitMgr.checkResourceLimit(templateOwner, ResourceType.secondary_storage, + new Long(volume != null ? volume.getSize() : snapshot.getSize())); if (!isAdmin || featured == null) { featured = Boolean.FALSE; @@ -1973,8 +1979,9 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager, this._templateDetailsDao.persist(template.getId(), cmd.getDetails()); } - _resourceLimitMgr.incrementResourceCount(templateOwner.getId(), - ResourceType.template); + _resourceLimitMgr.incrementResourceCount(templateOwner.getId(), ResourceType.template); + _resourceLimitMgr.incrementResourceCount(templateOwner.getId(), ResourceType.secondary_storage, + new Long(volume != null ? volume.getSize() : snapshot.getSize())); } if (template != null) { diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 6e9cd43d88e..24bce8b1705 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -3571,8 +3571,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use DataCenterVO zone = _dcDao.findById(vm.getDataCenterId()); - // Get serviceOffering for Virtual Machine + // Get serviceOffering and Volumes for Virtual Machine ServiceOfferingVO offering = _serviceOfferingDao.findByIdIncludingRemoved(vm.getServiceOfferingId()); + List volumes = _volsDao.findByInstance(cmd.getVmId()); //Remove vm from instance group removeInstanceFromInstanceGroup(cmd.getVmId()); @@ -3580,9 +3581,14 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use // VV 2: check if account/domain is with in resource limits to create a new vm resourceLimitCheck(newAccount, new Long(offering.getCpu()), new Long(offering.getRamSize())); - // VV 3: check if volumes are with in resource limits + // VV 3: check if volumes and primary storage space are with in resource limits _resourceLimitMgr.checkResourceLimit(newAccount, ResourceType.volume, _volsDao.findByInstance(cmd.getVmId()).size()); + Long totalVolumesSize = (long) 0; + for (VolumeVO volume : volumes) { + totalVolumesSize += volume.getSize(); + } + _resourceLimitMgr.checkResourceLimit(newAccount, ResourceType.primary_storage, totalVolumesSize); // VV 4: Check if new owner can use the vm template VirtualMachineTemplate template = _templateDao.findById(vm @@ -3614,14 +3620,17 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use _vmDao.persist(vm); // OS 2: update volume - List volumes = _volsDao.findByInstance(cmd.getVmId()); for (VolumeVO volume : volumes) { UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_DELETE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), Volume.class.getName(), volume.getUuid()); _resourceLimitMgr.decrementResourceCount(oldAccount.getAccountId(), ResourceType.volume); + _resourceLimitMgr.decrementResourceCount(oldAccount.getAccountId(), ResourceType.primary_storage, + new Long(volume.getSize())); volume.setAccountId(newAccount.getAccountId()); _volsDao.persist(volume); _resourceLimitMgr.incrementResourceCount(newAccount.getAccountId(), ResourceType.volume); + _resourceLimitMgr.incrementResourceCount(newAccount.getAccountId(), ResourceType.primary_storage, + new Long(volume.getSize())); UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), volume.getDiskOfferingId(), volume.getTemplateId(), volume.getSize(), Volume.class.getName(), diff --git a/server/test/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java b/server/test/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java index d311ad38170..4d2fd7df592 100644 --- a/server/test/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java +++ b/server/test/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java @@ -16,8 +16,6 @@ // under the License. package com.cloud.resourcelimit; -import javax.inject.Inject; - import junit.framework.TestCase; import org.apache.log4j.Logger; @@ -72,11 +70,23 @@ public class ResourceLimitManagerImplTest extends TestCase{ // update resource Limit for a domain for resource_type = 8 (CPU) resourceLimitServiceCall(null, (long) 1, 8, (long) 40); - // update resource Limit for an account for resource_type = 9 (Memory) + // update resource Limit for an account for resource_type = 9 (Memory (in MiB)) resourceLimitServiceCall((long) 1, (long) 1, 9, (long) 4096); - // update resource Limit for a domain for resource_type = 9 (Memory) + // update resource Limit for a domain for resource_type = 9 (Memory (in MiB)) resourceLimitServiceCall(null, (long) 1, 9, (long) 10240); + + // update resource Limit for an account for resource_type = 10 (Primary storage (in GiB)) + resourceLimitServiceCall((long) 1, (long) 1, 10, (long) 200); + + // update resource Limit for a domain for resource_type = 10 (Primary storage (in GiB)) + resourceLimitServiceCall(null, (long) 1, 10, (long) 200); + + // update resource Limit for an account for resource_type = 11 (Secondary storage (in GiB)) + resourceLimitServiceCall((long) 1, (long) 1, 10, (long) 400); + + // update resource Limit for a domain for resource_type = 11 (Secondary storage (in GiB)) + resourceLimitServiceCall(null, (long) 1, 10, (long) 400); } private void resourceLimitServiceCall(Long accountId, Long domainId, Integer resourceType, Long max) { diff --git a/server/test/com/cloud/vpc/MockResourceLimitManagerImpl.java b/server/test/com/cloud/vpc/MockResourceLimitManagerImpl.java index b9fc8617059..367ca455727 100644 --- a/server/test/com/cloud/vpc/MockResourceLimitManagerImpl.java +++ b/server/test/com/cloud/vpc/MockResourceLimitManagerImpl.java @@ -132,6 +132,14 @@ public class MockResourceLimitManagerImpl extends ManagerBase implements Resourc return 0; } + /* (non-Javadoc) + * @see com.cloud.user.ResourceLimitService#calculateSecondaryStorageForAccount(long) + */ + public long calculateSecondaryStorageForAccount(long accountId) { + // TODO Auto-generated method stub + return 0; + } + /* (non-Javadoc) * @see com.cloud.user.ResourceLimitService#getResourceCount(com.cloud.user.Account, com.cloud.configuration.Resource.ResourceType) */ diff --git a/setup/db/db/schema-40to410.sql b/setup/db/db/schema-40to410.sql index 60ad025c60e..3dae1cc50fe 100644 --- a/setup/db/db/schema-40to410.sql +++ b/setup/db/db/schema-40to410.sql @@ -263,14 +263,6 @@ CREATE TABLE `cloud`.`region` ( INSERT INTO `cloud`.`region` values ('1','Local','http://localhost:8080/client/'); -INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Account Defaults', 'DEFAULT', 'management-server', 'max.account.cpus', '40', 'The default maximum number of cpu cores that can be used for an account'); - -INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Account Defaults', 'DEFAULT', 'management-server', 'max.account.memory', '40960', 'The default maximum memory (in MB) that can be used for an account'); - -INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Project Defaults', 'DEFAULT', 'management-server', 'max.project.cpus', '40', 'The default maximum number of cpu cores that can be used for a project'); - -INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Project Defaults', 'DEFAULT', 'management-server', 'max.project.memory', '40960', 'The default maximum memory (in MB) that can be used for a project'); - ALTER TABLE `cloud_usage`.`account` ADD COLUMN `region_id` int unsigned NOT NULL DEFAULT '1'; CREATE TABLE `cloud`.`nicira_nvp_router_map` ( @@ -1293,10 +1285,6 @@ CREATE VIEW `cloud`.`account_view` AS projectcount.count projectTotal, networklimit.max networkLimit, networkcount.count networkTotal, - cpulimit.max cpuLimit, - cpucount.count cpuTotal, - memorylimit.max memoryLimit, - memorycount.count memoryTotal, async_job.id job_id, async_job.uuid job_uuid, async_job.job_status job_status, @@ -1365,18 +1353,6 @@ CREATE VIEW `cloud`.`account_view` AS `cloud`.`resource_count` networkcount ON account.id = networkcount.account_id and networkcount.type = 'network' left join - `cloud`.`resource_limit` cpulimit ON account.id = cpulimit.account_id - and cpulimit.type = 'cpu' - left join - `cloud`.`resource_count` cpucount ON account.id = cpucount.account_id - and cpucount.type = 'cpu' - left join - `cloud`.`resource_limit` memorylimit ON account.id = memorylimit.account_id - and memorylimit.type = 'memory' - left join - `cloud`.`resource_count` memorycount ON account.id = memorycount.account_id - and memorycount.type = 'memory' - left join `cloud`.`async_job` ON async_job.instance_id = account.id and async_job.instance_type = 'Account' and async_job.job_status = 0; diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql index 9eed0a65e2f..f1e24fa1010 100644 --- a/setup/db/db/schema-410to420.sql +++ b/setup/db/db/schema-410to420.sql @@ -202,7 +202,6 @@ CREATE VIEW `cloud`.`event_view` AS left join `cloud`.`event` eve ON event.start_id = eve.id; - ALTER TABLE `cloud`.`region` ADD COLUMN `gslb_service_enabled` tinyint(1) unsigned NOT NULL DEFAULT 1 COMMENT 'Is GSLB service enalbed in the Region'; ALTER TABLE `cloud`.`external_load_balancer_devices` ADD COLUMN `is_gslb_provider` int(1) unsigned NOT NULL DEFAULT 0 COMMENT '1 if load balancer appliance is acting as gslb service provider in the zone'; @@ -239,3 +238,155 @@ CREATE TABLE `cloud`.`global_load_balancer_lb_rule_map` ( CONSTRAINT `fk_gslb_rule_id` FOREIGN KEY(`gslb_rule_id`) REFERENCES `global_load_balancing_rules`(`id`) ON DELETE CASCADE, CONSTRAINT `fk_lb_rule_id` FOREIGN KEY(`lb_rule_id`) REFERENCES `load_balancing_rules`(`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Account Defaults', 'DEFAULT', 'management-server', 'max.account.cpus', '40', 'The default maximum number of cpu cores that can be used for an account'); +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Account Defaults', 'DEFAULT', 'management-server', 'max.account.memory', '40960', 'The default maximum memory (in MiB) that can be used for an account'); +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Account Defaults', 'DEFAULT', 'management-server', 'max.account.primary.storage', '200', 'The default maximum primary storage space (in GiB) that can be used for an account'); +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Account Defaults', 'DEFAULT', 'management-server', 'max.account.secondary.storage', '400', 'The default maximum secondary storage space (in GiB) that can be used for an account'); +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Project Defaults', 'DEFAULT', 'management-server', 'max.project.cpus', '40', 'The default maximum number of cpu cores that can be used for a project'); +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Project Defaults', 'DEFAULT', 'management-server', 'max.project.memory', '40960', 'The default maximum memory (in MiB) that can be used for a project'); +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Project Defaults', 'DEFAULT', 'management-server', 'max.project.primary.storage', '200', 'The default maximum primary storage space (in GiB) that can be used for a project'); +INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Project Defaults', 'DEFAULT', 'management-server', 'max.project.secondary.storage', '400', 'The default maximum secondary storage space (in GiB) that can be used for a project'); + +DROP VIEW IF EXISTS `cloud`.`account_view`; +CREATE VIEW `cloud`.`account_view` AS + select + account.id, + account.uuid, + account.account_name, + account.type, + account.state, + account.removed, + account.cleanup_needed, + account.network_domain, + domain.id domain_id, + domain.uuid domain_uuid, + domain.name domain_name, + domain.path domain_path, + data_center.id data_center_id, + data_center.uuid data_center_uuid, + data_center.name data_center_name, + account_netstats_view.bytesReceived, + account_netstats_view.bytesSent, + vmlimit.max vmLimit, + vmcount.count vmTotal, + runningvm.vmcount runningVms, + stoppedvm.vmcount stoppedVms, + iplimit.max ipLimit, + ipcount.count ipTotal, + free_ip_view.free_ip ipFree, + volumelimit.max volumeLimit, + volumecount.count volumeTotal, + snapshotlimit.max snapshotLimit, + snapshotcount.count snapshotTotal, + templatelimit.max templateLimit, + templatecount.count templateTotal, + vpclimit.max vpcLimit, + vpccount.count vpcTotal, + projectlimit.max projectLimit, + projectcount.count projectTotal, + networklimit.max networkLimit, + networkcount.count networkTotal, + cpulimit.max cpuLimit, + cpucount.count cpuTotal, + memorylimit.max memoryLimit, + memorycount.count memoryTotal, + primary_storage_limit.max primaryStorageLimit, + primary_storage_count.count primaryStorageTotal, + secondary_storage_limit.max secondaryStorageLimit, + secondary_storage_count.count secondaryStorageTotal, + async_job.id job_id, + async_job.uuid job_uuid, + async_job.job_status job_status, + async_job.account_id job_account_id + from + `cloud`.`free_ip_view`, + `cloud`.`account` + inner join + `cloud`.`domain` ON account.domain_id = domain.id + left join + `cloud`.`data_center` ON account.default_zone_id = data_center.id + left join + `cloud`.`account_netstats_view` ON account.id = account_netstats_view.account_id + left join + `cloud`.`resource_limit` vmlimit ON account.id = vmlimit.account_id + and vmlimit.type = 'user_vm' + left join + `cloud`.`resource_count` vmcount ON account.id = vmcount.account_id + and vmcount.type = 'user_vm' + left join + `cloud`.`account_vmstats_view` runningvm ON account.id = runningvm.account_id + and runningvm.state = 'Running' + left join + `cloud`.`account_vmstats_view` stoppedvm ON account.id = stoppedvm.account_id + and stoppedvm.state = 'Stopped' + left join + `cloud`.`resource_limit` iplimit ON account.id = iplimit.account_id + and iplimit.type = 'public_ip' + left join + `cloud`.`resource_count` ipcount ON account.id = ipcount.account_id + and ipcount.type = 'public_ip' + left join + `cloud`.`resource_limit` volumelimit ON account.id = volumelimit.account_id + and volumelimit.type = 'volume' + left join + `cloud`.`resource_count` volumecount ON account.id = volumecount.account_id + and volumecount.type = 'volume' + left join + `cloud`.`resource_limit` snapshotlimit ON account.id = snapshotlimit.account_id + and snapshotlimit.type = 'snapshot' + left join + `cloud`.`resource_count` snapshotcount ON account.id = snapshotcount.account_id + and snapshotcount.type = 'snapshot' + left join + `cloud`.`resource_limit` templatelimit ON account.id = templatelimit.account_id + and templatelimit.type = 'template' + left join + `cloud`.`resource_count` templatecount ON account.id = templatecount.account_id + and templatecount.type = 'template' + left join + `cloud`.`resource_limit` vpclimit ON account.id = vpclimit.account_id + and vpclimit.type = 'vpc' + left join + `cloud`.`resource_count` vpccount ON account.id = vpccount.account_id + and vpccount.type = 'vpc' + left join + `cloud`.`resource_limit` projectlimit ON account.id = projectlimit.account_id + and projectlimit.type = 'project' + left join + `cloud`.`resource_count` projectcount ON account.id = projectcount.account_id + and projectcount.type = 'project' + left join + `cloud`.`resource_limit` networklimit ON account.id = networklimit.account_id + and networklimit.type = 'network' + left join + `cloud`.`resource_count` networkcount ON account.id = networkcount.account_id + and networkcount.type = 'network' + left join + `cloud`.`resource_limit` cpulimit ON account.id = cpulimit.account_id + and cpulimit.type = 'cpu' + left join + `cloud`.`resource_count` cpucount ON account.id = cpucount.account_id + and cpucount.type = 'cpu' + left join + `cloud`.`resource_limit` memorylimit ON account.id = memorylimit.account_id + and memorylimit.type = 'memory' + left join + `cloud`.`resource_count` memorycount ON account.id = memorycount.account_id + and memorycount.type = 'memory' + left join + `cloud`.`resource_limit` primary_storage_limit ON account.id = primary_storage_limit.account_id + and primary_storage_limit.type = 'primary_storage' + left join + `cloud`.`resource_count` primary_storage_count ON account.id = primary_storage_count.account_id + and primary_storage_count.type = 'primary_storage' + left join + `cloud`.`resource_limit` secondary_storage_limit ON account.id = secondary_storage_limit.account_id + and secondary_storage_limit.type = 'secondary_storage' + left join + `cloud`.`resource_count` secondary_storage_count ON account.id = secondary_storage_count.account_id + and secondary_storage_count.type = 'secondary_storage' + left join + `cloud`.`async_job` ON async_job.instance_id = account.id + and async_job.instance_type = 'Account' + and async_job.job_status = 0; diff --git a/utils/src/com/cloud/utils/UriUtils.java b/utils/src/com/cloud/utils/UriUtils.java index a8b5ccb0934..3bcee7aeac4 100644 --- a/utils/src/com/cloud/utils/UriUtils.java +++ b/utils/src/com/cloud/utils/UriUtils.java @@ -17,10 +17,14 @@ package com.cloud.utils; import java.io.File; +import java.io.IOException; +import java.net.HttpURLConnection; import java.net.URI; import java.net.URISyntaxException; import java.net.URLEncoder; +import javax.net.ssl.HttpsURLConnection; + import com.cloud.utils.exception.CloudRuntimeException; public class UriUtils { @@ -78,4 +82,33 @@ public class UriUtils { // no need to do URL component encoding return url; } + + // Get the size of a file from URL response header. + public static Long getRemoteSize(String url) { + Long remoteSize = (long) 0; + HttpURLConnection httpConn = null; + HttpsURLConnection httpsConn = null; + try { + URI uri = new URI(url); + if(uri.getScheme().equalsIgnoreCase("http")) { + httpConn = (HttpURLConnection) uri.toURL().openConnection(); + remoteSize = Long.parseLong(httpConn.getHeaderField("content-length")); + } + else if(uri.getScheme().equalsIgnoreCase("https")) { + httpsConn = (HttpsURLConnection) uri.toURL().openConnection(); + remoteSize = Long.parseLong(httpsConn.getHeaderField("content-length")); + } + } catch (URISyntaxException e) { + throw new IllegalArgumentException("Invalid URL " + url); + } catch (IOException e) { + throw new IllegalArgumentException("Unable to establish connection with URL " + url); + } finally { + if (httpConn != null) { + httpConn.disconnect(); + } else if (httpsConn != null) { + httpsConn.disconnect(); + } + } + return remoteSize; + } }