CLOUDSTACK-3664:

scaling up vms was not considering parameter cluster.(memory/cpu).allocated.capacity.disablethreshold. Fixed it
Also added overprovisioning factor retrieval at the cluster level for host capacity check
This commit is contained in:
Nitin Mehta 2013-12-09 15:40:17 -08:00
parent 4a9da03760
commit e79350d256
5 changed files with 131 additions and 24 deletions

View File

@ -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);
}

View File

@ -45,11 +45,12 @@ public interface CapacityDao extends GenericDao<CapacityVO, Long> {
Pair<List<Long>, Map<Long, Double>> orderPodsByAggregateCapacity(long zoneId, short capacityType);
List<SummedCapacity> findCapacityBy(Integer capacityType, Long zoneId, Long podId, Long clusterId, String resourceState);
List<SummedCapacity> findCapacityBy(Integer capacityType, Long zoneId,
Long podId, Long clusterId, String resourceState);
List<SummedCapacity> 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<Long> listClustersCrossingThreshold(short capacityType, Long zoneId, String ConfigName, long computeRequested);
List<SummedCapacity> 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<Long> listClustersCrossingThreshold(short capacityType, Long zoneId, String ConfigName, long computeRequested);
float findClusterConsumption(Long clusterId, short capacityType, long computeRequested);
}

View File

@ -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<CapacityVO, Long> 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<CapacityVO, Long> 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;
}
}

View File

@ -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<State, VirtualMachine.Event, VirtualMachine>, 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

View File

@ -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));
}
}
}