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.
This commit is contained in:
Koushik Das 2016-12-02 23:08:32 +05:30
parent a2e65ac28c
commit 97ffe0711e
7 changed files with 129 additions and 4 deletions

View File

@ -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

View File

@ -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);

View File

@ -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;
@ -520,6 +523,11 @@ public class ImplicitPlannerTest {
return Mockito.mock(UserVmDao.class);
}
@Bean
public UserVmDetailsDao userVmDetailsDao() {
return Mockito.mock(UserVmDetailsDao.class);
}
@Bean
public VMInstanceDao vmInstanceDao() {
return Mockito.mock(VMInstanceDao.class);

View File

@ -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};
}
}

View File

@ -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");

View File

@ -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);
@ -382,6 +388,11 @@ public class DeploymentPlanningManagerImplTest {
return Mockito.mock(UserVmDao.class);
}
@Bean
public UserVmDetailsDao userVmDetailsDao() {
return Mockito.mock(UserVmDetailsDao.class);
}
@Bean
public VMInstanceDao vmInstanceDao() {
return Mockito.mock(VMInstanceDao.class);

View File

@ -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) {
@ -311,6 +370,11 @@ public class FirstFitPlannerTest {
return Mockito.mock(UserVmDao.class);
}
@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 {