Refactor of Allocator classes (#9074)

* Refactoring Allocator classes

* Break into smaller methods random and firfit allocators.

* Added unit tests for random and firstfit allocators

* Move random allocator from cloud-plugins to cloud-server

* Add BaseAllocator abstract class for duplicate code

* Add missing license

* Add missing license to unit test file

* Remove host allocator random dependency

* Change exception message on smoke tests

* Remove conditional as it was never actually reached in the original flow

* Fix tests

* Fix flipped parameters

* Fix NPE while listing hosts for migration when suitableHosts is null

* Remove unnecessary stubbings

* Fix checkstyle

* Remove unnecessary file

* Rename exception error messages

* Apply suggestions from code review

Co-authored-by: Fabricio Duarte <fabricio.duarte.jr@gmail.com>

* Rename UserVmDetailVO references to VMInstanceDetailVO

* Remove unused imports

* Add new line at EOF

* Remove unnecessary random allocator pom

* Fix GPU allocation mistake

* Fix failing tests

---------

Co-authored-by: Fabricio Duarte <fabricio.duarte@scclouds.com.br>
Co-authored-by: Fabricio Duarte <fabricio.duarte.jr@gmail.com>
This commit is contained in:
Bryan Lima 2026-04-30 15:30:02 +02:00 committed by GitHub
parent a73cc9a22c
commit c45596cca3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 1615 additions and 1003 deletions

View File

@ -22,19 +22,11 @@ import com.cloud.deploy.DeploymentPlan;
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
import com.cloud.host.Host;
import com.cloud.host.Host.Type;
import com.cloud.offering.ServiceOffering;
import com.cloud.utils.component.Adapter;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineProfile;
public interface HostAllocator extends Adapter {
/**
* @param UserVm vm
* @param ServiceOffering offering
**/
boolean isVirtualMachineUpgradable(final VirtualMachine vm, final ServiceOffering offering);
/**
* Determines which physical hosts are suitable to
* allocate the guest virtual machines on
@ -49,31 +41,6 @@ public interface HostAllocator extends Adapter {
public List<Host> allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan, Type type, ExcludeList avoid, int returnUpTo);
/**
* Determines which physical hosts are suitable to allocate the guest
* virtual machines on
*
* Allocators must set any other hosts not considered for allocation in the
* ExcludeList avoid. Thus the avoid set and the list of hosts suitable,
* together must cover the entire host set in the cluster.
*
* @param VirtualMachineProfile
* vmProfile
* @param DeploymentPlan
* plan
* @param GuestType
* type
* @param ExcludeList
* avoid
* @param int returnUpTo (use -1 to return all possible hosts)
* @param boolean considerReservedCapacity (default should be true, set to
* false if host capacity calculation should not look at reserved
* capacity)
* @return List<Host> List of hosts that are suitable for VM allocation
**/
public List<Host> allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan, Type type, ExcludeList avoid, int returnUpTo, boolean considerReservedCapacity);
/**
* Determines which physical hosts are suitable to allocate the guest
* virtual machines on

View File

@ -70,7 +70,7 @@ public interface DeploymentPlanner extends Adapter {
boolean canHandle(VirtualMachineProfile vm, DeploymentPlan plan, ExcludeList avoid);
public enum AllocationAlgorithm {
random, firstfit, userdispersing;
random, firstfit, userdispersing, firstfitleastconsumed;
}
public enum PlannerResourceUsage {

View File

@ -78,7 +78,7 @@ public class FindHostsForMigrationCmd extends BaseListCmd {
for (Host host : result.first()) {
HostForMigrationResponse hostResponse = _responseGenerator.createHostForMigrationResponse(host);
Boolean suitableForMigration = false;
if (hostsWithCapacity.contains(host)) {
if (hostsWithCapacity != null && hostsWithCapacity.contains(host)) {
suitableForMigration = true;
}
hostResponse.setSuitableForMigration(suitableForMigration);

View File

@ -252,7 +252,7 @@ public class ListHostsCmd extends BaseListCmd {
for (Host host : result.first()) {
HostResponse hostResponse = _responseGenerator.createHostResponse(host, getDetails());
Boolean suitableForMigration = false;
if (hostsWithCapacity.contains(host)) {
if (hostsWithCapacity != null && hostsWithCapacity.contains(host)) {
suitableForMigration = true;
}
hostResponse.setSuitableForMigration(suitableForMigration);

View File

@ -377,11 +377,6 @@
<artifactId>cloud-plugin-explicit-dedication</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-plugin-host-allocator-random</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-plugin-outofbandmanagement-driver-ipmitool</artifactId>

View File

@ -181,15 +181,6 @@ public interface VirtualMachineManager extends Manager {
void advanceReboot(String vmUuid, Map<VirtualMachineProfile.Param, Object> params) throws InsufficientCapacityException, ResourceUnavailableException,
ConcurrentOperationException, OperationTimedoutException;
/**
* Check to see if a virtual machine can be upgraded to the given service offering
*
* @param vm
* @param offering
* @return true if the host can handle the upgrade, false otherwise
*/
boolean isVirtualMachineUpgradable(final VirtualMachine vm, final ServiceOffering offering);
VirtualMachine findById(long vmId);
void storageMigration(String vmUuid, Map<Long, Long> volumeToPool);

View File

@ -4017,19 +4017,6 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
}
}
@Override
public boolean isVirtualMachineUpgradable(final VirtualMachine vm, final ServiceOffering offering) {
boolean isMachineUpgradable = true;
for (final HostAllocator allocator : hostAllocators) {
isMachineUpgradable = allocator.isVirtualMachineUpgradable(vm, offering);
if (!isMachineUpgradable) {
break;
}
}
return isMachineUpgradable;
}
@Override
public void reboot(final String vmUuid, final Map<VirtualMachineProfile.Param, Object> params) throws InsufficientCapacityException, ResourceUnavailableException {
try {
@ -4469,11 +4456,6 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
throw new InvalidParameterValueException("isSystem property is different for current service offering and new service offering");
}
if (!isVirtualMachineUpgradable(vmInstance, newServiceOffering)) {
throw new InvalidParameterValueException("Unable to upgrade virtual machine, not enough resources available " + "for an offering of " +
newServiceOffering.getCpu() + " cpu(s) at " + newServiceOffering.getSpeed() + " Mhz, and " + newServiceOffering.getRamSize() + " MB of memory");
}
final List<String> currentTags = StringUtils.csvTagsToList(currentDiskOffering.getTags());
final List<String> newTags = StringUtils.csvTagsToList(newDiskOffering.getTags());
if (VolumeApiServiceImpl.MatchStoragePoolTagsWithDiskOffering.valueIn(vmInstance.getDataCenterId())) {

View File

@ -218,7 +218,7 @@ public interface HostDao extends GenericDao<HostVO, Long>, StateDao<Status, Stat
*/
List<String> listOrderedHostsHypervisorVersionsInDatacenter(long datacenterId, HypervisorType hypervisorType);
List<HostVO> findHostsWithTagRuleThatMatchComputeOferringTags(String computeOfferingTags);
List<HostVO> findHostsWithTagRuleThatMatchComputeOfferingTags(String computeOfferingTags);
List<Long> findClustersThatMatchHostTagRule(String computeOfferingTags);

View File

@ -1520,7 +1520,7 @@ public class HostDaoImpl extends GenericDaoBase<HostVO, Long> implements HostDao
}
}
public List<HostVO> findHostsWithTagRuleThatMatchComputeOferringTags(String computeOfferingTags) {
public List<HostVO> findHostsWithTagRuleThatMatchComputeOfferingTags(String computeOfferingTags) {
List<HostTagVO> hostTagVOList = _hostTagsDao.findHostRuleTags();
List<HostVO> result = new ArrayList<>();
for (HostTagVO rule: hostTagVOList) {
@ -1534,7 +1534,7 @@ public class HostDaoImpl extends GenericDaoBase<HostVO, Long> implements HostDao
public List<Long> findClustersThatMatchHostTagRule(String computeOfferingTags) {
Set<Long> result = new HashSet<>();
List<HostVO> hosts = findHostsWithTagRuleThatMatchComputeOferringTags(computeOfferingTags);
List<HostVO> hosts = findHostsWithTagRuleThatMatchComputeOfferingTags(computeOfferingTags);
for (HostVO host: hosts) {
result.add(host.getClusterId());
}

View File

@ -1,30 +0,0 @@
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-plugin-host-allocator-random</artifactId>
<name>Apache CloudStack Plugin - Host Allocator Random</name>
<parent>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloudstack-plugins</artifactId>
<version>4.23.0.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
</project>

View File

@ -1,196 +0,0 @@
// 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.agent.manager.allocator.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.inject.Inject;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.ListUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import com.cloud.agent.manager.allocator.HostAllocator;
import com.cloud.capacity.CapacityManager;
import com.cloud.dc.ClusterDetailsDao;
import com.cloud.dc.dao.ClusterDao;
import com.cloud.deploy.DeploymentPlan;
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
import com.cloud.host.Host;
import com.cloud.host.Host.Type;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
import com.cloud.offering.ServiceOffering;
import com.cloud.resource.ResourceManager;
import com.cloud.storage.VMTemplateVO;
import com.cloud.utils.Pair;
import com.cloud.utils.component.AdapterBase;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineProfile;
@Component
public class RandomAllocator extends AdapterBase implements HostAllocator {
@Inject
private HostDao _hostDao;
@Inject
private ResourceManager _resourceMgr;
@Inject
private ClusterDao clusterDao;
@Inject
private ClusterDetailsDao clusterDetailsDao;
@Inject
private CapacityManager capacityManager;
protected List<HostVO> listHostsByTags(Host.Type type, long dcId, Long podId, Long clusterId, String offeringHostTag, String templateTag) {
List<HostVO> taggedHosts = new ArrayList<>();
if (offeringHostTag != null) {
taggedHosts.addAll(_hostDao.listByHostTag(type, clusterId, podId, dcId, offeringHostTag));
}
if (templateTag != null) {
List<HostVO> templateTaggedHosts = _hostDao.listByHostTag(type, clusterId, podId, dcId, templateTag);
if (taggedHosts.isEmpty()) {
taggedHosts = templateTaggedHosts;
} else {
taggedHosts.retainAll(templateTaggedHosts);
}
}
if (logger.isDebugEnabled()) {
logger.debug(String.format("Found %d hosts %s with type: %s, zone ID: %d, pod ID: %d, cluster ID: %s, offering host tag(s): %s, template tag: %s",
taggedHosts.size(),
(taggedHosts.isEmpty() ? "" : String.format("(%s)", StringUtils.join(taggedHosts.stream().map(HostVO::toString).toArray(), ","))),
type.name(), dcId, podId, clusterId, offeringHostTag, templateTag));
}
return taggedHosts;
}
private List<Host> findSuitableHosts(VirtualMachineProfile vmProfile, DeploymentPlan plan, Type type,
ExcludeList avoid, List<? extends Host> hosts, int returnUpTo,
boolean considerReservedCapacity) {
long dcId = plan.getDataCenterId();
Long podId = plan.getPodId();
Long clusterId = plan.getClusterId();
ServiceOffering offering = vmProfile.getServiceOffering();
List<? extends Host> hostsCopy = null;
List<Host> suitableHosts = new ArrayList<>();
if (type == Host.Type.Storage) {
return suitableHosts;
}
String offeringHostTag = offering.getHostTag();
VMTemplateVO template = (VMTemplateVO)vmProfile.getTemplate();
String templateTag = template.getTemplateTag();
String hostTag = null;
if (ObjectUtils.anyNotNull(offeringHostTag, templateTag)) {
hostTag = ObjectUtils.allNotNull(offeringHostTag, templateTag) ?
String.format("%s, %s", offeringHostTag, templateTag) :
ObjectUtils.firstNonNull(offeringHostTag, templateTag);
logger.debug("Looking for hosts in dc [{}], pod [{}], cluster [{}] and complying with host tag(s): [{}]", dcId, podId, clusterId, hostTag);
} else {
logger.debug("Looking for hosts in dc: {} pod: {} cluster: {}", dcId , podId, clusterId);
}
if (hosts != null) {
// retain all computing hosts, regardless of whether they support routing...it's random after all
hostsCopy = new ArrayList<>(hosts);
if (ObjectUtils.anyNotNull(offeringHostTag, templateTag)) {
hostsCopy.retainAll(listHostsByTags(type, dcId, podId, clusterId, offeringHostTag, templateTag));
} else {
hostsCopy.retainAll(_hostDao.listAllHostsThatHaveNoRuleTag(type, clusterId, podId, dcId));
}
} else {
// list all computing hosts, regardless of whether they support routing...it's random after all
if (offeringHostTag != null) {
hostsCopy = listHostsByTags(type, dcId, podId, clusterId, offeringHostTag, templateTag);
} else {
hostsCopy = _hostDao.listAllHostsThatHaveNoRuleTag(type, clusterId, podId, dcId);
}
}
hostsCopy = ListUtils.union(hostsCopy, _hostDao.findHostsWithTagRuleThatMatchComputeOferringTags(offeringHostTag));
if (hostsCopy.isEmpty()) {
logger.info("No suitable host found for VM [{}] in {}.", vmProfile, hostTag);
return null;
}
logger.debug("Random Allocator found {} hosts", hostsCopy.size());
if (hostsCopy.isEmpty()) {
return suitableHosts;
}
Collections.shuffle(hostsCopy);
for (Host host : hostsCopy) {
if (suitableHosts.size() == returnUpTo) {
break;
}
if (avoid.shouldAvoid(host)) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("Host %s is in avoid set, skipping this and trying other available hosts", host));
}
continue;
}
Pair<Boolean, Boolean> cpuCapabilityAndCapacity = capacityManager.checkIfHostHasCpuCapabilityAndCapacity(host, offering, considerReservedCapacity);
if (!cpuCapabilityAndCapacity.first() || !cpuCapabilityAndCapacity.second()) {
if (logger.isDebugEnabled()) {
logger.debug(String.format("Not using host %s; host has cpu capability? %s, host has capacity? %s", host, cpuCapabilityAndCapacity.first(), cpuCapabilityAndCapacity.second()));
}
continue;
}
if (logger.isDebugEnabled()) {
logger.debug(String.format("Found a suitable host, adding to list: %s", host));
}
suitableHosts.add(host);
}
if (logger.isDebugEnabled()) {
logger.debug("Random Host Allocator returning " + suitableHosts.size() + " suitable hosts");
}
return suitableHosts;
}
@Override
public List<Host> allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan, Type type, ExcludeList avoid, int returnUpTo) {
return allocateTo(vmProfile, plan, type, avoid, returnUpTo, true);
}
@Override
public List<Host> allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan, Type type,
ExcludeList avoid, List<? extends Host> hosts, int returnUpTo,
boolean considerReservedCapacity) {
if (CollectionUtils.isEmpty(hosts)) {
if (logger.isDebugEnabled()) {
logger.debug("Random Allocator found 0 hosts as given host list is empty");
}
return new ArrayList<>();
}
return findSuitableHosts(vmProfile, plan, type, avoid, hosts, returnUpTo, considerReservedCapacity);
}
@Override
public List<Host> allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan,
Type type, ExcludeList avoid, int returnUpTo, boolean considerReservedCapacity) {
return findSuitableHosts(vmProfile, plan, type, avoid, null, returnUpTo, considerReservedCapacity);
}
@Override
public boolean isVirtualMachineUpgradable(VirtualMachine vm, ServiceOffering offering) {
// currently we do no special checks to rule out a VM being upgradable to an offering, so
// return true
return true;
}
}

View File

@ -1,18 +0,0 @@
# 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.
name=host-allocator-random
parent=allocator

View File

@ -1,34 +0,0 @@
<!--
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.
-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"
>
<bean id="randomAllocator"
class="com.cloud.agent.manager.allocator.impl.RandomAllocator" />
</beans>

View File

@ -1,80 +0,0 @@
// 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.agent.manager.allocator.impl;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.collections.CollectionUtils;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import com.cloud.host.Host;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
@RunWith(MockitoJUnitRunner.class)
public class RandomAllocatorTest {
@Mock
HostDao hostDao;
@InjectMocks
RandomAllocator randomAllocator;
@Test
public void testListHostsByTags() {
Host.Type type = Host.Type.Routing;
Long id = 1L;
String templateTag = "tag1";
String offeringTag = "tag2";
HostVO host1 = Mockito.mock(HostVO.class);
HostVO host2 = Mockito.mock(HostVO.class);
Mockito.when(hostDao.listByHostTag(type, id, id, id, offeringTag)).thenReturn(List.of(host1, host2));
// No template tagged host
Mockito.when(hostDao.listByHostTag(type, id, id, id, templateTag)).thenReturn(new ArrayList<>());
List<HostVO> result = randomAllocator.listHostsByTags(type, id, id, id, offeringTag, templateTag);
Assert.assertTrue(CollectionUtils.isEmpty(result));
// Different template tagged host
HostVO host3 = Mockito.mock(HostVO.class);
Mockito.when(hostDao.listByHostTag(type, id, id, id, templateTag)).thenReturn(List.of(host3));
result = randomAllocator.listHostsByTags(type, id, id, id, offeringTag, templateTag);
Assert.assertTrue(CollectionUtils.isEmpty(result));
// Matching template tagged host
Mockito.when(hostDao.listByHostTag(type, id, id, id, templateTag)).thenReturn(List.of(host1));
result = randomAllocator.listHostsByTags(type, id, id, id, offeringTag, templateTag);
Assert.assertFalse(CollectionUtils.isEmpty(result));
Assert.assertEquals(1, result.size());
// No template tag
result = randomAllocator.listHostsByTags(type, id, id, id, offeringTag, null);
Assert.assertFalse(CollectionUtils.isEmpty(result));
Assert.assertEquals(2, result.size());
// No offering tag
result = randomAllocator.listHostsByTags(type, id, id, id, null, templateTag);
Assert.assertFalse(CollectionUtils.isEmpty(result));
Assert.assertEquals(1, result.size());
}
}

View File

@ -84,8 +84,6 @@
<module>ha-planners/skip-heurestics</module>
<module>host-allocators/random</module>
<module>hypervisors/baremetal</module>
<module>hypervisors/external</module>
<module>hypervisors/hyperv</module>

View File

@ -0,0 +1,90 @@
// 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.agent.manager.allocator.impl;
import com.cloud.agent.manager.allocator.HostAllocator;
import com.cloud.capacity.CapacityManager;
import com.cloud.host.Host;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
import com.cloud.offering.ServiceOffering;
import com.cloud.utils.Pair;
import com.cloud.utils.component.AdapterBase;
import org.apache.commons.collections.CollectionUtils;
import javax.inject.Inject;
import java.util.List;
public abstract class BaseAllocator extends AdapterBase implements HostAllocator {
@Inject
protected HostDao hostDao;
@Inject
protected CapacityManager capacityManager;
protected void retainHostsMatchingServiceOfferingAndTemplateTags(List<HostVO> availableHosts, Host.Type type, long dcId, Long podId, Long clusterId, String offeringHostTag, String templateTag) {
logger.debug("Hosts {} will be checked for template and host tags compatibility.", availableHosts);
if (offeringHostTag != null) {
logger.debug("Looking for hosts having the tag [{}] specified in the Service Offering.", offeringHostTag);
List<HostVO> hostsWithHostTag = hostDao.listByHostTag(type, clusterId, podId, dcId, offeringHostTag);
logger.debug("Retaining hosts {} because they match the offering host tag {}.", hostsWithHostTag, offeringHostTag);
availableHosts.retainAll(hostsWithHostTag);
}
if (templateTag != null) {
logger.debug("Looking for hosts having the tag [{}] specified in the Template.", templateTag);
List<HostVO> hostsWithTemplateTag = hostDao.listByHostTag(type, clusterId, podId, dcId, templateTag);
logger.debug("Retaining hosts {} because they match the template tag {}.", hostsWithTemplateTag, templateTag);
availableHosts.retainAll(hostsWithTemplateTag);
}
logger.debug("Remaining hosts after template tag and host tags validations are {}.", availableHosts);
}
protected void addHostsBasedOnTagRules(String hostTagOnOffering, List<HostVO> clusterHosts) {
List<HostVO> hostsWithTagRules = hostDao.findHostsWithTagRuleThatMatchComputeOfferingTags(hostTagOnOffering);
if (CollectionUtils.isEmpty(hostsWithTagRules)) {
logger.info("No hosts found with tag rules matching the compute offering tag [{}].", hostTagOnOffering);
return;
}
logger.info("Found hosts {} with tag rules matching the compute offering tag [{}].", hostsWithTagRules, hostTagOnOffering);
clusterHosts.addAll(hostsWithTagRules);
}
/**
* Adds hosts with enough CPU capability and enough CPU capacity to the suitable hosts list.
*/
protected boolean hostHasCpuCapabilityAndCapacity(boolean considerReservedCapacity, ServiceOffering offering, Host host) {
logger.debug("Looking for CPU frequency {} MHz and RAM {} MB.", () -> offering.getCpu() * offering.getSpeed(), offering::getRamSize);
Pair<Boolean, Boolean> cpuCapabilityAndCapacity = capacityManager.checkIfHostHasCpuCapabilityAndCapacity(host, offering, considerReservedCapacity);
Boolean hasCpuCapability = cpuCapabilityAndCapacity.first();
Boolean hasCpuCapacity = cpuCapabilityAndCapacity.second();
if (hasCpuCapability && hasCpuCapacity) {
logger.debug("Host {} is a suitable host as it has enough CPU capability and CPU capacity.", () -> host);
return true;
}
logger.debug("Cannot use host {}. Does the host have CPU capability? {}. Does the host have CPU capacity? {}..", () -> host, () -> hasCpuCapability, () -> hasCpuCapacity);
return false;
}
}

View File

@ -16,6 +16,41 @@
// under the License.
package com.cloud.agent.manager.allocator.impl;
import static com.cloud.deploy.DeploymentPlanner.AllocationAlgorithm.firstfitleastconsumed;
import static com.cloud.deploy.DeploymentPlanner.AllocationAlgorithm.random;
import static com.cloud.deploy.DeploymentPlanner.AllocationAlgorithm.userdispersing;
import com.cloud.capacity.Capacity;
import com.cloud.capacity.CapacityVO;
import com.cloud.capacity.dao.CapacityDao;
import com.cloud.configuration.Config;
import com.cloud.configuration.ConfigurationManager;
import com.cloud.deploy.DeploymentClusterPlanner;
import com.cloud.deploy.DeploymentPlan;
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
import com.cloud.deploy.FirstFitPlanner;
import com.cloud.gpu.GPU;
import com.cloud.host.DetailVO;
import com.cloud.host.Host;
import com.cloud.host.Host.Type;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDetailsDao;
import com.cloud.offering.ServiceOffering;
import com.cloud.resource.ResourceManager;
import com.cloud.service.dao.ServiceOfferingDetailsDao;
import com.cloud.storage.GuestOSCategoryVO;
import com.cloud.storage.GuestOSVO;
import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.dao.GuestOSCategoryDao;
import com.cloud.storage.dao.GuestOSDao;
import com.cloud.user.Account;
import com.cloud.utils.Pair;
import com.cloud.utils.StringUtils;
import com.cloud.vm.VMInstanceDetailVO;
import com.cloud.vm.VirtualMachineProfile;
import com.cloud.vm.dao.VMInstanceDao;
import com.cloud.vm.dao.VMInstanceDetailsDao;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
@ -29,59 +64,19 @@ import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.gpu.dao.VgpuProfileDao;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import org.springframework.stereotype.Component;
import com.cloud.agent.manager.allocator.HostAllocator;
import com.cloud.capacity.Capacity;
import com.cloud.capacity.CapacityManager;
import com.cloud.capacity.CapacityVO;
import com.cloud.capacity.dao.CapacityDao;
import com.cloud.configuration.Config;
import com.cloud.configuration.ConfigurationManager;
import com.cloud.dc.ClusterDetailsDao;
import com.cloud.dc.dao.ClusterDao;
import com.cloud.deploy.DeploymentPlan;
import com.cloud.deploy.DeploymentClusterPlanner;
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
import com.cloud.deploy.FirstFitPlanner;
import com.cloud.gpu.GPU;
import com.cloud.host.DetailVO;
import com.cloud.host.Host;
import com.cloud.host.Host.Type;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
import com.cloud.host.dao.HostDetailsDao;
import com.cloud.offering.ServiceOffering;
import com.cloud.resource.ResourceManager;
import com.cloud.service.ServiceOfferingDetailsVO;
import com.cloud.service.dao.ServiceOfferingDetailsDao;
import com.cloud.storage.GuestOSCategoryVO;
import com.cloud.storage.GuestOSVO;
import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.dao.GuestOSCategoryDao;
import com.cloud.storage.dao.GuestOSDao;
import com.cloud.user.Account;
import com.cloud.utils.Pair;
import com.cloud.utils.component.AdapterBase;
import com.cloud.vm.VMInstanceDetailVO;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineProfile;
import com.cloud.vm.dao.VMInstanceDetailsDao;
import com.cloud.vm.dao.VMInstanceDao;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
/**
* An allocator that tries to find a fit on a computing host. This allocator does not care whether or not the host supports routing.
*/
@Component
public class FirstFitAllocator extends AdapterBase implements HostAllocator {
@Inject
protected HostDao _hostDao = null;
public class FirstFitAllocator extends BaseAllocator {
@Inject
HostDetailsDao _hostDetailsDao = null;
@Inject
@ -95,289 +90,183 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator {
@Inject
protected ResourceManager _resourceMgr;
@Inject
ClusterDao _clusterDao;
@Inject
ClusterDetailsDao _clusterDetailsDao;
@Inject
ServiceOfferingDetailsDao _serviceOfferingDetailsDao;
@Inject
CapacityManager _capacityMgr;
@Inject
CapacityDao _capacityDao;
@Inject
VMInstanceDetailsDao _vmInstanceDetailsDao;
@Inject
private VgpuProfileDao vgpuProfileDao;
VMInstanceDetailsDao vmInstanceDetailsDao;
boolean _checkHvm = true;
static DecimalFormat decimalFormat = new DecimalFormat("#.##");
@Override
public List<Host> allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan, Type type, ExcludeList avoid, int returnUpTo) {
return allocateTo(vmProfile, plan, type, avoid, returnUpTo, true);
}
@Override
public List<Host> allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan, Type type, ExcludeList avoid, int returnUpTo, boolean considerReservedCapacity) {
long dcId = plan.getDataCenterId();
Long podId = plan.getPodId();
Long clusterId = plan.getClusterId();
ServiceOffering offering = vmProfile.getServiceOffering();
VMTemplateVO template = (VMTemplateVO)vmProfile.getTemplate();
Account account = vmProfile.getOwner();
boolean isVMDeployedWithUefi = false;
VMInstanceDetailVO vmInstanceDetailVO = _vmInstanceDetailsDao.findDetail(vmProfile.getId(), "UEFI");
if(vmInstanceDetailVO != null){
if ("secure".equalsIgnoreCase(vmInstanceDetailVO.getValue()) || "legacy".equalsIgnoreCase(vmInstanceDetailVO.getValue())) {
isVMDeployedWithUefi = true;
}
}
logger.info(" Guest VM is requested with Custom[UEFI] Boot Type "+ isVMDeployedWithUefi);
if (type == Host.Type.Storage) {
// FirstFitAllocator should be used for user VMs only since it won't care whether the host is capable of routing or not
return new ArrayList<>();
}
String paramAsStringToLog = String.format("zone [%s], pod [%s], cluster [%s]", dcId, podId, clusterId);
logger.debug("Looking for hosts in {}", paramAsStringToLog);
String hostTagOnOffering = offering.getHostTag();
String hostTagOnTemplate = template.getTemplateTag();
String hostTagUefi = "UEFI";
boolean hasSvcOfferingTag = hostTagOnOffering != null ? true : false;
boolean hasTemplateTag = hostTagOnTemplate != null ? true : false;
List<HostVO> clusterHosts = new ArrayList<>();
List<HostVO> hostsMatchingUefiTag = new ArrayList<>();
if(isVMDeployedWithUefi){
hostsMatchingUefiTag = _hostDao.listByHostCapability(type, clusterId, podId, dcId, Host.HOST_UEFI_ENABLE);
if (logger.isDebugEnabled()) {
logger.debug("Hosts with tag '" + hostTagUefi + "' are:" + hostsMatchingUefiTag);
}
}
String haVmTag = (String)vmProfile.getParameter(VirtualMachineProfile.Param.HaTag);
if (haVmTag != null) {
clusterHosts = _hostDao.listByHostTag(type, clusterId, podId, dcId, haVmTag);
} else {
if (hostTagOnOffering == null && hostTagOnTemplate == null) {
clusterHosts = _resourceMgr.listAllUpAndEnabledNonHAHosts(type, clusterId, podId, dcId);
} else {
List<HostVO> hostsMatchingOfferingTag = new ArrayList<>();
List<HostVO> hostsMatchingTemplateTag = new ArrayList<>();
if (hasSvcOfferingTag) {
if (logger.isDebugEnabled()) {
logger.debug("Looking for hosts having tag specified on SvcOffering:" + hostTagOnOffering);
}
hostsMatchingOfferingTag = _hostDao.listByHostTag(type, clusterId, podId, dcId, hostTagOnOffering);
if (logger.isDebugEnabled()) {
logger.debug("Hosts with tag '" + hostTagOnOffering + "' are:" + hostsMatchingOfferingTag);
}
}
if (hasTemplateTag) {
if (logger.isDebugEnabled()) {
logger.debug("Looking for hosts having tag specified on Template:" + hostTagOnTemplate);
}
hostsMatchingTemplateTag = _hostDao.listByHostTag(type, clusterId, podId, dcId, hostTagOnTemplate);
if (logger.isDebugEnabled()) {
logger.debug("Hosts with tag '" + hostTagOnTemplate + "' are:" + hostsMatchingTemplateTag);
}
}
if (hasSvcOfferingTag && hasTemplateTag) {
hostsMatchingOfferingTag.retainAll(hostsMatchingTemplateTag);
if (logger.isDebugEnabled()) {
logger.debug("Found " + hostsMatchingOfferingTag.size() + " Hosts satisfying both tags, host ids are:" + hostsMatchingOfferingTag);
}
clusterHosts = hostsMatchingOfferingTag;
} else {
if (hasSvcOfferingTag) {
clusterHosts = hostsMatchingOfferingTag;
} else {
clusterHosts = hostsMatchingTemplateTag;
}
}
}
}
if (isVMDeployedWithUefi) {
clusterHosts.retainAll(hostsMatchingUefiTag);
}
clusterHosts.addAll(_hostDao.findHostsWithTagRuleThatMatchComputeOferringTags(hostTagOnOffering));
if (clusterHosts.isEmpty()) {
logger.warn("No suitable host found for VM [{}] with tags {} in {}.", vmProfile, hostTagOnOffering, paramAsStringToLog);
return null;
}
// add all hosts that we are not considering to the avoid list
List<HostVO> allhostsInCluster = _hostDao.listAllUpAndEnabledNonHAHosts(type, clusterId, podId, dcId, null);
allhostsInCluster.removeAll(clusterHosts);
logger.debug(() -> String.format("Adding hosts [%s] to the avoid set because these hosts do not support HA.",
ReflectionToStringBuilderUtils.reflectOnlySelectedFields(allhostsInCluster, "uuid", "name")));
for (HostVO host : allhostsInCluster) {
avoid.addHost(host.getId());
}
return allocateTo(vmProfile, plan, offering, template, avoid, clusterHosts, returnUpTo, considerReservedCapacity, account);
return allocateTo(vmProfile, plan, type, avoid, null, returnUpTo, true);
}
@Override
public List<Host> allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan, Type type, ExcludeList avoid, List<? extends Host> hosts, int returnUpTo,
boolean considerReservedCapacity) {
boolean considerReservedCapacity) {
if (type == Host.Type.Storage) {
return null;
}
long dcId = plan.getDataCenterId();
Long podId = plan.getPodId();
Long clusterId = plan.getClusterId();
ServiceOffering offering = vmProfile.getServiceOffering();
VMTemplateVO template = (VMTemplateVO)vmProfile.getTemplate();
VMTemplateVO template = (VMTemplateVO) vmProfile.getTemplate();
Account account = vmProfile.getOwner();
List<Host> suitableHosts = new ArrayList<>();
List<Host> hostsCopy = new ArrayList<>(hosts);
if (type == Host.Type.Storage) {
// FirstFitAllocator should be used for user VMs only since it won't care whether the host is capable of
// routing or not.
return suitableHosts;
}
String hostTagOnOffering = offering.getHostTag();
String hostTagOnTemplate = template.getTemplateTag();
boolean hasSvcOfferingTag = hostTagOnOffering != null ? true : false;
boolean hasTemplateTag = hostTagOnTemplate != null ? true : false;
String paramAsStringToLog = String.format("zone [%s], pod [%s], cluster [%s]", dcId, podId, clusterId);
String haVmTag = (String)vmProfile.getParameter(VirtualMachineProfile.Param.HaTag);
if (haVmTag != null) {
hostsCopy.retainAll(_hostDao.listByHostTag(type, clusterId, podId, dcId, haVmTag));
List<HostVO> suitableHosts = retrieveHosts(vmProfile, type, (List<HostVO>) hosts, clusterId, podId, dcId, hostTagOnOffering, hostTagOnTemplate);
if (suitableHosts.isEmpty()) {
logger.info("No suitable host found for VM [{}] in {}.", vmProfile, paramAsStringToLog);
return null;
}
if (CollectionUtils.isEmpty(hosts)) {
addHostsToAvoidSet(type, avoid, clusterId, podId, dcId, suitableHosts);
}
return allocateTo(vmProfile, plan, offering, template, avoid, suitableHosts, returnUpTo, considerReservedCapacity, account);
}
protected List<HostVO> retrieveHosts(VirtualMachineProfile vmProfile, Type type, List<HostVO> hostsToFilter, Long clusterId, Long podId, long dcId, String hostTagOnOffering,
String hostTagOnTemplate) {
String haVmTag = (String) vmProfile.getParameter(VirtualMachineProfile.Param.HaTag);
List<HostVO> clusterHosts;
if (CollectionUtils.isNotEmpty(hostsToFilter)) {
clusterHosts = new ArrayList<>(hostsToFilter);
} else {
if (hostTagOnOffering == null && hostTagOnTemplate == null) {
hostsCopy.retainAll(_resourceMgr.listAllUpAndEnabledNonHAHosts(type, clusterId, podId, dcId));
} else {
if (hasSvcOfferingTag) {
if (logger.isDebugEnabled()) {
logger.debug("Looking for hosts having tag specified on SvcOffering:" + hostTagOnOffering);
}
hostsCopy.retainAll(_hostDao.listByHostTag(type, clusterId, podId, dcId, hostTagOnOffering));
if (logger.isDebugEnabled()) {
logger.debug("Hosts with tag '" + hostTagOnOffering + "' are:" + hostsCopy);
}
}
if (hasTemplateTag) {
if (logger.isDebugEnabled()) {
logger.debug("Looking for hosts having tag specified on Template:" + hostTagOnTemplate);
}
hostsCopy.retainAll(_hostDao.listByHostTag(type, clusterId, podId, dcId, hostTagOnTemplate));
if (logger.isDebugEnabled()) {
logger.debug("Hosts with tag '" + hostTagOnTemplate + "' are:" + hostsCopy);
}
}
}
clusterHosts = _resourceMgr.listAllUpAndEnabledHosts(type, clusterId, podId, dcId);
}
hostsCopy.addAll(_hostDao.findHostsWithTagRuleThatMatchComputeOferringTags(hostTagOnOffering));
if (!hostsCopy.isEmpty()) {
suitableHosts = allocateTo(vmProfile, plan, offering, template, avoid, hostsCopy, returnUpTo, considerReservedCapacity, account);
if (haVmTag != null) {
clusterHosts.retainAll(hostDao.listByHostTag(type, clusterId, podId, dcId, haVmTag));
} else if (ObjectUtils.allNull(hostTagOnOffering, hostTagOnTemplate)) {
clusterHosts.retainAll(_resourceMgr.listAllUpAndEnabledNonHAHosts(type, clusterId, podId, dcId));
} else {
retainHostsMatchingServiceOfferingAndTemplateTags(clusterHosts, type, dcId, podId, clusterId, hostTagOnOffering, hostTagOnTemplate);
}
filterHostsWithUefiEnabled(type, vmProfile, clusterId, podId, dcId, clusterHosts);
addHostsBasedOnTagRules(hostTagOnOffering, clusterHosts);
return clusterHosts;
}
/**
* Add all hosts to the avoid set that were not considered during the allocation
*/
protected void addHostsToAvoidSet(Type type, ExcludeList avoid, Long clusterId, Long podId, long dcId, List<HostVO> suitableHosts) {
List<HostVO> allHostsInCluster = hostDao.listAllUpAndEnabledNonHAHosts(type, clusterId, podId, dcId, null);
allHostsInCluster.removeAll(suitableHosts);
logger.debug("Adding hosts [{}] to the avoid set because these hosts were not considered for allocation.",
() -> ReflectionToStringBuilderUtils.reflectOnlySelectedFields(allHostsInCluster, "uuid", "name"));
for (HostVO host : allHostsInCluster) {
avoid.addHost(host.getId());
}
}
protected void filterHostsWithUefiEnabled(Type type, VirtualMachineProfile vmProfile, Long clusterId, Long podId, long dcId, List<HostVO> clusterHosts) {
VMInstanceDetailVO vmInstanceDetailVO = vmInstanceDetailsDao.findDetail(vmProfile.getId(), "UEFI");
if (vmInstanceDetailVO == null) {
return;
}
if (!StringUtils.equalsAnyIgnoreCase(vmInstanceDetailVO.getValue(), ApiConstants.BootMode.SECURE.toString(), ApiConstants.BootMode.LEGACY.toString())) {
return;
}
logger.info("Guest VM is requested with Custom[UEFI] Boot Type enabled.");
List<HostVO> hostsMatchingUefiTag = hostDao.listByHostCapability(type, clusterId, podId, dcId, Host.HOST_UEFI_ENABLE);
logger.debug("Hosts with UEFI enabled are {}.", hostsMatchingUefiTag);
clusterHosts.retainAll(hostsMatchingUefiTag);
}
protected List<Host> allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan, ServiceOffering offering, VMTemplateVO template, ExcludeList avoid, List<? extends Host> hosts, int returnUpTo,
boolean considerReservedCapacity, Account account) {
String vmAllocationAlgorithm = DeploymentClusterPlanner.VmAllocationAlgorithm.value();
if (random.toString().equals(vmAllocationAlgorithm)) {
Collections.shuffle(hosts);
} else if (userdispersing.toString().equals(vmAllocationAlgorithm)) {
hosts = reorderHostsByNumberOfVms(plan, hosts, account);
} else if (firstfitleastconsumed.toString().equals(vmAllocationAlgorithm)) {
hosts = reorderHostsByCapacity(plan, hosts);
}
logger.debug("FirstFitAllocator has {} hosts to check for allocation {}.", hosts.size(), hosts);
hosts = prioritizeHosts(template, offering, hosts);
logger.debug("Found {} hosts for allocation after prioritization: {}.", hosts.size(), hosts);
List<Host> suitableHosts = checkHostsCompatibilities(offering, vmProfile, avoid, hosts, returnUpTo, considerReservedCapacity);
logger.debug("Host Allocator returning {} suitable hosts.", suitableHosts.size());
return suitableHosts;
}
protected List<Host> allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan, ServiceOffering offering, VMTemplateVO template, ExcludeList avoid, List<? extends Host> hosts, int returnUpTo,
boolean considerReservedCapacity, Account account) {
String vmAllocationAlgorithm = DeploymentClusterPlanner.VmAllocationAlgorithm.value();
if (vmAllocationAlgorithm.equals("random")) {
// Shuffle this so that we don't check the hosts in the same order.
Collections.shuffle(hosts);
} else if (vmAllocationAlgorithm.equals("userdispersing")) {
hosts = reorderHostsByNumberOfVms(plan, hosts, account);
} else if(vmAllocationAlgorithm.equals("firstfitleastconsumed")){
hosts = reorderHostsByCapacity(plan, hosts);
}
if (logger.isDebugEnabled()) {
logger.debug("FirstFitAllocator has " + hosts.size() + " hosts to check for allocation: " + hosts);
}
// We will try to reorder the host lists such that we give priority to hosts that have
// the minimums to support a VM's requirements
hosts = prioritizeHosts(template, offering, hosts);
if (logger.isDebugEnabled()) {
logger.debug("Found " + hosts.size() + " hosts for allocation after prioritization: " + hosts);
}
if (logger.isDebugEnabled()) {
logger.debug("Looking for speed=" + (offering.getCpu() * offering.getSpeed()) + "Mhz, Ram=" + offering.getRamSize() + " MB");
}
long serviceOfferingId = offering.getId();
protected List<Host> checkHostsCompatibilities(ServiceOffering offering, VirtualMachineProfile vmProfile, ExcludeList avoid, List<? extends Host> hosts, int returnUpTo, boolean considerReservedCapacity) {
List<Host> suitableHosts = new ArrayList<>();
ServiceOfferingDetailsVO offeringDetails = null;
logger.debug("Checking compatibility for the following hosts {}.", suitableHosts);
for (Host host : hosts) {
if (suitableHosts.size() == returnUpTo) {
break;
}
if (avoid.shouldAvoid(host)) {
if (logger.isDebugEnabled()) {
logger.debug("Host: {} is in avoid set, skipping this and trying other available hosts", host);
}
logger.debug("Host [{}] is in avoid set, skipping this and trying other available hosts", host);
continue;
}
//find number of guest VMs occupying capacity on this host.
if (_capacityMgr.checkIfHostReachMaxGuestLimit(host)) {
if (capacityManager.checkIfHostReachMaxGuestLimit(host)) {
logger.debug("Adding host [{}] to the avoid set because this host already has the max number of running (user and/or system) VMs.", host);
avoid.addHost(host.getId());
continue;
}
// Check if GPU device is required by offering and host has the availability
if (_resourceMgr.isGPUDeviceAvailable(offering, host, vmProfile.getId())) {
logger.debug("Host [{}] has required GPU devices available.", host);
} else {
// If GPU is not available, skip this host
logger.debug("Adding host [{}] to avoid set, because this host does not have required GPU devices available.", host);
avoid.addHost(host.getId());
if (offeringRequestedVGpuAndHostDoesNotHaveIt(offering, vmProfile, avoid, host)) {
continue;
}
Pair<Boolean, Boolean> cpuCapabilityAndCapacity = _capacityMgr.checkIfHostHasCpuCapabilityAndCapacity(host, offering, considerReservedCapacity);
if (cpuCapabilityAndCapacity.first() && cpuCapabilityAndCapacity.second()) {
if (logger.isDebugEnabled()) {
logger.debug("Found a suitable host, adding to list: {}", host);
}
if (hostHasCpuCapabilityAndCapacity(considerReservedCapacity, offering, host)) {
suitableHosts.add(host);
} else {
if (logger.isDebugEnabled()) {
logger.debug("Not using host {}; host has cpu capability? {}, host has capacity?{}",
host, cpuCapabilityAndCapacity.first(), cpuCapabilityAndCapacity.second());
}
avoid.addHost(host.getId());
continue;
}
avoid.addHost(host.getId());
}
if (logger.isDebugEnabled()) {
logger.debug("Host Allocator returning " + suitableHosts.size() + " suitable hosts");
}
return suitableHosts;
}
// Reorder hosts in the decreasing order of free capacity.
protected boolean offeringRequestedVGpuAndHostDoesNotHaveIt(ServiceOffering offering, VirtualMachineProfile vmProfile, ExcludeList avoid, Host host) {
if (_resourceMgr.isGPUDeviceAvailable(offering, host, vmProfile.getId())) {
logger.debug("Host [{}] has required GPU devices available.", () -> host);
return false;
}
logger.debug("Adding host [{}] to avoid set, because this host does not have required GPU devices available.", () -> host);
avoid.addHost(host.getId());
return true;
}
/**
* Reorder hosts in the decreasing order of free capacity.
*/
private List<? extends Host> reorderHostsByCapacity(DeploymentPlan plan, List<? extends Host> hosts) {
Long zoneId = plan.getDataCenterId();
Long clusterId = plan.getClusterId();
@ -388,26 +277,10 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator {
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.collect(Collectors.toMap(Map.Entry::getKey, entry -> decimalFormat.format(entry.getValue() * 100) + "%",
(e1, e2) -> e1, LinkedHashMap::new));
if (logger.isDebugEnabled()) {
logger.debug("List of hosts: [{}] in descending order of free capacity (percentage) in the cluster: {}",
hostIdsByFreeCapacity, sortedHostByCapacity);
}
logger.debug("List of hosts: [{}] in descending order of free capacity (percentage) in the cluster: {}.",
hostIdsByFreeCapacity, sortedHostByCapacity);
//now filter the given list of Hosts by this ordered list
Map<Long, Host> hostMap = new HashMap<>();
for (Host host : hosts) {
hostMap.put(host.getId(), host);
}
List<Long> matchingHostIds = new ArrayList<>(hostMap.keySet());
hostIdsByFreeCapacity.retainAll(matchingHostIds);
List<Host> reorderedHosts = new ArrayList<>();
for(Long id: hostIdsByFreeCapacity){
reorderedHosts.add(hostMap.get(id));
}
return reorderedHosts;
return filterHosts(hosts, hostIdsByFreeCapacity);
}
private Pair<List<Long>, Map<Long, Double>> getOrderedHostsByCapacity(Long zoneId, Long clusterId) {
@ -450,116 +323,154 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator {
Long clusterId = plan.getClusterId();
List<Long> hostIdsByVmCount = _vmInstanceDao.listHostIdsByVmCount(dcId, podId, clusterId, account.getAccountId());
if (logger.isDebugEnabled()) {
logger.debug("List of hosts in ascending order of number of VMs: " + hostIdsByVmCount);
}
logger.debug("List of hosts in ascending order of number of VMs: {}.", hostIdsByVmCount);
//now filter the given list of Hosts by this ordered list
return filterHosts(hosts, hostIdsByVmCount);
}
/**
* Filter the given list of Hosts considering the ordered list
*/
private List<? extends Host> filterHosts(List<? extends Host> hosts, List<Long> orderedHostIdsList) {
Map<Long, Host> hostMap = new HashMap<>();
for (Host host : hosts) {
hostMap.put(host.getId(), host);
}
List<Long> matchingHostIds = new ArrayList<>(hostMap.keySet());
hostIdsByVmCount.retainAll(matchingHostIds);
orderedHostIdsList.retainAll(matchingHostIds);
List<Host> reorderedHosts = new ArrayList<>();
for (Long id : hostIdsByVmCount) {
for(Long id: orderedHostIdsList){
reorderedHosts.add(hostMap.get(id));
}
return reorderedHosts;
}
@Override
public boolean isVirtualMachineUpgradable(VirtualMachine vm, ServiceOffering offering) {
// currently we do no special checks to rule out a VM being upgradable to an offering, so
// return true
return true;
}
/**
* Reorder the host list giving priority to hosts that have the minimum to support the VM's requirements.
*/
protected List<? extends Host> prioritizeHosts(VMTemplateVO template, ServiceOffering offering, List<? extends Host> hosts) {
if (template == null) {
return hosts;
}
// Determine the guest OS category of the template
String templateGuestOSCategory = getTemplateGuestOSCategory(template);
List<Host> hostsToCheck = filterHostWithNoHvmIfTemplateRequested(template, hosts);
List<Host> prioritizedHosts = new ArrayList<>();
List<Host> noHvmHosts = new ArrayList<>();
// If a template requires HVM and a host doesn't support HVM, remove it from consideration
List<Host> hostsToCheck = new ArrayList<>();
if (template.isRequiresHvm()) {
for (Host host : hosts) {
if (hostSupportsHVM(host)) {
hostsToCheck.add(host);
} else {
noHvmHosts.add(host);
}
}
} else {
hostsToCheck.addAll(hosts);
}
if (logger.isDebugEnabled()) {
if (noHvmHosts.size() > 0) {
logger.debug("Not considering hosts: " + noHvmHosts + " to deploy template: " + template + " as they are not HVM enabled");
}
}
// If a host is tagged with the same guest OS category as the template, move it to a high priority list
// If a host is tagged with a different guest OS category than the template, move it to a low priority list
List<Host> highPriorityHosts = new ArrayList<>();
List<Host> lowPriorityHosts = new ArrayList<>();
for (Host host : hostsToCheck) {
String hostGuestOSCategory = getHostGuestOSCategory(host);
if (hostGuestOSCategory == null) {
continue;
} else if (templateGuestOSCategory != null && templateGuestOSCategory.equals(hostGuestOSCategory)) {
highPriorityHosts.add(host);
} else {
lowPriorityHosts.add(host);
}
}
prioritizeHostsWithMatchingGuestOs(template, hostsToCheck, highPriorityHosts, lowPriorityHosts);
hostsToCheck.removeAll(highPriorityHosts);
hostsToCheck.removeAll(lowPriorityHosts);
// Prioritize the remaining hosts by HVM capability
for (Host host : hostsToCheck) {
if (!template.isRequiresHvm() && !hostSupportsHVM(host)) {
// Host and template both do not support hvm, put it as first consideration
prioritizedHosts.add(0, host);
} else {
// Template doesn't require hvm, but the machine supports it, make it last for consideration
prioritizedHosts.add(host);
}
}
// Merge the lists
prioritizeHostsByHvmCapability(template, hostsToCheck, prioritizedHosts);
prioritizedHosts.addAll(0, highPriorityHosts);
prioritizedHosts.addAll(lowPriorityHosts);
// if service offering is not GPU enabled then move all the GPU enabled hosts to the end of priority list.
if (_serviceOfferingDetailsDao.findDetail(offering.getId(), GPU.Keys.vgpuType.toString()) == null && offering.getVgpuProfileId() == null) {
prioritizeHostsByGpuEnabled(offering, prioritizedHosts);
List<Host> gpuEnabledHosts = new ArrayList<>();
// Check for GPU enabled hosts.
for (Host host : prioritizedHosts) {
if (_resourceMgr.isHostGpuEnabled(host.getId())) {
gpuEnabledHosts.add(host);
}
}
// Move GPU enabled hosts to the end of list
if(!gpuEnabledHosts.isEmpty()) {
prioritizedHosts.removeAll(gpuEnabledHosts);
prioritizedHosts.addAll(gpuEnabledHosts);
}
}
return prioritizedHosts;
}
/**
* If a template requires HVM and a host doesn't support HVM, remove it from consideration.
*/
protected List<Host> filterHostWithNoHvmIfTemplateRequested(VMTemplateVO template, List<? extends Host> hosts) {
List<Host> hostsToCheck = new ArrayList<>();
if (!template.isRequiresHvm()) {
logger.debug("Template [{}] does not require HVM, therefore, the hosts {} will not be checked for HVM compatibility.", template, hostsToCheck);
hostsToCheck.addAll(hosts);
return hostsToCheck;
}
List<Host> noHvmHosts = new ArrayList<>();
logger.debug("Template [{}] requires HVM, therefore, the hosts %s will be checked for HVM compatibility.", template, hostsToCheck);
for (Host host : hosts) {
if (hostSupportsHVM(host)) {
hostsToCheck.add(host);
} else {
noHvmHosts.add(host);
}
}
if (!noHvmHosts.isEmpty()) {
logger.debug("Not considering hosts {} to deploy VM using template {} as they are not HVM enabled.", noHvmHosts, template);
}
return hostsToCheck;
}
/**
* If service offering did not request for vGPU, then move all host with GPU to the end of the host priority list.
*/
protected void prioritizeHostsByGpuEnabled(ServiceOffering offering, List<Host> prioritizedHosts) {
boolean serviceOfferingRequestedVGpu = _serviceOfferingDetailsDao.findDetail(offering.getId(), GPU.Keys.vgpuType.toString()) != null || offering.getVgpuProfileId() != null;
if (serviceOfferingRequestedVGpu) {
return;
}
List<Host> gpuEnabledHosts = new ArrayList<>();
for (Host host : prioritizedHosts) {
if (_resourceMgr.isHostGpuEnabled(host.getId())) {
gpuEnabledHosts.add(host);
}
}
if (!gpuEnabledHosts.isEmpty()) {
prioritizedHosts.removeAll(gpuEnabledHosts);
prioritizedHosts.addAll(gpuEnabledHosts);
}
}
/**
* Prioritize remaining host by HVM capability.
*
* <ul>
* <li>If host and template both do not support HVM, put it at the start of the list.</li>
* <li>If the template doesn't require HVM, but the machine supports it, append it to the list.</li>
* </ul>
*/
protected void prioritizeHostsByHvmCapability(VMTemplateVO template, List<Host> hostsToCheck, List<Host> prioritizedHosts) {
for (Host host : hostsToCheck) {
if (!template.isRequiresHvm() && !hostSupportsHVM(host)) {
prioritizedHosts.add(0, host);
} else {
prioritizedHosts.add(host);
}
}
}
/**
* <ul>
* <li>If a host is tagged with the same guest OS category as the template, move it to a high priority list.</li>
* <li>If a host is tagged with a different guest OS category than the template, move it to a low priority list.</li>
* </ul>
*/
protected void prioritizeHostsWithMatchingGuestOs(VMTemplateVO template, List<Host> hostsToCheck, List<Host> highPriorityHosts, List<Host> lowPriorityHosts) {
String templateGuestOSCategory = getTemplateGuestOSCategory(template);
for (Host host : hostsToCheck) {
String hostGuestOSCategory = getHostGuestOSCategory(host);
if (StringUtils.equals(templateGuestOSCategory, hostGuestOSCategory)) {
highPriorityHosts.add(host);
} else if (hostGuestOSCategory != null) {
lowPriorityHosts.add(host);
}
}
}
protected boolean hostSupportsHVM(Host host) {
if (!_checkHvm) {
return true;
@ -621,19 +532,8 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator {
if (_configDao != null) {
Map<String, String> configs = _configDao.getConfiguration(params);
String value = configs.get("xenserver.check.hvm");
_checkHvm = value == null ? true : Boolean.parseBoolean(value);
_checkHvm = value == null || Boolean.parseBoolean(value);
}
return true;
}
@Override
public boolean start() {
return true;
}
@Override
public boolean stop() {
return true;
}
}

View File

@ -0,0 +1,132 @@
// 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.agent.manager.allocator.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.inject.Inject;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Component;
import com.cloud.deploy.DeploymentPlan;
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
import com.cloud.host.Host;
import com.cloud.host.Host.Type;
import com.cloud.host.HostVO;
import com.cloud.offering.ServiceOffering;
import com.cloud.resource.ResourceManager;
import com.cloud.storage.VMTemplateVO;
import com.cloud.vm.VirtualMachineProfile;
@Component
public class RandomAllocator extends BaseAllocator {
@Inject
private ResourceManager _resourceMgr;
protected List<Host> findSuitableHosts(VirtualMachineProfile vmProfile, DeploymentPlan plan, Type type, ExcludeList avoid, List<? extends Host> hosts, int returnUpTo,
boolean considerReservedCapacity) {
if (type == Host.Type.Storage) {
return null;
}
long dcId = plan.getDataCenterId();
Long podId = plan.getPodId();
Long clusterId = plan.getClusterId();
ServiceOffering offering = vmProfile.getServiceOffering();
String offeringHostTag = offering.getHostTag();
VMTemplateVO template = (VMTemplateVO) vmProfile.getTemplate();
logger.debug("Looking for hosts in zone [{}], pod [{}], cluster [{}].", dcId, podId, clusterId);
List<? extends Host> availableHosts = retrieveHosts(type, (List<HostVO>) hosts, template, offeringHostTag, clusterId, podId, dcId);
if (availableHosts.isEmpty()) {
logger.info("No suitable host found for VM [{}] in zone [{}], pod [{}], cluster [{}].", vmProfile, dcId, podId, clusterId);
return null;
}
return filterAvailableHosts(avoid, returnUpTo, considerReservedCapacity, availableHosts, offering);
}
protected List<Host> filterAvailableHosts(ExcludeList avoid, int returnUpTo, boolean considerReservedCapacity, List<? extends Host> availableHosts, ServiceOffering offering) {
logger.debug("Random Allocator found [{}] available hosts. They will be checked if they are in the avoid set and for CPU capability and capacity.", availableHosts::size);
List<Host> suitableHosts = new ArrayList<>();
Collections.shuffle(availableHosts);
for (Host host : availableHosts) {
if (suitableHosts.size() == returnUpTo) {
break;
}
if (avoid.shouldAvoid(host)) {
logger.debug("Host [{}] is in the avoid set, skipping it and trying other available hosts.", () -> host);
continue;
}
if (!hostHasCpuCapabilityAndCapacity(considerReservedCapacity, offering, host)) {
continue;
}
logger.debug("Found the suitable host [{}], adding to list.", () -> host);
suitableHosts.add(host);
}
logger.debug("Random Host Allocator returning {} suitable hosts.", suitableHosts::size);
return suitableHosts;
}
/**
* @return all computing hosts, regardless of whether they support routing.
*/
protected List<HostVO> retrieveHosts(Type type, List<HostVO> hosts, VMTemplateVO template, String offeringHostTag, Long clusterId, Long podId, long dcId) {
List<HostVO> availableHosts;
String templateTag = template.getTemplateTag();
if (CollectionUtils.isNotEmpty(hosts)) {
availableHosts = new ArrayList<>(hosts);
} else {
availableHosts = _resourceMgr.listAllUpAndEnabledHosts(type, clusterId, podId, dcId);
}
if (ObjectUtils.anyNotNull(offeringHostTag, templateTag)) {
retainHostsMatchingServiceOfferingAndTemplateTags(availableHosts, type, dcId, podId, clusterId, offeringHostTag, templateTag);
} else {
List<HostVO> hostsWithNoRuleTag = hostDao.listAllHostsThatHaveNoRuleTag(type, clusterId, podId, dcId);
logger.debug("Retaining hosts {} because they do not have rule tags.", hostsWithNoRuleTag);
availableHosts.retainAll(hostsWithNoRuleTag);
}
addHostsBasedOnTagRules(offeringHostTag, availableHosts);
return availableHosts;
}
@Override
public List<Host> allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan, Type type, ExcludeList avoid, int returnUpTo) {
return allocateTo(vmProfile, plan, type, avoid, null, returnUpTo, true);
}
@Override
public List<Host> allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan, Type type, ExcludeList avoid, List<? extends Host> hosts, int returnUpTo,
boolean considerReservedCapacity) {
return findSuitableHosts(vmProfile, plan, type, avoid, hosts, returnUpTo, considerReservedCapacity);
}
}

View File

@ -28,32 +28,24 @@ import com.cloud.deploy.DeploymentPlanner.ExcludeList;
import com.cloud.host.Host;
import com.cloud.host.Host.Type;
import com.cloud.host.dao.HostDao;
import com.cloud.offering.ServiceOffering;
import com.cloud.utils.component.AdapterBase;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineProfile;
public class TestingAllocator extends AdapterBase implements HostAllocator {
@Inject
HostDao _hostDao;
Long _computingHost;
Long _storageHost;
Long _routingHost;
@Override
public List<Host> allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan, Type type, ExcludeList avoid, int returnUpTo) {
return allocateTo(vmProfile, plan, type, avoid, returnUpTo, true);
return allocateTo(vmProfile, plan, type, avoid, null, returnUpTo, true);
}
@Override
public List<Host> allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan, Type type, ExcludeList avoid, List<? extends Host> hosts, int returnUpTo,
boolean considerReservedCapacity) {
return allocateTo(vmProfile, plan, type, avoid, returnUpTo, considerReservedCapacity);
}
@Override
public List<Host> allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan, Type type, ExcludeList avoid, int returnUpTo, boolean considerReservedCapacity) {
List<Host> availableHosts = new ArrayList<Host>();
boolean considerReservedCapacity) {
List<Host> availableHosts = new ArrayList<>();
Host host = null;
if (type == Host.Type.Routing && _routingHost != null) {
host = _hostDao.findById(_routingHost);
@ -66,13 +58,6 @@ public class TestingAllocator extends AdapterBase implements HostAllocator {
return availableHosts;
}
@Override
public boolean isVirtualMachineUpgradable(VirtualMachine vm, ServiceOffering offering) {
// currently we do no special checks to rule out a VM being upgradable to an offering, so
// return true
return true;
}
@Override
public boolean configure(String name, Map<String, Object> params) {
String value = (String)params.get(Host.Type.Routing.toString());

View File

@ -20,7 +20,6 @@ import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
@ -1714,20 +1713,19 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
@Override
public void reorderHostsByPriority(Map<Long, Integer> priorities, List<Host> hosts) {
logger.debug("Re-ordering hosts {} by priorities {}", hosts, priorities);
if (CollectionUtils.isEmpty(hosts)){
logger.debug("Hosts list is empty; therefore, there is nothing to reorder.");
return;
}
logger.info("Re-ordering hosts [{}] by priorities [{}].", hosts, priorities);
hosts.removeIf(host -> DataCenterDeployment.PROHIBITED_HOST_PRIORITY.equals(getHostPriority(priorities, host.getId())));
hosts.sort((host1, host2) -> {
int res = getHostPriority(priorities, host1.getId()).compareTo(getHostPriority(priorities, host2.getId()));
return -res;
});
Collections.sort(hosts, new Comparator<>() {
@Override
public int compare(Host host1, Host host2) {
int res = getHostPriority(priorities, host1.getId()).compareTo(getHostPriority(priorities, host2.getId()));
return -res;
}
}
);
logger.debug("Hosts after re-ordering are: {}", hosts);
logger.info("Hosts after re-ordering are: [{}].", hosts);
}
private Integer getHostPriority(Map<Long, Integer> priorities, Long hostId) {

View File

@ -1696,11 +1696,7 @@ public class ManagementServerImpl extends MutualExclusiveIdsManagerBase implemen
List<Host> suitableHosts = new ArrayList<>();
for (final HostAllocator allocator : hostAllocators) {
if (CollectionUtils.isNotEmpty(compatibleHosts)) {
suitableHosts = allocator.allocateTo(vmProfile, plan, Host.Type.Routing, excludes, compatibleHosts, HostAllocator.RETURN_UPTO_ALL, false);
} else {
suitableHosts = allocator.allocateTo(vmProfile, plan, Host.Type.Routing, excludes, HostAllocator.RETURN_UPTO_ALL, false);
}
suitableHosts = allocator.allocateTo(vmProfile, plan, Host.Type.Routing, excludes, compatibleHosts, HostAllocator.RETURN_UPTO_ALL, false);
if (CollectionUtils.isNotEmpty(suitableHosts)) {
break;
@ -1709,10 +1705,10 @@ public class ManagementServerImpl extends MutualExclusiveIdsManagerBase implemen
_dpMgr.reorderHostsByPriority(plan.getHostPriorities(), suitableHosts);
if (suitableHosts.isEmpty()) {
if (CollectionUtils.isEmpty(suitableHosts)) {
logger.warn("No suitable hosts found.");
} else {
logger.debug("Hosts having capacity and suitable for migration: {}", suitableHosts);
logger.debug("Hosts having capacity and are suitable for migration: {}", suitableHosts);
}
// Only list hosts of the same architecture as the source Host in a multi-arch zone

View File

@ -34,6 +34,9 @@
<bean id="firstFitAllocator"
class="com.cloud.agent.manager.allocator.impl.FirstFitAllocator" />
<bean id="randomAllocator"
class="com.cloud.agent.manager.allocator.impl.RandomAllocator" />
<bean id="FirstFitRouting"
class="com.cloud.agent.manager.allocator.impl.FirstFitRoutingAllocator">
<property name="name" value="FirstFitRouting" />

View File

@ -0,0 +1,219 @@
// 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.agent.manager.allocator.impl;
import com.cloud.capacity.CapacityManager;
import com.cloud.deploy.DeploymentPlan;
import com.cloud.deploy.DeploymentPlanner;
import com.cloud.host.Host;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
import com.cloud.offering.ServiceOffering;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.utils.Pair;
import com.cloud.vm.VirtualMachineProfile;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@RunWith(MockitoJUnitRunner.class)
public class BaseAllocatorTest {
@Mock
HostDao hostDaoMock;
@Mock
CapacityManager capacityManagerMock;
@InjectMocks
@Spy
BaseAllocator baseAllocator = new MockBaseAllocator();
private final Host.Type type = Host.Type.Routing;
private final Long clusterId = 1L;
private final Long podId = 2L;
private final Long dcId = 3L;
private final HostVO host1 = Mockito.mock(HostVO.class);
private final HostVO host2 = Mockito.mock(HostVO.class);
private final HostVO host3 = Mockito.mock(HostVO.class);
private final ServiceOfferingVO serviceOffering = Mockito.mock(ServiceOfferingVO.class);
private final String hostTag = "hostTag";
@Test
public void retainHostsMatchingServiceOfferingAndTemplateTagsTestHasServiceOfferingTagShouldRetainHostsWithServiceOfferingTag() {
List<HostVO> suitableHosts = new ArrayList<>(Arrays.asList(host1, host2, host3));
List<HostVO> hostsWithMathingTags = new ArrayList<>(Arrays.asList(host1, host3));
String hostTagOnTemplate = "hostTagOnTemplate";
String hostTagOnOffering = null;
Mockito.doReturn(hostsWithMathingTags).when(hostDaoMock).listByHostTag(type, clusterId, podId, dcId, hostTagOnTemplate);
baseAllocator.retainHostsMatchingServiceOfferingAndTemplateTags(suitableHosts, type, dcId, podId, clusterId, hostTagOnOffering, hostTagOnTemplate);
Assert.assertEquals(2, suitableHosts.size());
Assert.assertEquals(host1, suitableHosts.get(0));
Assert.assertEquals(host3, suitableHosts.get(1));
}
@Test
public void retainHostsMatchingServiceOfferingAndTemplateTagsTestHasServiceOfferingTagAndHasHostTagOnTemplateShouldRetainHostsWithServiceOfferingTagAndTemplateTag() {
List<HostVO> suitableHosts = new ArrayList<>(Arrays.asList(host1, host2, host3));
List<HostVO> hostsWithMathingServiceTags = new ArrayList<>(Arrays.asList(host1, host3));
List<HostVO> hostsWithMathingTemplateTags = new ArrayList<>(Arrays.asList(host1, host2));
String hostTagOnTemplate = "hostTagOnTemplate";
String hostTagOnOffering = "hostTagOnOffering";
Mockito.doReturn(hostsWithMathingTemplateTags).when(hostDaoMock).listByHostTag(type, clusterId, podId, dcId, hostTagOnTemplate);
Mockito.doReturn(hostsWithMathingServiceTags).when(hostDaoMock).listByHostTag(type, clusterId, podId, dcId, hostTagOnOffering);
baseAllocator.retainHostsMatchingServiceOfferingAndTemplateTags(suitableHosts, type, dcId, podId, clusterId, hostTagOnOffering, hostTagOnTemplate);
Assert.assertEquals(1, suitableHosts.size());
Assert.assertEquals(host1, suitableHosts.get(0));
}
@Test
public void retainHostsMatchingServiceOfferingAndTemplateTagsTestHasHostTagOnTemplateShouldRetainHostsWithTemplateTag() {
List<HostVO> suitableHosts = new ArrayList<>(Arrays.asList(host1, host2, host3));
List<HostVO> hostsWithMathingServiceTags = new ArrayList<>(Arrays.asList(host1, host3));
String hostTagOnTemplate = null;
String hostTagOnOffering = "hostTagOnOffering";
Mockito.doReturn(hostsWithMathingServiceTags).when(hostDaoMock).listByHostTag(type, clusterId, podId, dcId, hostTagOnOffering);
baseAllocator.retainHostsMatchingServiceOfferingAndTemplateTags(suitableHosts, type, dcId, podId, clusterId, hostTagOnOffering, hostTagOnTemplate);
Assert.assertEquals(2, suitableHosts.size());
Assert.assertEquals(host1, suitableHosts.get(0));
Assert.assertEquals(host3, suitableHosts.get(1));
}
@Test
public void retainHostsMatchingServiceOfferingAndTemplateTagsTestNoServiceTagAndNoTemplateTagShouldHaveAllSuitableHosts() {
List<HostVO> suitableHosts = new ArrayList<>(Arrays.asList(host1, host2, host3));
String hostTagOnTemplate = null;
String hostTagOnOffering = null;
baseAllocator.retainHostsMatchingServiceOfferingAndTemplateTags(suitableHosts, type, dcId, podId, clusterId, hostTagOnOffering, hostTagOnTemplate);
Assert.assertEquals(3, suitableHosts.size());
Assert.assertEquals(host1, suitableHosts.get(0));
Assert.assertEquals(host2, suitableHosts.get(1));
Assert.assertEquals(host3, suitableHosts.get(2));
}
@Test
public void addHostsBasedOnTagRulesTestHostsWithTagRuleIsEmptyShouldNotAddToSuitableHosts() {
List<HostVO> suitableHosts = new ArrayList<>(Arrays.asList(host1, host2));
List<HostVO> emptyList = new ArrayList<>();
Mockito.doReturn(emptyList).when(hostDaoMock).findHostsWithTagRuleThatMatchComputeOfferingTags(Mockito.anyString());
baseAllocator.addHostsBasedOnTagRules(hostTag, suitableHosts);
Assert.assertEquals(2, suitableHosts.size());
Assert.assertEquals(host1, suitableHosts.get(0));
Assert.assertEquals(host2, suitableHosts.get(1));
}
@Test
public void addHostsBasedOnTagRulesTestHostsWithTagRuleIsNotEmptyShouldAddToSuitableHosts() {
List<HostVO> suitableHosts = new ArrayList<>(Arrays.asList(host1, host2));
List<HostVO> hostsMatchingRuleTag = new ArrayList<>(Arrays.asList(host3));
Mockito.doReturn(hostsMatchingRuleTag).when(hostDaoMock).findHostsWithTagRuleThatMatchComputeOfferingTags(Mockito.anyString());
baseAllocator.addHostsBasedOnTagRules(hostTag, suitableHosts);
Assert.assertEquals(3, suitableHosts.size());
Assert.assertEquals(host1, suitableHosts.get(0));
Assert.assertEquals(host2, suitableHosts.get(1));
Assert.assertEquals(host3, suitableHosts.get(2));
}
@Test
public void hostHasCpuCapabilityAndCapacityTestHostHasCpuCapabilityAndCpuCapacityShouldReturnTrue() {
Boolean hasCpuCapability = true;
Boolean hasCpuCapacity = true;
Pair<Boolean, Boolean> pair = new Pair<>(hasCpuCapability, hasCpuCapacity);
Mockito.doReturn(pair).when(capacityManagerMock).checkIfHostHasCpuCapabilityAndCapacity(Mockito.any(Host.class), Mockito.any(ServiceOffering.class), Mockito.anyBoolean());
boolean result = baseAllocator.hostHasCpuCapabilityAndCapacity(true, serviceOffering, host1);
Assert.assertTrue(result);
}
@Test
public void hostHasCpuCapabilityAndCapacityTestHostHasCpuCapabilityButNoCpuCapacityShouldReturnFalse() {
Boolean hasCpuCapability = true;
Boolean hasCpuCapacity = false;
Pair<Boolean, Boolean> pair = new Pair<>(hasCpuCapability, hasCpuCapacity);
Mockito.doReturn(pair).when(capacityManagerMock).checkIfHostHasCpuCapabilityAndCapacity(Mockito.any(Host.class), Mockito.any(ServiceOffering.class), Mockito.anyBoolean());
boolean result = baseAllocator.hostHasCpuCapabilityAndCapacity(true, serviceOffering, host1);
Assert.assertFalse(result);
}
@Test
public void hostHasCpuCapabilityAndCapacityTestHostDoesNotHaveCpuCapabilityButHasCpuCapacityShouldReturnFalse() {
Boolean hasCpuCapability = false;
Boolean hasCpuCapacity = true;
Pair<Boolean, Boolean> pair = new Pair<>(hasCpuCapability, hasCpuCapacity);
Mockito.doReturn(pair).when(capacityManagerMock).checkIfHostHasCpuCapabilityAndCapacity(Mockito.any(Host.class), Mockito.any(ServiceOffering.class), Mockito.anyBoolean());
boolean result = baseAllocator.hostHasCpuCapabilityAndCapacity(true, serviceOffering, host1);
Assert.assertFalse(result);
}
@Test
public void hostHasCpuCapabilityAndCapacityTestHostDoesNotHaveCpuCapabilityAndCpuCapacityShouldReturnFalse() {
Boolean hasCpuCapability = false;
Boolean hasCpuCapacity = false;
Pair<Boolean, Boolean> pair = new Pair<>(hasCpuCapability, hasCpuCapacity);
Mockito.doReturn(pair).when(capacityManagerMock).checkIfHostHasCpuCapabilityAndCapacity(Mockito.any(Host.class), Mockito.any(ServiceOffering.class), Mockito.anyBoolean());
boolean result = baseAllocator.hostHasCpuCapabilityAndCapacity(true, serviceOffering, host1);
Assert.assertFalse(result);
}
class MockBaseAllocator extends BaseAllocator {
@Override
public List<Host> allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan, Host.Type type, DeploymentPlanner.ExcludeList avoid, int returnUpTo) {
return null;
}
@Override
public List<Host> allocateTo(VirtualMachineProfile vmProfile, DeploymentPlan plan, Host.Type type, DeploymentPlanner.ExcludeList avoid, List<? extends Host> hosts, int returnUpTo, boolean considerReservedCapacity) {
return null;
}
}
}

View File

@ -18,214 +18,596 @@
package com.cloud.agent.manager.allocator.impl;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.cloud.agent.manager.allocator.HostAllocator;
import com.cloud.capacity.CapacityManager;
import com.cloud.capacity.CapacityVO;
import com.cloud.deploy.DeploymentPlan;
import com.cloud.deploy.DeploymentPlanner;
import com.cloud.host.Host;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
import com.cloud.offering.ServiceOffering;
import com.cloud.resource.ResourceManager;
import com.cloud.service.ServiceOfferingDetailsVO;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.service.dao.ServiceOfferingDetailsDao;
import com.cloud.storage.VMTemplateVO;
import com.cloud.user.Account;
import com.cloud.utils.Pair;
import com.cloud.vm.VMInstanceDetailVO;
import com.cloud.vm.VirtualMachineProfile;
import com.cloud.vm.dao.VMInstanceDetailsDao;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class FirstFitAllocatorTest {
private static final double TOLERANCE = 0.0001;
private FirstFitAllocator allocator;
private CapacityManager capacityMgr;
private ServiceOfferingDetailsDao offeringDetailsDao;
private ResourceManager resourceMgr;
private static final double TOLERANCE = 0.0001;
private DeploymentPlan plan;
private ServiceOffering offering;
private DeploymentPlanner.ExcludeList avoid;
private Account account;
@Mock
HostDao hostDaoMock;
private Host host1;
private Host host2;
private VirtualMachineProfile vmProfile;
ConfigurationDao configDao;
@Mock
ResourceManager resourceManagerMock;
@Before
public void setUp() {
allocator = new FirstFitAllocator();
capacityMgr = mock(CapacityManager.class);
offeringDetailsDao = mock(ServiceOfferingDetailsDao.class);
resourceMgr = mock(ResourceManager.class);
configDao = mock(ConfigurationDao.class);
@Mock
VMInstanceDetailsDao userVmDetailsDaoMock;
allocator._capacityMgr = capacityMgr;
allocator._serviceOfferingDetailsDao = offeringDetailsDao;
allocator._resourceMgr = resourceMgr;
allocator._configDao = configDao;
@Mock
ServiceOfferingDetailsDao serviceOfferingDetailsDao;
plan = mock(DeploymentPlan.class);
offering = mock(ServiceOffering.class);
avoid = mock(DeploymentPlanner.ExcludeList.class);
account = mock(Account.class);
@Mock
CapacityManager capacityMgr;
host1 = mock(Host.class);
host2 = mock(Host.class);
@Mock
ConfigurationDao configDao;
vmProfile = mock(VirtualMachineProfile.class);
when(vmProfile.getId()).thenReturn(1L);
@Spy
@InjectMocks
FirstFitAllocator firstFitAllocatorSpy;
when(plan.getDataCenterId()).thenReturn(1L);
when(offering.getCpu()).thenReturn(2);
when(offering.getSpeed()).thenReturn(1000);
when(offering.getRamSize()).thenReturn(2048);
when(offering.getId()).thenReturn(123L);
when(offering.getHostTag()).thenReturn(null);
when(offering.getVgpuProfileId()).thenReturn(null);
}
private final Host.Type type = Host.Type.Routing;
@Test
public void testConfigure() throws Exception {
when(configDao.getConfiguration(anyMap())).thenReturn(new HashMap<>());
assertTrue(allocator._checkHvm);
assertTrue(allocator.configure("test", new HashMap<>()));
}
private final Long clusterId = 1L;
@Test
public void testAllocateTo_SuccessfulMatch() {
List<Host> inputHosts = Arrays.asList(host1, host2);
private final Long podId = 2L;
// All hosts are allowed
when(avoid.shouldAvoid(host1)).thenReturn(false);
when(avoid.shouldAvoid(host2)).thenReturn(false);
private final Long dcId = 3L;
// No GPU requirement
when(offeringDetailsDao.findDetail(eq(123L), anyString())).thenReturn(null);
private final List<HostVO> emptyList = new ArrayList<>();
// CPU capability and capacity is met
when(capacityMgr.checkIfHostReachMaxGuestLimit(any())).thenReturn(false);
when(capacityMgr.checkIfHostHasCpuCapabilityAndCapacity(eq(host1), eq(offering), eq(true)))
.thenReturn(new Pair<>(true, true));
when(capacityMgr.checkIfHostHasCpuCapabilityAndCapacity(eq(host2), eq(offering), eq(true)))
.thenReturn(new Pair<>(true, false));
private final String hostTag = "hostTag";
private final String templateTag = "templateTag";
when(resourceMgr.isGPUDeviceAvailable(offering, host1, vmProfile.getId())).thenReturn(true);
when(resourceMgr.isGPUDeviceAvailable(offering, host2, vmProfile.getId())).thenReturn(true);
private final HostVO host1 = mock(HostVO.class);
List<Host> result = allocator.allocateTo(vmProfile, plan, offering, null, avoid, inputHosts, 2, true, account);
private final HostVO host2 = mock(HostVO.class);
// Only host1 should be returned
assertEquals(1, result.size());
assertTrue(result.contains(host1));
assertFalse(result.contains(host2));
}
private final HostVO host3 = mock(HostVO.class);
@Test
public void testAllocateTo_AvoidSetAndGuestLimit() {
List<Host> inputHosts = Arrays.asList(host1, host2);
private final ServiceOfferingVO serviceOffering = mock(ServiceOfferingVO.class);
when(avoid.shouldAvoid(host1)).thenReturn(true); // Avoided
when(avoid.shouldAvoid(host2)).thenReturn(false);
private final DeploymentPlanner.ExcludeList excludeList = mock(DeploymentPlanner.ExcludeList.class);
when(capacityMgr.checkIfHostReachMaxGuestLimit(host2)).thenReturn(true); // Reached limit
private final VirtualMachineProfile virtualMachineProfile = mock(VirtualMachineProfile.class);
List<Host> result = allocator.allocateTo(vmProfile, plan, offering, null, avoid, inputHosts, 2, true, account);
private final VMTemplateVO vmTemplateVO = mock(VMTemplateVO.class);
assertTrue(result.isEmpty());
}
private final Account account = mock(Account.class);
@Test
public void testAllocateTo_GPUNotAvailable() {
List<Host> inputHosts = Arrays.asList(host1);
when(avoid.shouldAvoid(host1)).thenReturn(false);
private final DeploymentPlan deploymentPlan = mock(DeploymentPlan.class);
// GPU required but not available
var vgpuDetail = mock(com.cloud.service.ServiceOfferingDetailsVO.class);
var pciDetail = mock(com.cloud.service.ServiceOfferingDetailsVO.class);
when(offeringDetailsDao.findDetail(eq(123L), eq("vgpuType"))).thenReturn(vgpuDetail);
when(offeringDetailsDao.findDetail(eq(123L), eq("pciDevice"))).thenReturn(pciDetail);
when(pciDetail.getValue()).thenReturn("NVIDIA");
when(vgpuDetail.getValue()).thenReturn("GRID");
private final DeploymentPlanner.ExcludeList avoid = mock(DeploymentPlanner.ExcludeList.class);
when(resourceMgr.isGPUDeviceAvailable(offering, host1, vmProfile.getId())).thenReturn(false);
private final ServiceOffering offering = mock(ServiceOffering.class);
List<Host> result = allocator.allocateTo(vmProfile, plan, offering, null, avoid, inputHosts, 1, true, account);
private final boolean considerReservedCapacity = true;
assertTrue(result.isEmpty());
}
@Test
public void testConfigure() throws Exception {
when(configDao.getConfiguration(Mockito.anyMap())).thenReturn(new HashMap<>());
Assert.assertTrue(firstFitAllocatorSpy._checkHvm);
Assert.assertTrue(firstFitAllocatorSpy.configure("test", new HashMap<>()));
}
@Test
public void testHostByCombinedCapacityOrder() {
// Test scenario 1: Default capacity usage (0.5 weight)
List<CapacityVO> mockCapacity = getHostCapacities();
Map<Long, Double> hostByCombinedCapacity = FirstFitAllocator.getHostByCombinedCapacities(mockCapacity, 0.5);
@Test
public void testAllocateTo_SuccessfulMatch() {
List<Host> inputHosts = Arrays.asList(host1, host2);
// Verify host ordering and capacity values
Long firstHostId = hostByCombinedCapacity.keySet().iterator().next();
Assert.assertEquals("Host with ID 1 should be first in ordering", Long.valueOf(1L), firstHostId);
Assert.assertEquals("Host 1 combined capacity should match expected value",
0.9609375, hostByCombinedCapacity.get(1L), TOLERANCE);
Assert.assertEquals("Host 2 combined capacity should match expected value",
0.9296875, hostByCombinedCapacity.get(2L), TOLERANCE);
// All hosts are allowed
when(avoid.shouldAvoid(host1)).thenReturn(false);
when(avoid.shouldAvoid(host2)).thenReturn(false);
// Test scenario 2: Modified capacity usage (0.7 weight)
when(mockCapacity.get(0).getUsedCapacity()).thenReturn(1500L);
hostByCombinedCapacity = FirstFitAllocator.getHostByCombinedCapacities(mockCapacity, 0.7);
// CPU capability and capacity is met
when(capacityMgr.checkIfHostReachMaxGuestLimit(Mockito.any(Host.class))).thenReturn(false);
when(capacityMgr.checkIfHostHasCpuCapabilityAndCapacity(host1, offering, true))
.thenReturn(new Pair<>(true, true));
when(capacityMgr.checkIfHostHasCpuCapabilityAndCapacity(host2, offering,true))
.thenReturn(new Pair<>(true, false));
// Verify new ordering after capacity change
firstHostId = hostByCombinedCapacity.keySet().iterator().next();
Assert.assertEquals("Host with ID 2 should be first after capacity change", Long.valueOf(2L), firstHostId);
Assert.assertEquals("Host 2 combined capacity should match expected value after change",
0.9515625, hostByCombinedCapacity.get(2L), TOLERANCE);
Assert.assertEquals("Host 1 combined capacity should match expected value after change",
0.9484375, hostByCombinedCapacity.get(1L), TOLERANCE);
}
when(resourceManagerMock.isGPUDeviceAvailable(offering, host1, virtualMachineProfile.getId())).thenReturn(true);
when(resourceManagerMock.isGPUDeviceAvailable(offering, host2, virtualMachineProfile.getId())).thenReturn(true);
List<CapacityVO> getHostCapacities() {
CapacityVO cpuCapacity1 = mock(CapacityVO.class);
when(cpuCapacity1.getHostOrPoolId()).thenReturn(1L);
when(cpuCapacity1.getTotalCapacity()).thenReturn(32000L);
when(cpuCapacity1.getReservedCapacity()).thenReturn(0L);
when(cpuCapacity1.getUsedCapacity()).thenReturn(500L);
when(cpuCapacity1.getCapacityType()).thenReturn(CapacityVO.CAPACITY_TYPE_CPU);
List<Host> result = firstFitAllocatorSpy.allocateTo(virtualMachineProfile, deploymentPlan, offering, null, avoid, inputHosts, 2, true, account);
CapacityVO cpuCapacity2 = mock(CapacityVO.class);
when(cpuCapacity2.getHostOrPoolId()).thenReturn(2L);
when(cpuCapacity2.getTotalCapacity()).thenReturn(32000L);
when(cpuCapacity2.getReservedCapacity()).thenReturn(0L);
when(cpuCapacity2.getUsedCapacity()).thenReturn(500L);
when(cpuCapacity2.getCapacityType()).thenReturn(CapacityVO.CAPACITY_TYPE_CPU);
// Only host1 should be returned
Assert.assertEquals(1, result.size());
Assert.assertTrue(result.contains(host1));
Assert.assertFalse(result.contains(host2));
}
CapacityVO memCapacity1 = mock(CapacityVO.class);
when(memCapacity1.getHostOrPoolId()).thenReturn(1L);
when(memCapacity1.getTotalCapacity()).thenReturn(8589934592L);
when(memCapacity1.getReservedCapacity()).thenReturn(0L);
when(memCapacity1.getUsedCapacity()).thenReturn(536870912L);
when(memCapacity1.getCapacityType()).thenReturn(CapacityVO.CAPACITY_TYPE_MEMORY);
@Test
public void testAllocateTo_AvoidSetAndGuestLimit() {
List<Host> inputHosts = Arrays.asList(host1, host2);
CapacityVO memCapacity2 = mock(CapacityVO.class);
when(memCapacity2.getHostOrPoolId()).thenReturn(2L);
when(memCapacity2.getTotalCapacity()).thenReturn(8589934592L);
when(memCapacity2.getReservedCapacity()).thenReturn(0L);
when(memCapacity2.getUsedCapacity()).thenReturn(1073741824L);
when(memCapacity1.getCapacityType()).thenReturn(CapacityVO.CAPACITY_TYPE_MEMORY);
return Arrays.asList(cpuCapacity1, memCapacity1, cpuCapacity2, memCapacity2);
}
when(avoid.shouldAvoid(host1)).thenReturn(true); // Avoided
when(avoid.shouldAvoid(host2)).thenReturn(false);
when(capacityMgr.checkIfHostReachMaxGuestLimit(host2)).thenReturn(true); // Reached limit
List<Host> result = firstFitAllocatorSpy.allocateTo(virtualMachineProfile, deploymentPlan, offering, null, avoid, inputHosts, 2, true, account);
Assert.assertTrue(result.isEmpty());
}
@Test
public void testAllocateTo_GPUNotAvailable() {
List<Host> inputHosts = Arrays.asList(host1);
when(avoid.shouldAvoid(host1)).thenReturn(false);
when(resourceManagerMock.isGPUDeviceAvailable(offering, host1, virtualMachineProfile.getId())).thenReturn(false);
List<Host> result = firstFitAllocatorSpy.allocateTo(virtualMachineProfile, deploymentPlan, offering, null, avoid, inputHosts, 1, true, account);
Assert.assertTrue(result.isEmpty());
}
@Test
public void allocateToTestHostTypeStorageShouldReturnNull() {
List<Host> suitableHosts = firstFitAllocatorSpy.allocateTo(virtualMachineProfile, deploymentPlan, Host.Type.Storage, excludeList, null, HostAllocator.RETURN_UPTO_ALL, considerReservedCapacity);
Assert.assertNull(suitableHosts);
}
@Test
public void allocateToTestSuitableHostsEmptyShouldReturnNull() {
Mockito.doReturn(serviceOffering).when(virtualMachineProfile).getServiceOffering();
Mockito.doReturn(vmTemplateVO).when(virtualMachineProfile).getTemplate();
Mockito.doReturn(account).when(virtualMachineProfile).getOwner();
Mockito.doReturn(hostTag).when(serviceOffering).getHostTag();
Mockito.doReturn(templateTag).when(vmTemplateVO).getTemplateTag();
Mockito.doReturn(emptyList).when(firstFitAllocatorSpy).retrieveHosts(Mockito.any(VirtualMachineProfile.class), Mockito.any(Host.Type.class), Mockito.nullable(List.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyString());
List<Host> suitableHosts = firstFitAllocatorSpy.allocateTo(virtualMachineProfile, deploymentPlan, type, excludeList, null, HostAllocator.RETURN_UPTO_ALL, considerReservedCapacity);
Assert.assertNull(suitableHosts);
}
@Test
public void allocateToTestSuitableHostsNotEmptyShouldCallAllocateToMethod() {
List<HostVO> hosts = new ArrayList<>(Arrays.asList(host1, host2));
Mockito.doReturn(serviceOffering).when(virtualMachineProfile).getServiceOffering();
Mockito.doReturn(vmTemplateVO).when(virtualMachineProfile).getTemplate();
Mockito.doReturn(account).when(virtualMachineProfile).getOwner();
Mockito.doReturn(hostTag).when(serviceOffering).getHostTag();
Mockito.doReturn(templateTag).when(vmTemplateVO).getTemplateTag();
Mockito.doReturn(hosts).when(firstFitAllocatorSpy).retrieveHosts(Mockito.any(VirtualMachineProfile.class), Mockito.any(Host.Type.class), Mockito.nullable(List.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyString());
Mockito.doReturn(hosts).when(firstFitAllocatorSpy).allocateTo(Mockito.any(VirtualMachineProfile.class), Mockito.any(DeploymentPlan.class), Mockito.any(ServiceOffering.class), Mockito.any(VMTemplateVO.class), Mockito.any(DeploymentPlanner.ExcludeList.class), Mockito.anyList(), Mockito.anyInt(), Mockito.anyBoolean(), Mockito.any(Account.class));
Mockito.doNothing().when(firstFitAllocatorSpy).addHostsToAvoidSet(Mockito.any(Host.Type.class), Mockito.any(DeploymentPlanner.ExcludeList.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyList());
List<Host> suitableHosts = firstFitAllocatorSpy.allocateTo(virtualMachineProfile, deploymentPlan, type, excludeList, null, HostAllocator.RETURN_UPTO_ALL, considerReservedCapacity);
Mockito.verify(firstFitAllocatorSpy, Mockito.times(1)).allocateTo(virtualMachineProfile, deploymentPlan, serviceOffering, vmTemplateVO, excludeList, hosts, HostAllocator.RETURN_UPTO_ALL, considerReservedCapacity, account);
Assert.assertEquals(2, suitableHosts.size());
}
@Test
public void allocateToTestProvidedHostsNotNullShouldCallAddHostsToAvoidSetMethod() {
List<HostVO> hosts = new ArrayList<>(Arrays.asList(host1, host2));
Mockito.doReturn(serviceOffering).when(virtualMachineProfile).getServiceOffering();
Mockito.doReturn(vmTemplateVO).when(virtualMachineProfile).getTemplate();
Mockito.doReturn(account).when(virtualMachineProfile).getOwner();
Mockito.doReturn(hostTag).when(serviceOffering).getHostTag();
Mockito.doReturn(templateTag).when(vmTemplateVO).getTemplateTag();
Mockito.doReturn(hosts).when(firstFitAllocatorSpy).retrieveHosts(Mockito.any(VirtualMachineProfile.class), Mockito.any(Host.Type.class), Mockito.nullable(List.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyString());
Mockito.doReturn(hosts).when(firstFitAllocatorSpy).allocateTo(Mockito.any(VirtualMachineProfile.class), Mockito.any(DeploymentPlan.class), Mockito.any(ServiceOffering.class), Mockito.any(VMTemplateVO.class), Mockito.any(DeploymentPlanner.ExcludeList.class), Mockito.anyList(), Mockito.anyInt(), Mockito.anyBoolean(), Mockito.any(Account.class));
Mockito.doNothing().when(firstFitAllocatorSpy).addHostsToAvoidSet(Mockito.any(Host.Type.class), Mockito.any(DeploymentPlanner.ExcludeList.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyList());
firstFitAllocatorSpy.allocateTo(virtualMachineProfile, deploymentPlan, type, excludeList, null, HostAllocator.RETURN_UPTO_ALL, considerReservedCapacity);
Mockito.verify(firstFitAllocatorSpy, Mockito.times(1)).addHostsToAvoidSet(Mockito.any(Host.Type.class), Mockito.any(DeploymentPlanner.ExcludeList.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyList());
}
@Test
public void retrieveHostsTestHostsToFilterIsNullAndHaTagNotNullShouldReturnOnlyHostsWithHaTag() {
List<HostVO> allUpAndEnabledHosts = new ArrayList<>(Arrays.asList(host1, host2, host3));
List<HostVO> hostsWithHaTag = new ArrayList<>(Arrays.asList(host1, host2));
String hostVmTag = "haVmTag";
Mockito.doReturn(hostVmTag).when(virtualMachineProfile).getParameter(Mockito.any(VirtualMachineProfile.Param.class));
Mockito.doReturn(allUpAndEnabledHosts).when(resourceManagerMock).listAllUpAndEnabledHosts(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong());
Mockito.doReturn(hostsWithHaTag).when(hostDaoMock).listByHostTag(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString());
Mockito.doNothing().when(firstFitAllocatorSpy).filterHostsWithUefiEnabled(Mockito.any(Host.Type.class), Mockito.any(VirtualMachineProfile.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyList());
Mockito.doNothing().when(firstFitAllocatorSpy).addHostsBasedOnTagRules(Mockito.anyString(), Mockito.anyList());
List<HostVO> resultHosts = firstFitAllocatorSpy.retrieveHosts(virtualMachineProfile, type, emptyList, clusterId, podId, dcId, hostTag, templateTag);
Assert.assertEquals(2, resultHosts.size());
Assert.assertEquals(host1, resultHosts.get(0));
Assert.assertEquals(host2, resultHosts.get(1));
}
@Test
public void retrieveHostsTestHostsToFilterIsNotNullAndHaTagNotNullShouldReturnOnlyHostsToFilterWithHaTag() {
List<HostVO> hostsToFilter = new ArrayList<>(Arrays.asList(host1, host2, host3));
List<HostVO> hostsWithHaTag = new ArrayList<>(Arrays.asList(host1, host2));
String hostVmTag = "haVmTag";
Mockito.doReturn(hostVmTag).when(virtualMachineProfile).getParameter(Mockito.any(VirtualMachineProfile.Param.class));
Mockito.doReturn(hostsWithHaTag).when(hostDaoMock).listByHostTag(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString());
Mockito.doNothing().when(firstFitAllocatorSpy).filterHostsWithUefiEnabled(Mockito.any(Host.Type.class), Mockito.any(VirtualMachineProfile.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyList());
Mockito.doNothing().when(firstFitAllocatorSpy).addHostsBasedOnTagRules(Mockito.anyString(), Mockito.anyList());
List<HostVO> resultHosts = firstFitAllocatorSpy.retrieveHosts(virtualMachineProfile, type, hostsToFilter, clusterId, podId, dcId, hostTag, templateTag);
Assert.assertEquals(2, resultHosts.size());
Assert.assertEquals(host1, resultHosts.get(0));
Assert.assertEquals(host2, resultHosts.get(1));
}
@Test
public void retrieveHostsTestHostsToFilterIsNullAndNoHaTagAndNoHostTagShouldReturnOnlyAllUpAndEnabledNonHaHosts() {
List<HostVO> allUpAndEnabledHosts = new ArrayList<>(Arrays.asList(host1, host2, host3));
List<HostVO> upAndEnabledHostsWithNoHa = new ArrayList<>(Arrays.asList(host1, host2));
Mockito.doReturn(allUpAndEnabledHosts).when(resourceManagerMock).listAllUpAndEnabledHosts(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong());
Mockito.doReturn(upAndEnabledHostsWithNoHa).when(resourceManagerMock).listAllUpAndEnabledNonHAHosts(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong());
Mockito.doNothing().when(firstFitAllocatorSpy).filterHostsWithUefiEnabled(Mockito.any(Host.Type.class), Mockito.any(VirtualMachineProfile.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyList());
Mockito.doNothing().when(firstFitAllocatorSpy).addHostsBasedOnTagRules(Mockito.nullable(String.class), Mockito.anyList());
List<HostVO> resultHosts = firstFitAllocatorSpy.retrieveHosts(virtualMachineProfile, type, emptyList, clusterId, podId, dcId, null, null);
Assert.assertEquals(2, resultHosts.size());
Assert.assertEquals(host1, resultHosts.get(0));
Assert.assertEquals(host2, resultHosts.get(1));
}
@Test
public void retrieveHostsTestHostsToFilterIsNullAndNoHaTagWithHostTagShouldCallRetainHostsMatchingServiceOfferingAndTemplateTags() {
List<HostVO> allUpAndEnabledHosts = new ArrayList<>(Arrays.asList(host1, host2, host3));
Mockito.doReturn(allUpAndEnabledHosts).when(resourceManagerMock).listAllUpAndEnabledHosts(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong());
Mockito.doNothing().when(firstFitAllocatorSpy).retainHostsMatchingServiceOfferingAndTemplateTags(Mockito.anyList(), Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyString());
Mockito.doNothing().when(firstFitAllocatorSpy).filterHostsWithUefiEnabled(Mockito.any(Host.Type.class), Mockito.any(VirtualMachineProfile.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyList());
Mockito.doNothing().when(firstFitAllocatorSpy).addHostsBasedOnTagRules(Mockito.anyString(), Mockito.anyList());
firstFitAllocatorSpy.retrieveHosts(virtualMachineProfile, type, emptyList, clusterId, podId, dcId, hostTag, templateTag);
Mockito.verify(firstFitAllocatorSpy, Mockito.times(1)).retainHostsMatchingServiceOfferingAndTemplateTags(Mockito.anyList(), Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString(), Mockito.anyString());
}
@Test
public void addHostsToAvoidSetTestAllHostsWereConsideredForAllocationShouldNotAddAnyHostToTheAvoidSet() {
List<HostVO> suitableHosts = new ArrayList<>(Arrays.asList(host1, host2, host3));
Mockito.doReturn(suitableHosts).when(hostDaoMock).listAllUpAndEnabledNonHAHosts(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.nullable(String.class));
firstFitAllocatorSpy.addHostsToAvoidSet(type, excludeList, clusterId, podId, dcId, suitableHosts);
Assert.assertTrue(excludeList.getHostsToAvoid().isEmpty());
}
@Test
public void addHostsToAvoidSetTestNotAllHostsWereConsideredForAllocationShouldAddHostToTheAvoidSet() {
List<HostVO> allUpAndEnabledNonHAHosts = new ArrayList<>(Arrays.asList(host1, host2, host3));
List<HostVO> consideredHosts = new ArrayList<>(Arrays.asList(host2, host3));
Mockito.doReturn(1L).when(host1).getId();
Mockito.doCallRealMethod().when(excludeList).addHost(Mockito.anyLong());
Mockito.doCallRealMethod().when(excludeList).getHostsToAvoid();
Mockito.doReturn(allUpAndEnabledNonHAHosts).when(hostDaoMock).listAllUpAndEnabledNonHAHosts(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.nullable(String.class));
firstFitAllocatorSpy.addHostsToAvoidSet(type, excludeList, clusterId, podId, dcId, consideredHosts);
Assert.assertEquals(1, excludeList.getHostsToAvoid().size());
Assert.assertTrue(excludeList.getHostsToAvoid().contains(1L));
}
@Test
public void filterHostsWithUefiEnabledTestNoDetailWithUefiShouldNotFilterAnyHost() {
List<HostVO> suitableHosts = new ArrayList<>(Arrays.asList(host1, host2, host3));
VMInstanceDetailVO userVmDetailVO = null;
Mockito.doReturn(userVmDetailVO).when(userVmDetailsDaoMock).findDetail(Mockito.anyLong(), Mockito.anyString());
firstFitAllocatorSpy.filterHostsWithUefiEnabled(type, virtualMachineProfile, clusterId, podId, dcId, suitableHosts);
Assert.assertEquals(3, suitableHosts.size());
Assert.assertEquals(host1, suitableHosts.get(0));
Assert.assertEquals(host2, suitableHosts.get(1));
Assert.assertEquals(host3, suitableHosts.get(2));
}
@Test
public void filterHostsWithUefiEnabledTestDetailWithUefiWithInvalidModeShouldNotFilterAnyHost() {
List<HostVO> suitableHosts = new ArrayList<>(Arrays.asList(host1, host2, host3));
VMInstanceDetailVO userVmDetailVO = mock(VMInstanceDetailVO.class);
String bootMode = "Invalid mode";
Mockito.doReturn(bootMode).when(userVmDetailVO).getValue();
Mockito.doReturn(userVmDetailVO).when(userVmDetailsDaoMock).findDetail(Mockito.anyLong(), Mockito.anyString());
firstFitAllocatorSpy.filterHostsWithUefiEnabled(type, virtualMachineProfile, clusterId, podId, dcId, suitableHosts);
Assert.assertEquals(3, suitableHosts.size());
Assert.assertEquals(host1, suitableHosts.get(0));
Assert.assertEquals(host2, suitableHosts.get(1));
Assert.assertEquals(host3, suitableHosts.get(2));
}
@Test
public void filterHostsWithUefiEnabledTestDetailWithUefiWithLegacyModeShouldFilterHost() {
List<HostVO> suitableHosts = new ArrayList<>(Arrays.asList(host1, host2, host3));
List<HostVO> uefiHosts = new ArrayList<>(Arrays.asList(host2, host3));
VMInstanceDetailVO userVmDetailVO = mock(VMInstanceDetailVO.class);
String bootMode = ApiConstants.BootMode.LEGACY.toString();
Mockito.doReturn(bootMode).when(userVmDetailVO).getValue();
Mockito.doReturn(userVmDetailVO).when(userVmDetailsDaoMock).findDetail(Mockito.anyLong(), Mockito.anyString());
Mockito.doReturn(uefiHosts).when(hostDaoMock).listByHostCapability(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString());
firstFitAllocatorSpy.filterHostsWithUefiEnabled(type, virtualMachineProfile, clusterId, podId, dcId, suitableHosts);
Assert.assertEquals(2, suitableHosts.size());
Assert.assertEquals(host2, suitableHosts.get(0));
Assert.assertEquals(host3, suitableHosts.get(1));
}
@Test
public void filterHostsWithUefiEnabledTestDetailWithUefiWithSecureModeShouldFilterHost() {
List<HostVO> suitableHosts = new ArrayList<>(Arrays.asList(host1, host2, host3));
List<HostVO> uefiHosts = new ArrayList<>(Arrays.asList(host2, host3));
VMInstanceDetailVO userVmDetailVO = mock(VMInstanceDetailVO.class);
String bootMode = ApiConstants.BootMode.SECURE.toString();
Mockito.doReturn(bootMode).when(userVmDetailVO).getValue();
Mockito.doReturn(userVmDetailVO).when(userVmDetailsDaoMock).findDetail(Mockito.anyLong(), Mockito.anyString());
Mockito.doReturn(uefiHosts).when(hostDaoMock).listByHostCapability(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString());
firstFitAllocatorSpy.filterHostsWithUefiEnabled(type, virtualMachineProfile, clusterId, podId, dcId, suitableHosts);
Assert.assertEquals(2, suitableHosts.size());
Assert.assertEquals(host2, suitableHosts.get(0));
Assert.assertEquals(host3, suitableHosts.get(1));
}
@Test
public void offeringRequestedVGpuAndHostDoesNotHaveItTestVGpuRequestedButHostDoesNotHaveItShouldReturnTrue() {
Mockito.doReturn(1L).when(host1).getId();
Mockito.doCallRealMethod().when(excludeList).addHost(Mockito.anyLong());
Mockito.doCallRealMethod().when(excludeList).getHostsToAvoid();
Mockito.doReturn(false).when(resourceManagerMock).isGPUDeviceAvailable(Mockito.any(ServiceOffering.class), Mockito.any(Host.class), Mockito.any(Long.class));
boolean result = firstFitAllocatorSpy.offeringRequestedVGpuAndHostDoesNotHaveIt(serviceOffering, virtualMachineProfile, excludeList, host1);
Assert.assertTrue(result);
Assert.assertEquals(1, excludeList.getHostsToAvoid().size());
Assert.assertTrue(excludeList.getHostsToAvoid().contains(1L));
}
@Test
public void offeringRequestedVGpuAndHostDoesNotHaveItTestVGpuRequestedAndHostDoesHaveItShouldReturnFalse() {
Mockito.doReturn(true).when(resourceManagerMock).isGPUDeviceAvailable(Mockito.any(ServiceOffering.class), Mockito.any(Host.class), Mockito.any(Long.class));
boolean result = firstFitAllocatorSpy.offeringRequestedVGpuAndHostDoesNotHaveIt(serviceOffering, virtualMachineProfile, excludeList, host1);
Assert.assertFalse(result);
Mockito.verify(excludeList, Mockito.never()).addHost(Mockito.anyLong());
}
@Test
public void filterHostWithNoHvmIfTemplateRequestedTestTemplateDoesNotRequireHvm() {
List<HostVO> hosts = new ArrayList<>(Arrays.asList(host1, host2, host3));
Mockito.doReturn(false).when(vmTemplateVO).isRequiresHvm();
List<Host> suitableHosts = firstFitAllocatorSpy.filterHostWithNoHvmIfTemplateRequested(vmTemplateVO, hosts);
Assert.assertEquals(3, suitableHosts.size());
Assert.assertEquals(host1, suitableHosts.get(0));
Assert.assertEquals(host2, suitableHosts.get(1));
Assert.assertEquals(host3, suitableHosts.get(2));
}
@Test
public void filterHostWithNoHvmIfTemplateRequestedTestTemplateRequiresHvmShouldReturnOnlyHvmHosts() {
List<HostVO> hosts = new ArrayList<>(Arrays.asList(host1, host2, host3));
Mockito.doReturn(true).when(vmTemplateVO).isRequiresHvm();
Mockito.doReturn(true).when(firstFitAllocatorSpy).hostSupportsHVM(host1);
Mockito.doReturn(false).when(firstFitAllocatorSpy).hostSupportsHVM(host2);
Mockito.doReturn(true).when(firstFitAllocatorSpy).hostSupportsHVM(host3);
List<Host> suitableHosts = firstFitAllocatorSpy.filterHostWithNoHvmIfTemplateRequested(vmTemplateVO, hosts);
Assert.assertEquals(2, suitableHosts.size());
Assert.assertEquals(host1, suitableHosts.get(0));
Assert.assertEquals(host3, suitableHosts.get(1));
}
@Test
public void prioritizeHostsByGpuEnabledTestServiceOfferingRequestedVGpuViaDetailShouldDoNothing() {
List<Host> hosts = new ArrayList<>(Arrays.asList(host1, host2, host3));
ServiceOfferingDetailsVO requestedVGpuType = mock(ServiceOfferingDetailsVO.class);
Mockito.doReturn(requestedVGpuType).when(serviceOfferingDetailsDao).findDetail(Mockito.anyLong(), Mockito.anyString());
firstFitAllocatorSpy.prioritizeHostsByGpuEnabled(serviceOffering, hosts);
Assert.assertEquals(3, hosts.size());
Assert.assertEquals(host1, hosts.get(0));
Assert.assertEquals(host2, hosts.get(1));
Assert.assertEquals(host3, hosts.get(2));
}
@Test
public void prioritizeHostsByGpuEnabledTestServiceOfferingRequestedVGpuViaProfileIdShouldDoNothing() {
List<Host> hosts = new ArrayList<>(Arrays.asList(host1, host2, host3));
Mockito.doReturn(1L).when(serviceOffering).getVgpuProfileId();
firstFitAllocatorSpy.prioritizeHostsByGpuEnabled(serviceOffering, hosts);
Assert.assertEquals(3, hosts.size());
Assert.assertEquals(host1, hosts.get(0));
Assert.assertEquals(host2, hosts.get(1));
Assert.assertEquals(host3, hosts.get(2));
}
@Test
public void prioritizeHostsByGpuEnabledTestServiceOfferingDidNotRequestVGpuShouldReorderList() {
List<Host> allHosts = new ArrayList<>(Arrays.asList(host1, host2, host3));
Mockito.doReturn(null).when(serviceOfferingDetailsDao).findDetail(Mockito.anyLong(), Mockito.anyString());
Mockito.doReturn(null).when(serviceOffering).getVgpuProfileId();
Mockito.doReturn(1L).when(host1).getId();
Mockito.doReturn(2L).when(host2).getId();
Mockito.doReturn(3L).when(host3).getId();
Mockito.doReturn(true).when(resourceManagerMock).isHostGpuEnabled(1L);
Mockito.doReturn(false).when(resourceManagerMock).isHostGpuEnabled(2L);
Mockito.doReturn(false).when(resourceManagerMock).isHostGpuEnabled(3L);
firstFitAllocatorSpy.prioritizeHostsByGpuEnabled(serviceOffering, allHosts);
Assert.assertEquals(3, allHosts.size());
Assert.assertEquals(host2, allHosts.get(0));
Assert.assertEquals(host3, allHosts.get(1));
Assert.assertEquals(host1, allHosts.get(2));
}
@Test
public void prioritizeHostsByGpuEnabledTestServiceOfferingDidNotRequestVGpuShouldNotReorderListIfThereIsNoHostWithVGpu() {
List<Host> allHosts = new ArrayList<>(Arrays.asList(host1, host2, host3));
Mockito.doReturn(null).when(serviceOfferingDetailsDao).findDetail(Mockito.anyLong(), Mockito.anyString());
Mockito.doReturn(null).when(serviceOffering).getVgpuProfileId();
Mockito.doReturn(1L).when(host1).getId();
Mockito.doReturn(2L).when(host2).getId();
Mockito.doReturn(3L).when(host3).getId();
Mockito.doReturn(false).when(resourceManagerMock).isHostGpuEnabled(Mockito.anyLong());
firstFitAllocatorSpy.prioritizeHostsByGpuEnabled(serviceOffering, allHosts);
Assert.assertEquals(3, allHosts.size());
Assert.assertEquals(host1, allHosts.get(0));
Assert.assertEquals(host2, allHosts.get(1));
Assert.assertEquals(host3, allHosts.get(2));
}
@Test
public void prioritizeHostsByHvmCapabilityTestTemplateDidNotRequestedHvmShouldPutHostThatDoesNotSupportHvmInStartOfThePriorityList() {
List<Host> hostsToCheck = new ArrayList<>(Arrays.asList(host1, host2, host3));
List<Host> prioritizedHosts = new ArrayList<>();
Mockito.doReturn(false).when(vmTemplateVO).isRequiresHvm();
Mockito.doReturn(true).when(firstFitAllocatorSpy).hostSupportsHVM(host1);
Mockito.doReturn(false).when(firstFitAllocatorSpy).hostSupportsHVM(host2);
Mockito.doReturn(true).when(firstFitAllocatorSpy).hostSupportsHVM(host3);
firstFitAllocatorSpy.prioritizeHostsByHvmCapability(vmTemplateVO, hostsToCheck, prioritizedHosts);
Assert.assertEquals(3, prioritizedHosts.size());
Assert.assertEquals(host2, prioritizedHosts.get(0));
Assert.assertEquals(host1, prioritizedHosts.get(1));
Assert.assertEquals(host3, prioritizedHosts.get(2));
}
@Test
public void prioritizeHostsByHvmCapabilityTestTemplateRequiresHvmShouldNotReorderList() {
List<Host> hostsToCheck = new ArrayList<>(Arrays.asList(host1, host2, host3));
List<Host> prioritizedHosts = new ArrayList<>();
Mockito.doReturn(true).when(vmTemplateVO).isRequiresHvm();
firstFitAllocatorSpy.prioritizeHostsByHvmCapability(vmTemplateVO, hostsToCheck, prioritizedHosts);
Assert.assertEquals(3, prioritizedHosts.size());
Assert.assertEquals(host1, prioritizedHosts.get(0));
Assert.assertEquals(host2, prioritizedHosts.get(1));
Assert.assertEquals(host3, prioritizedHosts.get(2));
}
@Test
public void prioritizeHostsWithMatchingGuestOsTestShouldPutMatchingHostInHighPriorityAndHostsThatDoesNotMatchInLowPriorityList() {
List<Host> hostsToCheck = new ArrayList<>(Arrays.asList(host1, host2, host3));
List<Host> highPriorityHosts = new ArrayList<>();
List<Host> lowPriorityHosts = new ArrayList<>();
String guestOsCategory1 = "guestOsCategory1";
String guestOsCategory2 = "guestOsCategory2";
Mockito.doReturn(guestOsCategory1).when(firstFitAllocatorSpy).getTemplateGuestOSCategory(vmTemplateVO);
Mockito.doReturn(guestOsCategory1).when(firstFitAllocatorSpy).getHostGuestOSCategory(host1);
Mockito.doReturn(guestOsCategory2).when(firstFitAllocatorSpy).getHostGuestOSCategory(host2);
Mockito.doReturn(null).when(firstFitAllocatorSpy).getHostGuestOSCategory(host3);
firstFitAllocatorSpy.prioritizeHostsWithMatchingGuestOs(vmTemplateVO,hostsToCheck, highPriorityHosts, lowPriorityHosts);
Assert.assertEquals(1, highPriorityHosts.size());
Assert.assertEquals(host1, highPriorityHosts.get(0));
Assert.assertEquals(1, lowPriorityHosts.size());
Assert.assertEquals(host2, lowPriorityHosts.get(0));
}
@Test
public void testHostByCombinedCapacityOrder() {
// Test scenario 1: Default capacity usage (0.5 weight)
List<CapacityVO> mockCapacity = getHostCapacities();
Map<Long, Double> hostByCombinedCapacity = FirstFitAllocator.getHostByCombinedCapacities(mockCapacity, 0.5);
// Verify host ordering and capacity values
Long firstHostId = hostByCombinedCapacity.keySet().iterator().next();
Assert.assertEquals("Host with ID 1 should be first in ordering", Long.valueOf(1L), firstHostId);
Assert.assertEquals("Host 1 combined capacity should match expected value",
0.9609375, hostByCombinedCapacity.get(1L), TOLERANCE);
Assert.assertEquals("Host 2 combined capacity should match expected value",
0.9296875, hostByCombinedCapacity.get(2L), TOLERANCE);
// Test scenario 2: Modified capacity usage (0.7 weight)
when(mockCapacity.get(0).getUsedCapacity()).thenReturn(1500L);
hostByCombinedCapacity = FirstFitAllocator.getHostByCombinedCapacities(mockCapacity, 0.7);
// Verify new ordering after capacity change
firstHostId = hostByCombinedCapacity.keySet().iterator().next();
Assert.assertEquals("Host with ID 2 should be first after capacity change", Long.valueOf(2L), firstHostId);
Assert.assertEquals("Host 2 combined capacity should match expected value after change",
0.9515625, hostByCombinedCapacity.get(2L), TOLERANCE);
Assert.assertEquals("Host 1 combined capacity should match expected value after change",
0.9484375, hostByCombinedCapacity.get(1L), TOLERANCE);
}
List<CapacityVO> getHostCapacities() {
CapacityVO cpuCapacity1 = mock(CapacityVO.class);
when(cpuCapacity1.getHostOrPoolId()).thenReturn(1L);
when(cpuCapacity1.getTotalCapacity()).thenReturn(32000L);
when(cpuCapacity1.getReservedCapacity()).thenReturn(0L);
when(cpuCapacity1.getUsedCapacity()).thenReturn(500L);
when(cpuCapacity1.getCapacityType()).thenReturn(CapacityVO.CAPACITY_TYPE_CPU);
CapacityVO cpuCapacity2 = mock(CapacityVO.class);
when(cpuCapacity2.getHostOrPoolId()).thenReturn(2L);
when(cpuCapacity2.getTotalCapacity()).thenReturn(32000L);
when(cpuCapacity2.getReservedCapacity()).thenReturn(0L);
when(cpuCapacity2.getUsedCapacity()).thenReturn(500L);
when(cpuCapacity2.getCapacityType()).thenReturn(CapacityVO.CAPACITY_TYPE_CPU);
CapacityVO memCapacity1 = mock(CapacityVO.class);
when(memCapacity1.getHostOrPoolId()).thenReturn(1L);
when(memCapacity1.getTotalCapacity()).thenReturn(8589934592L);
when(memCapacity1.getReservedCapacity()).thenReturn(0L);
when(memCapacity1.getUsedCapacity()).thenReturn(536870912L);
when(memCapacity1.getCapacityType()).thenReturn(CapacityVO.CAPACITY_TYPE_MEMORY);
CapacityVO memCapacity2 = mock(CapacityVO.class);
when(memCapacity2.getHostOrPoolId()).thenReturn(2L);
when(memCapacity2.getTotalCapacity()).thenReturn(8589934592L);
when(memCapacity2.getReservedCapacity()).thenReturn(0L);
when(memCapacity2.getUsedCapacity()).thenReturn(1073741824L);
when(memCapacity1.getCapacityType()).thenReturn(CapacityVO.CAPACITY_TYPE_MEMORY);
return Arrays.asList(cpuCapacity1, memCapacity1, cpuCapacity2, memCapacity2);
}
}

View File

@ -0,0 +1,332 @@
// 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.agent.manager.allocator.impl;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.cloud.agent.manager.allocator.HostAllocator;
import com.cloud.deploy.DeploymentPlan;
import com.cloud.deploy.DeploymentPlanner;
import com.cloud.offering.ServiceOffering;
import com.cloud.resource.ResourceManager;
import com.cloud.service.ServiceOfferingVO;
import com.cloud.storage.VMTemplateVO;
import com.cloud.vm.VirtualMachineProfile;
import org.apache.commons.collections.CollectionUtils;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
import com.cloud.host.Host;
import com.cloud.host.HostVO;
import com.cloud.host.dao.HostDao;
@RunWith(MockitoJUnitRunner.class)
public class RandomAllocatorTest {
@Mock
HostDao hostDao;
@Spy
@InjectMocks
RandomAllocator randomAllocator;
@Mock
ResourceManager resourceManagerMock;
private final Host.Type type = Host.Type.Routing;
private final Long clusterId = 1L;
private final Long podId = 2L;
private final Long zoneId = 3L;
private final List<HostVO> emptyList = new ArrayList<>();
private final String hostTag = "hostTag";
private final HostVO host1 = Mockito.mock(HostVO.class);
private final HostVO host2 = Mockito.mock(HostVO.class);
private final HostVO host3 = Mockito.mock(HostVO.class);
private final VMTemplateVO vmTemplateVO = Mockito.mock(VMTemplateVO.class);
private final ServiceOfferingVO serviceOffering = Mockito.mock(ServiceOfferingVO.class);
private final DeploymentPlanner.ExcludeList excludeList = Mockito.mock(DeploymentPlanner.ExcludeList.class);
private final VirtualMachineProfile virtualMachineProfile = Mockito.mock(VirtualMachineProfile.class);
private final DeploymentPlan deploymentPlan = Mockito.mock(DeploymentPlan.class);
private final boolean considerReservedCapacity = true;
@Test
public void testListHostsByTags() {
Host.Type type = Host.Type.Routing;
Long id = 1L;
String templateTag = "tag1";
String offeringTag = "tag2";
HostVO host1 = Mockito.mock(HostVO.class);
HostVO host2 = Mockito.mock(HostVO.class);
Mockito.when(hostDao.listByHostTag(type, clusterId, podId, zoneId, offeringTag)).thenReturn(List.of(host1, host2));
// No template tagged host
ArrayList<HostVO> noTemplateTaggedHosts = new ArrayList<>(Arrays.asList(host1, host2));
Mockito.when(hostDao.listByHostTag(type, clusterId, podId, zoneId, templateTag)).thenReturn(new ArrayList<>());
randomAllocator.retainHostsMatchingServiceOfferingAndTemplateTags(noTemplateTaggedHosts, type, zoneId, podId, clusterId, offeringTag, templateTag);
Assert.assertTrue(CollectionUtils.isEmpty(noTemplateTaggedHosts));
// Different template tagged host
ArrayList<HostVO> differentTemplateTaggedHost = new ArrayList<>(Arrays.asList(host1, host2));
HostVO host3 = Mockito.mock(HostVO.class);
Mockito.when(hostDao.listByHostTag(type, clusterId, podId, zoneId, templateTag)).thenReturn(List.of(host3));
randomAllocator.retainHostsMatchingServiceOfferingAndTemplateTags(differentTemplateTaggedHost, type, zoneId, podId, clusterId, offeringTag, templateTag);
Assert.assertTrue(CollectionUtils.isEmpty(differentTemplateTaggedHost));
// Matching template tagged host
ArrayList<HostVO> matchingTemplateTaggedHost = new ArrayList<>(Arrays.asList(host1, host2));
Mockito.when(hostDao.listByHostTag(type, clusterId, podId, zoneId, templateTag)).thenReturn(List.of(host1));
randomAllocator.retainHostsMatchingServiceOfferingAndTemplateTags(matchingTemplateTaggedHost, type, zoneId, podId, clusterId, offeringTag, templateTag);
Assert.assertFalse(CollectionUtils.isEmpty(matchingTemplateTaggedHost));
Assert.assertEquals(1, matchingTemplateTaggedHost.size());
// No template tag
ArrayList<HostVO> noTemplateTag = new ArrayList<>(Arrays.asList(host1, host2));
randomAllocator.retainHostsMatchingServiceOfferingAndTemplateTags(noTemplateTag, type, zoneId, podId, clusterId, offeringTag, null);
Assert.assertFalse(CollectionUtils.isEmpty(noTemplateTag));
Assert.assertEquals(2, noTemplateTag.size());
// No offering tag
ArrayList<HostVO> noOfferingTag = new ArrayList<>(Arrays.asList(host1, host2));
randomAllocator.retainHostsMatchingServiceOfferingAndTemplateTags(noOfferingTag, type, zoneId, podId, clusterId, null, templateTag);
Assert.assertFalse(CollectionUtils.isEmpty(noOfferingTag));
Assert.assertEquals(1, noOfferingTag.size());
}
@Test
public void findSuitableHostsTestHostTypeStorageShouldReturnNull() {
List<Host> suitableHosts = randomAllocator.findSuitableHosts(virtualMachineProfile, deploymentPlan, Host.Type.Storage, excludeList, null, HostAllocator.RETURN_UPTO_ALL, considerReservedCapacity);
Assert.assertNull(suitableHosts);
}
@Test
public void findSuitableHostsTestNoAvailableHostsShouldReturnNull() {
Mockito.doReturn(serviceOffering).when(virtualMachineProfile).getServiceOffering();
Mockito.doReturn(vmTemplateVO).when(virtualMachineProfile).getTemplate();
Mockito.doReturn(hostTag).when(serviceOffering).getHostTag();
Mockito.doReturn(emptyList).when(randomAllocator).retrieveHosts(Mockito.any(Host.Type.class), Mockito.nullable(List.class), Mockito.any(VMTemplateVO.class), Mockito.anyString(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong());
List<Host> suitableHosts = randomAllocator.findSuitableHosts(virtualMachineProfile, deploymentPlan, type, excludeList, null, HostAllocator.RETURN_UPTO_ALL, considerReservedCapacity);
Assert.assertNull(suitableHosts);
}
@Test
public void findSuitableHostsTestAvailableHostsShouldCallFilterAvailableHostsOnce() {
List<HostVO> hosts = new ArrayList<>(Arrays.asList(host1, host2));
Mockito.doReturn(serviceOffering).when(virtualMachineProfile).getServiceOffering();
Mockito.doReturn(vmTemplateVO).when(virtualMachineProfile).getTemplate();
Mockito.doReturn(hostTag).when(serviceOffering).getHostTag();
Mockito.doReturn(hosts).when(randomAllocator).retrieveHosts(Mockito.any(Host.Type.class), Mockito.nullable(List.class), Mockito.any(VMTemplateVO.class), Mockito.anyString(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong());
Mockito.doReturn(hosts).when(randomAllocator).filterAvailableHosts(Mockito.any(DeploymentPlanner.ExcludeList.class), Mockito.anyInt(), Mockito.anyBoolean(), Mockito.anyList(), Mockito.any(ServiceOffering.class));
List<Host> suitableHosts = randomAllocator.findSuitableHosts(virtualMachineProfile, deploymentPlan, type, excludeList, null, HostAllocator.RETURN_UPTO_ALL, considerReservedCapacity);
Mockito.verify(randomAllocator, Mockito.times(1)).filterAvailableHosts(Mockito.any(DeploymentPlanner.ExcludeList.class), Mockito.anyInt(), Mockito.anyBoolean(), Mockito.anyList(), Mockito.any(ServiceOffering.class));
Assert.assertEquals(2, suitableHosts.size());
}
@Test
public void filterAvailableHostsTestAvailableHostsReachedReturnUpToLimitShouldReturnOnlyHostsWithinLimit() {
List<HostVO> hosts = new ArrayList<>(Arrays.asList(host1, host2));
int returnUpTo = 1;
Mockito.doReturn(false).when(excludeList).shouldAvoid(Mockito.any(Host.class));
Mockito.doReturn(true).when(randomAllocator).hostHasCpuCapabilityAndCapacity(Mockito.anyBoolean(), Mockito.any(ServiceOffering.class), Mockito.any(Host.class));
List<Host> suitableHosts = randomAllocator.filterAvailableHosts(excludeList, returnUpTo, considerReservedCapacity, hosts, serviceOffering);
Assert.assertEquals(1, suitableHosts.size());
}
@Test
public void filterAvailableHostsTestReturnUpToAllShouldReturnAllAvailableHosts() {
List<HostVO> hosts = new ArrayList<>(Arrays.asList(host1, host2));
int returnUpTo = HostAllocator.RETURN_UPTO_ALL;
Mockito.doReturn(false).when(excludeList).shouldAvoid(Mockito.any(Host.class));
Mockito.doReturn(true).when(randomAllocator).hostHasCpuCapabilityAndCapacity(Mockito.anyBoolean(), Mockito.any(ServiceOffering.class), Mockito.any(Host.class));
List<Host> suitableHosts = randomAllocator.filterAvailableHosts(excludeList, returnUpTo, considerReservedCapacity, hosts, serviceOffering);
Assert.assertEquals(2, suitableHosts.size());
}
@Test
public void filterAvailableHostsTestHost1InAvoidShouldOnlyReturnHost2() {
List<HostVO> hosts = new ArrayList<>(Arrays.asList(host1, host2));
int returnUpTo = HostAllocator.RETURN_UPTO_ALL;
Mockito.doReturn(true).when(excludeList).shouldAvoid(host1);
Mockito.doReturn(false).when(excludeList).shouldAvoid(host2);
Mockito.doReturn(true).when(randomAllocator).hostHasCpuCapabilityAndCapacity(Mockito.anyBoolean(), Mockito.any(ServiceOffering.class), Mockito.any(Host.class));
List<Host> suitableHosts = randomAllocator.filterAvailableHosts(excludeList, returnUpTo, considerReservedCapacity, hosts, serviceOffering);
Assert.assertEquals(1, suitableHosts.size());
Assert.assertEquals(host2, suitableHosts.get(0));
}
@Test
public void filterAvailableHostsTestOnlyHost2HasCpuCapacityAndCapabilityShouldReturnOnlyHost2() {
List<HostVO> hosts = new ArrayList<>(Arrays.asList(host1, host2));
int returnUpTo = HostAllocator.RETURN_UPTO_ALL;
Mockito.doReturn(false).when(excludeList).shouldAvoid(Mockito.any(Host.class));
Mockito.doReturn(false).when(randomAllocator).hostHasCpuCapabilityAndCapacity(considerReservedCapacity, serviceOffering, host1);
Mockito.doReturn(true).when(randomAllocator).hostHasCpuCapabilityAndCapacity(considerReservedCapacity, serviceOffering, host2);
List<Host> suitableHosts = randomAllocator.filterAvailableHosts(excludeList, returnUpTo, considerReservedCapacity, hosts, serviceOffering);
Assert.assertEquals(1, suitableHosts.size());
Assert.assertEquals(host2, suitableHosts.get(0));
}
@Test
public void retrieveHostsTestProvidedHostsNullAndNoHostTagAndNoTagRuleShouldOnlyReturnHostsWithNoTags() {
List<HostVO> upAndEnabledHosts = new ArrayList<>(Arrays.asList(host1, host2));
List<HostVO> hostsWithNoRuleTagsAndHostTags = new ArrayList<>(Arrays.asList(host1));
Mockito.doReturn(upAndEnabledHosts).when(resourceManagerMock).listAllUpAndEnabledHosts(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong());
Mockito.doReturn(hostsWithNoRuleTagsAndHostTags).when(hostDao).listAllHostsThatHaveNoRuleTag(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong());
Mockito.doReturn(emptyList).when(hostDao).findHostsWithTagRuleThatMatchComputeOfferingTags(Mockito.nullable(String.class));
List<HostVO> availableHosts = randomAllocator.retrieveHosts(type, null, vmTemplateVO, null, clusterId, podId, zoneId);
Assert.assertEquals(1, availableHosts.size());
Assert.assertEquals(host1, availableHosts.get(0));
}
@Test
public void retrieveHostsTestProvidedHostsNullAndOnlyHostTagsRulesShouldReturnHostsThatMatchRuleTagsAndHostsWithNoTags() {
List<HostVO> upAndEnabledHosts = new ArrayList<>(Arrays.asList(host1, host2));
List<HostVO> hostsWithNoRuleTagsAndHostTags = new ArrayList<>(Arrays.asList(host1));
List<HostVO> hostsMatchingRuleTags = new ArrayList<>(Arrays.asList(host2));
Mockito.doReturn(upAndEnabledHosts).when(resourceManagerMock).listAllUpAndEnabledHosts(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong());
Mockito.doReturn(hostsWithNoRuleTagsAndHostTags).when(hostDao).listAllHostsThatHaveNoRuleTag(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong());
Mockito.doReturn(hostsMatchingRuleTags).when(hostDao).findHostsWithTagRuleThatMatchComputeOfferingTags(Mockito.nullable(String.class));
List<HostVO> availableHosts = randomAllocator.retrieveHosts(type, null, vmTemplateVO, null, clusterId, podId, zoneId);
Assert.assertEquals(2, availableHosts.size());
Assert.assertEquals(host1, availableHosts.get(0));
Assert.assertEquals(host2, availableHosts.get(1));
}
@Test
public void retrieveHostsTestProvidedHostsNullProvidedHostTagsNotNullAndNoHostWithMatchingRuleTagsShouldReturnHostWithMatchingTags() {
List<HostVO> upAndEnabledHosts = new ArrayList<>(Arrays.asList(host1, host2));
List<HostVO> hostsWithMatchingTags = new ArrayList<>(Arrays.asList(host1));
Mockito.doReturn(upAndEnabledHosts).when(resourceManagerMock).listAllUpAndEnabledHosts(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong());
Mockito.doReturn(hostsWithMatchingTags).when(hostDao).listByHostTag(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString());
Mockito.doReturn(emptyList).when(hostDao).findHostsWithTagRuleThatMatchComputeOfferingTags(Mockito.nullable(String.class));
List<HostVO> availableHosts = randomAllocator.retrieveHosts(type, null, vmTemplateVO, hostTag, clusterId, podId, zoneId);
Assert.assertEquals(1, availableHosts.size());
Assert.assertEquals(host1, availableHosts.get(0));
}
@Test
public void retrieveHostsTestProvidedHostsNullProvidedHostTagsNotNullAndHostWithMatchingRuleTagsShouldReturnHostWithHostMatchingTagsAndRuleTags() {
List<HostVO> upAndEnabledHosts = new ArrayList<>(Arrays.asList(host1, host2));
List<HostVO> hostsWithMatchingTags = new ArrayList<>(Arrays.asList(host1));
List<HostVO> hostsMatchingRuleTags = new ArrayList<>(Arrays.asList(host3));
Mockito.doReturn(upAndEnabledHosts).when(resourceManagerMock).listAllUpAndEnabledHosts(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong());
Mockito.doReturn(hostsWithMatchingTags).when(hostDao).listByHostTag(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString());
Mockito.doReturn(hostsMatchingRuleTags).when(hostDao).findHostsWithTagRuleThatMatchComputeOfferingTags(Mockito.nullable(String.class));
List<HostVO> availableHosts = randomAllocator.retrieveHosts(type, null, vmTemplateVO, hostTag, clusterId, podId, zoneId);
Assert.assertEquals(2, availableHosts.size());
Assert.assertEquals(host1, availableHosts.get(0));
Assert.assertEquals(host3, availableHosts.get(1));
}
@Test
public void retrieveHostsTestProvidedHostsNotNullAndNoHostTagAndNoTagRuleShouldOnlyReturnHostsWithNoTags() {
List<HostVO> providedHosts = new ArrayList<>(Arrays.asList(host1, host2));
List<HostVO> hostsWithNoRuleTagsAndHostTags = new ArrayList<>(Arrays.asList(host1));
Mockito.doReturn(hostsWithNoRuleTagsAndHostTags).when(hostDao).listAllHostsThatHaveNoRuleTag(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong());
Mockito.doReturn(emptyList).when(hostDao).findHostsWithTagRuleThatMatchComputeOfferingTags(Mockito.nullable(String.class));
List<HostVO> availableHosts = randomAllocator.retrieveHosts(type, providedHosts, vmTemplateVO, null, clusterId, podId, zoneId);
Assert.assertEquals(1, availableHosts.size());
Assert.assertEquals(host1, availableHosts.get(0));
}
@Test
public void retrieveHostsTestProvidedHostsNotNullAndOnlyHostTagsRulesShouldReturnHostsThatMatchRuleTagsAndHostsWithNoTags() {
List<HostVO> providedHosts = new ArrayList<>(Arrays.asList(host1, host2));
List<HostVO> hostsWithNoRuleTagsAndHostTags = new ArrayList<>(Arrays.asList(host1));
List<HostVO> hostsMatchingRuleTags = new ArrayList<>(Arrays.asList(host2));
Mockito.doReturn(hostsWithNoRuleTagsAndHostTags).when(hostDao).listAllHostsThatHaveNoRuleTag(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong());
Mockito.doReturn(hostsMatchingRuleTags).when(hostDao).findHostsWithTagRuleThatMatchComputeOfferingTags(Mockito.nullable(String.class));
List<HostVO> availableHosts = randomAllocator.retrieveHosts(type, providedHosts, vmTemplateVO, null, clusterId, podId, zoneId);
Assert.assertEquals(2, availableHosts.size());
Assert.assertEquals(host1, availableHosts.get(0));
Assert.assertEquals(host2, availableHosts.get(1));
}
@Test
public void retrieveHostsTestProvidedHostsNotNullProvidedHostTagsNotNullAndNoHostWithMatchingRuleTagsShouldReturnHostWithMatchingTags() {
List<HostVO> providedHosts = new ArrayList<>(Arrays.asList(host1, host2));
List<HostVO> hostsWithMatchingTags = new ArrayList<>(Arrays.asList(host1));
Mockito.doReturn(hostsWithMatchingTags).when(hostDao).listByHostTag(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString());
Mockito.doReturn(emptyList).when(hostDao).findHostsWithTagRuleThatMatchComputeOfferingTags(Mockito.nullable(String.class));
List<HostVO> availableHosts = randomAllocator.retrieveHosts(type, providedHosts, vmTemplateVO, hostTag, clusterId, podId, zoneId);
Assert.assertEquals(1, availableHosts.size());
Assert.assertEquals(host1, availableHosts.get(0));
}
@Test
public void retrieveHostsTestProvidedHostsNullNotProvidedHostTagsNotNullAndHostWithMatchingRuleTagsShouldReturnHostWithHostMatchingTagsAndRuleTags() {
List<HostVO> providedHosts = new ArrayList<>(Arrays.asList(host1, host2));
List<HostVO> hostsWithMatchingTags = new ArrayList<>(Arrays.asList(host1));
List<HostVO> hostsMatchingRuleTags = new ArrayList<>(Arrays.asList(host3));
Mockito.doReturn(hostsWithMatchingTags).when(hostDao).listByHostTag(Mockito.any(Host.Type.class), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyLong(), Mockito.anyString());
Mockito.doReturn(hostsMatchingRuleTags).when(hostDao).findHostsWithTagRuleThatMatchComputeOfferingTags(Mockito.nullable(String.class));
List<HostVO> availableHosts = randomAllocator.retrieveHosts(type, providedHosts, vmTemplateVO, hostTag, clusterId, podId, zoneId);
Assert.assertEquals(2, availableHosts.size());
Assert.assertEquals(host1, availableHosts.get(0));
Assert.assertEquals(host3, availableHosts.get(1));
}
}

View File

@ -423,7 +423,7 @@ class TestRestoreVMStrictTags(cloudstackTestCase):
vm.restore(self.apiclient, templateid=self.template_t2.id, expunge=True)
self.fail("VM should not be restored")
except Exception as e:
self.assertTrue("Unable to start VM with specified id" in str(e))
self.assertTrue("Unable to create a deployment for " in str(e))
class TestMigrateVMStrictTags(cloudstackTestCase):