Skip to content

Commit 313e6ca

Browse files
author
prachi
committed
Bug 8791 user dispersing allocator
Changes: - Added a two new deployment planners 'UserDispersingPlanner' and 'UserConcentratedPodPlanner' to the DeploymentPlanners - Planner can be chosen by setting the global config variable 'vm.allocation.algorithm' to either of the following values: ('random', 'firstfit', 'userdispersing', 'userconcentratedpod') - By default, the value is 'random'. When the value is 'random', FirstFitPlanner is invoked as before that shuffles the resource lists. - Now Admin can choose whether the deployment heuristic should be applied starting at cluster or pod level. This can be done by using the global config variable 'apply.allocation.algorithm.to.pods' which is false by default. Thus by default as earlier, planner starts at clusters directly. 'UserConcentratedPodPlanner' changes: - Earlier to 3.0, FirstFitPlanner used to reorder the clusters in case this heuristic was chosen. - Now this is done by a separate planner and is applied only when 'vm.allocation.algorithm' is set to this planner - It reorders the capacity based clusters/pods such that those pods having more number of Running Vms for the given account are tried first. - Note that this userconcentration is applied only to pods and clusters. Not to hosts or storagepools within a cluster. 'UserDispersingPlanner' changes: - 'UserDispersingPlanner' reorders the capacity ordered pods and clusters based on number of 'Running' VMs for the given account in ascending order. Aim is to choose thodes pods/clusters first which have less number of Running VMs for the given account - Admin can provide weights to capacity and user dispersion so that both parameters get considered in reordering the pods/clusters. This can be done by setting the global config parameter 'vm.user.dispersion.weight'. Default value is 1. Thus if this planner is chosen, by default, ordering will be done only by number of Running Vms, unless the weight is changed. - HostAlllocators and StoragePoolAllocators also reorder the hosts and pools by ascending order of number of Running VMS/ Ready Volumes respectively for the given account. Thus try to choose that host or pool within a cluster with less number of VMs for the account.
1 parent 36f6776 commit 313e6ca

24 files changed

Lines changed: 1036 additions & 195 deletions

api/src/com/cloud/deploy/DeploymentPlanner.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@ public interface DeploymentPlanner extends Adapter {
7373
*/
7474
boolean canHandle(VirtualMachineProfile<? extends VirtualMachine> vm, DeploymentPlan plan, ExcludeList avoid);
7575

76+
public enum AllocationAlgorithm {
77+
random,
78+
firstfit,
79+
userdispersing,
80+
userconcentratedpod;
81+
}
82+
7683
public static class ExcludeList {
7784
private Set<Long> _dcIds;
7885
private Set<Long> _podIds;

client/tomcatconf/components.xml.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@
9393
</adapters>
9494
<adapters key="com.cloud.deploy.DeploymentPlanner">
9595
<adapter name="First Fit" class="com.cloud.deploy.FirstFitPlanner"/>
96+
<adapter name="UserDispersing" class="com.cloud.deploy.UserDispersingPlanner"/>
97+
<adapter name="UserConcentratedPod" class="com.cloud.deploy.UserConcentratedPodPlanner"/>
9698
<adapter name="BareMetal Fit" class="com.cloud.deploy.BareMetalPlanner"/>
9799
</adapters>
98100
<adapters key="com.cloud.network.element.NetworkElement">

server/src/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import java.util.ArrayList;
2121
import java.util.Collections;
22+
import java.util.HashMap;
2223
import java.util.List;
2324
import java.util.Map;
2425

@@ -31,6 +32,7 @@
3132
import com.cloud.capacity.CapacityManager;
3233
import com.cloud.configuration.dao.ConfigurationDao;
3334
import com.cloud.deploy.DeploymentPlan;
35+
import com.cloud.deploy.DeploymentPlanner;
3436
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
3537
import com.cloud.host.DetailVO;
3638
import com.cloud.host.Host;
@@ -48,6 +50,7 @@
4850
import com.cloud.storage.VMTemplateVO;
4951
import com.cloud.storage.dao.GuestOSCategoryDao;
5052
import com.cloud.storage.dao.GuestOSDao;
53+
import com.cloud.user.Account;
5154
import com.cloud.uservm.UserVm;
5255
import com.cloud.utils.NumbersUtil;
5356
import com.cloud.utils.component.ComponentLocator;
@@ -99,6 +102,7 @@ public List<Host> allocateTo(VirtualMachineProfile<? extends VirtualMachine> vmP
99102
Long clusterId = plan.getClusterId();
100103
ServiceOffering offering = vmProfile.getServiceOffering();
101104
VMTemplateVO template = (VMTemplateVO)vmProfile.getTemplate();
105+
Account account = vmProfile.getOwner();
102106

103107
if (type == Host.Type.Storage) {
104108
// FirstFitAllocator should be used for user VMs only since it won't care whether the host is capable of routing or not
@@ -158,13 +162,15 @@ public List<Host> allocateTo(VirtualMachineProfile<? extends VirtualMachine> vmP
158162

159163
}
160164

161-
return allocateTo(offering, template, avoid, clusterHosts, returnUpTo, considerReservedCapacity);
165+
return allocateTo(plan, offering, template, avoid, clusterHosts, returnUpTo, considerReservedCapacity, account);
162166
}
163167

164-
protected List<Host> allocateTo(ServiceOffering offering, VMTemplateVO template, ExcludeList avoid, List<HostVO> hosts, int returnUpTo, boolean considerReservedCapacity) {
168+
protected List<Host> allocateTo(DeploymentPlan plan, ServiceOffering offering, VMTemplateVO template, ExcludeList avoid, List<HostVO> hosts, int returnUpTo, boolean considerReservedCapacity, Account account) {
165169
if (_allocationAlgorithm.equals("random")) {
166170
// Shuffle this so that we don't check the hosts in the same order.
167171
Collections.shuffle(hosts);
172+
}else if(_allocationAlgorithm.equals("userdispersing")){
173+
hosts = reorderHostsByNumberOfVms(plan, hosts, account);
168174
}
169175

170176
if (s_logger.isDebugEnabled()) {
@@ -230,6 +236,36 @@ protected List<Host> allocateTo(ServiceOffering offering, VMTemplateVO template,
230236
return suitableHosts;
231237
}
232238

239+
private List<HostVO> reorderHostsByNumberOfVms(DeploymentPlan plan, List<HostVO> hosts, Account account) {
240+
if(account == null){
241+
return hosts;
242+
}
243+
long dcId = plan.getDataCenterId();
244+
Long podId = plan.getPodId();
245+
Long clusterId = plan.getClusterId();
246+
247+
List<Long> hostIdsByVmCount = _vmInstanceDao.listHostIdsByVmCount(dcId, podId, clusterId, account.getAccountId());
248+
if (s_logger.isDebugEnabled()) {
249+
s_logger.debug("List of hosts in ascending order of number of VMs: "+ hostIdsByVmCount);
250+
}
251+
252+
//now filter the given list of Hosts by this ordered list
253+
Map<Long, HostVO> hostMap = new HashMap<Long, HostVO>();
254+
for (HostVO host : hosts) {
255+
hostMap.put(host.getId(), host);
256+
}
257+
List<Long> matchingHostIds = new ArrayList<Long>(hostMap.keySet());
258+
259+
hostIdsByVmCount.retainAll(matchingHostIds);
260+
261+
List<HostVO> reorderedHosts = new ArrayList<HostVO>();
262+
for(Long id: hostIdsByVmCount){
263+
reorderedHosts.add(hostMap.get(id));
264+
}
265+
266+
return reorderedHosts;
267+
}
268+
233269
@Override
234270
public boolean isVirtualMachineUpgradable(UserVm vm, ServiceOffering offering) {
235271
// currently we do no special checks to rule out a VM being upgradable to an offering, so
@@ -361,7 +397,9 @@ public boolean configure(String name, Map<String, Object> params) throws Configu
361397
_factor = NumbersUtil.parseFloat(opFactor, 1);
362398

363399
String allocationAlgorithm = configs.get("vm.allocation.algorithm");
364-
if (allocationAlgorithm != null && (allocationAlgorithm.equals("random") || allocationAlgorithm.equals("firstfit"))) {
400+
if (allocationAlgorithm != null && (allocationAlgorithm.equals(DeploymentPlanner.AllocationAlgorithm.random.toString())
401+
|| allocationAlgorithm.equals(DeploymentPlanner.AllocationAlgorithm.firstfit.toString())
402+
|| allocationAlgorithm.equals(DeploymentPlanner.AllocationAlgorithm.userdispersing.toString()))) {
365403
_allocationAlgorithm = allocationAlgorithm;
366404
}
367405
}

server/src/com/cloud/capacity/dao/CapacityDao.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@
1919
package com.cloud.capacity.dao;
2020

2121
import java.util.List;
22+
import java.util.Map;
2223

2324
import com.cloud.capacity.CapacityVO;
2425
import com.cloud.capacity.dao.CapacityDaoImpl.SummedCapacity;
26+
import com.cloud.utils.Pair;
2527
import com.cloud.utils.db.GenericDao;
2628

2729
public interface CapacityDao extends GenericDao<CapacityVO, Long> {
@@ -31,6 +33,9 @@ public interface CapacityDao extends GenericDao<CapacityVO, Long> {
3133
boolean removeBy(Short capacityType, Long zoneId, Long podId, Long clusterId, Long hostId);
3234
List<SummedCapacity> findByClusterPodZone(Long zoneId, Long podId, Long clusterId);
3335
List<SummedCapacity> findNonSharedStorageForClusterPodZone(Long zoneId,Long podId, Long clusterId);
34-
List<Long> orderClustersByAggregateCapacity(long id, short capacityType, boolean isZone, float cpuOverprovisioningFactor);
35-
List<SummedCapacity> findCapacityBy(Integer capacityType, Long zoneId, Long podId, Long clusterId);
36+
Pair<List<Long>, Map<Long, Double>> orderClustersByAggregateCapacity(long id, short capacityType, boolean isZone, float cpuOverprovisioningFactor);
37+
List<SummedCapacity> findCapacityBy(Integer capacityType, Long zoneId, Long podId, Long clusterId);
38+
39+
List<Long> listPodsByHostCapacities(long zoneId, int requiredCpu, long requiredRam, short capacityType, float cpuOverprovisioningFactor);
40+
Pair<List<Long>, Map<Long, Double>> orderPodsByAggregateCapacity(long zoneId, short capacityType, float cpuOverprovisioningFactor);
3641
}

server/src/com/cloud/capacity/dao/CapacityDaoImpl.java

Lines changed: 88 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
import java.sql.ResultSet;
2323
import java.sql.SQLException;
2424
import java.util.ArrayList;
25+
import java.util.HashMap;
2526
import java.util.List;
27+
import java.util.Map;
2628

2729
import javax.ejb.Local;
2830

@@ -33,6 +35,7 @@
3335
import com.cloud.storage.Storage;
3436
import com.cloud.storage.StoragePoolVO;
3537
import com.cloud.storage.dao.StoragePoolDaoImpl;
38+
import com.cloud.utils.Pair;
3639
import com.cloud.utils.component.ComponentLocator;
3740
import com.cloud.utils.db.Filter;
3841
import com.cloud.utils.db.GenericDaoBase;
@@ -68,9 +71,17 @@ public class CapacityDaoImpl extends GenericDaoBase<CapacityVO, Long> implements
6871
"AND (a.total_capacity * ? - a.used_capacity) >= ? and a.capacity_type = 1) " +
6972
"JOIN op_host_capacity b ON a.host_id = b.host_id AND b.total_capacity - b.used_capacity >= ? AND b.capacity_type = 0";
7073

71-
private static final String ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_PART1 = "SELECT cluster_id FROM `cloud`.`op_host_capacity` WHERE " ;
74+
private static final String ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_PART1 = "SELECT cluster_id, SUM(used_capacity+reserved_capacity)/SUM(total_capacity * ?) FROM `cloud`.`op_host_capacity` WHERE " ;
7275
private static final String ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_PART2 = " AND capacity_type = ? GROUP BY cluster_id ORDER BY SUM(used_capacity+reserved_capacity)/SUM(total_capacity * ?) ASC";
7376

77+
private static final String LIST_PODSINZONE_BY_HOST_CAPACITIES = "SELECT DISTINCT capacity.pod_id FROM `cloud`.`op_host_capacity` capacity INNER JOIN `cloud`.`host_pod_ref` pod " +
78+
" ON (pod.id = capacity.pod_id AND pod.removed is NULL) WHERE " +
79+
" capacity.data_center_id = ? AND capacity_type = ? AND ((total_capacity * ?) - used_capacity + reserved_capacity) >= ? " +
80+
" AND pod_id IN (SELECT distinct pod_id FROM `cloud`.`op_host_capacity` WHERE " +
81+
" capacity.data_center_id = ? AND capacity_type = ? AND ((total_capacity * ?) - used_capacity + reserved_capacity) >= ?) ";
82+
83+
private static final String ORDER_PODS_BY_AGGREGATE_CAPACITY = "SELECT pod_id, SUM(used_capacity+reserved_capacity)/SUM(total_capacity * ?) FROM `cloud`.`op_host_capacity` WHERE data_center_id = ? " +
84+
" AND capacity_type = ? GROUP BY pod_id ORDER BY SUM(used_capacity+reserved_capacity)/SUM(total_capacity * ?) ASC";
7485

7586
public CapacityDaoImpl() {
7687
_hostIdTypeSearch = createSearchBuilder();
@@ -380,10 +391,11 @@ public boolean removeBy(Short capacityType, Long zoneId, Long podId, Long cluste
380391
}
381392

382393
@Override
383-
public List<Long> orderClustersByAggregateCapacity(long id, short capacityTypeForOrdering, boolean isZone, float cpuOverprovisioningFactor){
394+
public Pair<List<Long>, Map<Long, Double>> orderClustersByAggregateCapacity(long id, short capacityTypeForOrdering, boolean isZone, float cpuOverprovisioningFactor){
384395
Transaction txn = Transaction.currentTxn();
385396
PreparedStatement pstmt = null;
386397
List<Long> result = new ArrayList<Long>();
398+
Map<Long, Double> clusterCapacityMap = new HashMap<Long, Double>();
387399

388400
StringBuilder sql = new StringBuilder(ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_PART1);
389401

@@ -395,15 +407,48 @@ public List<Long> orderClustersByAggregateCapacity(long id, short capacityTypeFo
395407
sql.append(ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_PART2);
396408
try {
397409
pstmt = txn.prepareAutoCloseStatement(sql.toString());
398-
pstmt.setLong(1, id);
399-
pstmt.setShort(2, capacityTypeForOrdering);
400-
401410
if(capacityTypeForOrdering == CapacityVO.CAPACITY_TYPE_CPU){
402-
pstmt.setFloat(3, cpuOverprovisioningFactor);
411+
pstmt.setFloat(1, cpuOverprovisioningFactor);
412+
pstmt.setFloat(4, cpuOverprovisioningFactor);
403413
}else{
404-
pstmt.setFloat(3, 1);
414+
pstmt.setFloat(1, 1);
415+
pstmt.setFloat(4, 1);
405416
}
406-
417+
pstmt.setLong(2, id);
418+
pstmt.setShort(3, capacityTypeForOrdering);
419+
ResultSet rs = pstmt.executeQuery();
420+
while (rs.next()) {
421+
Long clusterId = rs.getLong(1);
422+
result.add(clusterId);
423+
clusterCapacityMap.put(clusterId, rs.getDouble(2));
424+
}
425+
return new Pair<List<Long>, Map<Long, Double>>(result, clusterCapacityMap);
426+
} catch (SQLException e) {
427+
throw new CloudRuntimeException("DB Exception on: " + sql, e);
428+
} catch (Throwable e) {
429+
throw new CloudRuntimeException("Caught: " + sql, e);
430+
}
431+
}
432+
433+
@Override
434+
public List<Long> listPodsByHostCapacities(long zoneId, int requiredCpu, long requiredRam, short capacityType, float cpuOverprovisioningFactor) {
435+
Transaction txn = Transaction.currentTxn();
436+
PreparedStatement pstmt = null;
437+
List<Long> result = new ArrayList<Long>();
438+
439+
StringBuilder sql = new StringBuilder(LIST_PODSINZONE_BY_HOST_CAPACITIES);
440+
441+
try {
442+
pstmt = txn.prepareAutoCloseStatement(sql.toString());
443+
pstmt.setLong(1, zoneId);
444+
pstmt.setShort(2, CapacityVO.CAPACITY_TYPE_CPU);
445+
pstmt.setFloat(3, cpuOverprovisioningFactor);
446+
pstmt.setLong(4, requiredCpu);
447+
pstmt.setLong(5, zoneId);
448+
pstmt.setShort(6, CapacityVO.CAPACITY_TYPE_MEMORY);
449+
pstmt.setFloat(7, 1);
450+
pstmt.setLong(8, requiredRam);
451+
407452
ResultSet rs = pstmt.executeQuery();
408453
while (rs.next()) {
409454
result.add(rs.getLong(1));
@@ -414,5 +459,40 @@ public List<Long> orderClustersByAggregateCapacity(long id, short capacityTypeFo
414459
} catch (Throwable e) {
415460
throw new CloudRuntimeException("Caught: " + sql, e);
416461
}
462+
}
463+
464+
@Override
465+
public Pair<List<Long>, Map<Long, Double>> orderPodsByAggregateCapacity(long zoneId, short capacityTypeForOrdering, float cpuOverprovisioningFactor) {
466+
Transaction txn = Transaction.currentTxn();
467+
PreparedStatement pstmt = null;
468+
List<Long> result = new ArrayList<Long>();
469+
Map<Long, Double> podCapacityMap = new HashMap<Long, Double>();
470+
471+
StringBuilder sql = new StringBuilder(ORDER_PODS_BY_AGGREGATE_CAPACITY);
472+
try {
473+
pstmt = txn.prepareAutoCloseStatement(sql.toString());
474+
pstmt.setLong(2, zoneId);
475+
pstmt.setShort(3, capacityTypeForOrdering);
476+
477+
if(capacityTypeForOrdering == CapacityVO.CAPACITY_TYPE_CPU){
478+
pstmt.setFloat(1, cpuOverprovisioningFactor);
479+
pstmt.setFloat(4, cpuOverprovisioningFactor);
480+
}else{
481+
pstmt.setFloat(1, 1);
482+
pstmt.setFloat(4, 1);
483+
}
484+
485+
ResultSet rs = pstmt.executeQuery();
486+
while (rs.next()) {
487+
Long podId = rs.getLong(1);
488+
result.add(podId);
489+
podCapacityMap.put(podId, rs.getDouble(2));
490+
}
491+
return new Pair<List<Long>, Map<Long, Double>>(result, podCapacityMap);
492+
} catch (SQLException e) {
493+
throw new CloudRuntimeException("DB Exception on: " + sql, e);
494+
} catch (Throwable e) {
495+
throw new CloudRuntimeException("Caught: " + sql, e);
496+
}
417497
}
418498
}

server/src/com/cloud/configuration/Config.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,8 +209,10 @@ public enum Config {
209209

210210
ControlCidr("Advanced", ManagementServer.class, String.class, "control.cidr", "169.254.0.0/16", "Changes the cidr for the control network traffic. Defaults to using link local. Must be unique within pods", null),
211211
ControlGateway("Advanced", ManagementServer.class, String.class, "control.gateway", "169.254.0.1", "gateway for the control network traffic", null),
212-
UseUserConcentratedPodAllocation("Advanced", ManagementServer.class, Boolean.class, "use.user.concentrated.pod.allocation", "true", "If true, deployment planner applies the user concentration heuristic during VM resource allocation", "true,false"),
213212
HostCapacityTypeToOrderClusters("Advanced", ManagementServer.class, String.class, "host.capacityType.to.order.clusters", "CPU", "The host capacity type (CPU or RAM) is used by deployment planner to order clusters during VM resource allocation", "CPU,RAM"),
213+
ApplyAllocationAlgorithmToPods("Advanced", ManagementServer.class, Boolean.class, "apply.allocation.algorithm.to.pods", "false", "If true, deployment planner applies the allocation heuristics at pods first in the given datacenter during VM resource allocation", "true,false"),
214+
VmUserDispersionWeight("Advanced", ManagementServer.class, Float.class, "vm.user.dispersion.weight", "1", "Weight for user dispersion heuristic (as a value between 0 and 1) applied to resource allocation during vm deployment. Weight for capacity heuristic will be (1 – weight of user dispersion)", null),
215+
VmAllocationAlgorithm("Advanced", ManagementServer.class, String.class, "vm.allocation.algorithm", "random", "'random', 'firstfit', 'userdispersing', 'userconcentratedpod' : Order in which hosts within a cluster will be considered for VM/volume allocation.", null),
214216
EndpointeUrl("Advanced", ManagementServer.class, String.class, "endpointe.url", "http://localhost:8080/client/api", "Endpointe Url", "The endpoint callback URL"),
215217
ElasticLoadBalancerEnabled("Advanced", ManagementServer.class, String.class, "network.loadbalancer.basiczone.elb.enabled", "false", "Whether the load balancing service is enabled for basic zones", "true,false"),
216218
ElasticLoadBalancerNetwork("Advanced", ManagementServer.class, String.class, "network.loadbalancer.basiczone.elb.network", "guest", "Whether the elastic load balancing service public ips are taken from the public or guest network", "guest,public"),
@@ -226,7 +228,6 @@ public enum Config {
226228
OvmGuestNetwork("Advanced", ManagementServer.class, String.class, "ovm.guest.network.device", null, "Specify the private bridge on host for private network", null),
227229

228230
// XenServer
229-
VmAllocationAlgorithm("Advanced", ManagementServer.class, String.class, "vm.allocation.algorithm", "random", "If 'random', hosts within a pod will be randomly considered for VM/volume allocation. If 'firstfit', they will be considered on a first-fit basis.", null),
230231
XenPublicNetwork("Network", ManagementServer.class, String.class, "xen.public.network.device", null, "[ONLY IF THE PUBLIC NETWORK IS ON A DEDICATED NIC]:The network name label of the physical device dedicated to the public network on a XenServer host", null),
231232
XenStorageNetwork1("Network", ManagementServer.class, String.class, "xen.storage.network.device1", "cloud-stor1", "Specify when there are storage networks", null),
232233
XenStorageNetwork2("Network", ManagementServer.class, String.class, "xen.storage.network.device2", "cloud-stor2", "Specify when there are storage networks", null),

server/src/com/cloud/dc/dao/HostPodDao.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,9 @@
1818

1919
package com.cloud.dc.dao;
2020

21-
import java.util.HashMap;
22-
import java.util.List;
23-
import java.util.Vector;
24-
21+
import java.util.HashMap;
22+
import java.util.List;
23+
2524
import com.cloud.dc.HostPodVO;
2625
import com.cloud.utils.db.GenericDao;
2726

@@ -30,6 +29,8 @@ public interface HostPodDao extends GenericDao<HostPodVO, Long> {
3029

3130
public HostPodVO findByName(String name, long dcId);
3231

33-
public HashMap<Long, List<Object>> getCurrentPodCidrSubnets(long zoneId, long podIdToSkip);
32+
public HashMap<Long, List<Object>> getCurrentPodCidrSubnets(long zoneId, long podIdToSkip);
33+
34+
public List<Long> listDisabledPods(long zoneId);
3435

3536
}

0 commit comments

Comments
 (0)