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 <[email protected]> Authored: Fri Dec 2 23:08:32 2016 +0530 Committer: Koushik Das <[email protected]> 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 {
