Repository: cloudstack
Updated Branches:
refs/heads/master 57db64717 -> bc86baeaa
CLOUDSTACK-9650: Allow starting VMs regardless of cpu/memory cluster.disablethreshold setting
Introduced a global configuration flag 'cluster.threshold.enabled'. By default the flag is
true.
If the value is false, then a VM can be started in a cluster even if the cluster thresholds
are
crossed. However, for a new VM deployment the cluster threshold will always be honoured.
Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/97ffe071
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/97ffe071
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/97ffe071
Branch: refs/heads/master
Commit: 97ffe0711ec3abbadda01b1c92ca050fe4cac59e
Parents: a2e65ac
Author: Koushik Das <koushik@apache.org>
Authored: Fri Dec 2 23:08:32 2016 +0530
Committer: Koushik Das <koushik@apache.org>
Committed: Fri Dec 2 23:08:32 2016 +0530
----------------------------------------------------------------------
.../cloud/deploy/DeploymentClusterPlanner.java | 10 +++
.../com/cloud/vm/VirtualMachineManagerImpl.java | 7 ++
.../implicitplanner/ImplicitPlannerTest.java | 8 +++
.../src/com/cloud/deploy/FirstFitPlanner.java | 20 +++++-
server/src/com/cloud/vm/UserVmManagerImpl.java | 1 +
.../vm/DeploymentPlanningManagerImplTest.java | 11 +++
.../test/com/cloud/vm/FirstFitPlannerTest.java | 76 +++++++++++++++++++-
7 files changed, 129 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/97ffe071/api/src/com/cloud/deploy/DeploymentClusterPlanner.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/deploy/DeploymentClusterPlanner.java b/api/src/com/cloud/deploy/DeploymentClusterPlanner.java
index 6c09a6d..c2f0776 100644
--- a/api/src/com/cloud/deploy/DeploymentClusterPlanner.java
+++ b/api/src/com/cloud/deploy/DeploymentClusterPlanner.java
@@ -29,6 +29,7 @@ public interface DeploymentClusterPlanner extends DeploymentPlanner {
static final String ClusterCPUCapacityDisableThresholdCK = "cluster.cpu.allocated.capacity.disablethreshold";
static final String ClusterMemoryCapacityDisableThresholdCK = "cluster.memory.allocated.capacity.disablethreshold";
+ static final String ClusterThresholdEnabledCK = "cluster.threshold.enabled";
static final ConfigKey<Float> ClusterCPUCapacityDisableThreshold =
new ConfigKey<Float>(
@@ -46,6 +47,15 @@ public interface DeploymentClusterPlanner extends DeploymentPlanner {
"0.85",
"Percentage (as a value between 0 and 1) of memory utilization above which allocators
will disable using the cluster for low memory available. Keep the corresponding notification
threshold lower than this to be notified beforehand.",
true, ConfigKey.Scope.Cluster, null);
+ static final ConfigKey<Boolean> ClusterThresholdEnabled =
+ new ConfigKey<Boolean>(
+ "Advanced",
+ Boolean.class,
+ ClusterThresholdEnabledCK,
+ "true",
+ "Enable/Disable cluster thresholds. If disabled, an instance can start in a cluster
even though the threshold may be crossed.",
+ false,
+ ConfigKey.Scope.Zone);
/**
* This is called to determine list of possible clusters where a virtual
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/97ffe071/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java
----------------------------------------------------------------------
diff --git a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java
index 99693ba..e1ce697 100644
--- a/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java
+++ b/engine/orchestration/src/com/cloud/vm/VirtualMachineManagerImpl.java
@@ -1052,6 +1052,13 @@ public class VirtualMachineManagerImpl extends ManagerBase implements
VirtualMac
_resourceMgr.updateGPUDetails(destHostId, gpuDevice.getGroupDetails());
}
+ // Remove the information on whether it was a deploy vm request.The
deployvm=true information
+ // is set only when the vm is being deployed. When a vm is started
from a stop state the
+ // information isn't set,
+ if (_uservmDetailsDao.findDetail(vm.getId(), "deployvm") != null)
{
+ _uservmDetailsDao.removeDetail(vm.getId(), "deployvm");
+ }
+
startedVm = vm;
if (s_logger.isDebugEnabled()) {
s_logger.debug("Start completed for VM " + vm);
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/97ffe071/plugins/deployment-planners/implicit-dedication/test/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java
----------------------------------------------------------------------
diff --git a/plugins/deployment-planners/implicit-dedication/test/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java
b/plugins/deployment-planners/implicit-dedication/test/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java
index 754f36e..79cb1b4 100644
--- a/plugins/deployment-planners/implicit-dedication/test/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java
+++ b/plugins/deployment-planners/implicit-dedication/test/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java
@@ -94,6 +94,7 @@ import com.cloud.vm.UserVmVO;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachineProfileImpl;
import com.cloud.vm.dao.UserVmDao;
+import com.cloud.vm.dao.UserVmDetailsDao;
import com.cloud.vm.dao.VMInstanceDao;
@RunWith(SpringJUnit4ClassRunner.class)
@@ -121,6 +122,8 @@ public class ImplicitPlannerTest {
@Inject
UserVmDao vmDao;
@Inject
+ UserVmDetailsDao vmDetailsDao;
+ @Inject
VMInstanceDao vmInstanceDao;
@Inject
VolumeDao volsDao;
@@ -521,6 +524,11 @@ public class ImplicitPlannerTest {
}
@Bean
+ public UserVmDetailsDao userVmDetailsDao() {
+ return Mockito.mock(UserVmDetailsDao.class);
+ }
+
+ @Bean
public VMInstanceDao vmInstanceDao() {
return Mockito.mock(VMInstanceDao.class);
}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/97ffe071/server/src/com/cloud/deploy/FirstFitPlanner.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/deploy/FirstFitPlanner.java b/server/src/com/cloud/deploy/FirstFitPlanner.java
index 640834b..c3df417 100644
--- a/server/src/com/cloud/deploy/FirstFitPlanner.java
+++ b/server/src/com/cloud/deploy/FirstFitPlanner.java
@@ -66,6 +66,7 @@ import com.cloud.utils.component.AdapterBase;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineProfile;
import com.cloud.vm.dao.UserVmDao;
+import com.cloud.vm.dao.UserVmDetailsDao;
import com.cloud.vm.dao.VMInstanceDao;
public class FirstFitPlanner extends AdapterBase implements DeploymentClusterPlanner, Configurable,
DeploymentPlanner {
@@ -89,6 +90,8 @@ public class FirstFitPlanner extends AdapterBase implements DeploymentClusterPla
@Inject
protected UserVmDao vmDao;
@Inject
+ protected UserVmDetailsDao vmDetailsDao;
+ @Inject
protected VMInstanceDao vmInstanceDao;
@Inject
protected VolumeDao volsDao;
@@ -309,6 +312,16 @@ public class FirstFitPlanner extends AdapterBase implements DeploymentClusterPla
protected void removeClustersCrossingThreshold(List<Long> clusterListForVmAllocation,
ExcludeList avoid,
VirtualMachineProfile vmProfile, DeploymentPlan plan) {
+ // Check if cluster threshold for cpu/memory has to be checked or not. By default
we
+ // always check cluster threshold isn't crossed. However, the check may be skipped
for
+ // starting (not deploying) an instance.
+ VirtualMachine vm = vmProfile.getVirtualMachine();
+ Map<String, String> details = vmDetailsDao.listDetailsKeyPairs(vm.getId());
+ Boolean isThresholdEnabled = ClusterThresholdEnabled.value();
+ if (!(isThresholdEnabled || (details != null && details.containsKey("deployvm"))))
{
+ return;
+ }
+
List<Short> capacityList = getCapacitiesForCheckingThreshold();
List<Long> clustersCrossingThreshold = new ArrayList<Long>();
@@ -323,12 +336,13 @@ public class FirstFitPlanner extends AdapterBase implements DeploymentClusterPla
if (clusterListForVmAllocation == null || clusterListForVmAllocation.size() ==
0) {
return;
}
+
if (capacity == Capacity.CAPACITY_TYPE_CPU) {
clustersCrossingThreshold =
- capacityDao.listClustersCrossingThreshold(capacity, plan.getDataCenterId(), ClusterCPUCapacityDisableThreshold.key(),
cpu_requested);
+ capacityDao.listClustersCrossingThreshold(capacity, plan.getDataCenterId(),
ClusterCPUCapacityDisableThreshold.key(), cpu_requested);
} else if (capacity == Capacity.CAPACITY_TYPE_MEMORY) {
clustersCrossingThreshold =
- capacityDao.listClustersCrossingThreshold(capacity, plan.getDataCenterId(), ClusterMemoryCapacityDisableThreshold.key(),
ram_requested);
+ capacityDao.listClustersCrossingThreshold(capacity, plan.getDataCenterId(),
ClusterMemoryCapacityDisableThreshold.key(), ram_requested);
}
if (clustersCrossingThreshold != null && clustersCrossingThreshold.size()
!= 0) {
@@ -565,6 +579,6 @@ public class FirstFitPlanner extends AdapterBase implements DeploymentClusterPla
@Override
public ConfigKey<?>[] getConfigKeys() {
- return new ConfigKey<?>[] {ClusterCPUCapacityDisableThreshold, ClusterMemoryCapacityDisableThreshold};
+ return new ConfigKey<?>[] {ClusterCPUCapacityDisableThreshold, ClusterMemoryCapacityDisableThreshold,
ClusterThresholdEnabled};
}
}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/97ffe071/server/src/com/cloud/vm/UserVmManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java
index 17cbbde..d60b8c1 100644
--- a/server/src/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/com/cloud/vm/UserVmManagerImpl.java
@@ -3582,6 +3582,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager,
Vir
for (String key : customParameters.keySet()) {
vm.setDetail(key, customParameters.get(key));
}
+ vm.setDetail("deployvm", "true");
_vmDao.saveDetails(vm);
s_logger.debug("Allocating in the DB for vm");
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/97ffe071/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java
----------------------------------------------------------------------
diff --git a/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java b/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java
index 72960e3..2e87817 100644
--- a/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java
+++ b/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java
@@ -94,6 +94,7 @@ import com.cloud.storage.dao.VolumeDao;
import com.cloud.user.AccountManager;
import com.cloud.utils.component.ComponentContext;
import com.cloud.vm.dao.UserVmDao;
+import com.cloud.vm.dao.UserVmDetailsDao;
import com.cloud.vm.dao.VMInstanceDao;
@RunWith(SpringJUnit4ClassRunner.class)
@@ -130,6 +131,9 @@ public class DeploymentPlanningManagerImplTest {
@Inject
DedicatedResourceDao _dedicatedDao;
+ @Inject
+ UserVmDetailsDao vmDetailsDao;
+
private static long domainId = 5L;
private static long dataCenterId = 1L;
@@ -150,6 +154,8 @@ public class DeploymentPlanningManagerImplTest {
VMInstanceVO vm = new VMInstanceVO();
Mockito.when(vmProfile.getVirtualMachine()).thenReturn(vm);
+ Mockito.when(vmDetailsDao.listDetailsKeyPairs(Matchers.anyLong())).thenReturn(null);
+
Mockito.when(_dcDao.findById(Matchers.anyLong())).thenReturn(dc);
Mockito.when(dc.getId()).thenReturn(dataCenterId);
@@ -383,6 +389,11 @@ public class DeploymentPlanningManagerImplTest {
}
@Bean
+ public UserVmDetailsDao userVmDetailsDao() {
+ return Mockito.mock(UserVmDetailsDao.class);
+ }
+
+ @Bean
public VMInstanceDao vmInstanceDao() {
return Mockito.mock(VMInstanceDao.class);
}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/97ffe071/server/test/com/cloud/vm/FirstFitPlannerTest.java
----------------------------------------------------------------------
diff --git a/server/test/com/cloud/vm/FirstFitPlannerTest.java b/server/test/com/cloud/vm/FirstFitPlannerTest.java
index b7b103b..41dd8c0 100644
--- a/server/test/com/cloud/vm/FirstFitPlannerTest.java
+++ b/server/test/com/cloud/vm/FirstFitPlannerTest.java
@@ -32,7 +32,12 @@ import javax.inject.Inject;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
+import org.apache.cloudstack.framework.config.ConfigDepot;
+import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.framework.config.ScopedConfigStorage;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
+import org.apache.cloudstack.framework.config.impl.ConfigDepotImpl;
+import org.apache.cloudstack.framework.config.impl.ConfigurationVO;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.test.utils.SpringUtils;
import org.junit.After;
@@ -63,6 +68,7 @@ import com.cloud.dc.dao.ClusterDao;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.dc.dao.HostPodDao;
import com.cloud.deploy.DataCenterDeployment;
+import com.cloud.deploy.DeploymentClusterPlanner;
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
import com.cloud.deploy.FirstFitPlanner;
import com.cloud.exception.InsufficientServerCapacityException;
@@ -86,6 +92,7 @@ import com.cloud.user.AccountVO;
import com.cloud.utils.Pair;
import com.cloud.utils.component.ComponentContext;
import com.cloud.vm.dao.UserVmDao;
+import com.cloud.vm.dao.UserVmDetailsDao;
import com.cloud.vm.dao.VMInstanceDao;
@RunWith(SpringJUnit4ClassRunner.class)
@@ -101,6 +108,8 @@ public class FirstFitPlannerTest {
@Inject
UserVmDao vmDao;
@Inject
+ UserVmDetailsDao vmDetailsDao;
+ @Inject
ConfigurationDao configDao;
@Inject
CapacityDao capacityDao;
@@ -114,6 +123,10 @@ public class FirstFitPlannerTest {
HostGpuGroupsDao hostGpuGroupsDao;
@Inject
HostTagsDao hostTagsDao;
+ @Inject
+ ConfigDepotImpl configDepot;
+ @Inject
+ ScopedConfigStorage scopedStorage;
private static long domainId = 1L;
long dataCenterId = 1L;
@@ -126,6 +139,8 @@ public class FirstFitPlannerTest {
@Before
public void setUp() {
+ ConfigKey.init(configDepot);
+
when(configDao.getValue(Mockito.anyString())).thenReturn(null);
when(configDao.getValue(Config.ImplicitHostTags.key())).thenReturn("GPU");
@@ -138,6 +153,7 @@ public class FirstFitPlannerTest {
@After
public void tearDown() {
+ ConfigKey.init(null);
CallContext.unregister();
}
@@ -157,7 +173,50 @@ public class FirstFitPlannerTest {
reorderedClusterList.add(6L);
reorderedClusterList.add(2L);
- assertTrue("Reordered cluster list is not ownering the implict host tags", (clusterList.equals(reorderedClusterList)));
+ assertTrue("Reordered cluster list is not honoring the implict host tags", (clusterList.equals(reorderedClusterList)));
+ }
+
+ @Test
+ public void checkClusterReorderingForDeployVMWithThresholdCheckDisabled() throws InsufficientServerCapacityException
{
+ VirtualMachineProfileImpl vmProfile = mock(VirtualMachineProfileImpl.class);
+ DataCenterDeployment plan = mock(DataCenterDeployment.class);
+ ExcludeList avoids = mock(ExcludeList.class);
+ initializeForTest(vmProfile, plan, avoids);
+ List<Long> clustersCrossingThreshold = initializeForClusterThresholdDisabled();
+
+ Map<String, String> details = new HashMap<String, String>();
+ details.put("deployvm", "true");
+ when(vmDetailsDao.listDetailsKeyPairs(vmProfile.getVirtualMachine().getId())).thenReturn(details);
+
+ List<Long> clusterList = planner.orderClusters(vmProfile, plan, avoids);
+ assertTrue("Reordered cluster list have clusters exceeding threshold", (!clusterList.containsAll(clustersCrossingThreshold)));
+ }
+
+ @Test
+ public void checkClusterReorderingForStartVMWithThresholdCheckDisabled() throws InsufficientServerCapacityException
{
+ VirtualMachineProfileImpl vmProfile = mock(VirtualMachineProfileImpl.class);
+ DataCenterDeployment plan = mock(DataCenterDeployment.class);
+ ExcludeList avoids = mock(ExcludeList.class);
+ initializeForTest(vmProfile, plan, avoids);
+ List<Long> clustersCrossingThreshold = initializeForClusterThresholdDisabled();
+
+ List<Long> clusterList = planner.orderClusters(vmProfile, plan, avoids);
+ assertTrue("Reordered cluster list does not have clusters exceeding threshold", (clusterList.containsAll(clustersCrossingThreshold)));
+ }
+
+ private List<Long> initializeForClusterThresholdDisabled() {
+ when(configDepot.global()).thenReturn(configDao);
+
+ ConfigurationVO config = mock(ConfigurationVO.class);
+ when(config.getValue()).thenReturn(String.valueOf(false));
+ when(configDao.findById(DeploymentClusterPlanner.ClusterThresholdEnabled.key())).thenReturn(config);
+
+ List<Long> clustersCrossingThreshold = new ArrayList<Long>();
+ clustersCrossingThreshold.add(3L);
+ when(capacityDao.listClustersCrossingThreshold(
+ Mockito.anyShort(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyLong())).thenReturn(clustersCrossingThreshold);
+
+ return clustersCrossingThreshold;
}
private void initializeForTest(VirtualMachineProfileImpl vmProfile, DataCenterDeployment
plan, ExcludeList avoids) {
@@ -312,6 +371,11 @@ public class FirstFitPlannerTest {
}
@Bean
+ public UserVmDetailsDao userVmDetailsDao() {
+ return Mockito.mock(UserVmDetailsDao.class);
+ }
+
+ @Bean
public VMInstanceDao vmInstanceDao() {
return Mockito.mock(VMInstanceDao.class);
}
@@ -376,6 +440,16 @@ public class FirstFitPlannerTest {
return Mockito.mock(ResourceManager.class);
}
+ @Bean
+ public ConfigDepot configDepot() {
+ return Mockito.mock(ConfigDepotImpl.class);
+ }
+
+ @Bean
+ public ScopedConfigStorage configStorage() {
+ return Mockito.mock(ScopedConfigStorage.class);
+ }
+
public static class Library implements TypeFilter {
@Override
public boolean match(MetadataReader mdr, MetadataReaderFactory arg1) throws IOException
{
|