cloudstack/server/test/com/cloud/vm/FirstFitPlannerTest.java
Harikrishna Patnala f34469a41b CLOUDSTACK-9112: deployVM thread is holding the global lock on network longer and cause delays and some improvements in the planner
There are some VM deployment failures happening when multiple VMs are deployed at a time, failures mainly due to NetworkModel code that iterates over all the vlans in the pod. This causes each deployVM thread to hold the global lock on Network longer and cause delays. This delay in turn causes more threads to choose same host and fail since capacity is not available on that host.

Following are some changes required to be done to reduce delays during VM deployments which in turn causes some vm deployment failures when multiple VMs are launched at a time.

    In Planner, remove the clusters that do not contain a host with matching service offering tag. This will save some iterations over clusters that dont have matching tagged host
    In NetworkModel, do not query the vlans for the pod within the loop. Also optimized the logic to query the ip/ipv6
    In DeploymentPlanningManagerImpl, do not process the affinity group if the plan has hostId provided.
2017-03-15 15:21:29 +05:30

491 lines
20 KiB
Java

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with 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.
package com.cloud.vm;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import com.cloud.offering.ServiceOffering;
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;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Matchers;
import org.mockito.Mockito;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import com.cloud.capacity.Capacity;
import com.cloud.capacity.CapacityManager;
import com.cloud.capacity.dao.CapacityDao;
import com.cloud.configuration.Config;
import com.cloud.dc.ClusterDetailsDao;
import com.cloud.dc.DataCenterVO;
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;
import com.cloud.gpu.dao.HostGpuGroupsDao;
import com.cloud.host.Host;
import com.cloud.host.dao.HostDao;
import com.cloud.host.dao.HostTagsDao;
import com.cloud.resource.ResourceManager;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.service.dao.ServiceOfferingDao;
import com.cloud.service.dao.ServiceOfferingDetailsDao;
import com.cloud.storage.StorageManager;
import com.cloud.storage.dao.DiskOfferingDao;
import com.cloud.storage.dao.GuestOSCategoryDao;
import com.cloud.storage.dao.GuestOSDao;
import com.cloud.storage.dao.StoragePoolHostDao;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
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)
@ContextConfiguration(loader = AnnotationConfigContextLoader.class)
public class FirstFitPlannerTest {
@Inject
FirstFitPlanner planner = new FirstFitPlanner();
@Inject
DataCenterDao dcDao;
@Inject
ClusterDao clusterDao;
@Inject
UserVmDao vmDao;
@Inject
UserVmDetailsDao vmDetailsDao;
@Inject
ConfigurationDao configDao;
@Inject
CapacityDao capacityDao;
@Inject
AccountManager accountMgr;
@Inject
ServiceOfferingDao serviceOfferingDao;
@Inject
ServiceOfferingDetailsDao serviceOfferingDetailsDao;
@Inject
HostGpuGroupsDao hostGpuGroupsDao;
@Inject
HostTagsDao hostTagsDao;
@Inject
ConfigDepotImpl configDepot;
@Inject
ScopedConfigStorage scopedStorage;
@Inject
HostDao hostDao;
private static long domainId = 1L;
long dataCenterId = 1L;
long accountId = 1L;
long offeringId = 12L;
int noOfCpusInOffering = 1;
int cpuSpeedInOffering = 500;
int ramInOffering = 512;
AccountVO acct = new AccountVO(accountId);
@Before
public void setUp() {
ConfigKey.init(configDepot);
when(configDao.getValue(Mockito.anyString())).thenReturn(null);
when(configDao.getValue(Config.ImplicitHostTags.key())).thenReturn("GPU");
ComponentContext.initComponentsLifeCycle();
acct.setType(Account.ACCOUNT_TYPE_ADMIN);
acct.setAccountName("user1");
acct.setDomainId(domainId);
acct.setId(accountId);
}
@After
public void tearDown() {
ConfigKey.init(null);
CallContext.unregister();
}
@Test
public void checkClusterReorderingBasedOnImplicitHostTags() throws InsufficientServerCapacityException {
VirtualMachineProfileImpl vmProfile = mock(VirtualMachineProfileImpl.class);
DataCenterDeployment plan = mock(DataCenterDeployment.class);
ExcludeList avoids = mock(ExcludeList.class);
initializeForTest(vmProfile, plan, avoids);
List<Long> clusterList = planner.orderClusters(vmProfile, plan, avoids);
List<Long> reorderedClusterList = new ArrayList<Long>();
reorderedClusterList.add(4L);
reorderedClusterList.add(3L);
reorderedClusterList.add(1L);
reorderedClusterList.add(5L);
reorderedClusterList.add(6L);
reorderedClusterList.add(2L);
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 checkClusterListBasedOnHostTag() throws InsufficientServerCapacityException {
VirtualMachineProfileImpl vmProfile = mock(VirtualMachineProfileImpl.class);
DataCenterDeployment plan = mock(DataCenterDeployment.class);
ExcludeList avoids = mock(ExcludeList.class);
initializeForTest(vmProfile, plan, avoids);
List<Long> matchingClusters = initializeForClusterListBasedOnHostTag(vmProfile.getServiceOffering());
List<Long> clusterList = planner.orderClusters(vmProfile, plan, avoids);
assertTrue("Reordered cluster list have clusters which has hosts with specified host tag on offering", (clusterList.containsAll(matchingClusters)));
assertTrue("Reordered cluster list does not have clusters which dont have hosts with matching host tag on offering", (!clusterList.contains(2L)));
}
private List<Long> initializeForClusterListBasedOnHostTag(ServiceOffering offering) {
when(offering.getHostTag()).thenReturn("hosttag1");
initializeForClusterThresholdDisabled();
List<Long> matchingClusters = new ArrayList<>();
matchingClusters.add(3L);
matchingClusters.add(5L);
when(hostDao.listClustersByHostTag("hosttag1")).thenReturn(matchingClusters);
return matchingClusters;
}
@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) {
DataCenterVO mockDc = mock(DataCenterVO.class);
VMInstanceVO vm = mock(VMInstanceVO.class);
UserVmVO userVm = mock(UserVmVO.class);
ServiceOfferingVO offering = mock(ServiceOfferingVO.class);
AccountVO account = mock(AccountVO.class);
when(account.getId()).thenReturn(accountId);
when(account.getAccountId()).thenReturn(accountId);
when(vmProfile.getOwner()).thenReturn(account);
when(vmProfile.getVirtualMachine()).thenReturn(vm);
when(vmProfile.getId()).thenReturn(12L);
when(vmDao.findById(12L)).thenReturn(userVm);
when(userVm.getAccountId()).thenReturn(accountId);
when(vm.getDataCenterId()).thenReturn(dataCenterId);
when(dcDao.findById(1L)).thenReturn(mockDc);
when(avoids.shouldAvoid(mockDc)).thenReturn(false);
when(plan.getDataCenterId()).thenReturn(dataCenterId);
when(plan.getClusterId()).thenReturn(null);
when(plan.getPodId()).thenReturn(null);
// Mock offering details.
when(vmProfile.getServiceOffering()).thenReturn(offering);
when(offering.getId()).thenReturn(offeringId);
when(vmProfile.getServiceOfferingId()).thenReturn(offeringId);
when(offering.getCpu()).thenReturn(noOfCpusInOffering);
when(offering.getSpeed()).thenReturn(cpuSpeedInOffering);
when(offering.getRamSize()).thenReturn(ramInOffering);
List<Long> clustersWithEnoughCapacity = new ArrayList<Long>();
clustersWithEnoughCapacity.add(1L);
clustersWithEnoughCapacity.add(2L);
clustersWithEnoughCapacity.add(3L);
clustersWithEnoughCapacity.add(4L);
clustersWithEnoughCapacity.add(5L);
clustersWithEnoughCapacity.add(6L);
when(
capacityDao.listClustersInZoneOrPodByHostCapacities(dataCenterId, noOfCpusInOffering * cpuSpeedInOffering, ramInOffering * 1024L * 1024L,
Capacity.CAPACITY_TYPE_CPU, true)).thenReturn(clustersWithEnoughCapacity);
Map<Long, Double> clusterCapacityMap = new HashMap<Long, Double>();
clusterCapacityMap.put(1L, 2048D);
clusterCapacityMap.put(2L, 2048D);
clusterCapacityMap.put(3L, 2048D);
clusterCapacityMap.put(4L, 2048D);
clusterCapacityMap.put(5L, 2048D);
clusterCapacityMap.put(6L, 2048D);
Pair<List<Long>, Map<Long, Double>> clustersOrderedByCapacity = new Pair<List<Long>, Map<Long, Double>>(clustersWithEnoughCapacity, clusterCapacityMap);
when(capacityDao.orderClustersByAggregateCapacity(dataCenterId, Capacity.CAPACITY_TYPE_CPU, true)).thenReturn(clustersOrderedByCapacity);
List<Long> disabledClusters = new ArrayList<Long>();
List<Long> clustersWithDisabledPods = new ArrayList<Long>();
when(clusterDao.listDisabledClusters(dataCenterId, null)).thenReturn(disabledClusters);
when(clusterDao.listClustersWithDisabledPods(dataCenterId)).thenReturn(clustersWithDisabledPods);
List<Long> hostList0 = new ArrayList<Long>();
List<Long> hostList1 = new ArrayList<Long>();
List<Long> hostList2 = new ArrayList<Long>();
List<Long> hostList3 = new ArrayList<Long>();
List<Long> hostList4 = new ArrayList<Long>();
List<Long> hostList5 = new ArrayList<Long>();
List<Long> hostList6 = new ArrayList<Long>();
hostList0.add(new Long(1));
hostList1.add(new Long(10));
hostList2.add(new Long(11));
hostList3.add(new Long(12));
hostList4.add(new Long(13));
hostList5.add(new Long(14));
hostList6.add(new Long(15));
String[] implicitHostTags = {"GPU"};
int ramInBytes = ramInOffering * 1024 * 1024;
when(serviceOfferingDetailsDao.findDetail(Matchers.anyLong(), anyString())).thenReturn(null);
when(hostGpuGroupsDao.listHostIds()).thenReturn(hostList0);
when(capacityDao.listHostsWithEnoughCapacity(noOfCpusInOffering * cpuSpeedInOffering, ramInBytes, new Long(1), Host.Type.Routing.toString())).thenReturn(hostList1);
when(capacityDao.listHostsWithEnoughCapacity(noOfCpusInOffering * cpuSpeedInOffering, ramInBytes, new Long(2), Host.Type.Routing.toString())).thenReturn(hostList2);
when(capacityDao.listHostsWithEnoughCapacity(noOfCpusInOffering * cpuSpeedInOffering, ramInBytes, new Long(3), Host.Type.Routing.toString())).thenReturn(hostList3);
when(capacityDao.listHostsWithEnoughCapacity(noOfCpusInOffering * cpuSpeedInOffering, ramInBytes, new Long(4), Host.Type.Routing.toString())).thenReturn(hostList4);
when(capacityDao.listHostsWithEnoughCapacity(noOfCpusInOffering * cpuSpeedInOffering, ramInBytes, new Long(5), Host.Type.Routing.toString())).thenReturn(hostList5);
when(capacityDao.listHostsWithEnoughCapacity(noOfCpusInOffering * cpuSpeedInOffering, ramInBytes, new Long(6), Host.Type.Routing.toString())).thenReturn(hostList6);
when(hostTagsDao.getDistinctImplicitHostTags(hostList1, implicitHostTags)).thenReturn(Arrays.asList("abc", "pqr","xyz"));
when(hostTagsDao.getDistinctImplicitHostTags(hostList2, implicitHostTags)).thenReturn(Arrays.asList("abc", "123", "pqr", "456", "xyz"));
when(hostTagsDao.getDistinctImplicitHostTags(hostList3, implicitHostTags)).thenReturn(Arrays.asList("abc", "pqr"));
when(hostTagsDao.getDistinctImplicitHostTags(hostList4, implicitHostTags)).thenReturn(Arrays.asList("abc"));
when(hostTagsDao.getDistinctImplicitHostTags(hostList5, implicitHostTags)).thenReturn(Arrays.asList("abc", "pqr","xyz"));
when(hostTagsDao.getDistinctImplicitHostTags(hostList6, implicitHostTags)).thenReturn(Arrays.asList("abc", "123", "pqr","xyz"));
}
@Configuration
@ComponentScan(basePackageClasses = {FirstFitPlanner.class},
includeFilters = {@Filter(value = TestConfiguration.Library.class, type = FilterType.CUSTOM)},
useDefaultFilters = false)
public static class TestConfiguration extends SpringUtils.CloudStackTestConfiguration {
@Bean
public HostDao hostDao() {
return Mockito.mock(HostDao.class);
}
@Bean
public HostTagsDao hostTagsDao() {
return Mockito.mock(HostTagsDao.class);
}
@Bean
public HostGpuGroupsDao hostGpuGroupsDao() {
return Mockito.mock(HostGpuGroupsDao.class);
}
@Bean
public DataCenterDao dcDao() {
return Mockito.mock(DataCenterDao.class);
}
@Bean
public HostPodDao hostPodDao() {
return Mockito.mock(HostPodDao.class);
}
@Bean
public ClusterDao clusterDao() {
return Mockito.mock(ClusterDao.class);
}
@Bean
public GuestOSDao guestOsDao() {
return Mockito.mock(GuestOSDao.class);
}
@Bean
public GuestOSCategoryDao guestOsCategoryDao() {
return Mockito.mock(GuestOSCategoryDao.class);
}
@Bean
public DiskOfferingDao diskOfferingDao() {
return Mockito.mock(DiskOfferingDao.class);
}
@Bean
public StoragePoolHostDao storagePoolHostDao() {
return Mockito.mock(StoragePoolHostDao.class);
}
@Bean
public UserVmDao userVmDao() {
return Mockito.mock(UserVmDao.class);
}
@Bean
public UserVmDetailsDao userVmDetailsDao() {
return Mockito.mock(UserVmDetailsDao.class);
}
@Bean
public VMInstanceDao vmInstanceDao() {
return Mockito.mock(VMInstanceDao.class);
}
@Bean
public VolumeDao volumeDao() {
return Mockito.mock(VolumeDao.class);
}
@Bean
public CapacityManager capacityManager() {
return Mockito.mock(CapacityManager.class);
}
@Bean
public ConfigurationDao configurationDao() {
return Mockito.mock(ConfigurationDao.class);
}
@Bean
public PrimaryDataStoreDao primaryDataStoreDao() {
return Mockito.mock(PrimaryDataStoreDao.class);
}
@Bean
public CapacityDao capacityDao() {
return Mockito.mock(CapacityDao.class);
}
@Bean
public AccountManager accountManager() {
return Mockito.mock(AccountManager.class);
}
@Bean
public StorageManager storageManager() {
return Mockito.mock(StorageManager.class);
}
@Bean
public DataStoreManager dataStoreManager() {
return Mockito.mock(DataStoreManager.class);
}
@Bean
public ClusterDetailsDao clusterDetailsDao() {
return Mockito.mock(ClusterDetailsDao.class);
}
@Bean
public ServiceOfferingDao serviceOfferingDao() {
return Mockito.mock(ServiceOfferingDao.class);
}
@Bean
public ServiceOfferingDetailsDao serviceOfferingDetailsDao() {
return Mockito.mock(ServiceOfferingDetailsDao.class);
}
@Bean
public ResourceManager resourceManager() {
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 {
ComponentScan cs = TestConfiguration.class.getAnnotation(ComponentScan.class);
return SpringUtils.includedInBasePackageClasses(mdr.getClassMetadata().getClassName(), cs);
}
}
}
}