diff --git a/plugins/deployment-planners/user-dispersing/.classpath b/plugins/deployment-planners/user-dispersing/.classpath
new file mode 100755
index 00000000000..a246f5e509f
--- /dev/null
+++ b/plugins/deployment-planners/user-dispersing/.classpath
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/deployment-planners/user-dispersing/.project b/plugins/deployment-planners/user-dispersing/.project
new file mode 100755
index 00000000000..d9b2d401aa8
--- /dev/null
+++ b/plugins/deployment-planners/user-dispersing/.project
@@ -0,0 +1,17 @@
+
+
+ user-dispersing
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/plugins/deployment-planners/user-dispersing/.settings/org.eclipse.jdt.core.prefs b/plugins/deployment-planners/user-dispersing/.settings/org.eclipse.jdt.core.prefs
new file mode 100755
index 00000000000..d0ee7df1827
--- /dev/null
+++ b/plugins/deployment-planners/user-dispersing/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,12 @@
+#Tue Jun 19 15:34:37 PDT 2012
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.7
diff --git a/plugins/deployment-planners/user-dispersing/build.xml b/plugins/deployment-planners/user-dispersing/build.xml
new file mode 100755
index 00000000000..ba72332586a
--- /dev/null
+++ b/plugins/deployment-planners/user-dispersing/build.xml
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+ Cloud Stack ant build file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/deployment-planners/user-dispersing/src/com/cloud/deploy/UserDispersingPlanner.java b/plugins/deployment-planners/user-dispersing/src/com/cloud/deploy/UserDispersingPlanner.java
new file mode 100755
index 00000000000..dcad1e70aff
--- /dev/null
+++ b/plugins/deployment-planners/user-dispersing/src/com/cloud/deploy/UserDispersingPlanner.java
@@ -0,0 +1,215 @@
+// Copyright 2012 Citrix Systems, Inc. Licensed under the
+// Apache License, Version 2.0 (the "License"); you may not use this
+// file except in compliance with the License. Citrix Systems, Inc.
+// reserves all rights not expressly granted by the License.
+// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Automatically generated by addcopyright.py at 04/03/2012
+package com.cloud.deploy;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import javax.ejb.Local;
+import javax.naming.ConfigurationException;
+
+import org.apache.log4j.Logger;
+
+import com.cloud.configuration.Config;
+import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.utils.NumbersUtil;
+import com.cloud.utils.Pair;
+import com.cloud.vm.VirtualMachine;
+import com.cloud.vm.VirtualMachineProfile;
+
+@Local(value=DeploymentPlanner.class)
+public class UserDispersingPlanner extends FirstFitPlanner implements DeploymentPlanner {
+
+ private static final Logger s_logger = Logger.getLogger(UserDispersingPlanner.class);
+
+ /**
+ * This method should reorder the given list of Cluster Ids by applying any necessary heuristic
+ * for this planner
+ * For UserDispersingPlanner we need to order the clusters by considering the number of VMs for this account
+ * @return List ordered list of Cluster Ids
+ */
+ @Override
+ protected List reorderClusters(long id, boolean isZone, Pair, Map> clusterCapacityInfo, VirtualMachineProfile extends VirtualMachine> vmProfile, DeploymentPlan plan){
+ List clusterIdsByCapacity = clusterCapacityInfo.first();
+ if(vmProfile.getOwner() == null){
+ return clusterIdsByCapacity;
+ }
+ long accountId = vmProfile.getOwner().getAccountId();
+ Pair, Map> clusterIdsVmCountInfo = listClustersByUserDispersion(id, isZone, accountId);
+
+ //now we have 2 cluster lists - one ordered by capacity and the other by number of VMs for this account
+ //need to apply weights to these to find the correct ordering to follow
+
+ if(_userDispersionWeight == 1.0f){
+ List clusterIds = clusterIdsVmCountInfo.first();
+ clusterIds.retainAll(clusterIdsByCapacity);
+ return clusterIds;
+ }else{
+ //apply weights to the two lists
+ return orderByApplyingWeights(clusterCapacityInfo, clusterIdsVmCountInfo, accountId);
+ }
+
+
+ }
+
+ /**
+ * This method should reorder the given list of Pod Ids by applying any necessary heuristic
+ * for this planner
+ * For UserDispersingPlanner we need to order the pods by considering the number of VMs for this account
+ * @return List ordered list of Pod Ids
+ */
+ @Override
+ protected List reorderPods(Pair, Map> podCapacityInfo, VirtualMachineProfile extends VirtualMachine> vmProfile, DeploymentPlan plan){
+ List podIdsByCapacity = podCapacityInfo.first();
+ if(vmProfile.getOwner() == null){
+ return podIdsByCapacity;
+ }
+ long accountId = vmProfile.getOwner().getAccountId();
+
+ Pair, Map> podIdsVmCountInfo = listPodsByUserDispersion(plan.getDataCenterId(), accountId);
+
+ //now we have 2 pod lists - one ordered by capacity and the other by number of VMs for this account
+ //need to apply weights to these to find the correct ordering to follow
+
+ if(_userDispersionWeight == 1.0f){
+ List podIds = podIdsVmCountInfo.first();
+ podIds.retainAll(podIdsByCapacity);
+ return podIds;
+ }else{
+ //apply weights to the two lists
+ return orderByApplyingWeights(podCapacityInfo, podIdsVmCountInfo, accountId);
+ }
+
+ }
+
+ protected Pair, Map> listClustersByUserDispersion(long id, boolean isZone, long accountId){
+ if (s_logger.isDebugEnabled()) {
+ s_logger.debug("Applying Userdispersion heuristic to clusters for account: "+ accountId);
+ }
+ Pair, Map> clusterIdsVmCountInfo;
+ if(isZone){
+ clusterIdsVmCountInfo = _vmInstanceDao.listClusterIdsInZoneByVmCount(id, accountId);
+ }else{
+ clusterIdsVmCountInfo = _vmInstanceDao.listClusterIdsInPodByVmCount(id, accountId);
+ }
+ if (s_logger.isTraceEnabled()) {
+ s_logger.trace("List of clusters in ascending order of number of VMs: "+ clusterIdsVmCountInfo.first());
+ }
+ return clusterIdsVmCountInfo;
+ }
+
+ protected Pair, Map> listPodsByUserDispersion(long dataCenterId, long accountId) {
+ if (s_logger.isDebugEnabled()) {
+ s_logger.debug("Applying Userdispersion heuristic to pods for account: "+ accountId);
+ }
+ Pair, Map> podIdsVmCountInfo = _vmInstanceDao.listPodIdsInZoneByVmCount(dataCenterId, accountId);
+ if (s_logger.isTraceEnabled()) {
+ s_logger.trace("List of pods in ascending order of number of VMs: "+ podIdsVmCountInfo.first());
+ }
+
+ return podIdsVmCountInfo;
+ }
+
+
+ private List orderByApplyingWeights(Pair, Map> capacityInfo, Pair, Map> vmCountInfo, long accountId){
+ List capacityOrderedIds = capacityInfo.first();
+ List vmCountOrderedIds = vmCountInfo.first();
+ Map capacityMap = capacityInfo.second();
+ Map vmCountMap = vmCountInfo.second();
+
+ if (s_logger.isTraceEnabled()) {
+ s_logger.trace("Capacity Id list: "+ capacityOrderedIds + " , capacityMap:"+capacityMap);
+ }
+ if (s_logger.isTraceEnabled()) {
+ s_logger.trace("Vm Count Id list: "+ vmCountOrderedIds + " , vmCountMap:"+vmCountMap);
+ }
+
+
+ List idsReorderedByWeights = new ArrayList();
+ float capacityWeight = (1.0f -_userDispersionWeight);
+
+ if (s_logger.isDebugEnabled()) {
+ s_logger.debug("Applying userDispersionWeight: "+ _userDispersionWeight);
+ }
+ //normalize the vmCountMap
+ LinkedHashMap normalisedVmCountIdMap= new LinkedHashMap();
+
+ Long totalVmsOfAccount = _vmInstanceDao.countRunningByAccount(accountId);
+ if (s_logger.isDebugEnabled()) {
+ s_logger.debug("Total VMs for account: "+ totalVmsOfAccount);
+ }
+ for(Long id : vmCountOrderedIds){
+ Double normalisedCount = vmCountMap.get(id) / totalVmsOfAccount;
+ normalisedVmCountIdMap.put(id, normalisedCount);
+ }
+
+ //consider only those ids that are in capacity map.
+
+ SortedMap> sortedMap= new TreeMap>();
+ for(Long id : capacityOrderedIds){
+ Double weightedCapacityValue = capacityMap.get(id) * capacityWeight;
+ Double weightedVmCountValue = normalisedVmCountIdMap.get(id) * _userDispersionWeight;
+ Double totalWeight = weightedCapacityValue + weightedVmCountValue;
+ if(sortedMap.containsKey(totalWeight)){
+ List idList = sortedMap.get(totalWeight);
+ idList.add(id);
+ sortedMap.put(totalWeight, idList);
+ }else{
+ List idList = new ArrayList();
+ idList.add(id);
+ sortedMap.put(totalWeight, idList);
+ }
+ }
+
+ for(List idList : sortedMap.values()){
+ idsReorderedByWeights.addAll(idList);
+ }
+
+ if (s_logger.isTraceEnabled()) {
+ s_logger.trace("Reordered Id list: "+ idsReorderedByWeights);
+ }
+
+ return idsReorderedByWeights;
+ }
+
+
+ @Override
+ public boolean canHandle(VirtualMachineProfile extends VirtualMachine> vm, DeploymentPlan plan, ExcludeList avoid) {
+ if(vm.getHypervisorType() != HypervisorType.BareMetal){
+ //check the allocation strategy
+ if (_allocationAlgorithm != null && _allocationAlgorithm.equals(AllocationAlgorithm.userdispersing.toString())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ float _userDispersionWeight;
+
+
+ @Override
+ public boolean configure(String name, Map params) throws ConfigurationException {
+ super.configure(name, params);
+
+ String weight = _configDao.getValue(Config.VmUserDispersionWeight.key());
+ _userDispersionWeight = NumbersUtil.parseFloat(weight, 1.0f);
+
+
+ return true;
+ }
+
+}