/* * Copyright (c) 2012-2013 EMC Corporation * All Rights Reserved */ package com.emc.storageos.api.service.impl.resource.utils; import java.math.BigDecimal; import java.math.BigInteger; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.api.service.impl.resource.ArgValidator; import com.emc.storageos.coordinator.client.service.CoordinatorClient; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.constraint.ContainmentConstraint; import com.emc.storageos.db.client.constraint.URIQueryResultList; import com.emc.storageos.db.client.model.Bucket; import com.emc.storageos.db.client.model.DiscoveredDataObject.CompatibilityStatus; import com.emc.storageos.db.client.model.DiscoveredDataObject.DiscoveryStatus; import com.emc.storageos.db.client.model.FileShare; import com.emc.storageos.db.client.model.Project; import com.emc.storageos.db.client.model.StoragePool; import com.emc.storageos.db.client.model.StoragePool.PoolServiceType; import com.emc.storageos.db.client.model.StorageSystem; import com.emc.storageos.db.client.model.TenantOrg; import com.emc.storageos.db.client.model.VirtualPool; import com.emc.storageos.db.client.model.Volume; import com.emc.storageos.db.client.util.CustomQueryUtility; import com.emc.storageos.db.client.util.SumPrimitiveFieldAggregator; import com.emc.storageos.model.vpool.CapacityResponse; import com.emc.storageos.svcs.errorhandling.resources.APIException; import com.emc.storageos.svcs.errorhandling.resources.ServiceCodeException; import com.emc.storageos.volumecontroller.impl.utils.ObjectLocalCache; import com.emc.storageos.volumecontroller.impl.utils.ProvisioningAttributeMapBuilder; import com.emc.storageos.volumecontroller.impl.utils.attrmatchers.CapacityMatcher; import com.emc.storageos.volumecontroller.impl.utils.attrmatchers.MaxResourcesMatcher; import com.emc.storageos.volumecontroller.impl.utils.attrmatchers.NeighborhoodsMatcher; public class CapacityUtils { private static final Logger _log = LoggerFactory.getLogger(CapacityUtils.class); public static final long BASE = 1024L; public static final long KB = BASE; public static final long MB = KB * BASE; public static final long GB = MB * BASE; public static final long kbToGb = GB / KB; private static final BigDecimal kbToGB_BD = new BigDecimal(kbToGb); private static final BigInteger kbToGB_BI = BigInteger.valueOf(kbToGb); private static final int DEFAULT_BATCH_SIZE = 1000; private static final String CAPACITY_STR = "capacity"; private static final String USED_CAPACITY_STR = "usedCapacity"; private static final String PROVISIONED_CAPACITY_STR = "provisionedCapacity"; private static final String ALLOCATED_CAPACITY_STR = "allocatedCapacity"; private static final String POOL_STR = "pool"; private static final BigDecimal MINUS_ONE = new BigDecimal("-1"); private static final long MINUS_ONE_LONG = -1; public static class Capacity { public double _usedCapacity; public double _provisionedCapacity; } /** * Names for storage capacity metrics. */ public enum StorageMetrics { USABLE, FREE, SUBSCRIBED, USED, PERCENT_USED, NET_FREE, PERCENT_SUBSCRIBED } /** * Returns aggregate capacity data for storage pools. Capacity is returned * in the same unit as it was discovered and stored in storage pools (KB). * Calculation is using BigInteger type to prevent overflow. * * @param storagePools storage pools * @return map of capacity metrics * @throws ServiceCodeException */ public static Map<String, BigInteger> getPoolCapacityMetrics(StoragePool storagePool) { Map<String, BigInteger> capacityMetrics = new HashMap<String, BigInteger>(); if (storagePool.getTotalCapacity() == 0) { _log.error("Storage pool: {} has zero total capacity, skipping the pool.", storagePool.getId().toString()); capacityMetrics.put(StorageMetrics.USABLE.toString(), BigInteger.ZERO); capacityMetrics.put(StorageMetrics.FREE.toString(), BigInteger.ZERO); capacityMetrics.put(StorageMetrics.SUBSCRIBED.toString(), BigInteger.ZERO); } else { BigInteger totalPoolCapacity = BigInteger.valueOf(storagePool.getTotalCapacity()); BigInteger freePoolCapacity = BigInteger.valueOf(storagePool.getFreeCapacity()); BigInteger subscribedPoolCapacity = BigInteger.ZERO; if (null != storagePool.getSubscribedCapacity()) { subscribedPoolCapacity = BigInteger.valueOf(storagePool.getSubscribedCapacity()); } long usedPoolCapacity = storagePool.getTotalCapacity() - storagePool.getFreeCapacity(); // calculate subscribed capacity only for block CoS if (PoolServiceType.block.toString().equalsIgnoreCase(storagePool.getPoolServiceType())) { if (storagePool.getSubscribedCapacity() < 0) { // device pool subscribedPoolCapacity = BigInteger.valueOf(usedPoolCapacity); } else { subscribedPoolCapacity = BigInteger.valueOf(storagePool.getSubscribedCapacity()); } } capacityMetrics.put(StorageMetrics.USABLE.toString(), totalPoolCapacity); capacityMetrics.put(StorageMetrics.FREE.toString(), freePoolCapacity); capacityMetrics.put(StorageMetrics.SUBSCRIBED.toString(), subscribedPoolCapacity); } return capacityMetrics; } /** * Returns aggregate capacity data for storage pools. Capacity is returned * in the same unit as it was discovered and stored in storage pools (KB). * Calculation is using BigInteger type to prevent overflow. * * @param storagePools storage pools * @param DbClient dbClient * @param VirtualPool vPool * @return map of capacity metrics * @throws ServiceCodeException */ private static Map<String, BigInteger> getPoolCapacityMetrics(List<StoragePool> storagePools, VirtualPool vPool, DbClient dbClient, CoordinatorClient coordinator) { BigInteger totalCapacity = BigInteger.ZERO; BigInteger freeCapacity = BigInteger.ZERO; BigInteger subscribedCapacity = BigInteger.ZERO; BigInteger netFreeCapacity = BigInteger.ZERO; Map<String, BigInteger> capacityMetrics = new HashMap<String, BigInteger>(); if (storagePools == null || storagePools.isEmpty()) { _log.warn("There are no pools passed to this method. Returning zero capacity metrics."); capacityMetrics.put(StorageMetrics.USABLE.toString(), totalCapacity); capacityMetrics.put(StorageMetrics.FREE.toString(), freeCapacity); capacityMetrics.put(StorageMetrics.SUBSCRIBED.toString(), subscribedCapacity); capacityMetrics.put(StorageMetrics.NET_FREE.toString(), netFreeCapacity); return capacityMetrics; } // Cache of sharedStorageCapacity flags of storage systems // The flag means if all storage pools share the same capacity Map<String, Boolean> storageSharedFlags = new HashMap<String, Boolean>(); for (StoragePool storagePool : storagePools) { if (storagePool.getTotalCapacity() == 0) { _log.error( "Storage pool: {} has zero total capacity, skipping the pool.", storagePool.getId().toString()); continue; } String storageDeviceId = storagePool.getStorageDevice().toString(); if (!storageSharedFlags.containsKey(storageDeviceId)) { StorageSystem system = dbClient.queryObject(StorageSystem.class, storagePool.getStorageDevice()); storageSharedFlags.put(storageDeviceId, system.getSharedStorageCapacity()); } else if (storageSharedFlags.get(storageDeviceId).booleanValue()) { // Another pool of storage with shared capacity feature has been processed // Skip current pool continue; } totalCapacity = totalCapacity.add(BigInteger.valueOf(storagePool.getTotalCapacity())); freeCapacity = freeCapacity.add(BigInteger.valueOf(storagePool.getFreeCapacity())); long usedPoolCapacity = storagePool.getTotalCapacity() - storagePool.getFreeCapacity(); long subscribedPoolCapacity = 0; long netFreeSubscription = 0; boolean isBlock = VirtualPool.Type.block.name().equalsIgnoreCase(vPool.getType()); // calculate subscribed capacity only for block CoS if (isBlock) { subscribedPoolCapacity = storagePool.getSubscribedCapacity(); if (storagePool.getSubscribedCapacity() < 0) { // device pool subscribedPoolCapacity = usedPoolCapacity; } subscribedCapacity = subscribedCapacity.add(BigInteger.valueOf(subscribedPoolCapacity)); // 3) for thin pools check thin Pool Subscription percentage double maxSubscribedPercentage = CapacityMatcher.getMaxPoolSubscriptionPercentage(storagePool, coordinator); long poolMaximumSubscribedCapacity = (long) (storagePool.getTotalCapacity() * maxSubscribedPercentage / 100); netFreeSubscription = poolMaximumSubscribedCapacity - subscribedPoolCapacity; } else { // if isFile // For the file system we do not have information on its subscribe limit. // Instead we used all available capacity as its netSubscription for thin provisioning. netFreeSubscription = storagePool.getTotalCapacity() - usedPoolCapacity; } // Compute Net Free capacity. // 1) Check that the maximum resource limit is not reached for the pool or the storage system if (MaxResourcesMatcher.checkPoolMaximumResourcesApproached(storagePool, dbClient, 0)) { // ignore this pool, it does not contain any net free capacity continue; } // 2) Check against Maximum Utilizaiton Percentage double maxPoolUtilizationPercentage = CapacityMatcher.getMaxPoolUtilizationPercentage(storagePool, coordinator); long poolMaximumCapacity = (long) (storagePool.getTotalCapacity() * maxPoolUtilizationPercentage / 100); long netFreeUtilization = poolMaximumCapacity - usedPoolCapacity; if (netFreeUtilization < 0) { // ignore this pool continue; } // THICK virtual pool if (VirtualPool.ProvisioningType.Thick.name().equals(vPool.getSupportedProvisioningType())) { netFreeCapacity = netFreeCapacity.add(BigInteger.valueOf(netFreeUtilization)); } else { // THIN virtual polls // 3) Check against Maximum Subscription. Ignore if it pool is oversubscribed. if (netFreeSubscription > 0) { netFreeCapacity = netFreeCapacity.add(BigInteger.valueOf(netFreeSubscription)); } // else ignore. } } capacityMetrics.put(StorageMetrics.USABLE.toString(), totalCapacity); capacityMetrics.put(StorageMetrics.FREE.toString(), freeCapacity); capacityMetrics.put(StorageMetrics.SUBSCRIBED.toString(), subscribedCapacity); capacityMetrics.put(StorageMetrics.NET_FREE.toString(), netFreeCapacity); return capacityMetrics; } /** * Prepares capacity metrics. Transforms metrics to GB (required unit) and * calculates "used", "percent_used" and "percent_subscribed" metrics. * * @param capacityMetrics * @return map with capacity metrics * @throws ServiceCodeException */ public static Map<String, Long> preparePoolCapacityMetrics(Map<String, BigInteger> capacityMetrics) { // Get integer value of GBs rounded down. BigInteger totalCapacity = capacityMetrics.get(StorageMetrics.USABLE.toString()); BigDecimal totalCapacityGb = new BigDecimal(totalCapacity).divideToIntegralValue(kbToGB_BD); long totalCapacityGbLong = totalCapacityGb.longValue(); // Get integer value of GBs rounded down. BigInteger freeCapacity = capacityMetrics.get(StorageMetrics.FREE.toString()); BigDecimal freeCapacityGb = new BigDecimal(freeCapacity).divideToIntegralValue(kbToGB_BD); long freeCapacityGbLong = freeCapacityGb.longValue(); // Get integer value of GBs rounded up. BigInteger subscribedCapacity = capacityMetrics.get(StorageMetrics.SUBSCRIBED.toString()); long subscribedCapacityGbLong; if (!subscribedCapacity.toString().equals(MINUS_ONE.toString())) { BigDecimal[] result = new BigDecimal(subscribedCapacity).divideAndRemainder(kbToGB_BD); subscribedCapacityGbLong = result[0].longValue(); if (!result[1].equals(BigDecimal.ZERO)) { subscribedCapacityGbLong += 1; } } else { subscribedCapacityGbLong = MINUS_ONE_LONG; } // Calculate used capacity long usedCapacityGbLong = totalCapacityGbLong - freeCapacityGbLong; long percentUsed = 0; long percentSubscribed = 0; // if total capacity is 0, do not calculate percent metrics if (totalCapacityGbLong > 0) { // Calculate percent used --- integer value rounded up long temp = usedCapacityGbLong * 100; percentUsed = (temp % totalCapacityGbLong == 0) ? temp / totalCapacityGbLong : temp / totalCapacityGbLong + 1; // Calculate percent subscribed --- integer value rounded up temp = subscribedCapacityGbLong * 100; percentSubscribed = (temp % totalCapacityGbLong == 0) ? temp / totalCapacityGbLong : temp / totalCapacityGbLong + 1; } Map<String, Long> metrics = new HashMap<String, Long>(); metrics.put(StorageMetrics.USABLE.toString(), totalCapacityGbLong); metrics.put(StorageMetrics.FREE.toString(), freeCapacityGbLong); metrics.put(StorageMetrics.USED.toString(), usedCapacityGbLong); metrics.put(StorageMetrics.SUBSCRIBED.toString(), subscribedCapacityGbLong); metrics.put(StorageMetrics.PERCENT_USED.toString(), percentUsed); metrics.put(StorageMetrics.PERCENT_SUBSCRIBED.toString(), percentSubscribed); return metrics; } /** * Convert the bytes to 2-digit decimal value after precision. * Ex: 1376787345L bytes => 1.28GB * * @param size : size in bytes. * @return String: size in GB. */ public static String convertBytesToGBInStr(Long size) { if (size == null) { return String.format("0"); } return String.format("%.2f", (size / (double) GB)); } /** * Convert the bytes to 2-digit decimal value after precision. * Ex: 1376787345L bytes => 1.28GB * * @param size : size in bytes. * @return size in GB. */ public static Double convertBytesToGB(Long size) { if (size == null) { return 0.0; } return size / (double) GB; } /** * Convert the KB to GB. * * @param size : size in KB. * @return Long: size in GB. */ public static Long convertKBToGB(Long size) { if (size == null) { return 0L; } return size / MB; } /** * Finds if a pool is file storage pool * * @param storagePool * @return true for file pool, false block pools */ public static boolean isFileStoragePool(StoragePool storagePool, DbClient dbClient) { URI storageSystemUri = storagePool.getStorageDevice(); StorageSystem storageSystem = dbClient.queryObject(StorageSystem.class, storageSystemUri); ArgValidator.checkEntity(storageSystem, storageSystemUri, false); StorageSystem.Type storageSystemType = StorageSystem.Type.valueOf(storageSystem.getSystemType()); return (storageSystemType.equals(StorageSystem.Type.isilon) || storageSystemType.equals(StorageSystem.Type.vnxfile)); } private static Capacity getVirtualPoolCapacityForPools(DbClient dbClient, URI cosId, VirtualPool.Type cosType, Set<String> pools) { Capacity capacity = new Capacity(); if (cosType == VirtualPool.Type.block) { URIQueryResultList list = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory.getContainedObjectsConstraint(cosId, Volume.class, "virtualPool"), list); Iterator<URI> volumeList = CustomQueryUtility.filterDataObjectsFieldValueInSet(dbClient, Volume.class, POOL_STR, list.iterator(), pools); SumPrimitiveFieldAggregator agg = CustomQueryUtility.aggregateActiveObject( dbClient, Volume.class, new String[] { PROVISIONED_CAPACITY_STR }, volumeList); capacity._provisionedCapacity += agg.getAggregate(PROVISIONED_CAPACITY_STR); agg = CustomQueryUtility.aggregateActiveObject( dbClient, Volume.class, new String[] { ALLOCATED_CAPACITY_STR }, volumeList); capacity._usedCapacity += agg.getAggregate(ALLOCATED_CAPACITY_STR); } else { URIQueryResultList list = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory.getContainedObjectsConstraint(cosId, FileShare.class, "virtualPool"), list); Iterator<URI> fileList = CustomQueryUtility.filterDataObjectsFieldValueInSet(dbClient, FileShare.class, POOL_STR, list.iterator(), pools); SumPrimitiveFieldAggregator agg = CustomQueryUtility.aggregateActiveObject( dbClient, FileShare.class, new String[] { CAPACITY_STR }, fileList); capacity._provisionedCapacity += agg.getAggregate(CAPACITY_STR); agg = CustomQueryUtility.aggregateActiveObject( dbClient, FileShare.class, new String[] { USED_CAPACITY_STR }, fileList); capacity._usedCapacity += agg.getAggregate(USED_CAPACITY_STR); } return capacity; } public static double getVirtualPoolCapacity(DbClient dbClient, URI cosId, VirtualPool.Type cosType) { double capacity = 0; if (cosType == VirtualPool.Type.block) { capacity = CustomQueryUtility.aggregatedPrimitiveField(dbClient, Volume.class, "virtualPool", cosId.toString(), PROVISIONED_CAPACITY_STR). getValue(); } else if (cosType == VirtualPool.Type.file) { capacity = CustomQueryUtility.aggregatedPrimitiveField(dbClient, FileShare.class, "virtualPool", cosId.toString(), CAPACITY_STR). getValue(); } else if (cosType == VirtualPool.Type.object) { capacity = CustomQueryUtility.aggregatedPrimitiveField(dbClient, Bucket.class, "virtualPool", cosId.toString(), CAPACITY_STR). getValue(); } return capacity; } public static double getProjectCapacity(DbClient dbClient, URI projectID) { double capacity = CustomQueryUtility.aggregatedPrimitiveField(dbClient, Volume.class, "project", projectID.toString(), PROVISIONED_CAPACITY_STR). getValue(); capacity += CustomQueryUtility.aggregatedPrimitiveField(dbClient, FileShare.class, "project", projectID.toString(), CAPACITY_STR). getValue(); return capacity; } public static double getTenantCapacity(DbClient dbClient, URI tenantId) { double projectCap = 0.0; List<Project> projects = CustomQueryUtility. queryActiveResourcesByRelation(dbClient, tenantId, Project.class, "tenantOrg"); for (Project project : projects) { projectCap += getProjectCapacity(dbClient, project.getId()); } return projectCap; } public static long totalSubtenantQuota(DbClient dbClient, URI tenantId) { long totalQuota = 0L; URIQueryResultList subtenants = new URIQueryResultList(); dbClient.queryByConstraint( ContainmentConstraint.Factory.getTenantOrgSubTenantConstraint(tenantId), subtenants); while (subtenants.iterator().hasNext()) { TenantOrg subtenant = dbClient.queryObject(TenantOrg.class, subtenants.iterator().next()); if (!subtenant.getInactive() && subtenant.getQuotaEnabled()) { totalQuota += subtenant.getQuota(); } } return totalQuota; } public static long totalProjectQuota(DbClient dbClient, URI tenantId) { long totalQuota = 0L; URIQueryResultList projects = new URIQueryResultList(); dbClient.queryByConstraint( ContainmentConstraint.Factory.getTenantOrgProjectConstraint(tenantId), projects); while (projects.iterator().hasNext()) { Project project = dbClient.queryObject(Project.class, projects.iterator().next()); if (!project.getInactive() && project.getQuotaEnabled()) { totalQuota += project.getQuota(); } } return totalQuota; } public static boolean validateVirtualPoolQuota(DbClient dbClient, VirtualPool cos, long requestedSize) { if (!cos.getQuotaEnabled()) { return true; } else { double cap = getVirtualPoolCapacity(dbClient, cos.getId(), VirtualPool.Type.valueOf(cos.getType())); return ((double) cos.getQuota() * GB >= cap + requestedSize); } } public static boolean validateProjectQuota(DbClient dbClient, Project proj, long requestedSize) { if (!proj.getQuotaEnabled()) { return true; } else { double cap = getProjectCapacity(dbClient, proj.getId()); return ((double) proj.getQuota() * GB >= cap + requestedSize); } } public static boolean validateTenantQuota(DbClient dbClient, TenantOrg tenant, long requestedSize) { if (!tenant.getQuotaEnabled()) { return true; } else { double cap = getTenantCapacity(dbClient, tenant.getId()); return ((double) tenant.getQuota() * GB >= cap + requestedSize); } } public static boolean validateQuotasForProvisioning(DbClient dbClient, VirtualPool cos, Project proj, TenantOrg tenant, long requestedSize, String type) { _log.debug("Requested UnManagedVolume Capacity {}", requestedSize); if (cos != null && !CapacityUtils.validateVirtualPoolQuota(dbClient, cos, requestedSize)) { throw APIException.badRequests.insufficientQuotaForVirtualPool(cos.getLabel(), type); } if (proj != null && !CapacityUtils.validateProjectQuota(dbClient, proj, requestedSize)) { throw APIException.badRequests.insufficientQuotaForProject(proj.getLabel(), type); } if (proj != null && !proj.getQuotaEnabled() && tenant != null && !CapacityUtils.validateTenantQuota(dbClient, tenant, requestedSize)) { throw APIException.badRequests.insufficientQuotaForTenant(tenant.getLabel(), type); } return true; } public static CapacityResponse getCapacityForVirtualPoolAndVirtualArray(VirtualPool vPool, URI vArrayId, DbClient dbClient, CoordinatorClient coordinator) { List<StoragePool> validPoolsOfvPool = VirtualPool.getValidStoragePools(vPool, dbClient, false); List<StoragePool> invalidPoolsOfvPool = VirtualPool.getInvalidStoragePools(vPool, dbClient); Map<String, Object> attributeMap = new ProvisioningAttributeMapBuilder(0L, vArrayId.toString(), 0L).buildMap(); NeighborhoodsMatcher matcher = new NeighborhoodsMatcher(); matcher.setCoordinatorClient(coordinator); matcher.setObjectCache(new ObjectLocalCache(dbClient)); StringBuffer errorMessageForValidPools = new StringBuffer(); StringBuffer errorMessageForInValidPools = new StringBuffer(); validPoolsOfvPool = matcher.runMatchStoragePools(validPoolsOfvPool, attributeMap, errorMessageForValidPools); invalidPoolsOfvPool = matcher.runMatchStoragePools(invalidPoolsOfvPool, attributeMap, errorMessageForInValidPools); List<StoragePool> validPools = new ArrayList<StoragePool>(); for (StoragePool pool : validPoolsOfvPool) { if (StoragePool.RegistrationStatus.REGISTERED.toString().equals(pool.getRegistrationStatus()) && CompatibilityStatus.COMPATIBLE.toString().equals(pool.getCompatibilityStatus()) && DiscoveryStatus.VISIBLE.toString().equals(pool.getDiscoveryStatus())) { validPools.add(pool); } else { invalidPoolsOfvPool.add(pool); } } Map<String, BigInteger> rawCapacityMetrics = getPoolCapacityMetrics(validPools, vPool, dbClient, coordinator); Set<String> poolSet = new HashSet<String>(); for (StoragePool pool : validPools) { poolSet.add(pool.getId().toString()); } Capacity capacity = getVirtualPoolCapacityForPools(dbClient, vPool.getId(), VirtualPool.Type.valueOf(vPool.getType()), poolSet); poolSet.clear(); for (StoragePool pool : invalidPoolsOfvPool) { poolSet.add(pool.getId().toString()); } Capacity invalidPoolCapacity = getVirtualPoolCapacityForPools(dbClient, vPool.getId(), VirtualPool.Type.valueOf(vPool.getType()), poolSet); // Free Capacity is rounded down BigInteger freeCapacity = rawCapacityMetrics.get(StorageMetrics.FREE.toString()); freeCapacity = freeCapacity.divide(kbToGB_BI); long freeCapacityGb = freeCapacity.longValue(); BigInteger netFreeCapacity = rawCapacityMetrics.get(StorageMetrics.NET_FREE.toString()); netFreeCapacity = netFreeCapacity.divide(kbToGB_BI); long netFreeCapacityGb = netFreeCapacity.longValue(); // 4) Check netFreeCapacity against Quota if (vPool.getQuotaEnabled()) { long netFreeQuota = vPool.getQuota() - (long) ((capacity._provisionedCapacity + invalidPoolCapacity._provisionedCapacity) / GB); if (netFreeQuota < 0) { netFreeCapacityGb = 0; } else if (netFreeQuota < netFreeCapacityGb) { netFreeCapacityGb = netFreeQuota; } } // Used Capacity is rounded up. BigDecimal[] result = new BigDecimal(capacity._usedCapacity + invalidPoolCapacity._usedCapacity) .divideAndRemainder(new BigDecimal(GB)); long usedCapacityGb = result[0].longValue(); if (!result[1].equals(BigDecimal.ZERO)) { usedCapacityGb += 1; } // Subscribed Capacity is rounded up. result = new BigDecimal(capacity._provisionedCapacity + invalidPoolCapacity._provisionedCapacity) .divideAndRemainder(new BigDecimal(GB)); long subscribedCapacityGb = result[0].longValue(); if (!result[1].equals(BigDecimal.ZERO)) { subscribedCapacityGb += 1; } long totalCapacityGB = freeCapacityGb + usedCapacityGb; CapacityResponse response = new CapacityResponse(); response.setFreeGb(Long.toString(freeCapacityGb)); response.setNetFreeGb(Long.toString(netFreeCapacityGb)); response.setProvissionedGb(Long.toString(subscribedCapacityGb)); response.setUsedGb(Long.toString(usedCapacityGb)); if (totalCapacityGB != 0) { result = new BigDecimal(subscribedCapacityGb * 100).divideAndRemainder(new BigDecimal( totalCapacityGB)); int percentage = result[0].intValue(); if (!result[1].equals(BigDecimal.ZERO)) { percentage += 1; } response.setPercentProvisioned(Integer.toString(percentage)); result = new BigDecimal(usedCapacityGb * 100).divideAndRemainder(new BigDecimal( totalCapacityGB)); percentage = result[0].intValue(); if (!result[1].equals(BigDecimal.ZERO)) { percentage += 1; } response.setPercentUsed(Integer.toString(percentage)); } return response; } }