mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
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:
parent
a2e65ac28c
commit
97ffe0711e
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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};
|
||||
}
|
||||
}
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user