diff --git a/engine/schema/src/com/cloud/capacity/dao/CapacityDao.java b/engine/schema/src/com/cloud/capacity/dao/CapacityDao.java index a900e64c127..50d6052bc46 100644 --- a/engine/schema/src/com/cloud/capacity/dao/CapacityDao.java +++ b/engine/schema/src/com/cloud/capacity/dao/CapacityDao.java @@ -56,4 +56,6 @@ public interface CapacityDao extends GenericDao { List listClustersCrossingThreshold(short capacityType, Long zoneId, String configName, long computeRequested); float findClusterConsumption(Long clusterId, short capacityType, long computeRequested); + + List orderHostsByFreeCapacity(Long clusterId, short capacityType); } diff --git a/engine/schema/src/com/cloud/capacity/dao/CapacityDaoImpl.java b/engine/schema/src/com/cloud/capacity/dao/CapacityDaoImpl.java index d9e61841ed4..2bd6bcc4863 100644 --- a/engine/schema/src/com/cloud/capacity/dao/CapacityDaoImpl.java +++ b/engine/schema/src/com/cloud/capacity/dao/CapacityDaoImpl.java @@ -98,6 +98,8 @@ public class CapacityDaoImpl extends GenericDaoBase implements private static final String ORDER_PODS_BY_AGGREGATE_OVERCOMMIT_CAPACITY = "SELECT capacity.pod_id, SUM(used_capacity+reserved_capacity)/SUM(total_capacity * cluster_details.value) FROM `cloud`.`op_host_capacity` capacity INNER JOIN `cloud`.`cluster_details` cluster_details ON (capacity.cluster_id = cluster_details.cluster_id) WHERE data_center_id=? AND capacity_type = ? AND cluster_details.name = ? GROUP BY capacity.pod_id ORDER BY SUM(used_capacity+reserved_capacity)/SUM(total_capacity * cluster_details.value) ASC"; + private static final String ORDER_HOSTS_BY_FREE_CAPACITY = "SELECT host_id, SUM(total_capacity - (used_capacity+reserved_capacity))/SUM(total_capacity) FROM `cloud`.`op_host_capacity` WHERE " + + " cluster_id = ? AND capacity_type = ? GROUP BY host_id ORDER BY SUM(total_capacity - (used_capacity+reserved_capacity))/SUM(total_capacity) DESC "; private static final String LIST_CAPACITY_BY_RESOURCE_STATE = "SELECT capacity.data_center_id, sum(capacity.used_capacity), sum(capacity.reserved_quantity), sum(capacity.total_capacity), capacity_capacity_type " @@ -857,6 +859,29 @@ public class CapacityDaoImpl extends GenericDaoBase implements } } + @Override + public List orderHostsByFreeCapacity(Long clusterId, short capacityTypeForOrdering){ + TransactionLegacy txn = TransactionLegacy.currentTxn(); + PreparedStatement pstmt = null; + List result = new ArrayList(); + StringBuilder sql = new StringBuilder(ORDER_HOSTS_BY_FREE_CAPACITY); + + try { + pstmt = txn.prepareAutoCloseStatement(sql.toString()); + pstmt.setLong(1, clusterId); + pstmt.setShort(2, capacityTypeForOrdering); + ResultSet rs = pstmt.executeQuery(); + while (rs.next()) { + result.add(rs.getLong(1)); + } + return result; + } catch (SQLException e) { + throw new CloudRuntimeException("DB Exception on: " + sql, e); + } catch (Throwable e) { + throw new CloudRuntimeException("Caught: " + sql, e); + } + } + @Override public List listPodsByHostCapacities(long zoneId, int requiredCpu, long requiredRam, short capacityType) { TransactionLegacy txn = TransactionLegacy.currentTxn(); diff --git a/engine/storage/src/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java b/engine/storage/src/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java index 1bb85c12059..73a8544f51a 100644 --- a/engine/storage/src/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java +++ b/engine/storage/src/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java @@ -35,6 +35,8 @@ import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import com.cloud.capacity.Capacity; +import com.cloud.capacity.dao.CapacityDao; import com.cloud.dc.ClusterVO; import com.cloud.dc.dao.ClusterDao; import com.cloud.deploy.DeploymentPlan; @@ -72,6 +74,8 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement protected String _allocationAlgorithm = "random"; @Inject DiskOfferingDao _diskOfferingDao; + @Inject + CapacityDao _capacityDao; @Override public boolean configure(String name, Map params) throws ConfigurationException { @@ -100,6 +104,39 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement return reOrder(pools, vmProfile, plan); } + protected List reorderPoolsByCapacity(DeploymentPlan plan, + List pools) { + Long clusterId = plan.getClusterId(); + short capacityType; + if(pools != null && pools.size() != 0){ + capacityType = pools.get(0).getPoolType().isShared() == true ? + Capacity.CAPACITY_TYPE_STORAGE_ALLOCATED : Capacity.CAPACITY_TYPE_LOCAL_STORAGE; + } else{ + return null; + } + + List poolIdsByCapacity = _capacityDao.orderHostsByFreeCapacity(clusterId, capacityType); + if (s_logger.isDebugEnabled()) { + s_logger.debug("List of pools in descending order of free capacity: "+ poolIdsByCapacity); + } + + //now filter the given list of Pools by this ordered list + Map poolMap = new HashMap(); + for (StoragePool pool : pools) { + poolMap.put(pool.getId(), pool); + } + List matchingPoolIds = new ArrayList(poolMap.keySet()); + + poolIdsByCapacity.retainAll(matchingPoolIds); + + List reorderedPools = new ArrayList(); + for(Long id: poolIdsByCapacity){ + reorderedPools.add(poolMap.get(id)); + } + + return reorderedPools; + } + protected List reorderPoolsByNumberOfVolumes(DeploymentPlan plan, List pools, Account account) { if (account == null) { return pools; @@ -144,6 +181,8 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement Collections.shuffle(pools); } else if (_allocationAlgorithm.equals("userdispersing")) { pools = reorderPoolsByNumberOfVolumes(plan, pools, account); + } else if(_allocationAlgorithm.equals("firstfitleastconsumed")){ + pools = reorderPoolsByCapacity(plan, pools); } return pools; } diff --git a/server/src/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java b/server/src/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java index a746eb756ca..44552af8647 100644 --- a/server/src/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java +++ b/server/src/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java @@ -32,6 +32,9 @@ import org.springframework.stereotype.Component; import com.cloud.agent.manager.allocator.HostAllocator; import com.cloud.capacity.CapacityManager; +import com.cloud.capacity.CapacityVO; +import com.cloud.capacity.dao.CapacityDao; +import com.cloud.configuration.Config; import com.cloud.dc.ClusterDetailsDao; import com.cloud.dc.ClusterDetailsVO; import com.cloud.dc.dao.ClusterDao; @@ -89,6 +92,8 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { ServiceOfferingDetailsDao _serviceOfferingDetailsDao; @Inject CapacityManager _capacityMgr; + @Inject + CapacityDao _capacityDao; boolean _checkHvm = true; protected String _allocationAlgorithm = "random"; @@ -235,6 +240,8 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { Collections.shuffle(hosts); } else if (_allocationAlgorithm.equals("userdispersing")) { hosts = reorderHostsByNumberOfVms(plan, hosts, account); + }else if(_allocationAlgorithm.equals("firstfitleastconsumed")){ + hosts = reorderHostsByCapacity(plan, hosts); } if (s_logger.isDebugEnabled()) { @@ -318,6 +325,37 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator { return suitableHosts; } + // Reorder hosts in the decreasing order of free capacity. + private List reorderHostsByCapacity(DeploymentPlan plan, List hosts) { + Long clusterId = plan.getClusterId(); + //Get capacity by which we should reorder + String capacityTypeToOrder = _configDao.getValue(Config.HostCapacityTypeToOrderClusters.key()); + short capacityType = CapacityVO.CAPACITY_TYPE_CPU; + if("RAM".equalsIgnoreCase(capacityTypeToOrder)){ + capacityType = CapacityVO.CAPACITY_TYPE_MEMORY; + } + List hostIdsByFreeCapacity = _capacityDao.orderHostsByFreeCapacity(clusterId, capacityType); + if (s_logger.isDebugEnabled()) { + s_logger.debug("List of hosts in descending order of free capacity in the cluster: "+ hostIdsByFreeCapacity); + } + + //now filter the given list of Hosts by this ordered list + Map hostMap = new HashMap(); + for (Host host : hosts) { + hostMap.put(host.getId(), host); + } + List matchingHostIds = new ArrayList(hostMap.keySet()); + + hostIdsByFreeCapacity.retainAll(matchingHostIds); + + List reorderedHosts = new ArrayList(); + for(Long id: hostIdsByFreeCapacity){ + reorderedHosts.add(hostMap.get(id)); + } + + return reorderedHosts; + } + private List reorderHostsByNumberOfVms(DeploymentPlan plan, List hosts, Account account) { if (account == null) { return hosts; diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java index f9e9bddc30e..453a3bc6a1a 100644 --- a/server/src/com/cloud/configuration/Config.java +++ b/server/src/com/cloud/configuration/Config.java @@ -980,7 +980,7 @@ public enum Config { String.class, "vm.allocation.algorithm", "random", - "'random', 'firstfit', 'userdispersing', 'userconcentratedpod_random', 'userconcentratedpod_firstfit' : Order in which hosts within a cluster will be considered for VM/volume allocation.", + "'random', 'firstfit', 'userdispersing', 'userconcentratedpod_random', 'userconcentratedpod_firstfit', 'firstfitleastconsumed' : Order in which hosts within a cluster will be considered for VM/volume allocation.", null), VmDeploymentPlanner( "Advanced",