diff --git a/engine/components-api/src/com/cloud/capacity/CapacityManager.java b/engine/components-api/src/com/cloud/capacity/CapacityManager.java index 13624e69453..bd1a61096c0 100755 --- a/engine/components-api/src/com/cloud/capacity/CapacityManager.java +++ b/engine/components-api/src/com/cloud/capacity/CapacityManager.java @@ -91,4 +91,15 @@ public interface CapacityManager { * @return true if the count of host's running VMs >= hypervisor limit */ boolean checkIfHostHasCpuCapability(long hostId, Integer cpuNum, Integer cpuSpeed); + + /** + * Check if cluster will cross threshold if the cpu/memory requested are accomodated + * @param clusterId the clusterId to check + * @param cpuRequested cpu requested + * @param ramRequested cpu requested + * @return true if the customer crosses threshold, false otherwise + */ + boolean checkIfClusterCrossesThreshold(Long clusterId, Integer cpuRequested, long ramRequested); + + float getClusterOverProvisioningFactor(Long clusterId, short capacityType); } diff --git a/engine/schema/src/com/cloud/capacity/dao/CapacityDao.java b/engine/schema/src/com/cloud/capacity/dao/CapacityDao.java index e32f96e953c..079d9a87649 100755 --- a/engine/schema/src/com/cloud/capacity/dao/CapacityDao.java +++ b/engine/schema/src/com/cloud/capacity/dao/CapacityDao.java @@ -45,11 +45,12 @@ public interface CapacityDao extends GenericDao { Pair, Map> orderPodsByAggregateCapacity(long zoneId, short capacityType); - List findCapacityBy(Integer capacityType, Long zoneId, Long podId, Long clusterId, String resourceState); + List findCapacityBy(Integer capacityType, Long zoneId, + Long podId, Long clusterId, String resourceState); + List listCapacitiesGroupedByLevelAndType(Integer capacityType, Long zoneId, Long podId, Long clusterId, int level, Long limit); + void updateCapacityState(Long dcId, Long podId, Long clusterId, + Long hostId, String capacityState); + List listClustersCrossingThreshold(short capacityType, Long zoneId, String ConfigName, long computeRequested); - List listCapacitiesGroupedByLevelAndType(Integer capacityType, Long zoneId, Long podId, Long clusterId, int level, Long limit); - - void updateCapacityState(Long dcId, Long podId, Long clusterId, Long hostId, String capacityState); - - List listClustersCrossingThreshold(short capacityType, Long zoneId, String ConfigName, long computeRequested); + float findClusterConsumption(Long clusterId, short capacityType, long computeRequested); } diff --git a/engine/schema/src/com/cloud/capacity/dao/CapacityDaoImpl.java b/engine/schema/src/com/cloud/capacity/dao/CapacityDaoImpl.java index cb916c37733..7919ed5bcd4 100755 --- a/engine/schema/src/com/cloud/capacity/dao/CapacityDaoImpl.java +++ b/engine/schema/src/com/cloud/capacity/dao/CapacityDaoImpl.java @@ -27,6 +27,9 @@ import java.util.Map; import javax.ejb.Local; import javax.inject.Inject; +import com.cloud.dc.ClusterDetailsDao; +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -161,19 +164,24 @@ public class CapacityDaoImpl extends GenericDaoBase implements * query from the configuration table * * */ - private static final String LIST_CLUSTERS_CROSSING_THRESHOLD = - "SELECT clusterList.cluster_id " - + "FROM (SELECT cluster.cluster_id cluster_id, ( (sum(cluster.used) + sum(cluster.reserved) + ?)/sum(cluster.total) ) ratio, cluster.configValue value " - + "FROM (SELECT capacity.cluster_id cluster_id, capacity.used_capacity used, capacity.reserved_capacity reserved, capacity.total_capacity * overcommit.value total, " - + "CASE (SELECT count(*) FROM `cloud`.`cluster_details` details WHERE details.cluster_id = capacity.cluster_id AND details.name = ? ) " - + "WHEN 1 THEN (CASE WHEN (SELECT details.value FROM `cloud`.`cluster_details` details WHERE details.cluster_id = capacity.cluster_id AND details.name = ?) is NULL " - + "THEN (SELECT config.value FROM `cloud`.`configuration` config WHERE config.name = ?)" - + "ELSE (SELECT details.value FROM `cloud`.`cluster_details` details WHERE details.cluster_id = capacity.cluster_id AND details.name = ? ) END )" - + "ELSE ( SELECT config.value FROM `cloud`.`configuration` config WHERE config.name = ?) " + "END configValue " - + "FROM `cloud`.`op_host_capacity` capacity INNER JOIN `cloud`.`cluster_details` overcommit ON overcommit.cluster_id = capacity.cluster_id " - + "WHERE capacity.data_center_id = ? AND capacity.capacity_type = ? AND capacity.total_capacity > 0 AND overcommit.name = ?) cluster " + - "GROUP BY cluster.cluster_id) clusterList " + "WHERE clusterList.ratio > clusterList.value; "; + private static final String LIST_CLUSTERS_CROSSING_THRESHOLD = "SELECT clusterList.cluster_id " + + "FROM ( SELECT cluster.cluster_id cluster_id, ( (sum(cluster.used) + sum(cluster.reserved) + ?)/sum(cluster.total) ) ratio, cluster.configValue value " + + "FROM ( SELECT capacity.cluster_id cluster_id, capacity.used_capacity used, capacity.reserved_capacity reserved, capacity.total_capacity * overcommit.value total, " + + "CASE (SELECT count(*) FROM `cloud`.`cluster_details` details WHERE details.cluster_id = capacity.cluster_id AND details.name = ? ) " + + "WHEN 1 THEN ( CASE WHEN (SELECT details.value FROM `cloud`.`cluster_details` details WHERE details.cluster_id = capacity.cluster_id AND details.name = ?) is NULL " + + "THEN (SELECT config.value FROM `cloud`.`configuration` config WHERE config.name = ?)" + + "ELSE (SELECT details.value FROM `cloud`.`cluster_details` details WHERE details.cluster_id = capacity.cluster_id AND details.name = ? ) END )" + + "ELSE ( SELECT config.value FROM `cloud`.`configuration` config WHERE config.name = ?) " + + "END configValue " + + "FROM `cloud`.`op_host_capacity` capacity INNER JOIN `cloud`.`cluster_details` overcommit ON overcommit.cluster_id = capacity.cluster_id " + + "WHERE capacity.data_center_id = ? AND capacity.capacity_type = ? AND capacity.total_capacity > 0 AND overcommit.name = ?) cluster " + + + "GROUP BY cluster.cluster_id) clusterList " + + "WHERE clusterList.ratio > clusterList.value; "; + + private static final String FIND_CLUSTER_CONSUMPTION_RATIO = "select ( (sum(capacity.used_capacity) + sum(capacity.reserved_capacity) + ?)/sum(capacity.total_capacity) ) " + + "from op_host_capacity capacity where cluster_id = ? and capacity_type = ?;"; public CapacityDaoImpl() { _hostIdTypeSearch = createSearchBuilder(); @@ -883,4 +891,26 @@ public class CapacityDaoImpl extends GenericDaoBase implements s_logger.warn("Error updating CapacityVO", e); } } + + @Override + public float findClusterConsumption(Long clusterId, short capacityType, long computeRequested){ + TransactionLegacy txn = TransactionLegacy.currentTxn(); + StringBuilder sql = new StringBuilder(FIND_CLUSTER_CONSUMPTION_RATIO); + PreparedStatement pstmt = null; + try { + pstmt = txn.prepareAutoCloseStatement(sql.toString()); + + pstmt.setLong(1, computeRequested); + pstmt.setLong(2, clusterId); + pstmt.setShort(3, capacityType); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + return rs.getFloat(1); + } + } catch (Exception e) { + s_logger.warn("Error checking cluster threshold", e); + } + return 0; + } + } diff --git a/server/src/com/cloud/capacity/CapacityManagerImpl.java b/server/src/com/cloud/capacity/CapacityManagerImpl.java index 2358f92ade4..e42879cbbd2 100755 --- a/server/src/com/cloud/capacity/CapacityManagerImpl.java +++ b/server/src/com/cloud/capacity/CapacityManagerImpl.java @@ -27,7 +27,10 @@ import javax.ejb.Local; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.deploy.DeploymentClusterPlanner; +import com.cloud.deploy.DeploymentPlanner; import com.cloud.event.UsageEventVO; +import com.cloud.utils.exception.CloudRuntimeException; import org.apache.log4j.Logger; import org.apache.cloudstack.framework.config.ConfigDepot; @@ -94,6 +97,7 @@ import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.UserVmDetailsDao; import com.cloud.vm.dao.VMInstanceDao; import com.cloud.vm.snapshot.dao.VMSnapshotDao; +import org.springframework.instrument.classloading.glassfish.GlassFishLoadTimeWeaver; @Local(value = CapacityManager.class) public class CapacityManagerImpl extends ManagerBase implements CapacityManager, StateListener, Listener, ResourceListener, @@ -852,6 +856,50 @@ public class CapacityManagerImpl extends ManagerBase implements CapacityManager, } + @Override + public float getClusterOverProvisioningFactor(Long clusterId, short capacityType){ + + String capacityOverProvisioningName = ""; + if(capacityType == Capacity.CAPACITY_TYPE_CPU){ + capacityOverProvisioningName = "cpuOvercommitRatio"; + }else if(capacityType == Capacity.CAPACITY_TYPE_MEMORY){ + capacityOverProvisioningName = "memoryOvercommitRatio"; + }else{ + throw new CloudRuntimeException("Invalid capacityType - " + capacityType); + } + + ClusterDetailsVO clusterDetailCpu = _clusterDetailsDao.findDetail(clusterId, capacityOverProvisioningName); + Float clusterOverProvisioningRatio = Float.parseFloat(clusterDetailCpu.getValue()); + return clusterOverProvisioningRatio; + + } + + @Override + public boolean checkIfClusterCrossesThreshold(Long clusterId, Integer cpuRequested, long ramRequested){ + + Float clusterCpuOverProvisioning = getClusterOverProvisioningFactor(clusterId, Capacity.CAPACITY_TYPE_CPU); + Float clusterMemoryOverProvisioning = getClusterOverProvisioningFactor(clusterId, Capacity.CAPACITY_TYPE_MEMORY); + Float clusterCpuCapacityDisableThreshold = DeploymentClusterPlanner.ClusterCPUCapacityDisableThreshold.valueIn(clusterId); + Float clusterMemoryCapacityDisableThreshold = DeploymentClusterPlanner.ClusterMemoryCapacityDisableThreshold.valueIn(clusterId); + + float cpuConsumption = _capacityDao.findClusterConsumption(clusterId, Capacity.CAPACITY_TYPE_CPU, cpuRequested); + if(cpuConsumption/clusterCpuOverProvisioning > clusterCpuCapacityDisableThreshold){ + s_logger.debug("Cluster: " +clusterId + " cpu consumption " + cpuConsumption/clusterCpuOverProvisioning + + " crosses disable threshold " + clusterCpuCapacityDisableThreshold); + return true; + } + + float memoryConsumption = _capacityDao.findClusterConsumption(clusterId, Capacity.CAPACITY_TYPE_MEMORY, ramRequested); + if(memoryConsumption/clusterMemoryOverProvisioning > clusterMemoryCapacityDisableThreshold){ + s_logger.debug("Cluster: " +clusterId + " memory consumption " + memoryConsumption/clusterMemoryOverProvisioning + + " crosses disable threshold " + clusterMemoryCapacityDisableThreshold); + return true; + } + + return false; + + } + @Override public boolean processAnswers(long agentId, long seq, Answer[] answers) { // TODO Auto-generated method stub diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 1752c22a1e3..582ddcfea15 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -36,6 +36,8 @@ import javax.naming.ConfigurationException; import com.cloud.event.UsageEventVO; import com.cloud.uuididentity.UUIDManager; +import com.cloud.capacity.Capacity; +import com.cloud.exception.InsufficientServerCapacityException; import org.apache.commons.codec.binary.Base64; import org.apache.log4j.Logger; @@ -1306,6 +1308,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir int currentCpu = currentServiceOffering.getCpu(); int currentMemory = currentServiceOffering.getRamSize(); int currentSpeed = currentServiceOffering.getSpeed(); + int memoryDiff = newMemory - currentMemory; + int cpuDiff = newCpu*newSpeed - currentCpu*currentSpeed; // Don't allow to scale when (Any of the new values less than current values) OR (All current and new values are same) if ((newSpeed < currentSpeed || newMemory < currentMemory || newCpu < currentCpu) || @@ -1328,14 +1332,24 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (vmInstance.getState().equals(State.Running)) { int retry = _scaleRetry; ExcludeList excludes = new ExcludeList(); + + // Check zone wide flag boolean enableDynamicallyScaleVm = EnableDynamicallyScaleVm.valueIn(vmInstance.getDataCenterId()); if (!enableDynamicallyScaleVm) { throw new PermissionDeniedException("Dynamically scaling virtual machines is disabled for this zone, please contact your admin"); } + + // Check vm flag if (!vmInstance.isDynamicallyScalable()) { throw new CloudRuntimeException("Unable to Scale the vm: " + vmInstance.getUuid() + " as vm does not have tools to support dynamic scaling"); } + // Check disable threshold for cluster is not crossed + HostVO host = _hostDao.findById(vmInstance.getHostId()); + if(_capacityMgr.checkIfClusterCrossesThreshold(host.getClusterId(), cpuDiff, memoryDiff)){ + throw new CloudRuntimeException("Unable to scale vm: " + vmInstance.getUuid() + " due to insufficient resources"); + } + while (retry-- != 0) { // It's != so that it can match -1. try { boolean existingHostHasCapacity = false; @@ -1344,15 +1358,17 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (newCpu > currentCpu) { _resourceLimitMgr.incrementResourceCount(caller.getAccountId(), ResourceType.cpu, new Long(newCpu - currentCpu)); } - if (newMemory > currentMemory) { - _resourceLimitMgr.incrementResourceCount(caller.getAccountId(), ResourceType.memory, new Long(newMemory - currentMemory)); + + if (memoryDiff > 0) { + _resourceLimitMgr.incrementResourceCount(caller.getAccountId(), ResourceType.memory, new Long (memoryDiff)); } // #1 Check existing host has capacity if( !excludes.shouldAvoid(ApiDBUtils.findHostById(vmInstance.getHostId())) ){ existingHostHasCapacity = _capacityMgr.checkIfHostHasCpuCapability(vmInstance.getHostId(), newCpu, newSpeed) - && _capacityMgr.checkIfHostHasCapacity(vmInstance.getHostId(), newServiceOffering.getSpeed() - currentServiceOffering.getSpeed(), - (newServiceOffering.getRamSize() - currentServiceOffering.getRamSize()) * 1024L * 1024L, false, ApiDBUtils.getCpuOverprovisioningFactor(), 1f, false); // TO DO fill it with mem. + && _capacityMgr.checkIfHostHasCapacity(vmInstance.getHostId(), cpuDiff, + (memoryDiff) * 1024L * 1024L, false, _capacityMgr.getClusterOverProvisioningFactor(host.getClusterId(), Capacity.CAPACITY_TYPE_CPU), + _capacityMgr.getClusterOverProvisioningFactor(host.getClusterId(), Capacity.CAPACITY_TYPE_MEMORY), false); excludes.addHost(vmInstance.getHostId()); } @@ -1392,8 +1408,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir if (newCpu > currentCpu) { _resourceLimitMgr.decrementResourceCount(caller.getAccountId(), ResourceType.cpu, new Long(newCpu - currentCpu)); } - if (newMemory > currentMemory) { - _resourceLimitMgr.decrementResourceCount(caller.getAccountId(), ResourceType.memory, new Long(newMemory - currentMemory)); + + if (memoryDiff > 0) { + _resourceLimitMgr.decrementResourceCount(caller.getAccountId(), ResourceType.memory, new Long (memoryDiff)); } } }