Merge branch 'main' into feature-kms

This commit is contained in:
Vishesh 2026-05-04 11:52:34 +05:30 committed by GitHub
commit 1979f2a4d0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
66 changed files with 3126 additions and 1295 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

@ -157,6 +157,7 @@ public class ApiConstants {
public static final String CUSTOM_ID = "customid";
public static final String CUSTOM_ACTION_ID = "customactionid";
public static final String CUSTOM_JOB_ID = "customjobid";
public static final String CURRENCY = "currency";
public static final String CURRENT_START_IP = "currentstartip";
public static final String CURRENT_END_IP = "currentendip";
public static final String ENCRYPT = "encrypt";
@ -542,6 +543,7 @@ public class ApiConstants {
public static final String SESSIONKEY = "sessionkey";
public static final String SHOW_CAPACITIES = "showcapacities";
public static final String SHOW_REMOVED = "showremoved";
public static final String SHOW_RESOURCES = "showresources";
public static final String SHOW_RESOURCE_ICON = "showicon";
public static final String SHOW_INACTIVE = "showinactive";
public static final String SHOW_UNIQUE = "showunique";
@ -607,9 +609,11 @@ public class ApiConstants {
public static final String TENANT_NAME = "tenantname";
public static final String TOTAL = "total";
public static final String TOTAL_SUBNETS = "totalsubnets";
public static final String TOTAL_QUOTA = "totalquota";
public static final String TYPE = "type";
public static final String TRUST_STORE = "truststore";
public static final String TRUST_STORE_PASSWORD = "truststorepass";
public static final String UNIT = "unit";
public static final String URL = "url";
public static final String USAGE_INTERFACE = "usageinterface";
public static final String USED = "used";
@ -1308,6 +1312,8 @@ public class ApiConstants {
public static final String OBJECT_LOCKING = "objectlocking";
public static final String ENCRYPTION = "encryption";
public static final String QUOTA = "quota";
public static final String QUOTA_CONSUMED = "quotaconsumed";
public static final String QUOTA_USAGE = "quotausage";
public static final String ACCESS_KEY = "accesskey";
public static final String SOURCE_NAT_IP = "sourcenatipaddress";

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

@ -387,11 +387,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

@ -358,7 +358,8 @@ public class VMInstanceDaoImpl extends GenericDaoBase<VMInstanceVO, Long> implem
IdsPowerStateSelectSearch.entity().getPowerHostId(),
IdsPowerStateSelectSearch.entity().getPowerState(),
IdsPowerStateSelectSearch.entity().getPowerStateUpdateCount(),
IdsPowerStateSelectSearch.entity().getPowerStateUpdateTime());
IdsPowerStateSelectSearch.entity().getPowerStateUpdateTime(),
IdsPowerStateSelectSearch.entity().getState());
IdsPowerStateSelectSearch.done();
CountByOfferingId = createSearchBuilder(Integer.class);
@ -1105,10 +1106,14 @@ public class VMInstanceDaoImpl extends GenericDaoBase<VMInstanceVO, Long> implem
private boolean isPowerStateInSyncWithInstanceState(final VirtualMachine.PowerState powerState, final long powerHostId, final VMInstanceVO instance) {
State instanceState = instance.getState();
if (instanceState == null) {
logger.warn("VM {} has null instance state during power state sync check, treating as out of sync", instance);
return false;
}
if ((powerState == VirtualMachine.PowerState.PowerOff && instanceState == State.Running)
|| (powerState == VirtualMachine.PowerState.PowerOn && instanceState == State.Stopped)) {
HostVO instanceHost = hostDao.findById(instance.getHostId());
HostVO powerHost = powerHostId == instance.getHostId() ? instanceHost : hostDao.findById(powerHostId);
HostVO powerHost = instance.getHostId() != null && powerHostId == instance.getHostId() ? instanceHost : hostDao.findById(powerHostId);
logger.debug("VM: {} on host: {} and power host : {} is in {} state, but power state is {}",
instance, instanceHost, powerHost, instanceState, powerState);
return false;

View File

@ -18,3 +18,9 @@
--;
-- Schema upgrade cleanup from 4.22.0.0 to 4.22.1.0
--;
-- Entries remaining on `cloud`.`resource_reservation` during the upgrade process are stale, so delete them.
-- This script was added to normalize volume/primary storage reservations that got stuck due to a bug on VM deployment,
-- but it is more interesting to introduce a smarter logic to clean these stale reservations in the future without the need
-- for upgrades (for instance, by having a heartbeat_time column for the reservations and automatically cleaning old entries).
DELETE FROM `cloud`.`resource_reservation`;

View File

@ -282,3 +282,13 @@ CREATE TABLE IF NOT EXISTS `cloud`.`kms_database_kek_objects` (
--- Disable/enable NICs
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.nics','enabled', 'TINYINT(1) NOT NULL DEFAULT 1 COMMENT ''Indicates whether the NIC is enabled or not'' ');
--- Quota tariff/usage mapping
CREATE TABLE IF NOT EXISTS `cloud_usage`.`quota_tariff_usage` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`tariff_id` bigint(20) unsigned NOT NULL COMMENT 'ID of the tariff of the Quota usage detail calculated, foreign key to quota_tariff table',
`quota_usage_id` bigint(20) unsigned NOT NULL COMMENT 'ID of the aggregation of Quota usage details, foreign key to quota_usage table',
`quota_used` decimal(20,8) NOT NULL COMMENT 'Amount of quota used',
PRIMARY KEY (`id`),
CONSTRAINT `fk_quota_tariff_usage__tariff_id` FOREIGN KEY (`tariff_id`) REFERENCES `cloud_usage`.`quota_tariff` (`id`),
CONSTRAINT `fk_quota_tariff_usage__quota_usage_id` FOREIGN KEY (`quota_usage_id`) REFERENCES `cloud_usage`.`quota_usage` (`id`));

View File

@ -0,0 +1,35 @@
-- 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.
-- VIEW `cloud_usage`.`quota_usage_view`;
DROP VIEW IF EXISTS `cloud_usage`.`quota_usage_view`;
CREATE VIEW `cloud_usage`.`quota_usage_view` AS
SELECT qu.id,
qu.usage_item_id,
qu.zone_id,
qu.account_id,
qu.domain_id,
qu.usage_type,
qu.quota_used,
qu.start_date,
qu.end_date,
cu.usage_id AS resource_id,
cu.network_id as network_id,
cu.offering_id as offering_id
FROM `cloud_usage`.`quota_usage` qu
INNER JOIN `cloud_usage`.`cloud_usage` cu ON (cu.id = qu.usage_item_id);

View File

@ -205,6 +205,12 @@ public class SearchCriteria<K> {
}
public void setJoinParametersIfNotNull(String joinName, String conditionName, Object... params) {
if (ArrayUtils.isNotEmpty(params) && (params.length > 1 || params[0] != null)) {
setJoinParameters(joinName, conditionName, params);
}
}
public SearchCriteria<?> getJoin(String joinName) {
return _joins.get(joinName).getT();
}

View File

@ -35,6 +35,7 @@ import com.cloud.network.vpc.VpcVO;
import javax.inject.Inject;
import com.cloud.storage.StoragePoolTagVO;
import com.cloud.vm.VirtualMachine;
import org.apache.cloudstack.acl.RoleVO;
import org.apache.cloudstack.acl.dao.RoleDao;
import org.apache.cloudstack.backup.BackupOfferingVO;
@ -783,10 +784,21 @@ public class PresetVariableHelper {
value.setId(network.getUuid());
value.setName(network.getName());
value.setState(usageRecord.getState());
value.setResourceCounting(getPresetVariableValueNetworkResourceCounting(networkId));
value.setNetworkOffering(getPresetVariableValueNetworkOffering(network.getNetworkOfferingId()));
}
protected ResourceCounting getPresetVariableValueNetworkResourceCounting(Long networkId) {
ResourceCounting resourceCounting = new ResourceCounting();
List<VMInstanceVO> vmInstancesVO = vmInstanceDao.listNonRemovedVmsByTypeAndNetwork(networkId, VirtualMachine.Type.User);
int runningVms = (int) vmInstancesVO.stream().filter(vm -> vm.getState().equals(VirtualMachine.State.Running)).count();
int stoppedVms = (int) vmInstancesVO.stream().filter(vm -> vm.getState().equals(VirtualMachine.State.Stopped)).count();
resourceCounting.setRunningVms(runningVms);
resourceCounting.setStoppedVms(stoppedVms);
return resourceCounting;
}
protected GenericPresetVariable getPresetVariableValueNetworkOffering(Long networkOfferingId) {
NetworkOfferingVO networkOfferingVo = networkOfferingDao.findByIdIncludingRemoved(networkOfferingId);
validateIfObjectIsNull(networkOfferingVo, networkOfferingId, "network offering");

View File

@ -0,0 +1,52 @@
// 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 org.apache.cloudstack.quota.activationrule.presetvariables;
import org.apache.cloudstack.quota.constant.QuotaTypes;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
public class ResourceCounting {
@PresetVariableDefinition(description = "The number of running user instances.", supportedTypes = {QuotaTypes.NETWORK})
private int runningVms;
@PresetVariableDefinition(description = "The number of stopped user instances.", supportedTypes = {QuotaTypes.NETWORK})
private int stoppedVms;
public int getRunningVms() {
return runningVms;
}
public void setRunningVms(int runningVms) {
this.runningVms = runningVms;
}
public int getStoppedVms() {
return stoppedVms;
}
public void setStoppedVms(int stoppedVms) {
this.stoppedVms = stoppedVms;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE);
}
}

View File

@ -84,6 +84,9 @@ public class Value extends GenericPresetVariable {
@PresetVariableDefinition(description = "Backup offering of the backup.", supportedTypes = {QuotaTypes.BACKUP})
private BackupOffering backupOffering;
@PresetVariableDefinition(description = "The amount of resources of the usage type.")
private ResourceCounting resourceCounting;
@PresetVariableDefinition(description = "The hypervisor where the resource was deployed. Values can be: XenServer, KVM, VMware, Hyperv, BareMetal, Ovm, Ovm3 and LXC.",
supportedTypes = {QuotaTypes.RUNNING_VM, QuotaTypes.ALLOCATED_VM, QuotaTypes.VM_SNAPSHOT, QuotaTypes.SNAPSHOT})
private String hypervisorType;
@ -262,6 +265,14 @@ public class Value extends GenericPresetVariable {
this.state = state;
}
public ResourceCounting getResourceCounting() {
return resourceCounting;
}
public void setResourceCounting(ResourceCounting resourceCounting) {
this.resourceCounting = resourceCounting;
}
public GenericPresetVariable getNetworkOffering() {
return networkOffering;
}

View File

@ -0,0 +1,29 @@
//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 org.apache.cloudstack.quota.dao;
import com.cloud.utils.db.GenericDao;
import org.apache.cloudstack.quota.vo.QuotaTariffUsageVO;
import java.util.List;
public interface QuotaTariffUsageDao extends GenericDao<QuotaTariffUsageVO, Long> {
void persistQuotaTariffUsage(QuotaTariffUsageVO quotaTariffUsage);
List<QuotaTariffUsageVO> listQuotaTariffUsages(Long quotaUsageId);
}

View File

@ -0,0 +1,56 @@
//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 org.apache.cloudstack.quota.dao;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import org.apache.cloudstack.quota.vo.QuotaTariffUsageVO;
import org.springframework.stereotype.Component;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionCallback;
import com.cloud.utils.db.TransactionLegacy;
import javax.annotation.PostConstruct;
import java.util.List;
@Component
public class QuotaTariffUsageDaoImpl extends GenericDaoBase<QuotaTariffUsageVO, Long> implements QuotaTariffUsageDao {
private SearchBuilder<QuotaTariffUsageVO> searchQuotaTariffUsages;
@PostConstruct
public void init() {
searchQuotaTariffUsages = createSearchBuilder();
searchQuotaTariffUsages.and("quotaUsageId", searchQuotaTariffUsages.entity().getQuotaUsageId(), SearchCriteria.Op.EQ);
searchQuotaTariffUsages.done();
}
@Override
public void persistQuotaTariffUsage(final QuotaTariffUsageVO quotaTariffUsage) {
logger.trace("Persisting quota tariff usage [{}].", quotaTariffUsage);
Transaction.execute(TransactionLegacy.USAGE_DB, (TransactionCallback<QuotaTariffUsageVO>) status -> persist(quotaTariffUsage));
}
@Override
public List<QuotaTariffUsageVO> listQuotaTariffUsages(Long quotaUsageId) {
SearchCriteria<QuotaTariffUsageVO> sc = searchQuotaTariffUsages.create();
sc.setParameters("quotaUsageId", quotaUsageId);
return Transaction.execute(TransactionLegacy.USAGE_DB, (TransactionCallback<List<QuotaTariffUsageVO>>) status -> listBy(sc));
}
}

View File

@ -0,0 +1,31 @@
// 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 org.apache.cloudstack.quota.dao;
import com.cloud.utils.db.GenericDao;
import org.apache.cloudstack.quota.vo.QuotaUsageJoinVO;
import java.util.Date;
import java.util.List;
public interface QuotaUsageJoinDao extends GenericDao<QuotaUsageJoinVO, Long> {
List<QuotaUsageJoinVO> findQuotaUsage(Long accountId, Long domainId, Integer usageType, Long resourceId, Long networkId, Long offeringId, Date startDate, Date endDate, Long tariffId);
}

View File

@ -0,0 +1,94 @@
// 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 org.apache.cloudstack.quota.dao;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.JoinBuilder;
import com.cloud.utils.db.SearchBuilder;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionCallback;
import com.cloud.utils.db.TransactionLegacy;
import org.apache.cloudstack.quota.vo.QuotaTariffUsageVO;
import org.apache.cloudstack.quota.vo.QuotaUsageJoinVO;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.inject.Inject;
import java.util.Date;
import java.util.List;
@Component
public class QuotaUsageJoinDaoImpl extends GenericDaoBase<QuotaUsageJoinVO, Long> implements QuotaUsageJoinDao {
private SearchBuilder<QuotaUsageJoinVO> searchQuotaUsages;
private SearchBuilder<QuotaUsageJoinVO> searchQuotaUsagesJoinTariffUsages;
@Inject
private QuotaTariffUsageDao quotaTariffUsageDao;
@PostConstruct
public void init() {
searchQuotaUsages = createSearchBuilder();
prepareQuotaUsageSearchBuilder(searchQuotaUsages);
searchQuotaUsages.done();
SearchBuilder<QuotaTariffUsageVO> searchQuotaTariffUsages = quotaTariffUsageDao.createSearchBuilder();
searchQuotaTariffUsages.and("tariffId", searchQuotaTariffUsages.entity().getTariffId(), SearchCriteria.Op.EQ);
searchQuotaUsagesJoinTariffUsages = createSearchBuilder();
prepareQuotaUsageSearchBuilder(searchQuotaUsagesJoinTariffUsages);
searchQuotaUsagesJoinTariffUsages.join("searchQuotaTariffUsages", searchQuotaTariffUsages, searchQuotaUsagesJoinTariffUsages.entity().getId(),
searchQuotaTariffUsages.entity().getQuotaUsageId(), JoinBuilder.JoinType.INNER);
searchQuotaUsagesJoinTariffUsages.done();
}
private void prepareQuotaUsageSearchBuilder(SearchBuilder<QuotaUsageJoinVO> searchBuilder) {
searchBuilder.and("accountId", searchBuilder.entity().getAccountId(), SearchCriteria.Op.EQ);
searchBuilder.and("domainId", searchBuilder.entity().getDomainId(), SearchCriteria.Op.EQ);
searchBuilder.and("usageType", searchBuilder.entity().getUsageType(), SearchCriteria.Op.EQ);
searchBuilder.and("resourceId", searchBuilder.entity().getResourceId(), SearchCriteria.Op.EQ);
searchBuilder.and("networkId", searchBuilder.entity().getNetworkId(), SearchCriteria.Op.EQ);
searchBuilder.and("offeringId", searchBuilder.entity().getOfferingId(), SearchCriteria.Op.EQ);
searchBuilder.and("startDate", searchBuilder.entity().getStartDate(), SearchCriteria.Op.BETWEEN);
searchBuilder.and("endDate", searchBuilder.entity().getEndDate(), SearchCriteria.Op.BETWEEN);
}
@Override
public List<QuotaUsageJoinVO> findQuotaUsage(Long accountId, Long domainId, Integer usageType, Long resourceId, Long networkId, Long offeringId, Date startDate, Date endDate, Long tariffId) {
SearchCriteria<QuotaUsageJoinVO> sc = tariffId == null ? searchQuotaUsages.create() : searchQuotaUsagesJoinTariffUsages.create();
sc.setParametersIfNotNull("accountId", accountId);
sc.setParametersIfNotNull("domainId", domainId);
sc.setParametersIfNotNull("usageType", usageType);
sc.setParametersIfNotNull("resourceId", resourceId);
sc.setParametersIfNotNull("networkId", networkId);
sc.setParametersIfNotNull("offeringId", offeringId);
if (ObjectUtils.allNotNull(startDate, endDate)) {
sc.setParameters("startDate", startDate, endDate);
sc.setParameters("endDate", startDate, endDate);
}
sc.setJoinParametersIfNotNull("searchQuotaTariffUsages", "tariffId", tariffId);
return Transaction.execute(TransactionLegacy.USAGE_DB, (TransactionCallback<List<QuotaUsageJoinVO>>) status -> listBy(sc));
}
}

View File

@ -0,0 +1,86 @@
//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 org.apache.cloudstack.quota.vo;
import java.math.BigDecimal;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.apache.cloudstack.api.InternalIdentity;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@Entity
@Table(name = "quota_tariff_usage")
public class QuotaTariffUsageVO implements InternalIdentity {
@Id
@Column(name = "id")
private Long id;
@Column(name = "tariff_id")
private Long tariffId;
@Column(name = "quota_usage_id")
private Long quotaUsageId;
@Column(name = "quota_used")
private BigDecimal quotaUsed;
public QuotaTariffUsageVO() {
quotaUsed = new BigDecimal(0);
}
@Override
public long getId() {
return id;
}
public Long getTariffId() {
return tariffId;
}
public Long getQuotaUsageId() {
return quotaUsageId;
}
public BigDecimal getQuotaUsed() {
return quotaUsed;
}
public void setId(Long id) {
this.id = id;
}
public void setTariffId(Long tariffId) {
this.tariffId = tariffId;
}
public void setQuotaUsageId(Long quotaUsageId) {
this.quotaUsageId = quotaUsageId;
}
public void setQuotaUsed(BigDecimal quotaUsed) {
this.quotaUsed = quotaUsed;
}
@Override
public String toString() {
return new ReflectionToStringBuilder(this, ToStringStyle.JSON_STYLE).toString();
}
}

View File

@ -0,0 +1,179 @@
//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 org.apache.cloudstack.quota.vo;
import org.apache.cloudstack.api.InternalIdentity;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import java.math.BigDecimal;
import java.util.Date;
@Entity
@Table(name = "quota_usage_view")
public class QuotaUsageJoinVO implements InternalIdentity {
@Id
@Column(name = "id", updatable = false, nullable = false)
private Long id;
@Column(name = "zone_id")
private Long zoneId = null;
@Column(name = "account_id")
private Long accountId = null;
@Column(name = "domain_id")
private Long domainId = null;
@Column(name = "usage_item_id")
private Long usageItemId;
@Column(name = "usage_type")
private int usageType;
@Column(name = "quota_used")
private BigDecimal quotaUsed;
@Column(name = "start_date")
@Temporal(value = TemporalType.TIMESTAMP)
private Date startDate = null;
@Column(name = "end_date")
@Temporal(value = TemporalType.TIMESTAMP)
private Date endDate = null;
@Column(name = "resource_id")
private Long resourceId = null;
@Column(name = "network_id")
private Long networkId = null;
@Column(name = "offering_id")
private Long offeringId = null;
@Override
public long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getZoneId() {
return zoneId;
}
public void setZoneId(Long zoneId) {
this.zoneId = zoneId;
}
public Long getAccountId() {
return accountId;
}
public void setAccountId(Long accountId) {
this.accountId = accountId;
}
public Long getDomainId() {
return domainId;
}
public void setDomainId(Long domainId) {
this.domainId = domainId;
}
public Long getUsageItemId() {
return usageItemId;
}
public void setUsageItemId(Long usageItemId) {
this.usageItemId = usageItemId;
}
public int getUsageType() {
return usageType;
}
public void setUsageType(int usageType) {
this.usageType = usageType;
}
public BigDecimal getQuotaUsed() {
return quotaUsed;
}
public void setQuotaUsed(BigDecimal quotaUsed) {
this.quotaUsed = quotaUsed;
}
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
public Date getEndDate() {
return endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
public Long getResourceId() {
return resourceId;
}
public void setResourceId(Long resourceId) {
this.resourceId = resourceId;
}
public Long getNetworkId() {
return networkId;
}
public void setNetworkId(Long networkId) {
this.networkId = networkId;
}
public Long getOfferingId() {
return offeringId;
}
public void setOfferingId(Long offeringId) {
this.offeringId = offeringId;
}
public QuotaUsageJoinVO () {
}
@Override
public String toString() {
return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "zoneId", "accountId", "domainId", "usageItemId", "usageType", "quotaUsed", "startDate",
"endDate", "resourceId");
}
}

View File

@ -0,0 +1,62 @@
//
// 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 org.apache.cloudstack.quota.vo;
import java.util.Date;
public class QuotaUsageResourceVO {
private String uuid;
private String name;
private Date removed;
public String getUuid() {
return uuid;
}
public void setUuid(String uuid) {
this.uuid = uuid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getRemoved() {
return removed;
}
public void setRemoved(Date removed) {
this.removed = removed;
}
public boolean isRemoved() {
return this.removed != null;
}
public QuotaUsageResourceVO(String uuid, String name, Date removed) {
this.uuid = uuid;
this.name = name;
this.removed = removed;
}
}

View File

@ -26,7 +26,9 @@
<bean id="QuotaEmailTemplatesDao"
class="org.apache.cloudstack.quota.dao.QuotaEmailTemplatesDaoImpl" />
<bean id="QuotaUsageDao" class="org.apache.cloudstack.quota.dao.QuotaUsageDaoImpl" />
<bean id="UserVmDetailsDao" class="org.apache.cloudstack.quota.dao.VMInstanceDetailsDaoImpl" />
<bean id="QuotaUsageJoinDao" class="org.apache.cloudstack.quota.dao.QuotaUsageJoinDaoImpl"/>
<bean id="QuotaTariffUsageDao" class="org.apache.cloudstack.quota.dao.QuotaTariffUsageDaoImpl" />
<bean id="UserVmDetailsDao" class="org.apache.cloudstack.quota.dao.VMInstanceDetailsDaoImpl" />
<bean id="QuotaManager" class="org.apache.cloudstack.quota.QuotaManagerImpl" />
<bean id="QuotaAlertManager" class="org.apache.cloudstack.quota.QuotaAlertManagerImpl" />

View File

@ -31,11 +31,13 @@ import com.cloud.dc.ClusterDetailsDao;
import com.cloud.dc.ClusterDetailsVO;
import com.cloud.host.HostTagVO;
import com.cloud.hypervisor.Hypervisor;
import com.cloud.network.Network;
import com.cloud.network.dao.NetworkVO;
import com.cloud.network.vpc.VpcOfferingVO;
import com.cloud.network.vpc.VpcVO;
import com.cloud.network.vpc.dao.VpcOfferingDao;
import com.cloud.storage.StoragePoolTagVO;
import com.cloud.vm.VirtualMachine;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.acl.RoleVO;
import org.apache.cloudstack.acl.dao.RoleDao;
@ -238,6 +240,8 @@ public class PresetVariableHelperTest {
value.setVmSnapshotType(VMSnapshot.Type.Disk.toString());
value.setComputingResources(getComputingResourcesForTests());
value.setVolumeType(Volume.Type.DATADISK.toString());
value.setState(Network.State.Implemented.toString());
value.setResourceCounting(getResourceCountingForTests());
value.setNetworkOffering(getNetworkOfferingForTests());
value.setVpcOffering(getVpcOfferingForTests());
return value;
@ -276,6 +280,13 @@ public class PresetVariableHelperTest {
return configuration;
}
private ResourceCounting getResourceCountingForTests() {
ResourceCounting resourceCounting = new ResourceCounting();
resourceCounting.setRunningVms(1);
resourceCounting.setStoppedVms(1);
return resourceCounting;
}
private List<HostTagVO> getHostTagsForTests() {
return Arrays.asList(new HostTagVO(1, "tag1", false), new HostTagVO(1, "tag2", false));
}
@ -1343,8 +1354,8 @@ public class PresetVariableHelperTest {
Mockito.doReturn(expected.getId()).when(networkVoMock).getUuid();
Mockito.doReturn(expected.getName()).when(networkVoMock).getName();
Mockito.doReturn(expected.getState()).when(usageVoMock).getState();
Mockito.doReturn(expected.getResourceCounting()).when(presetVariableHelperSpy).getPresetVariableValueNetworkResourceCounting(Mockito.anyLong());
Mockito.doReturn(expected.getNetworkOffering()).when(presetVariableHelperSpy).getPresetVariableValueNetworkOffering(Mockito.anyLong());
Mockito.doReturn(UsageTypes.NETWORK).when(usageVoMock).getUsageType();
Value result = new Value();
@ -1352,7 +1363,27 @@ public class PresetVariableHelperTest {
assertPresetVariableIdAndName(expected, result);
Assert.assertEquals(expected.getState(), result.getState());
Assert.assertEquals(expected.getNetworkOffering(), result.getNetworkOffering());
Assert.assertEquals(expected.getResourceCounting(), result.getResourceCounting());
}
@Test
public void getPresetVariableValueNetworkResourceCountingTestSetValueAndReturnObject() {
VMInstanceVO vmInstanceVoMock1 = Mockito.spy(VMInstanceVO.class);
vmInstanceVoMock1.setState(VirtualMachine.State.Stopped);
VMInstanceVO vmInstanceVoMock2 = Mockito.spy(VMInstanceVO.class);
vmInstanceVoMock2.setState(VirtualMachine.State.Running);
Mockito.doReturn(List.of(vmInstanceVoMock1, vmInstanceVoMock2)).when(vmInstanceDaoMock).listNonRemovedVmsByTypeAndNetwork(Mockito.anyLong(), Mockito.any());
mockMethodValidateIfObjectIsNull();
ResourceCounting expected = getResourceCountingForTests();
ResourceCounting result = presetVariableHelperSpy.getPresetVariableValueNetworkResourceCounting(1L);
Assert.assertEquals(expected.getRunningVms(), result.getRunningVms());
Assert.assertEquals(expected.getStoppedVms(), result.getStoppedVms());
}
@Test
@ -1362,7 +1393,7 @@ public class PresetVariableHelperTest {
presetVariableHelperSpy.loadPresetVariableValueForVpc(usageVoMock, null);
});
Mockito.verifyNoInteractions(networkDaoMock);
Mockito.verifyNoInteractions(vpcDaoMock);
}
@Test

View File

@ -17,7 +17,6 @@
package org.apache.cloudstack.api.command;
import java.util.Date;
import java.util.List;
import javax.inject.Inject;
@ -28,24 +27,25 @@ import org.apache.cloudstack.api.BaseCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.response.AccountResponse;
import org.apache.cloudstack.api.response.DomainResponse;
import org.apache.cloudstack.api.response.ProjectResponse;
import org.apache.cloudstack.api.response.QuotaResponseBuilder;
import org.apache.cloudstack.api.response.QuotaStatementItemResponse;
import org.apache.cloudstack.api.response.QuotaStatementResponse;
import org.apache.cloudstack.quota.vo.QuotaUsageVO;
import com.cloud.user.Account;
import org.apache.commons.lang3.ObjectUtils;
@APICommand(name = "quotaStatement", responseObject = QuotaStatementItemResponse.class, description = "Create a quota statement", since = "4.7.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false,
httpMethod = "GET")
@APICommand(name = "quotaStatement", responseObject = QuotaStatementItemResponse.class, description = "Create a Quota statement for the provided Account, Project, or Domain.",
since = "4.7.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false, httpMethod = "GET")
public class QuotaStatementCmd extends BaseCmd {
@Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING, required = true, description = "Optional, Account Id for which statement needs to be generated")
@ACL
@Parameter(name = ApiConstants.ACCOUNT, type = CommandType.STRING,
description = "Name of the Account for which the Quota statement will be generated. Deprecated, please use accountid instead.")
private String accountName;
@ACL
@Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, required = true, entityType = DomainResponse.class, description = "Optional, If domain Id is given and the caller is domain admin then the statement is generated for domain.")
@Parameter(name = ApiConstants.DOMAIN_ID, type = CommandType.UUID, entityType = DomainResponse.class,
description = "ID of the Domain for which the Quota statement will be generated. May be used individually or with account.")
private Long domainId;
@Parameter(name = ApiConstants.END_DATE, type = CommandType.DATE, required = true, description = "End of the period of the Quota statement. " +
@ -56,15 +56,25 @@ public class QuotaStatementCmd extends BaseCmd {
ApiConstants.PARAMETER_DESCRIPTION_START_DATE_POSSIBLE_FORMATS)
private Date startDate;
@Parameter(name = ApiConstants.TYPE, type = CommandType.INTEGER, description = "List quota usage records for the specified usage type")
@Parameter(name = ApiConstants.TYPE, type = CommandType.INTEGER,
description = "Consider only Quota usage records for the specified usage type in the statement.")
private Integer usageType;
@ACL
@Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class, description = "List usage records for the specified Account")
@Parameter(name = ApiConstants.ACCOUNT_ID, type = CommandType.UUID, entityType = AccountResponse.class,
description = "ID of the Account for which the Quota statement will be generated. Can not be specified with projectid.")
private Long accountId;
@ACL
@Parameter(name = ApiConstants.PROJECT_ID, type = CommandType.UUID, entityType = ProjectResponse.class,
description = "ID of the Project for which the Quota statement will be generated. Can not be specified with accountid.", since = "4.23.0")
private Long projectId;
@Parameter(name = ApiConstants.SHOW_RESOURCES, type = CommandType.BOOLEAN, description = "List the resources of each Quota type in the period.", since = "4.23.0")
private boolean showResources;
@Inject
private QuotaResponseBuilder _responseBuilder;
QuotaResponseBuilder responseBuilder;
public Long getAccountId() {
return accountId;
@ -99,43 +109,47 @@ public class QuotaStatementCmd extends BaseCmd {
}
public Date getEndDate() {
return _responseBuilder.startOfNextDay(endDate == null ? new Date() : new Date(endDate.getTime()));
return endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate == null ? null : new Date(endDate.getTime());
this.endDate = endDate;
}
public Date getStartDate() {
return startDate == null ? null : new Date(startDate.getTime());
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate == null ? null : new Date(startDate.getTime());
this.startDate = startDate;
}
public boolean isShowResources() {
return showResources;
}
public void setShowResources(boolean showResources) {
this.showResources = showResources;
}
public Long getProjectId() {
return projectId;
}
@Override
public long getEntityOwnerId() {
if (accountId != null) {
return accountId;
if (ObjectUtils.allNull(accountId, accountName, projectId)) {
return -1;
}
Account activeAccountByName = _accountService.getActiveAccountByName(accountName, domainId);
if (activeAccountByName != null) {
return activeAccountByName.getAccountId();
}
return Account.ACCOUNT_ID_SYSTEM;
return _accountService.finalizeAccountId(accountId, accountName, domainId, projectId);
}
@Override
public void execute() {
List<QuotaUsageVO> quotaUsage = _responseBuilder.getQuotaUsage(this);
QuotaStatementResponse response = _responseBuilder.createQuotaStatementResponse(quotaUsage);
response.setStartDate(startDate == null ? null : new Date(startDate.getTime()));
response.setEndDate(endDate == null ? null : new Date(endDate.getTime()));
QuotaStatementResponse response = responseBuilder.createQuotaStatementResponse(this);
response.setStartDate(startDate);
response.setEndDate(endDate);
response.setResponseName(getCommandName());
setResponseObject(response);
}
}

View File

@ -32,7 +32,6 @@ import org.apache.cloudstack.api.command.QuotaValidateActivationRuleCmd;
import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO;
import org.apache.cloudstack.quota.vo.QuotaTariffVO;
import org.apache.cloudstack.quota.vo.QuotaUsageVO;
import java.util.Date;
import java.util.List;
@ -49,7 +48,7 @@ public interface QuotaResponseBuilder {
boolean isUserAllowedToSeeActivationRules(User user);
QuotaStatementResponse createQuotaStatementResponse(List<QuotaUsageVO> quotaUsage);
QuotaStatementResponse createQuotaStatementResponse(QuotaStatementCmd cmd);
QuotaBalanceResponse createQuotaBalanceResponse(List<QuotaBalanceVO> quotaUsage, Date startDate, Date endDate);
@ -57,8 +56,6 @@ public interface QuotaResponseBuilder {
QuotaBalanceResponse createQuotaLastBalanceResponse(List<QuotaBalanceVO> quotaBalance, Date startDate);
List<QuotaUsageVO> getQuotaUsage(QuotaStatementCmd cmd);
List<QuotaBalanceVO> getQuotaBalance(QuotaBalanceCmd cmd);
QuotaCreditsResponse addQuotaCredits(Long accountId, Long domainId, Double amount, Long updatedBy, Boolean enforce);

View File

@ -21,13 +21,11 @@ import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
@ -36,6 +34,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@ -43,12 +42,36 @@ import java.util.stream.Collectors;
import javax.inject.Inject;
import com.cloud.domain.Domain;
import com.cloud.domain.DomainVO;
import com.cloud.domain.dao.DomainDao;
import com.cloud.event.ActionEvent;
import com.cloud.event.EventTypes;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.PermissionDeniedException;
import com.cloud.projects.dao.ProjectDao;
import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.IPAddressVO;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkVO;
import com.cloud.offerings.dao.NetworkOfferingDao;
import com.cloud.offerings.NetworkOfferingVO;
import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.storage.dao.SnapshotDao;
import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.SnapshotVO;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.user.AccountVO;
import com.cloud.user.dao.AccountDao;
import com.cloud.user.dao.UserDao;
import com.cloud.user.User;
import com.cloud.user.UserVO;
import com.cloud.utils.DateUtil;
import com.cloud.utils.Pair;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.dao.VMInstanceDao;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.ApiErrorCode;
import org.apache.cloudstack.api.ServerApiException;
@ -75,6 +98,7 @@ import org.apache.cloudstack.quota.activationrule.presetvariables.ComputingResou
import org.apache.cloudstack.quota.activationrule.presetvariables.GenericPresetVariable;
import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariableDefinition;
import org.apache.cloudstack.quota.activationrule.presetvariables.PresetVariables;
import org.apache.cloudstack.quota.activationrule.presetvariables.ResourceCounting;
import org.apache.cloudstack.quota.activationrule.presetvariables.Value;
import org.apache.cloudstack.quota.constant.QuotaConfig;
import org.apache.cloudstack.quota.constant.QuotaTypes;
@ -94,7 +118,8 @@ import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO;
import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
import org.apache.cloudstack.quota.vo.QuotaSummaryVO;
import org.apache.cloudstack.quota.vo.QuotaTariffVO;
import org.apache.cloudstack.quota.vo.QuotaUsageVO;
import org.apache.cloudstack.quota.vo.QuotaUsageJoinVO;
import org.apache.cloudstack.quota.vo.QuotaUsageResourceVO;
import org.apache.cloudstack.utils.jsinterpreter.JsInterpreter;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import org.apache.commons.collections4.CollectionUtils;
@ -106,18 +131,6 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;
import com.cloud.domain.DomainVO;
import com.cloud.domain.dao.DomainDao;
import com.cloud.event.ActionEvent;
import com.cloud.event.EventTypes;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.user.Account;
import com.cloud.user.AccountManager;
import com.cloud.user.AccountVO;
import com.cloud.user.dao.AccountDao;
import com.cloud.user.dao.UserDao;
import com.cloud.utils.Pair;
@Component
public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
protected Logger logger = LogManager.getLogger(getClass());
@ -140,8 +153,6 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
@Inject
private AccountDao _accountDao;
@Inject
private ProjectDao projectDao;
@Inject
private QuotaAccountDao quotaAccountDao;
@Inject
private DomainDao domainDao;
@ -159,8 +170,23 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
private JsInterpreterHelper jsInterpreterHelper;
@Inject
private ApiDiscoveryService apiDiscoveryService;
@Inject
private IPAddressDao ipAddressDao;
@Inject
private NetworkDao networkDao;
@Inject
private NetworkOfferingDao networkOfferingDao;
@Inject
private SnapshotDao snapshotDao;
@Inject
private VMInstanceDao vmInstanceDao;
@Inject
private VMTemplateDao vmTemplateDao;
@Inject
private VolumeDao volumeDao;
private final Class<?>[] assignableClasses = {GenericPresetVariable.class, ComputingResources.class};
private final Class<?>[] assignableClasses = {GenericPresetVariable.class, ComputingResources.class, ResourceCounting.class};
private Set<Account.Type> accountTypesThatCanListAllQuotaSummaries = Sets.newHashSet(Account.Type.ADMIN, Account.Type.DOMAIN_ADMIN);
@ -393,78 +419,212 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
}
@Override
public QuotaStatementResponse createQuotaStatementResponse(final List<QuotaUsageVO> quotaUsage) {
if (quotaUsage == null || quotaUsage.isEmpty()) {
throw new InvalidParameterValueException("There is no usage data found for period mentioned.");
}
public QuotaStatementResponse createQuotaStatementResponse(QuotaStatementCmd cmd) {
Long accountId = getAccountIdForQuotaStatement(cmd);
Long domainId = getDomainIdForQuotaStatement(cmd, accountId);
List<QuotaUsageJoinVO> quotaUsages = _quotaService.getQuotaUsage(accountId, null, domainId, cmd.getUsageType(), cmd.getStartDate(), cmd.getEndDate());
logger.debug("Creating quota statement from [{}] usage records for parameters [{}].", quotaUsages.size(),
ReflectionToStringBuilderUtils.reflectOnlySelectedFields(cmd, "accountName", "accountId", "projectId", "domainId", "startDate", "endDate", "usageType", "showResources"));
createDummyRecordForEachQuotaTypeIfUsageTypeIsNotInformed(quotaUsages, cmd.getUsageType());
Map<Integer, List<QuotaUsageJoinVO>> recordsPerUsageTypes = quotaUsages.stream()
.sorted(Comparator.comparingInt(QuotaUsageJoinVO::getUsageType))
.collect(Collectors.groupingBy(QuotaUsageJoinVO::getUsageType));
List<QuotaStatementItemResponse> items = new ArrayList<>();
recordsPerUsageTypes.forEach((key, value) -> items.add(createStatementItem(key, value, cmd.isShowResources())));
QuotaStatementResponse statement = new QuotaStatementResponse();
HashMap<Integer, QuotaTypes> quotaTariffMap = new HashMap<Integer, QuotaTypes>();
Collection<QuotaTypes> result = QuotaTypes.listQuotaTypes().values();
for (QuotaTypes quotaTariff : result) {
quotaTariffMap.put(quotaTariff.getQuotaType(), quotaTariff);
// add dummy record for each usage type
QuotaUsageVO dummy = new QuotaUsageVO(quotaUsage.get(0));
dummy.setUsageType(quotaTariff.getQuotaType());
dummy.setQuotaUsed(new BigDecimal(0));
quotaUsage.add(dummy);
}
if (logger.isDebugEnabled()) {
logger.debug(
"createQuotaStatementResponse Type=" + quotaUsage.get(0).getUsageType() + " usage=" + quotaUsage.get(0).getQuotaUsed().setScale(2, RoundingMode.HALF_EVEN)
+ " rec.id=" + quotaUsage.get(0).getUsageItemId() + " SD=" + quotaUsage.get(0).getStartDate() + " ED=" + quotaUsage.get(0).getEndDate());
}
Collections.sort(quotaUsage, new Comparator<QuotaUsageVO>() {
@Override
public int compare(QuotaUsageVO o1, QuotaUsageVO o2) {
if (o1.getUsageType() == o2.getUsageType()) {
return 0;
}
return o1.getUsageType() < o2.getUsageType() ? -1 : 1;
}
});
List<QuotaStatementItemResponse> items = new ArrayList<QuotaStatementItemResponse>();
QuotaStatementItemResponse lineitem;
int type = -1;
BigDecimal usage = new BigDecimal(0);
BigDecimal totalUsage = new BigDecimal(0);
quotaUsage.add(new QuotaUsageVO());// boundary
QuotaUsageVO prev = quotaUsage.get(0);
if (logger.isDebugEnabled()) {
logger.debug("createQuotaStatementResponse record count=" + quotaUsage.size());
}
for (final QuotaUsageVO quotaRecord : quotaUsage) {
if (type != quotaRecord.getUsageType()) {
if (type != -1) {
lineitem = new QuotaStatementItemResponse(type);
lineitem.setQuotaUsed(usage);
lineitem.setAccountId(prev.getAccountId());
lineitem.setDomainId(prev.getDomainId());
lineitem.setUsageUnit(quotaTariffMap.get(type).getQuotaUnit());
lineitem.setUsageName(quotaTariffMap.get(type).getQuotaName());
lineitem.setObjectName("quotausage");
items.add(lineitem);
totalUsage = totalUsage.add(usage);
usage = new BigDecimal(0);
}
type = quotaRecord.getUsageType();
}
prev = quotaRecord;
usage = usage.add(quotaRecord.getQuotaUsed());
}
statement.setLineItem(items);
statement.setTotalQuota(totalUsage);
statement.setTotalQuota(items.stream().map(QuotaStatementItemResponse::getQuotaUsed).reduce(BigDecimal.ZERO, BigDecimal::add));
statement.setCurrency(QuotaConfig.QuotaCurrencySymbol.value());
statement.setObjectName("statement");
if (accountId != null) {
Account account = _accountDao.findByIdIncludingRemoved(accountId);
statement.setAccountId(account.getUuid());
statement.setAccountName(account.getAccountName());
domainId = account.getDomainId();
}
if (domainId != null) {
DomainVO domain = domainDao.findByIdIncludingRemoved(domainId);
statement.setDomainId(domain.getUuid());
}
return statement;
}
protected Long getAccountIdForQuotaStatement(QuotaStatementCmd cmd) {
if (Account.Type.NORMAL.equals(CallContext.current().getCallingAccount().getType())) {
logger.debug("Limiting the Quota statement for the calling Account, as they are a User Account.");
return CallContext.current().getCallingAccountId();
}
long accountId = cmd.getEntityOwnerId();
if (accountId != -1) {
return accountId;
}
if (cmd.getDomainId() == null) {
logger.debug("Limiting the Quota statement for the calling Account, as 'domainid' was not informed.");
return CallContext.current().getCallingAccountId();
}
logger.debug("Allowing admin/domain admin to generate the Quota statement for the provided Domain.");
return null;
}
protected Long getDomainIdForQuotaStatement(QuotaStatementCmd cmd, Long accountId) {
if (accountId != null) {
logger.debug("Quota statement is already limited to Account [{}].", accountId);
Account account = _accountDao.findByIdIncludingRemoved(accountId);
return account.getDomainId();
}
Long domainId = cmd.getDomainId();
if (domainId != null) {
return domainId;
}
logger.debug("Limiting the Quota statement for the calling Account's Domain.");
return CallContext.current().getCallingAccount().getDomainId();
}
protected void createDummyRecordForEachQuotaTypeIfUsageTypeIsNotInformed(List<QuotaUsageJoinVO> quotaUsages, Integer usageType) {
if (usageType != null) {
logger.debug("As the usage type [{}] was informed as parameter of the API quotaStatement, we will not create dummy records.", usageType);
return;
}
for (Integer quotaType : QuotaTypes.listQuotaTypes().keySet()) {
QuotaUsageJoinVO dummy = new QuotaUsageJoinVO();
dummy.setUsageType(quotaType);
dummy.setQuotaUsed(BigDecimal.ZERO);
quotaUsages.add(dummy);
}
}
protected QuotaStatementItemResponse createStatementItem(int usageType, List<QuotaUsageJoinVO> usageRecords, boolean showResources) {
QuotaUsageJoinVO firstRecord = usageRecords.get(0);
int type = firstRecord.getUsageType();
QuotaTypes quotaType = QuotaTypes.listQuotaTypes().get(type);
QuotaStatementItemResponse item = new QuotaStatementItemResponse(type);
item.setQuotaUsed(usageRecords.stream().map(QuotaUsageJoinVO::getQuotaUsed).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add));
item.setUsageUnit(quotaType.getQuotaUnit());
item.setUsageName(quotaType.getQuotaName());
setStatementItemResources(item, usageType, usageRecords, showResources);
return item;
}
protected void setStatementItemResources(QuotaStatementItemResponse statementItem, int usageType, List<QuotaUsageJoinVO> quotaUsageRecords, boolean showResources) {
if (!showResources) {
return;
}
List<QuotaStatementItemResourceResponse> itemDetails = new ArrayList<>();
Map<Long, BigDecimal> quotaUsagesValuesAggregatedById = quotaUsageRecords
.stream()
.filter(quotaUsageJoinVo -> getResourceIdByUsageType(quotaUsageJoinVo, usageType) != null)
.collect(Collectors.groupingBy(
quotaUsageJoinVo -> getResourceIdByUsageType(quotaUsageJoinVo, usageType),
Collectors.reducing(new BigDecimal(0), QuotaUsageJoinVO::getQuotaUsed, BigDecimal::add)
));
for (Map.Entry<Long, BigDecimal> entry : quotaUsagesValuesAggregatedById.entrySet()) {
QuotaStatementItemResourceResponse detail = new QuotaStatementItemResourceResponse();
detail.setQuotaUsed(entry.getValue());
QuotaUsageResourceVO resource = getResourceFromIdAndType(entry.getKey(), usageType);
if (resource != null) {
detail.setResourceId(resource.getUuid());
detail.setDisplayName(resource.getName());
detail.setRemoved(resource.isRemoved());
} else {
detail.setDisplayName("<untraceable>");
}
itemDetails.add(detail);
}
statementItem.setResources(itemDetails);
}
protected Long getResourceIdByUsageType(QuotaUsageJoinVO quotaUsageJoinVo, int usageType) {
switch (usageType) {
case QuotaTypes.NETWORK_BYTES_SENT:
case QuotaTypes.NETWORK_BYTES_RECEIVED:
return quotaUsageJoinVo.getNetworkId();
case QuotaTypes.NETWORK_OFFERING:
return quotaUsageJoinVo.getOfferingId();
default:
return quotaUsageJoinVo.getResourceId();
}
}
protected QuotaUsageResourceVO getResourceFromIdAndType(long resourceId, int usageType) {
switch (usageType) {
case QuotaTypes.ALLOCATED_VM:
case QuotaTypes.RUNNING_VM:
VMInstanceVO vmInstance = vmInstanceDao.findByIdIncludingRemoved(resourceId);
if (vmInstance != null) {
return new QuotaUsageResourceVO(vmInstance.getUuid(), vmInstance.getHostName(), vmInstance.getRemoved());
}
break;
case QuotaTypes.VOLUME:
case QuotaTypes.VOLUME_SECONDARY:
case QuotaTypes.VM_DISK_BYTES_READ:
case QuotaTypes.VM_DISK_BYTES_WRITE:
case QuotaTypes.VM_DISK_IO_READ:
case QuotaTypes.VM_DISK_IO_WRITE:
VolumeVO volume = volumeDao.findByIdIncludingRemoved(resourceId);
if (volume != null) {
return new QuotaUsageResourceVO(volume.getUuid(), volume.getName(), volume.getRemoved());
}
break;
case QuotaTypes.VM_SNAPSHOT_ON_PRIMARY:
case QuotaTypes.VM_SNAPSHOT:
case QuotaTypes.SNAPSHOT:
SnapshotVO snapshot = snapshotDao.findByIdIncludingRemoved(resourceId);
if (snapshot != null) {
return new QuotaUsageResourceVO(snapshot.getUuid(), snapshot.getName(), snapshot.getRemoved());
}
break;
case QuotaTypes.NETWORK_BYTES_SENT:
case QuotaTypes.NETWORK_BYTES_RECEIVED:
NetworkVO network = networkDao.findByIdIncludingRemoved(resourceId);
if (network != null) {
return new QuotaUsageResourceVO(network.getUuid(), network.getName(), network.getRemoved());
}
break;
case QuotaTypes.TEMPLATE:
case QuotaTypes.ISO:
VMTemplateVO vmTemplate = vmTemplateDao.findByIdIncludingRemoved(resourceId);
if (vmTemplate != null) {
return new QuotaUsageResourceVO(vmTemplate.getUuid(), vmTemplate.getName(), vmTemplate.getRemoved());
}
break;
case QuotaTypes.NETWORK_OFFERING:
NetworkOfferingVO networkOffering = networkOfferingDao.findByIdIncludingRemoved(resourceId);
if (networkOffering != null) {
return new QuotaUsageResourceVO(networkOffering.getUuid(), networkOffering.getName(), networkOffering.getRemoved());
}
break;
case QuotaTypes.IP_ADDRESS:
IPAddressVO ipAddress = ipAddressDao.findByIdIncludingRemoved(resourceId);
if (ipAddress != null) {
return new QuotaUsageResourceVO(ipAddress.getUuid(), ipAddress.getName(), ipAddress.getRemoved());
}
break;
}
return null;
}
@Override
public Pair<List<QuotaTariffVO>, Integer> listQuotaTariffPlans(final QuotaTariffListCmd cmd) {
Date startDate = cmd.getEffectiveDate();
@ -518,14 +678,14 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
}
protected void warnQuotaTariffUpdateDeprecatedFields(QuotaTariffUpdateCmd cmd) {
String warnMessage = "The parameter 's%s' for API 'quotaTariffUpdate' is no longer needed and it will be removed in future releases.";
String warnMessage = "The parameter '{}' for API 'quotaTariffUpdate' is no longer needed and it will be removed in future releases.";
if (cmd.getStartDate() != null) {
logger.warn(String.format(warnMessage,"startdate"));
logger.warn(warnMessage, "startdate");
}
if (cmd.getUsageType() != null) {
logger.warn(String.format(warnMessage,"usagetype"));
logger.warn(warnMessage, "usagetype");
}
}
@ -712,11 +872,6 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
return resp;
}
@Override
public List<QuotaUsageVO> getQuotaUsage(QuotaStatementCmd cmd) {
return _quotaService.getQuotaUsage(cmd.getAccountId(), cmd.getAccountName(), cmd.getDomainId(), cmd.getUsageType(), cmd.getStartDate(), cmd.getEndDate());
}
@Override
public List<QuotaBalanceVO> getQuotaBalance(QuotaBalanceCmd cmd) {
return _quotaService.findQuotaBalanceVO(cmd.getAccountId(), cmd.getAccountName(), cmd.getDomainId(), cmd.getStartDate(), cmd.getEndDate());

View File

@ -0,0 +1,61 @@
//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 org.apache.cloudstack.api.response;
import java.math.BigDecimal;
import com.google.gson.annotations.SerializedName;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
import com.cloud.serializer.Param;
public class QuotaStatementItemResourceResponse extends BaseResponse {
@SerializedName(ApiConstants.QUOTA_CONSUMED)
@Param(description = "Quota consumed.")
private BigDecimal quotaUsed;
@SerializedName(ApiConstants.RESOURCE_ID)
@Param(description = "Resources's ID.")
private String resourceId;
@SerializedName(ApiConstants.DISPLAY_NAME)
@Param(description = "Resource's display name.")
private String displayName;
@SerializedName(ApiConstants.REMOVED)
@Param(description = "Indicates whether the resource is removed or active.")
private boolean removed;
public void setQuotaUsed(BigDecimal quotaUsed) {
this.quotaUsed = quotaUsed;
}
public void setResourceId(String resourceId) {
this.resourceId = resourceId;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
public void setRemoved(boolean removed) {
this.removed = removed;
}
}

View File

@ -17,72 +17,41 @@
package org.apache.cloudstack.api.response;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import com.google.gson.annotations.SerializedName;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
import com.cloud.serializer.Param;
public class QuotaStatementItemResponse extends BaseResponse {
@SerializedName("type")
@Param(description = "Usage type")
@SerializedName(ApiConstants.TYPE)
@Param(description = "Usage type.")
private int usageType;
@SerializedName("accountid")
@Param(description = "Account id")
private Long accountId;
@SerializedName("account")
@Param(description = "Account name")
private String accountName;
@SerializedName("domain")
@Param(description = "Domain id")
private Long domainId;
@SerializedName("name")
@Param(description = "Usage type name")
@SerializedName(ApiConstants.NAME)
@Param(description = "Name of the Usage type.")
private String usageName;
@SerializedName("unit")
@Param(description = "Usage unit")
@SerializedName(ApiConstants.UNIT)
@Param(description = "Unit of the Usage type.")
private String usageUnit;
@SerializedName("quota")
@Param(description = "Quota consumed")
@SerializedName(ApiConstants.QUOTA)
@Param(description = "Quota consumed.")
private BigDecimal quotaUsed;
@SerializedName(ApiConstants.RESOURCES)
@Param(description = "Item's resources.")
private List<QuotaStatementItemResourceResponse> resources;
public QuotaStatementItemResponse(final int usageType) {
this.usageType = usageType;
}
public Long getAccountId() {
return accountId;
}
public void setAccountId(Long accountId) {
this.accountId = accountId;
}
public String getAccountName() {
return accountName;
}
public void setAccountName(String accountName) {
this.accountName = accountName;
}
public Long getDomainId() {
return domainId;
}
public void setDomainId(Long domainId) {
this.domainId = domainId;
}
public String getUsageName() {
return usageName;
}
@ -112,7 +81,15 @@ public class QuotaStatementItemResponse extends BaseResponse {
}
public void setQuotaUsed(BigDecimal quotaUsed) {
this.quotaUsed = quotaUsed.setScale(2, RoundingMode.HALF_EVEN);
this.quotaUsed = quotaUsed;
}
public List<QuotaStatementItemResourceResponse> getResources() {
return resources;
}
public void setResources(List<QuotaStatementItemResourceResponse> resources) {
this.resources = resources;
}
}

View File

@ -18,56 +18,56 @@ package org.apache.cloudstack.api.response;
import com.cloud.serializer.Param;
import com.google.gson.annotations.SerializedName;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseResponse;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.List;
public class QuotaStatementResponse extends BaseResponse {
@SerializedName("accountid")
@Param(description = "Account ID")
private Long accountId;
@SerializedName(ApiConstants.ACCOUNT_ID)
@Param(description = "ID of the Account.")
private String accountId;
@SerializedName("account")
@Param(description = "Account name")
@SerializedName(ApiConstants.ACCOUNT)
@Param(description = "Name of the Account.")
private String accountName;
@SerializedName("domain")
@Param(description = "Domain ID")
private Long domainId;
@SerializedName(ApiConstants.DOMAIN)
@Param(description = "ID of the Domain.")
private String domainId;
@SerializedName("quotausage")
@Param(description = "List of quota usage under various types", responseObject = QuotaStatementItemResponse.class)
@SerializedName(ApiConstants.QUOTA_USAGE)
@Param(description = "List of Quota usage under various types.", responseObject = QuotaStatementItemResponse.class)
private List<QuotaStatementItemResponse> lineItem;
@SerializedName("totalquota")
@Param(description = "Total quota used during this period")
@SerializedName(ApiConstants.TOTAL_QUOTA)
@Param(description = "Total Quota consumed during this period.")
private BigDecimal totalQuota;
@SerializedName("startdate")
@Param(description = "Start date")
@SerializedName(ApiConstants.START_DATE)
@Param(description = "Start date of the Quota statement.")
private Date startDate = null;
@SerializedName("enddate")
@Param(description = "End date")
@SerializedName(ApiConstants.END_DATE)
@Param(description = "End date of the Quota statement.")
private Date endDate = null;
@SerializedName("currency")
@Param(description = "Currency")
@SerializedName(ApiConstants.CURRENCY)
@Param(description = "Currency of the Quota statement.")
private String currency;
public QuotaStatementResponse() {
super();
}
public Long getAccountId() {
public String getAccountId() {
return accountId;
}
public void setAccountId(Long accountId) {
public void setAccountId(String accountId) {
this.accountId = accountId;
}
@ -79,45 +79,36 @@ public class QuotaStatementResponse extends BaseResponse {
this.accountName = accountName;
}
public Long getDomainId() {
public String getDomainId() {
return domainId;
}
public void setDomainId(Long domainId) {
public void setDomainId(String domainId) {
this.domainId = domainId;
}
public List<QuotaStatementItemResponse> getLineItem() {
return lineItem;
}
public void setLineItem(List<QuotaStatementItemResponse> lineItem) {
this.lineItem = lineItem;
}
public Date getStartDate() {
return startDate == null ? null : new Date(startDate.getTime());
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate == null ? null : new Date(startDate.getTime());
this.startDate = startDate;
}
public Date getEndDate() {
return endDate == null ? null : new Date(endDate.getTime());
return endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate == null ? null : new Date(endDate.getTime());
}
public BigDecimal getTotalQuota() {
return totalQuota;
this.endDate = endDate;
}
public void setTotalQuota(BigDecimal totalQuota) {
this.totalQuota = totalQuota.setScale(2, RoundingMode.HALF_EVEN);
this.totalQuota = totalQuota;
}
public String getCurrency() {

View File

@ -21,14 +21,14 @@ import java.util.Date;
import java.util.List;
import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
import org.apache.cloudstack.quota.vo.QuotaUsageVO;
import org.apache.cloudstack.quota.vo.QuotaUsageJoinVO;
import com.cloud.user.AccountVO;
import com.cloud.utils.component.PluggableService;
public interface QuotaService extends PluggableService {
List<QuotaUsageVO> getQuotaUsage(Long accountId, String accountName, Long domainId, Integer usageType, Date startDate, Date endDate);
List<QuotaUsageJoinVO> getQuotaUsage(Long accountId, String accountName, Long domainId, Integer usageType, Date startDate, Date endDate);
List<QuotaBalanceVO> findQuotaBalanceVO(Long accountId, String accountName, Long domainId, Date startDate, Date endDate);

View File

@ -53,10 +53,10 @@ import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.quota.constant.QuotaConfig;
import org.apache.cloudstack.quota.dao.QuotaAccountDao;
import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
import org.apache.cloudstack.quota.dao.QuotaUsageDao;
import org.apache.cloudstack.quota.dao.QuotaUsageJoinDao;
import org.apache.cloudstack.quota.vo.QuotaAccountVO;
import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
import org.apache.cloudstack.quota.vo.QuotaUsageVO;
import org.apache.cloudstack.quota.vo.QuotaUsageJoinVO;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Component;
@ -80,7 +80,7 @@ public class QuotaServiceImpl extends ManagerBase implements QuotaService, Confi
@Inject
private QuotaAccountDao _quotaAcc;
@Inject
private QuotaUsageDao _quotaUsageDao;
private QuotaUsageJoinDao quotaUsageJoinDao;
@Inject
private DomainDao _domainDao;
@Inject
@ -213,27 +213,7 @@ public class QuotaServiceImpl extends ManagerBase implements QuotaService, Confi
}
@Override
public List<QuotaUsageVO> getQuotaUsage(Long accountId, String accountName, Long domainId, Integer usageType, Date startDate, Date endDate) {
// if accountId is not specified, use accountName and domainId
if ((accountId == null) && (accountName != null) && (domainId != null)) {
Account userAccount = null;
Account caller = CallContext.current().getCallingAccount();
if (_domainDao.isChildDomain(caller.getDomainId(), domainId)) {
Filter filter = new Filter(AccountVO.class, "id", Boolean.FALSE, null, null);
List<AccountVO> accounts = _accountDao.listAccounts(accountName, domainId, filter);
if (!accounts.isEmpty()) {
userAccount = accounts.get(0);
}
if (userAccount != null) {
accountId = userAccount.getId();
} else {
throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId);
}
} else {
throw new PermissionDeniedException("Invalid Domain Id or Account");
}
}
public List<QuotaUsageJoinVO> getQuotaUsage(Long accountId, String accountName, Long domainId, Integer usageType, Date startDate, Date endDate) {
if (startDate.after(endDate)) {
throw new InvalidParameterValueException("Incorrect Date Range. Start date: " + startDate + " is after end date:" + endDate);
}
@ -241,7 +221,7 @@ public class QuotaServiceImpl extends ManagerBase implements QuotaService, Confi
logger.debug("Getting quota records of type [{}] for account [{}] in domain [{}], between [{}] and [{}].",
usageType, accountId, domainId, startDate, endDate);
return _quotaUsageDao.findQuotaUsage(accountId, domainId, usageType, startDate, endDate);
return quotaUsageJoinDao.findQuotaUsage(accountId, domainId, usageType, null, null, null, startDate, endDate, null);
}
@Override

View File

@ -16,38 +16,29 @@
// under the License.
package org.apache.cloudstack.api.command;
import junit.framework.TestCase;
import org.apache.cloudstack.api.response.QuotaResponseBuilder;
import org.apache.cloudstack.api.response.QuotaStatementResponse;
import org.apache.cloudstack.quota.vo.QuotaUsageVO;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
@RunWith(MockitoJUnitRunner.class)
public class QuotaStatementCmdTest extends TestCase {
public class QuotaStatementCmdTest {
@Mock
QuotaResponseBuilder responseBuilder;
QuotaResponseBuilder responseBuilderMock;
@Test
public void testQuotaStatementCmd() throws NoSuchFieldException, IllegalAccessException {
public void executeTestVerifyCalls() {
QuotaStatementCmd cmd = new QuotaStatementCmd();
cmd.setAccountName("admin");
cmd.responseBuilder = responseBuilderMock;
Field rbField = QuotaStatementCmd.class.getDeclaredField("_responseBuilder");
rbField.setAccessible(true);
rbField.set(cmd, responseBuilder);
Mockito.doReturn(new QuotaStatementResponse()).when(responseBuilderMock).createQuotaStatementResponse(Mockito.any());
List<QuotaUsageVO> quotaUsageVOList = new ArrayList<QuotaUsageVO>();
Mockito.when(responseBuilder.getQuotaUsage(Mockito.eq(cmd))).thenReturn(quotaUsageVOList);
Mockito.when(responseBuilder.createQuotaStatementResponse(Mockito.eq(quotaUsageVOList))).thenReturn(new QuotaStatementResponse());
cmd.execute();
Mockito.verify(responseBuilder, Mockito.times(1)).getQuotaUsage(Mockito.eq(cmd));
Mockito.verify(responseBuilderMock).createQuotaStatementResponse(cmd);
}
}

View File

@ -17,6 +17,8 @@
package org.apache.cloudstack.api.response;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
@ -42,6 +44,7 @@ import org.apache.cloudstack.api.command.QuotaConfigureEmailCmd;
import org.apache.cloudstack.api.command.QuotaCreditsListCmd;
import org.apache.cloudstack.api.command.QuotaEmailTemplateListCmd;
import org.apache.cloudstack.api.command.QuotaEmailTemplateUpdateCmd;
import org.apache.cloudstack.api.command.QuotaStatementCmd;
import org.apache.cloudstack.api.command.QuotaSummaryCmd;
import org.apache.cloudstack.api.command.QuotaValidateActivationRuleCmd;
import org.apache.cloudstack.context.CallContext;
@ -67,6 +70,7 @@ import org.apache.cloudstack.quota.vo.QuotaCreditsVO;
import org.apache.cloudstack.quota.vo.QuotaEmailConfigurationVO;
import org.apache.cloudstack.quota.vo.QuotaEmailTemplatesVO;
import org.apache.cloudstack.quota.vo.QuotaTariffVO;
import org.apache.cloudstack.quota.vo.QuotaUsageJoinVO;
import org.apache.cloudstack.utils.jsinterpreter.JsInterpreter;
import org.apache.commons.lang3.time.DateUtils;
@ -914,4 +918,199 @@ public class QuotaResponseBuilderImplTest extends TestCase {
Assert.assertTrue(formattedVariables.containsValue("accountname"));
Assert.assertTrue(formattedVariables.containsValue("zonename"));
}
@Test
public void createDummyRecordForEachQuotaTypeIfUsageTypeIsNotInformedTestUsageTypeDifferentFromNullDoNothing() {
List<QuotaUsageJoinVO> listUsage = new ArrayList<>();
quotaResponseBuilderSpy.createDummyRecordForEachQuotaTypeIfUsageTypeIsNotInformed(listUsage, 1);
Assert.assertTrue(listUsage.isEmpty());
}
@Test
public void createDummyRecordForEachQuotaTypeIfUsageTypeIsNotInformedTestUsageTypeIsNullAddDummyForAllQuotaTypes() {
List<QuotaUsageJoinVO> listUsage = new ArrayList<>();
listUsage.add(new QuotaUsageJoinVO());
quotaResponseBuilderSpy.createDummyRecordForEachQuotaTypeIfUsageTypeIsNotInformed(listUsage, null);
Assert.assertEquals(QuotaTypes.listQuotaTypes().size() + 1, listUsage.size());
QuotaTypes.listQuotaTypes().entrySet().forEach(entry -> {
Assert.assertTrue(listUsage.stream().anyMatch(usage -> usage.getUsageType() == entry.getKey() && usage.getQuotaUsed().equals(BigDecimal.ZERO)));
});
}
private List<QuotaUsageJoinVO> getQuotaUsagesForTest() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
List<QuotaUsageJoinVO> quotaUsages = new ArrayList<>();
QuotaUsageJoinVO quotaUsage = new QuotaUsageJoinVO();
quotaUsage.setAccountId(1l);
quotaUsage.setDomainId(2l);
quotaUsage.setUsageType(3);
quotaUsage.setQuotaUsed(BigDecimal.valueOf(10));
try {
quotaUsage.setStartDate(sdf.parse("2022-01-01"));
quotaUsage.setEndDate(sdf.parse("2022-01-02"));
} catch (ParseException ignored) {
}
quotaUsages.add(quotaUsage);
quotaUsage = new QuotaUsageJoinVO();
quotaUsage.setAccountId(4l);
quotaUsage.setDomainId(5l);
quotaUsage.setUsageType(3);
quotaUsage.setQuotaUsed(null);
try {
quotaUsage.setStartDate(sdf.parse("2022-01-03"));
quotaUsage.setEndDate(sdf.parse("2022-01-04"));
} catch (ParseException ignored) {
}
quotaUsages.add(quotaUsage);
quotaUsage = new QuotaUsageJoinVO();
quotaUsage.setAccountId(6l);
quotaUsage.setDomainId(7l);
quotaUsage.setUsageType(3);
quotaUsage.setQuotaUsed(BigDecimal.valueOf(5));
try {
quotaUsage.setStartDate(sdf.parse("2022-01-05"));
quotaUsage.setEndDate(sdf.parse("2022-01-06"));
} catch (ParseException ignored) {
}
quotaUsages.add(quotaUsage);
return quotaUsages;
}
@Test
public void createStatementItemTestReturnItem() {
List<QuotaUsageJoinVO> quotaUsages = getQuotaUsagesForTest();
Mockito.doNothing().when(quotaResponseBuilderSpy).setStatementItemResources(Mockito.any(), Mockito.anyInt(), Mockito.any(), Mockito.anyBoolean());
QuotaStatementItemResponse result = quotaResponseBuilderSpy.createStatementItem(0, quotaUsages, false);
QuotaUsageJoinVO expected = quotaUsages.get(0);
QuotaTypes quotaTypeExpected = QuotaTypes.listQuotaTypes().get(expected.getUsageType());
Assert.assertEquals(BigDecimal.valueOf(15), result.getQuotaUsed());
Assert.assertEquals(quotaTypeExpected.getQuotaUnit(), result.getUsageUnit());
Assert.assertEquals(quotaTypeExpected.getQuotaName(), result.getUsageName());
}
@Test
public void setStatementItemResourcesTestDoNotShowResourcesDoNothing() {
QuotaStatementItemResponse item = new QuotaStatementItemResponse(1);
quotaResponseBuilderSpy.setStatementItemResources(item, 0, getQuotaUsagesForTest(), false);
Assert.assertNull(item.getResources());
}
@Test
public void getAccountIdForQuotaStatementTestLimitsToCallingAccountForNormalUser() {
QuotaStatementCmd cmd = Mockito.mock(QuotaStatementCmd.class);
Mockito.doReturn(accountMock).when(callContextMock).getCallingAccount();
Mockito.doReturn(Account.Type.NORMAL).when(accountMock).getType();
try (MockedStatic<CallContext> callContextMocked = Mockito.mockStatic(CallContext.class)) {
callContextMocked.when(CallContext::current).thenReturn(callContextMock);
Long result = quotaResponseBuilderSpy.getAccountIdForQuotaStatement(cmd);
Assert.assertEquals(Long.valueOf(callerAccountMock.getAccountId()), result);
}
}
@Test
public void getAccountIdForQuotaStatementTestReturnsEntityOwnerIdWhenProvided() {
QuotaStatementCmd cmd = Mockito.mock(QuotaStatementCmd.class);
Mockito.doReturn(42L).when(cmd).getEntityOwnerId();
Long result = quotaResponseBuilderSpy.getAccountIdForQuotaStatement(cmd);
Assert.assertEquals(Long.valueOf(42L), result);
}
@Test
public void getAccountIdForQuotaStatementTestLimitsToCallingAccountWhenCallerIsAdminAndDomainIsNotProvided() {
QuotaStatementCmd cmd = Mockito.mock(QuotaStatementCmd.class);
Mockito.doReturn(accountMock).when(callContextMock).getCallingAccount();
Mockito.doReturn(Account.Type.ADMIN).when(accountMock).getType();
Mockito.doReturn(-1L).when(cmd).getEntityOwnerId();
Mockito.doReturn(null).when(cmd).getDomainId();
try (MockedStatic<CallContext> callContextMocked = Mockito.mockStatic(CallContext.class)) {
callContextMocked.when(CallContext::current).thenReturn(callContextMock);
Long result = quotaResponseBuilderSpy.getAccountIdForQuotaStatement(cmd);
Assert.assertEquals(Long.valueOf(callerAccountMock.getAccountId()), result);
}
}
@Test
public void getAccountIdForQuotaStatementTestReturnsNullWhenCallerIsAdminAndDomainIsProvided() {
QuotaStatementCmd cmd = Mockito.mock(QuotaStatementCmd.class);
Mockito.doReturn(accountMock).when(callContextMock).getCallingAccount();
Mockito.doReturn(Account.Type.ADMIN).when(accountMock).getType();
Mockito.doReturn(-1L).when(cmd).getEntityOwnerId();
Mockito.doReturn(10L).when(cmd).getDomainId();
try (MockedStatic<CallContext> callContextMocked = Mockito.mockStatic(CallContext.class)) {
callContextMocked.when(CallContext::current).thenReturn(callContextMock);
Long result = quotaResponseBuilderSpy.getAccountIdForQuotaStatement(cmd);
Assert.assertNull(result);
}
}
@Test
public void getDomainIdForQuotaStatementTestReturnsAccountDomainIdWhenAccountIdIsProvided() {
QuotaStatementCmd cmd = Mockito.mock(QuotaStatementCmd.class);
AccountVO account = Mockito.mock(AccountVO.class);
Mockito.doReturn(account).when(accountDaoMock).findByIdIncludingRemoved(55L);
Mockito.doReturn(77L).when(account).getDomainId();
Long result = quotaResponseBuilderSpy.getDomainIdForQuotaStatement(cmd, 55L);
Assert.assertEquals(Long.valueOf(77L), result);
}
@Test
public void getDomainIdForQuotaStatementTestReturnsProvidedDomainIdWhenAccountIdIsNull() {
QuotaStatementCmd cmd = Mockito.mock(QuotaStatementCmd.class);
Mockito.doReturn(99L).when(cmd).getDomainId();
Long result = quotaResponseBuilderSpy.getDomainIdForQuotaStatement(cmd, null);
Assert.assertEquals(Long.valueOf(99L), result);
}
@Test
public void getDomainIdForQuotaStatementTestFallsBackToCallingAccountDomainIdWhenNeitherAccountNorDomainIsProvided() {
QuotaStatementCmd cmd = Mockito.mock(QuotaStatementCmd.class);
Account account = Mockito.mock(Account.class);
Mockito.doReturn(null).when(cmd).getDomainId();
Mockito.doReturn(123L).when(account).getDomainId();
Mockito.doReturn(account).when(callContextMock).getCallingAccount();
try (MockedStatic<CallContext> callContextMocked = Mockito.mockStatic(CallContext.class)) {
callContextMocked.when(CallContext::current).thenReturn(callContextMock);
Long result = quotaResponseBuilderSpy.getDomainIdForQuotaStatement(cmd, null);
Assert.assertEquals(123L, result.longValue());
}
}
}

View File

@ -28,6 +28,7 @@ import org.apache.cloudstack.quota.constant.QuotaTypes;
import org.apache.cloudstack.quota.dao.QuotaAccountDao;
import org.apache.cloudstack.quota.dao.QuotaBalanceDao;
import org.apache.cloudstack.quota.dao.QuotaUsageDao;
import org.apache.cloudstack.quota.dao.QuotaUsageJoinDao;
import org.apache.cloudstack.quota.vo.QuotaAccountVO;
import org.apache.cloudstack.quota.vo.QuotaBalanceVO;
import org.joda.time.DateTime;
@ -63,6 +64,8 @@ public class QuotaServiceImplTest extends TestCase {
@Mock
QuotaBalanceDao quotaBalanceDao;
@Mock
QuotaUsageJoinDao quotaUsageJoinDaoMock;
@Mock
QuotaResponseBuilder respBldr;
@Mock
private AccountVO accountVoMock;
@ -85,9 +88,9 @@ public class QuotaServiceImplTest extends TestCase {
quotaAccountDaoField.setAccessible(true);
quotaAccountDaoField.set(quotaServiceImplSpy, quotaAcc);
Field quotaUsageDaoField = QuotaServiceImpl.class.getDeclaredField("_quotaUsageDao");
Field quotaUsageDaoField = QuotaServiceImpl.class.getDeclaredField("quotaUsageJoinDao");
quotaUsageDaoField.setAccessible(true);
quotaUsageDaoField.set(quotaServiceImplSpy, quotaUsageDao);
quotaUsageDaoField.set(quotaServiceImplSpy, quotaUsageJoinDaoMock);
Field domainDaoField = QuotaServiceImpl.class.getDeclaredField("_domainDao");
domainDaoField.setAccessible(true);
@ -142,7 +145,8 @@ public class QuotaServiceImplTest extends TestCase {
final Date endDate = new Date();
quotaServiceImplSpy.getQuotaUsage(accountId, accountName, domainId, QuotaTypes.IP_ADDRESS, startDate, endDate);
Mockito.verify(quotaUsageDao, Mockito.times(1)).findQuotaUsage(Mockito.eq(accountId), Mockito.eq(domainId), Mockito.eq(QuotaTypes.IP_ADDRESS), Mockito.any(Date.class), Mockito.any(Date.class));
Mockito.verify(quotaUsageJoinDaoMock, Mockito.times(1)).findQuotaUsage(Mockito.eq(accountId), Mockito.eq(domainId), Mockito.eq(QuotaTypes.IP_ADDRESS), Mockito.any(),
Mockito.any(), Mockito.any(), Mockito.any(Date.class), Mockito.any(Date.class), Mockito.any());
}
@Test

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

@ -57,7 +57,7 @@ public class QemuImgTest {
Connect conn = new Connect("qemu:///system", false);
conn.getVersion();
libVirtAvailable = true;
} catch (LibvirtException ignored) {}
} catch (LibvirtException | UnsatisfiedLinkError | ExceptionInInitializerError ignored) {}
Assume.assumeTrue("libvirt not available", libVirtAvailable);
}

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

@ -153,6 +153,8 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
// CAN_CREATE_VOLUME_FROM_SNAPSHOT see note from CAN_CREATE_VOLUME_FROM_VOLUME
mapCapabilities.put(DataStoreCapabilities.CAN_CREATE_VOLUME_FROM_SNAPSHOT.toString(), Boolean.TRUE.toString());
mapCapabilities.put(DataStoreCapabilities.CAN_REVERT_VOLUME_TO_SNAPSHOT.toString(), Boolean.TRUE.toString());
mapCapabilities.put(DataStoreCapabilities.CAN_CREATE_TEMPLATE_FROM_SNAPSHOT.toString(),
Boolean.toString(system_snapshot));
return mapCapabilities;
}
@ -720,6 +722,13 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
}
}
private static boolean canCopySnapshotToVolumeCond(DataObject srcData, DataObject dstData) {
return srcData.getType() == DataObjectType.SNAPSHOT && dstData.getType() == DataObjectType.VOLUME
&& srcData.getDataStore().getRole() == DataStoreRole.Primary
&& dstData.getDataStore().getRole() == DataStoreRole.Primary
&& srcData.getDataStore().getId() == dstData.getDataStore().getId();
}
private static boolean canCopySnapshotCond(DataObject srcData, DataObject dstData) {
return srcData.getType() == DataObjectType.SNAPSHOT && dstData.getType() == DataObjectType.SNAPSHOT
&& (dstData.getDataStore().getRole() == DataStoreRole.Image
@ -747,7 +756,10 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
{
logger.debug("LinstorPrimaryDataStoreDriverImpl.canCopy: " + srcData.getType() + " -> " + dstData.getType());
if (canCopySnapshotCond(srcData, dstData)) {
if (canCopySnapshotToVolumeCond(srcData, dstData)) {
StoragePoolVO storagePool = _storagePoolDao.findById(srcData.getDataStore().getId());
return storagePool.getStorageProviderName().equals(LinstorUtil.PROVIDER_NAME);
} else if (canCopySnapshotCond(srcData, dstData)) {
SnapshotInfo sinfo = (SnapshotInfo) srcData;
VolumeInfo volume = sinfo.getBaseVolume();
StoragePoolVO storagePool = _storagePoolDao.findById(volume.getPoolId());
@ -766,6 +778,18 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
return false;
}
private CopyCommandResult copySnapshotToVolume(SnapshotInfo snapshotInfo, VolumeInfo volumeInfo) {
StoragePoolVO storagePoolVO = _storagePoolDao.findById(snapshotInfo.getDataStore().getId());
String rscName = LinstorUtil.RSC_PREFIX + volumeInfo.getUuid();
createResourceFromSnapshot(snapshotInfo.getId(), rscName, storagePoolVO);
VolumeObjectTO volumeTO = (VolumeObjectTO) volumeInfo.getTO();
volumeTO.setPath(volumeInfo.getUuid());
volumeTO.setSize(volumeInfo.getSize());
return new CopyCommandResult(null, new CopyCmdAnswer(volumeTO));
}
@Override
public void copyAsync(DataObject srcData, DataObject dstData, AsyncCompletionCallback<CopyCommandResult> callback)
{
@ -773,7 +797,9 @@ public class LinstorPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
+ srcData.getType() + " -> " + dstData.getType());
final CopyCommandResult res;
if (canCopySnapshotCond(srcData, dstData)) {
if (canCopySnapshotToVolumeCond(srcData, dstData)) {
res = copySnapshotToVolume((SnapshotInfo) srcData, (VolumeInfo) dstData);
} else if (canCopySnapshotCond(srcData, dstData)) {
String errMsg = null;
Answer answer = copySnapshot(srcData, dstData);
if (answer != null && !answer.getResult()) {

View File

@ -25,13 +25,23 @@ import com.linbit.linstor.api.model.ResourceGroup;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import com.cloud.agent.api.to.DataObjectType;
import com.cloud.storage.DataStoreRole;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreCapabilities;
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.storage.datastore.util.LinstorUtil;
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.junit.MockitoJUnitRunner;
import static org.mockito.Mockito.mock;
@ -42,6 +52,9 @@ public class LinstorPrimaryDataStoreDriverImplTest {
private DevelopersApi api;
@Mock
private PrimaryDataStoreDao _storagePoolDao;
@InjectMocks
private LinstorPrimaryDataStoreDriverImpl linstorPrimaryDataStoreDriver;
@ -85,4 +98,100 @@ public class LinstorPrimaryDataStoreDriverImplTest {
layers = LinstorUtil.getEncryptedLayerList(api, "EncryptedGrp");
Assert.assertEquals(Arrays.asList(LayerType.DRBD, LayerType.LUKS, LayerType.STORAGE), layers);
}
@Test
public void testGetCapabilitiesIncludesCreateTemplateFromSnapshotMatchesSystemSnapshot() {
Map<String, String> caps = linstorPrimaryDataStoreDriver.getCapabilities();
Assert.assertEquals(
"CAN_CREATE_TEMPLATE_FROM_SNAPSHOT should match STORAGE_SYSTEM_SNAPSHOT",
caps.get(DataStoreCapabilities.STORAGE_SYSTEM_SNAPSHOT.toString()),
caps.get(DataStoreCapabilities.CAN_CREATE_TEMPLATE_FROM_SNAPSHOT.toString()));
}
@Test
public void testCanCopySnapshotToVolumeOnSamePrimary() {
DataStore primaryStore = mock(DataStore.class);
when(primaryStore.getRole()).thenReturn(DataStoreRole.Primary);
when(primaryStore.getId()).thenReturn(1L);
SnapshotInfo snapshot = mock(SnapshotInfo.class);
when(snapshot.getType()).thenReturn(DataObjectType.SNAPSHOT);
when(snapshot.getDataStore()).thenReturn(primaryStore);
VolumeInfo volume = mock(VolumeInfo.class);
when(volume.getType()).thenReturn(DataObjectType.VOLUME);
when(volume.getDataStore()).thenReturn(primaryStore);
StoragePoolVO pool = mock(StoragePoolVO.class);
when(pool.getStorageProviderName()).thenReturn(LinstorUtil.PROVIDER_NAME);
when(_storagePoolDao.findById(1L)).thenReturn(pool);
Assert.assertTrue("canCopy should return true for SNAPSHOT -> VOLUME on same Linstor primary",
linstorPrimaryDataStoreDriver.canCopy(snapshot, volume));
}
@Test
public void testCanCopySnapshotToVolumeRejectsNonLinstor() {
DataStore primaryStore = mock(DataStore.class);
when(primaryStore.getRole()).thenReturn(DataStoreRole.Primary);
when(primaryStore.getId()).thenReturn(1L);
SnapshotInfo snapshot = mock(SnapshotInfo.class);
when(snapshot.getType()).thenReturn(DataObjectType.SNAPSHOT);
when(snapshot.getDataStore()).thenReturn(primaryStore);
VolumeInfo volume = mock(VolumeInfo.class);
when(volume.getType()).thenReturn(DataObjectType.VOLUME);
when(volume.getDataStore()).thenReturn(primaryStore);
StoragePoolVO pool = mock(StoragePoolVO.class);
when(pool.getStorageProviderName()).thenReturn("SomeOtherProvider");
when(_storagePoolDao.findById(1L)).thenReturn(pool);
Assert.assertFalse("canCopy should return false for non-Linstor storage",
linstorPrimaryDataStoreDriver.canCopy(snapshot, volume));
}
@Test
public void testCanCopySnapshotToVolumeRejectsCrossPrimary() {
DataStore srcStore = mock(DataStore.class);
when(srcStore.getRole()).thenReturn(DataStoreRole.Primary);
when(srcStore.getId()).thenReturn(1L);
DataStore destStore = mock(DataStore.class);
when(destStore.getRole()).thenReturn(DataStoreRole.Primary);
when(destStore.getId()).thenReturn(2L);
SnapshotInfo snapshot = mock(SnapshotInfo.class);
when(snapshot.getType()).thenReturn(DataObjectType.SNAPSHOT);
when(snapshot.getDataStore()).thenReturn(srcStore);
VolumeInfo volume = mock(VolumeInfo.class);
when(volume.getType()).thenReturn(DataObjectType.VOLUME);
when(volume.getDataStore()).thenReturn(destStore);
Assert.assertFalse("canCopy should return false for SNAPSHOT -> VOLUME across different primary stores",
linstorPrimaryDataStoreDriver.canCopy(snapshot, volume));
}
@Test
public void testCanCopySnapshotToVolumeRejectsImageDest() {
DataStore primaryStore = mock(DataStore.class);
when(primaryStore.getRole()).thenReturn(DataStoreRole.Primary);
DataStore imageStore = mock(DataStore.class);
when(imageStore.getRole()).thenReturn(DataStoreRole.Image);
SnapshotInfo snapshot = mock(SnapshotInfo.class);
when(snapshot.getType()).thenReturn(DataObjectType.SNAPSHOT);
when(snapshot.getDataStore()).thenReturn(primaryStore);
VolumeInfo volume = mock(VolumeInfo.class);
when(volume.getType()).thenReturn(DataObjectType.VOLUME);
when(volume.getDataStore()).thenReturn(imageStore);
Assert.assertFalse("canCopy should return false when destination is Image store",
linstorPrimaryDataStoreDriver.canCopy(snapshot, volume));
}
}

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

@ -1042,7 +1042,8 @@ public class ResourceLimitManagerImpl extends ManagerBase implements ResourceLim
ResourceLimitVO limit = _resourceLimitDao.findByOwnerIdAndTypeAndTag(ownerId, ownerType, resourceType, tag);
ActionEventUtils.onActionEvent(caller.getId(), caller.getAccountId(),
Long callingUserId = CallContext.current().getCallingUserId();
ActionEventUtils.onActionEvent(callingUserId, caller.getAccountId(),
caller.getDomainId(), EventTypes.EVENT_RESOURCE_LIMIT_UPDATE,
"Resource limit updated. Resource Type: " + resourceType + ", New Value: " + max,
ownerResourceId, ownerResourceType.toString());

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

@ -4359,9 +4359,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
return resourceLimitService.getResourceLimitStorageTags(diskOfferingVO);
}
private List<CheckedReservation> reserveStorageResourcesForVm(Account owner, Long diskOfferingId, Long diskSize, List<VmDiskInfo> dataDiskInfoList, Long rootDiskOfferingId, ServiceOfferingVO offering, Long rootDiskSize) throws ResourceAllocationException {
List <CheckedReservation> checkedReservations = new ArrayList<>();
private void reserveStorageResourcesForVm(List<Reserver> checkedReservations, Account owner, Long diskOfferingId, Long diskSize, List<VmDiskInfo> dataDiskInfoList, Long rootDiskOfferingId, ServiceOfferingVO offering, Long rootDiskSize) throws ResourceAllocationException {
List<String> rootResourceLimitStorageTags = getResourceLimitStorageTags(rootDiskOfferingId != null ? rootDiskOfferingId : offering.getDiskOfferingId());
CheckedReservation rootVolumeReservation = new CheckedReservation(owner, ResourceType.volume, rootResourceLimitStorageTags, 1L, reservationDao, resourceLimitService);
checkedReservations.add(rootVolumeReservation);
@ -4369,12 +4367,12 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
checkedReservations.add(rootPrimaryStorageReservation);
if (diskOfferingId != null) {
List<String> additionalResourceLimitStorageTags = diskOfferingId != null ? getResourceLimitStorageTags(diskOfferingId) : null;
List<String> additionalResourceLimitStorageTags = getResourceLimitStorageTags(diskOfferingId);
DiskOfferingVO diskOffering = _diskOfferingDao.findById(diskOfferingId);
Long size = verifyAndGetDiskSize(diskOffering, diskSize);
CheckedReservation additionalVolumeReservation = diskOfferingId != null ? new CheckedReservation(owner, ResourceType.volume, additionalResourceLimitStorageTags, 1L, reservationDao, resourceLimitService) : null;
CheckedReservation additionalVolumeReservation = new CheckedReservation(owner, ResourceType.volume, additionalResourceLimitStorageTags, 1L, reservationDao, resourceLimitService);
checkedReservations.add(additionalVolumeReservation);
CheckedReservation additionalPrimaryStorageReservation = diskOfferingId != null ? new CheckedReservation(owner, ResourceType.primary_storage, additionalResourceLimitStorageTags, size, reservationDao, resourceLimitService) : null;
CheckedReservation additionalPrimaryStorageReservation = new CheckedReservation(owner, ResourceType.primary_storage, additionalResourceLimitStorageTags, size, reservationDao, resourceLimitService);
checkedReservations.add(additionalPrimaryStorageReservation);
}
@ -4390,7 +4388,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
checkedReservations.add(additionalPrimaryStorageReservation);
}
}
return checkedReservations;
}
private UserVm getUncheckedUserVmResource(DataCenter zone, String hostName, String displayName, Account owner,
@ -4404,8 +4401,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
Long rootDiskOfferingId, Long rootDiskKmsKeyId, long volumesSize, Volume volume, Snapshot snapshot) throws ResourceAllocationException {
List<CheckedReservation> checkedReservations = new ArrayList<>();
try {
checkedReservations = reserveStorageResourcesForVm(owner, diskOfferingId, diskSize, dataDiskInfoList, rootDiskOfferingId, offering, volumesSize);
reserveStorageResourcesForVm(checkedReservations, owner, diskOfferingId, diskSize, dataDiskInfoList, rootDiskOfferingId, offering, volumesSize);
// verify security group ids
if (securityGroupIdList != null) {
@ -4696,14 +4694,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
logger.error("error during resource reservation and allocation", e);
throw new CloudRuntimeException(e);
} finally {
for (CheckedReservation checkedReservation : checkedReservations) {
try {
checkedReservation.close();
} catch (Exception e) {
logger.error("error during resource reservation and allocation", e);
throw new CloudRuntimeException(e);
}
}
ReservationHelper.closeAll(checkedReservations);
}
}

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):

View File

@ -4191,6 +4191,7 @@
"message.warn.select.template": "Please select a Template for Registration.",
"message.warn.zone.mtu.update": "Please note that this limit won't affect pre-existing Network's MTU settings",
"message.webhook.deliveries.time.filter": "Webhook deliveries list can be filtered based on date-time. Select 'Custom' for specifying start and end date range.",
"message.webhook.filter.add": "Webhook deliveries can be controlled using filters (currently by Event type). Please select the parameters to add to the applied filters list.",
"message.zone.creation.complete": "Zone creation complete.",
"message.zone.detail.description": "Populate Zone details.",
"message.zone.detail.hint": "A Zone is the largest organizational unit in CloudStack, and it typically corresponds to a single datacenter. Zones provide physical isolation and redundancy. A zone consists of one or more Pods (each of which contains hosts and primary storage servers) and a secondary storage server which is shared by all pods in the zone.",

View File

@ -953,7 +953,7 @@
style="margin-left: 5px"
:actions="actions"
:resource="record"
:enabled="quickViewEnabled() && actions.length > 0"
:enabled="quickViewEnabled(actions, columns, column.key)"
@exec-action="$parent.execAction"
/>
</template>

View File

@ -18,6 +18,7 @@
<template>
<div>
<div class="add-row">
<p>{{ $t('message.webhook.filter.add') }}</p>
<a-form
:ref="addFormRef"
:model="addFilterForm"
@ -90,7 +91,7 @@
{{ (selectedRowKeys && selectedRowKeys.length > 0) ? $t('label.action.delete.webhook.filters') : $t('label.action.clear.webhook.filters') }}
</a-button>
<list-view
:tabLoading="tabLoading"
:loading="tabLoading"
:columns="columns"
:items="filters"
:actions="actions"

View File

@ -372,9 +372,13 @@ export default {
'Group 15': 'modp3072',
'Group 16': 'modp4096',
'Group 17': 'modp6144',
'Group 18': 'modp8192'
'Group 18': 'modp8192',
'Group 22': 'modp1024s160',
'Group 23': 'modp2048s224',
'Group 24': 'modp2048s256',
'Group 31': 'curve25519'
},
ikeDhGroupInitialKey: 'Group 5',
ikeDhGroupInitialKey: 'Group 31',
isSubmitted: false,
ikeversion: 'ike',
allowedEncryptionAlgos: [],
@ -401,12 +405,12 @@ export default {
gateway: '',
cidrlist: '',
ipsecpsk: '',
ikeEncryption: '',
ikeHash: '',
ikeversion: '',
ikeDh: '',
espEncryption: '',
espHash: '',
ikeEncryption: 'aes256',
ikeHash: 'sha1',
ikeversion: 'ike',
ikeDh: 'Group 31(curve 25519)',
espEncryption: 'aes256',
espHash: 'sha256',
perfectForwardSecrecy: 'None',
ikelifetime: '86400',
esplifetime: '3600',

View File

@ -1265,7 +1265,7 @@ public class NetUtils {
if (group == null && policyType.toLowerCase().matches("ike")) {
return false; // StrongSwan requires a DH group for the IKE policy
}
if (group != null && !group.matches("modp1024|modp1536|modp2048|modp3072|modp4096|modp6144|modp8192")) {
if (group != null && !group.matches("modp1024|modp1536|modp2048|modp3072|modp4096|modp6144|modp8192|modp1024s160|modp2048s224|modp2048s256|curve25519")) {
return false;
}
}

View File

@ -131,6 +131,10 @@ public class NetUtilsTest {
assertTrue(NetUtils.isValidS2SVpnPolicy("ike", "3des-md5;modp1024"));
assertTrue(NetUtils.isValidS2SVpnPolicy("ike", "3des-sha1;modp3072,aes128-sha1;modp1536"));
assertTrue(NetUtils.isValidS2SVpnPolicy("ike", "3des-sha256;modp3072,aes128-sha512;modp1536"));
assertTrue(NetUtils.isValidS2SVpnPolicy("ike", "aes256-sha256;modp1024s160"));
assertTrue(NetUtils.isValidS2SVpnPolicy("ike", "aes256-sha256;modp2048s224"));
assertTrue(NetUtils.isValidS2SVpnPolicy("ike", "aes256-sha256;modp2048s256"));
assertTrue(NetUtils.isValidS2SVpnPolicy("ike", "aes256-sha256;curve25519"));
assertFalse(NetUtils.isValidS2SVpnPolicy("ike", "aes128-sha1"));
assertFalse(NetUtils.isValidS2SVpnPolicy("ike", "3des-sha1"));
assertFalse(NetUtils.isValidS2SVpnPolicy("ike", "3des-sha1,aes256-sha1"));