Guest OS rules (#10098)

Co-authored-by: Fabricio Duarte <fabricio.duarte.jr@gmail.com>
This commit is contained in:
Bryan Lima 2026-06-15 13:25:55 +02:00 committed by GitHub
parent 2fd83e13b1
commit 2081ac4666
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 341 additions and 93 deletions

View File

@ -63,6 +63,8 @@ public interface Host extends StateObject<Status>, Identity, Partition, HAResour
String HOST_OVFTOOL_VERSION = "host.ovftool.version";
String HOST_VIRTV2V_VERSION = "host.virtv2v.version";
String HOST_SSH_PORT = "host.ssh.port";
String GUEST_OS_CATEGORY_ID = "guest.os.category.id";
String GUEST_OS_RULE = "guest.os.rule";
int DEFAULT_SSH_PORT = 22;

View File

@ -449,6 +449,7 @@ public class ApiConstants {
public static final String MAX_VGPU_PER_PHYSICAL_GPU = "maxvgpuperphysicalgpu";
public static final String GUEST_OS_LIST = "guestoslist";
public static final String GUEST_OS_COUNT = "guestoscount";
public static final String GUEST_OS_RULE = "guestosrule";
public static final String OS_MAPPING_CHECK_ENABLED = "osmappingcheckenabled";
public static final String OUTOFBANDMANAGEMENT_POWERSTATE = "outofbandmanagementpowerstate";
public static final String OUTOFBANDMANAGEMENT_ENABLED = "outofbandmanagementenabled";

View File

@ -49,9 +49,14 @@ public class UpdateHostCmd extends BaseCmd {
@Parameter(name = ApiConstants.OS_CATEGORY_ID,
type = CommandType.UUID,
entityType = GuestOSCategoryResponse.class,
description = "The ID of OS category to update the host with")
description = "the ID of OS category used to prioritize VMs with matching OS category during the allocation process. " +
"It cannot be used alongside the 'guestosrule' parameter.")
private Long osCategoryId;
@Parameter(name = ApiConstants.GUEST_OS_RULE, type = CommandType.STRING, description = "the guest OS rule written in JavaScript to match with the OS of the VM." +
"It cannot be used alongside the 'oscategoryid' parameter.")
private String guestOsRule;
@Parameter(name = ApiConstants.ALLOCATION_STATE,
type = CommandType.STRING,
description = "Change resource state of host, valid values are [Enable, Disable]. Operation may failed if host in states not allowing Enable/Disable")
@ -96,6 +101,10 @@ public class UpdateHostCmd extends BaseCmd {
return osCategoryId;
}
public String getGuestOsRule() {
return guestOsRule;
}
public String getAllocationState() {
return allocationState;
}

View File

@ -63,6 +63,10 @@ public class HostResponse extends BaseResponseWithAnnotations {
@Param(description = "The OS category name of the host")
private String osCategoryName;
@SerializedName(ApiConstants.GUEST_OS_RULE)
@Param(description = "the guest OS rule")
private String guestOsRule;
@SerializedName(ApiConstants.IP_ADDRESS)
@Param(description = "The IP address of the host")
private String ipAddress;
@ -999,4 +1003,12 @@ public class HostResponse extends BaseResponseWithAnnotations {
public String getExtensionName() {
return extensionName;
}
public String getGuestOsRule() {
return guestOsRule;
}
public void setGuestOsRule(String guestOsRule) {
this.guestOsRule = guestOsRule;
}
}

View File

@ -45,7 +45,7 @@ import javax.persistence.Transient;
import com.cloud.cpu.CPU;
import org.apache.cloudstack.util.CPUArchConverter;
import org.apache.cloudstack.util.HypervisorTypeConverter;
import org.apache.cloudstack.utils.jsinterpreter.TagAsRuleHelper;
import org.apache.cloudstack.utils.jsinterpreter.GenericRuleHelper;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.BooleanUtils;
@ -856,7 +856,8 @@ public class HostVO implements Host {
}
if (BooleanUtils.isTrue(this.getIsTagARule())) {
return TagAsRuleHelper.interpretTagAsRule(this.getHostTags().get(0), serviceOffering.getHostTag(), HostTagsDao.hostTagRuleExecutionTimeout.value());
return GenericRuleHelper.interpretTagAsRule(this.getHostTags().get(0), serviceOffering.getHostTag(), HostTagsDao.hostTagRuleExecutionTimeout.value(),
HostTagsDao.hostTagRuleExecutionTimeout.key());
}
if (StringUtils.isEmpty(serviceOffering.getHostTag())) {

View File

@ -32,12 +32,17 @@ import com.cloud.utils.Pair;
import com.cloud.utils.db.GenericDao;
import com.cloud.utils.fsm.StateDao;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
import org.apache.cloudstack.framework.config.ConfigKey;
/**
* Data Access Object for server
*
*/
public interface HostDao extends GenericDao<HostVO, Long>, StateDao<Status, Status.Event, Host> {
ConfigKey<Long> guestOsRuleExecutionTimeout = new ConfigKey<>("Advanced", Long.class, "guest.os.rule.execution.timeout", "3000", "The maximum runtime, in milliseconds, " +
"to execute a guest OS rule; if it is reached, a timeout will happen.", true);
long countBy(long clusterId, ResourceState... states);
Integer countAllByType(final Host.Type type);
@ -220,6 +225,8 @@ public interface HostDao extends GenericDao<HostVO, Long>, StateDao<Status, Stat
List<HostVO> findHostsWithTagRuleThatMatchComputeOfferingTags(String computeOfferingTags);
List<HostVO> findHostsWithGuestOsRulesThatDidNotMatchOsOfGuestVm(String templateGuestOSName);
List<Long> findClustersThatMatchHostTagRule(String computeOfferingTags);
List<Long> listSsvmHostsWithPendingMigrateJobsOrderedByJobCount();

View File

@ -37,7 +37,9 @@ import javax.persistence.TableGenerator;
import com.cloud.vm.VirtualMachine;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
import org.apache.cloudstack.utils.jsinterpreter.TagAsRuleHelper;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.Configurable;
import org.apache.cloudstack.utils.jsinterpreter.GenericRuleHelper;
import org.apache.commons.collections.CollectionUtils;
import com.cloud.agent.api.VgpuTypesInfo;
@ -84,7 +86,7 @@ import org.apache.commons.lang3.ObjectUtils;
@DB
@TableGenerator(name = "host_req_sq", table = "op_host", pkColumnName = "id", valueColumnName = "sequence", allocationSize = 1)
public class HostDaoImpl extends GenericDaoBase<HostVO, Long> implements HostDao { //FIXME: , ExternalIdDao {
public class HostDaoImpl extends GenericDaoBase<HostVO, Long> implements HostDao, Configurable {
private static final String LIST_HOST_IDS_BY_HOST_TAGS = "SELECT filtered.host_id, COUNT(filtered.tag) AS tag_count "
+ "FROM (SELECT host_id, tag, is_tag_a_rule FROM host_tags GROUP BY host_id,tag,is_tag_a_rule) AS filtered "
@ -1520,11 +1522,13 @@ public class HostDaoImpl extends GenericDaoBase<HostVO, Long> implements HostDao
}
}
@Override
public List<HostVO> findHostsWithTagRuleThatMatchComputeOfferingTags(String computeOfferingTags) {
List<HostTagVO> hostTagVOList = _hostTagsDao.findHostRuleTags();
List<HostVO> result = new ArrayList<>();
for (HostTagVO rule: hostTagVOList) {
if (TagAsRuleHelper.interpretTagAsRule(rule.getTag(), computeOfferingTags, HostTagsDao.hostTagRuleExecutionTimeout.value())) {
if (GenericRuleHelper.interpretTagAsRule(rule.getTag(), computeOfferingTags, HostTagsDao.hostTagRuleExecutionTimeout.value(),
HostTagsDao.hostTagRuleExecutionTimeout.key())) {
result.add(findById(rule.getHostId()));
}
}
@ -1532,6 +1536,23 @@ public class HostDaoImpl extends GenericDaoBase<HostVO, Long> implements HostDao
return result;
}
@Override
public List<HostVO> findHostsWithGuestOsRulesThatDidNotMatchOsOfGuestVm(String templateGuestOSName) {
List<DetailVO> hostIdsWithGuestOsRule = _detailsDao.findByName(Host.GUEST_OS_RULE);
List<HostVO> hostsWithIncompatibleRules = new ArrayList<>();
for (DetailVO guestOsRule : hostIdsWithGuestOsRule) {
if (!GenericRuleHelper.interpretGuestOsRule(guestOsRule.getValue(), templateGuestOSName, HostDao.guestOsRuleExecutionTimeout.value(),
HostDao.guestOsRuleExecutionTimeout.key())) {
logger.trace("The guest OS rule [{}] of the host with ID [{}] is incompatible with the OS of the VM.",
guestOsRule.getHostId(), guestOsRule.getValue());
hostsWithIncompatibleRules.add(findById(guestOsRule.getHostId()));
}
}
logger.trace("The hosts with the following IDs [{}] are incompatible with the VM considering their guest OS rule.",
hostsWithIncompatibleRules);
return hostsWithIncompatibleRules;
}
public List<Long> findClustersThatMatchHostTagRule(String computeOfferingTags) {
Set<Long> result = new HashSet<>();
List<HostVO> hosts = findHostsWithTagRuleThatMatchComputeOfferingTags(computeOfferingTags);
@ -1984,4 +2005,14 @@ public class HostDaoImpl extends GenericDaoBase<HostVO, Long> implements HostDao
return customSearch(sc, null);
}
@Override
public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] {guestOsRuleExecutionTimeout};
}
@Override
public String getConfigComponentName() {
return HostDaoImpl.class.getSimpleName();
}
}

View File

@ -64,6 +64,7 @@ SELECT
guest_os_category.id guest_os_category_id,
guest_os_category.uuid guest_os_category_uuid,
guest_os_category.name guest_os_category_name,
(SELECT `value` FROM `cloud`.`host_details` `hd` WHERE `hd`.`host_id` = `cloud`.`host`.`id` AND `hd`.`name` = 'guest.os.rule') AS `guest_os_rule`,
mem_caps.used_capacity memory_used_capacity,
mem_caps.reserved_capacity memory_reserved_capacity,
cpu_caps.used_capacity cpu_used_capacity,

View File

@ -320,7 +320,7 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
Map<UsageVO, Pair<QuotaUsageVO, List<QuotaTariffUsageVO>>> mapUsageAndQuotaUsage = new LinkedHashMap<>();
try (JsInterpreter jsInterpreter = new JsInterpreter(QuotaConfig.QuotaActivationRuleTimeout.value())) {
try (JsInterpreter jsInterpreter = new JsInterpreter(QuotaConfig.QuotaActivationRuleTimeout.value(), QuotaConfig.QuotaActivationRuleTimeout.key())) {
for (UsageVO usageRecord : usageRecords) {
int usageType = usageRecord.getUsageType();

View File

@ -1115,7 +1115,7 @@ public class QuotaResponseBuilderImpl implements QuotaResponseBuilder {
addAllPresetVariables(PresetVariables.class, quotaType, usageTypeVariablesAndDescriptions, null);
List<String> usageTypeVariables = usageTypeVariablesAndDescriptions.stream().map(Pair::first).collect(Collectors.toList());
try (JsInterpreter jsInterpreter = new JsInterpreter(QuotaConfig.QuotaActivationRuleTimeout.value())) {
try (JsInterpreter jsInterpreter = new JsInterpreter(QuotaConfig.QuotaActivationRuleTimeout.value(), QuotaConfig.QuotaActivationRuleTimeout.key())) {
Map<String, String> newVariables = injectUsageTypeVariables(jsInterpreter, usageTypeVariables);
String scriptToExecute = jsInterpreterHelper.replaceScriptVariables(activationRule, newVariables);
jsInterpreter.executeScript(String.format("new Function(\"%s\")", scriptToExecute.replaceAll("\n", "")));

View File

@ -17,11 +17,13 @@
package com.cloud.agent.manager.allocator.impl;
import com.cloud.agent.manager.allocator.HostAllocator;
import com.cloud.api.ApiDBUtils;
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.storage.VMTemplateVO;
import com.cloud.utils.Pair;
import com.cloud.utils.component.AdapterBase;
import org.apache.commons.collections.CollectionUtils;
@ -69,6 +71,29 @@ public abstract class BaseAllocator extends AdapterBase implements HostAllocator
clusterHosts.addAll(hostsWithTagRules);
}
protected void filterHostsBasedOnGuestOsRules(VMTemplateVO vmTemplate, List<? extends Host> clusterHosts) {
if (clusterHosts.isEmpty()) {
logger.info("Will not filter hosts based on guest OS as there is no available hosts left to verify.");
return;
}
String templateGuestOSName = ApiDBUtils.getTemplateGuestOSName(vmTemplate);
List<HostVO> incompatibleHosts = hostDao.findHostsWithGuestOsRulesThatDidNotMatchOsOfGuestVm(templateGuestOSName);
if (incompatibleHosts.isEmpty()) {
logger.info("No incompatible hosts found with guest OS rules matching the VM guest OS [{}].", templateGuestOSName);
return;
}
logger.info("Found incompatible hosts {} with guest OS rules that did not match the VM guest OS [{}]. They will be removed from the suitable hosts list.",
incompatibleHosts, templateGuestOSName);
clusterHosts.removeAll(incompatibleHosts);
if (clusterHosts.isEmpty()) {
logger.info("After filtering by guest OS rules, no compatible hosts were found for VM with OS [{}].", templateGuestOSName);
}
}
/**
* Adds hosts with enough CPU capability and enough CPU capacity to the suitable hosts list.
*/

View File

@ -123,7 +123,7 @@ public class FirstFitAllocator extends BaseAllocator {
String hostTagOnTemplate = template.getTemplateTag();
String paramAsStringToLog = String.format("zone [%s], pod [%s], cluster [%s]", dcId, podId, clusterId);
List<HostVO> suitableHosts = retrieveHosts(vmProfile, type, (List<HostVO>) hosts, clusterId, podId, dcId, hostTagOnOffering, hostTagOnTemplate);
List<HostVO> suitableHosts = retrieveHosts(vmProfile, type, (List<HostVO>) hosts, template, clusterId, podId, dcId, hostTagOnOffering, hostTagOnTemplate);
if (suitableHosts.isEmpty()) {
logger.info("No suitable host found for VM [{}] in {}.", vmProfile, paramAsStringToLog);
@ -137,8 +137,8 @@ public class FirstFitAllocator extends BaseAllocator {
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) {
protected List<HostVO> retrieveHosts(VirtualMachineProfile vmProfile, Type type, List<HostVO> hostsToFilter, VMTemplateVO template,
Long clusterId, Long podId, long dcId, String hostTagOnOffering, String hostTagOnTemplate) {
String haVmTag = (String) vmProfile.getParameter(VirtualMachineProfile.Param.HaTag);
List<HostVO> clusterHosts;
@ -159,6 +159,7 @@ public class FirstFitAllocator extends BaseAllocator {
filterHostsWithUefiEnabled(type, vmProfile, clusterId, podId, dcId, clusterHosts);
addHostsBasedOnTagRules(hostTagOnOffering, clusterHosts);
filterHostsBasedOnGuestOsRules(template, clusterHosts);
return clusterHosts;

View File

@ -42,7 +42,7 @@ public class RandomAllocator extends BaseAllocator {
private ResourceManager _resourceMgr;
protected List<Host> findSuitableHosts(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;
}
@ -115,6 +115,7 @@ public class RandomAllocator extends BaseAllocator {
}
addHostsBasedOnTagRules(offeringHostTag, availableHosts);
filterHostsBasedOnGuestOsRules(template, availableHosts);
return availableHosts;
}

View File

@ -31,6 +31,7 @@ import javax.annotation.PostConstruct;
import javax.inject.Inject;
import com.cloud.cpu.CPU;
import com.cloud.storage.GuestOSVO;
import org.apache.cloudstack.acl.Role;
import org.apache.cloudstack.acl.RoleService;
import org.apache.cloudstack.affinity.AffinityGroup;
@ -2345,4 +2346,15 @@ public class ApiDBUtils {
public static List<CPU.CPUArch> listZoneClustersArchs(long zoneId) {
return s_clusterDao.getClustersArchsByZone(zoneId);
}
public static String getTemplateGuestOSName(VMTemplateVO template) {
long guestOSId = template.getGuestOSId();
GuestOSVO guestOS = s_guestOSDao.findById(guestOSId);
if (guestOS == null) {
return null;
}
return guestOS.getDisplayName();
}
}

View File

@ -225,6 +225,7 @@ public class HostJoinDaoImpl extends GenericDaoBase<HostJoinVO, Long> implements
hostResponse.setHaHost(containsHostHATag(hostTags));
hostResponse.setExplicitHostTags(host.getExplicitTag());
hostResponse.setImplicitHostTags(host.getImplicitTag());
hostResponse.setGuestOsRule(host.getGuestOsRule());
hostResponse.setHypervisorVersion(host.getHypervisorVersion());
if (host.getArch() != null) {

View File

@ -35,7 +35,7 @@ import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO;
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.utils.jsinterpreter.TagAsRuleHelper;
import org.apache.cloudstack.utils.jsinterpreter.GenericRuleHelper;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.springframework.stereotype.Component;
@ -401,7 +401,8 @@ public class StoragePoolJoinDaoImpl extends GenericDaoBase<StoragePoolJoinVO, Lo
String injectableTag = injectableTagsBuilder.toString();
for (StoragePoolJoinVO storagePoolJoinVO : storagePools) {
if (TagAsRuleHelper.interpretTagAsRule(storagePoolJoinVO.getTag(), injectableTag, VolumeApiServiceImpl.storageTagRuleExecutionTimeout.value())) {
if (GenericRuleHelper.interpretTagAsRule(storagePoolJoinVO.getTag(), injectableTag, VolumeApiServiceImpl.storageTagRuleExecutionTimeout.value(),
VolumeApiServiceImpl.storageTagRuleExecutionTimeout.key())) {
StoragePoolVO storagePoolVO = storagePoolDao.findById(storagePoolJoinVO.getId());
if (storagePoolVO != null) {
filteredPools.add(storagePoolVO);

View File

@ -173,6 +173,9 @@ public class HostJoinVO extends BaseViewVO implements InternalIdentity, Identity
@Column(name = "guest_os_category_name")
private String osCategoryName;
@Column(name = "guest_os_rule")
private String guestOsRule;
@Column(name = "tag")
private String tag;
@ -373,6 +376,10 @@ public class HostJoinVO extends BaseViewVO implements InternalIdentity, Identity
return osCategoryName;
}
public String getGuestOsRule() {
return guestOsRule;
}
public Long getJobId() {
return jobId;
}

View File

@ -143,7 +143,7 @@ import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.userdata.UserDataManager;
import org.apache.cloudstack.utils.jsinterpreter.TagAsRuleHelper;
import org.apache.cloudstack.utils.jsinterpreter.GenericRuleHelper;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import org.apache.cloudstack.vm.UnmanagedVMsManager;
import org.apache.cloudstack.vm.lease.VMLeaseManager;
@ -5115,7 +5115,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
if ((CollectionUtils.isNotEmpty(tagsAsString) && tagsAsString.containsAll(listOfTags)) ||
(tagsOnPool.size() == 1 && tagsOnPool.get(0).isTagARule() &&
TagAsRuleHelper.interpretTagAsRule(tagsOnPool.get(0).getTag(), tags, VolumeApiServiceImpl.storageTagRuleExecutionTimeout.value()))) {
GenericRuleHelper.interpretTagAsRule(tagsOnPool.get(0).getTag(), tags, VolumeApiServiceImpl.storageTagRuleExecutionTimeout.value(),
VolumeApiServiceImpl.storageTagRuleExecutionTimeout.key()))) {
continue;
}
@ -5158,7 +5159,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
if ((CollectionUtils.isNotEmpty(tagsAsString) && tagsAsString.containsAll(listOfHostTags)) ||
(tagsOnHost.size() == 1 && tagsOnHost.get(0).getIsTagARule() &&
TagAsRuleHelper.interpretTagAsRule(tagsOnHost.get(0).getTag(), hostTags, HostTagsDao.hostTagRuleExecutionTimeout.value()))) {
GenericRuleHelper.interpretTagAsRule(tagsOnHost.get(0).getTag(), hostTags, HostTagsDao.hostTagRuleExecutionTimeout.value(),
HostTagsDao.hostTagRuleExecutionTimeout.key()))) {
continue;
}

View File

@ -35,6 +35,7 @@ import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import com.cloud.api.ApiDBUtils;
import com.cloud.gpu.dao.VgpuProfileDao;
import com.cloud.resource.ResourceState;
import org.apache.cloudstack.affinity.AffinityGroupDomainMapVO;
@ -805,18 +806,49 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
return false;
}
}
DetailVO guestOsRule = _hostDetailsDao.findDetail(host.getId(), HostVO.GUEST_OS_RULE);
if (guestOsRule != null) {
return isHostGuestOsCompatible(vmProfile, host, guestOsRule);
}
DetailVO guestOsCategoryId = _hostDetailsDao.findDetail(host.getId(), HostVO.GUEST_OS_CATEGORY_ID);
if (guestOsCategoryId != null) {
return isHostGuestOsCategoryCompatible(vmProfile, host, guestOsCategoryId);
}
return true;
}
private boolean isHostGuestOsCategoryCompatible(VirtualMachineProfile vmProfile, HostVO host, DetailVO guestOsCategoryId) {
long guestOSId = vmProfile.getTemplate().getGuestOSId();
GuestOSVO guestOS = _guestOSDao.findById(guestOSId);
if (guestOS != null) {
long guestOSCategoryId = guestOS.getCategoryId();
DetailVO hostDetail = _hostDetailsDao.findDetail(host.getId(), "guest.os.category.id");
if (hostDetail != null) {
String guestOSCategoryIdString = hostDetail.getValue();
if (String.valueOf(guestOSCategoryId) != guestOSCategoryIdString) {
logger.debug("The last host has different guest.os.category.id than guest os category of VM, skipping");
return false;
}
}
if (guestOS == null) {
return true;
}
long guestOSCategoryId = guestOS.getCategoryId();
String guestOSCategoryIdString = guestOsCategoryId.getValue();
if (!String.valueOf(guestOSCategoryId).equals(guestOSCategoryIdString)) {
logger.debug("The last host has different `{}` than guest os category of VM, skipping", Host.GUEST_OS_CATEGORY_ID);
return false;
}
return true;
}
private boolean isHostGuestOsCompatible(VirtualMachineProfile vmProfile, HostVO host, DetailVO guestOsRule) {
VMTemplateVO template = (VMTemplateVO) vmProfile.getTemplate();
String templateGuestOSName = ApiDBUtils.getTemplateGuestOSName(template);
List<HostVO> incompatibleHosts = _hostDao.findHostsWithGuestOsRulesThatDidNotMatchOsOfGuestVm(templateGuestOSName);
boolean isHostCompatible = incompatibleHosts.stream().noneMatch(incompatibleHost -> incompatibleHost.getId() == host.getId());
if (!isHostCompatible) {
logger.debug("The template [{}] is incompatible with the host [{}] due to the guest OS rule [{}].", template, host, guestOsRule);
return false;
}
return true;
}

View File

@ -84,8 +84,8 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.utils.identity.ManagementServerNode;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Component;
@ -2046,31 +2046,74 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
_hostDao.update(host.getId(), host);
}
private void updateHostGuestOSCategory(Long hostId, Long guestOSCategoryId) {
// Verify that the guest OS Category exists
if (!(guestOSCategoryId > 0) || _guestOSCategoryDao.findById(guestOSCategoryId) == null) {
/**
* Updates the guest OS details related to the host ({@code guest.os.rule} and {@code guest.os.category.id}); only one can be set at a time for a given host.
*/
private void updateGuestOsRelatedFields(Long hostId, Long cmdOsCategoryId, String cmdGuestOsRule) {
if (ObjectUtils.allNotNull(cmdOsCategoryId, cmdGuestOsRule)) {
throw new InvalidParameterValueException("Informing both guest OS category and guest OS rule is invalid; please, inform only one of them.");
}
if (cmdOsCategoryId != null) {
updateHostCategoryOs(hostId, cmdOsCategoryId);
} else if (cmdGuestOsRule != null) {
updateHostGuestOsRule(hostId, cmdGuestOsRule);
}
}
/**
* Associates/removes a guest OS rule to a host and removes any guest OS category associated with it.
*/
private void updateHostGuestOsRule(Long hostId, String cmdGuestOsRule) {
final DetailVO hostGuestOsRule = _hostDetailsDao.findDetail(hostId, Host.GUEST_OS_RULE);
if (StringUtils.isNotBlank(cmdGuestOsRule)) {
createOrUpdateHostDetails(hostGuestOsRule, cmdGuestOsRule, Host.GUEST_OS_RULE, hostId);
} else if (hostGuestOsRule != null) {
_hostDetailsDao.remove(hostGuestOsRule.getId());
}
removeHostDetails(hostId, Host.GUEST_OS_CATEGORY_ID);
}
/**
* Removes a host details if it exists.
*/
private void removeHostDetails(Long hostId, String detailString) {
DetailVO detailVO = _hostDetailsDao.findDetail(hostId, detailString);
if (detailVO != null) {
_hostDetailsDao.remove(detailVO.getId());
}
}
private void createOrUpdateHostDetails(DetailVO hostDetail, String detailValue, String detailName, Long hostId) {
if (hostDetail != null) {
hostDetail.setValue(detailValue);
_hostDetailsDao.update(hostDetail.getId(), hostDetail);
} else {
Map<String, String> detail = new HashMap<>();
detail.put(detailName, detailValue);
_hostDetailsDao.persist(hostId, detail);
}
}
/**
* Associates/removes a valid guest OS category to a host and removes any guest OS rules associated with it.
*/
private void updateHostCategoryOs(Long hostId, Long cmdOsCategoryId) {
if (cmdOsCategoryId <= 0 || _guestOSCategoryDao.findById(cmdOsCategoryId) == null) {
throw new InvalidParameterValueException("Please specify a valid guest OS category.");
}
final GuestOSCategoryVO guestOSCategory = _guestOSCategoryDao.findById(guestOSCategoryId);
final DetailVO guestOSDetail = _hostDetailsDao.findDetail(hostId, "guest.os.category.id");
GuestOSCategoryVO guestOSCategory = _guestOSCategoryDao.findById(cmdOsCategoryId);
DetailVO guestOSDetail = _hostDetailsDao.findDetail(hostId, Host.GUEST_OS_CATEGORY_ID);
if (guestOSCategory != null && !GuestOSCategoryVO.CATEGORY_NONE.equalsIgnoreCase(guestOSCategory.getName())) {
// Create/Update an entry for guest.os.category.id
if (guestOSDetail != null) {
guestOSDetail.setValue(String.valueOf(guestOSCategory.getId()));
_hostDetailsDao.update(guestOSDetail.getId(), guestOSDetail);
} else {
final Map<String, String> detail = new HashMap<>();
detail.put("guest.os.category.id", String.valueOf(guestOSCategory.getId()));
_hostDetailsDao.persist(hostId, detail);
}
} else {
// Delete any existing entry for guest.os.category.id
if (guestOSDetail != null) {
_hostDetailsDao.remove(guestOSDetail.getId());
}
createOrUpdateHostDetails(guestOSDetail, String.valueOf(guestOSCategory.getId()), Host.GUEST_OS_CATEGORY_ID, hostId);
} else if (guestOSDetail != null) {
_hostDetailsDao.remove(guestOSDetail.getId());
}
removeHostDetails(hostId, Host.GUEST_OS_RULE);
}
private void removeStorageAccessGroupsOnPodsInZone(long zoneId, List<String> newStoragePoolTags, List<String> tagsToDeleteOnZone) {
@ -2821,16 +2864,17 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
@Override
public Host updateHost(final UpdateHostCmd cmd) throws NoTransitionException {
return updateHost(cmd.getId(), cmd.getName(), cmd.getOsCategoryId(),
return updateHost(cmd.getId(), cmd.getName(), cmd.getOsCategoryId(), cmd.getGuestOsRule(),
cmd.getAllocationState(), cmd.getUrl(), cmd.getHostTags(), cmd.getIsTagARule(), cmd.getAnnotation(), false,
cmd.getExternalDetails(), cmd.isCleanupExternalDetails());
}
private Host updateHost(Long hostId, String name, Long guestOSCategoryId, String allocationState,
private Host updateHost(Long hostId, String name, Long guestOSCategoryId, String guestOsRule, String allocationState,
String url, List<String> hostTags, Boolean isTagARule, String annotation,
boolean isUpdateFromHostHealthCheck, Map<String, String> externalDetails,
boolean cleanupExternalDetails) throws NoTransitionException {
jsInterpreterHelper.ensureInterpreterEnabledIfParameterProvided(ApiConstants.IS_TAG_A_RULE, Boolean.TRUE.equals(isTagARule));
jsInterpreterHelper.ensureInterpreterEnabledIfParameterProvided(ApiConstants.GUEST_OS_RULE, guestOsRule != null);
// Verify that the host exists
final HostVO host = _hostDao.findById(hostId);
@ -2847,9 +2891,7 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
updateHostName(host, name);
}
if (guestOSCategoryId != null) {
updateHostGuestOSCategory(hostId, guestOSCategoryId);
}
updateGuestOsRelatedFields(hostId, guestOSCategoryId, guestOsRule);
if (hostTags != null) {
updateHostTags(host, hostId, hostTags, isTagARule);
@ -2919,7 +2961,7 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
@Override
public Host autoUpdateHostAllocationState(Long hostId, ResourceState.Event resourceEvent) throws NoTransitionException {
return updateHost(hostId, null, null, resourceEvent.toString(), null, null, null, null, true, null, false);
return updateHost(hostId, null, null, null, resourceEvent.toString(), null, null, null, null, true, null, false);
}
@Override
@ -3639,7 +3681,7 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
// If the server's private IP is the same as is public IP, this host has
// a host-only private network. Don't check for conflicts with the
// private IP address table.
if (!ObjectUtils.equals(serverPrivateIP, serverPublicIP)) {
if (!StringUtils.equals(serverPrivateIP, serverPublicIP)) {
if (!_privateIPAddressDao.mark(dc.getId(), pod.getId(), serverPrivateIP)) {
// If the server's private IP address is already in the
// database, return false
@ -4284,7 +4326,7 @@ public class ResourceManagerImpl extends ManagerBase implements ResourceManager,
return null;
} else {
_hostDao.loadDetails(host);
final DetailVO detail = _hostDetailsDao.findDetail(hostId, "guest.os.category.id");
final DetailVO detail = _hostDetailsDao.findDetail(hostId, Host.GUEST_OS_CATEGORY_ID);
if (detail == null) {
return null;
} else {

View File

@ -112,7 +112,7 @@ import org.apache.cloudstack.storage.image.datastore.ImageStoreEntity;
import org.apache.cloudstack.storage.to.VolumeObjectTO;
import org.apache.cloudstack.utils.identity.ManagementServerNode;
import org.apache.cloudstack.utils.imagestore.ImageStoreUtil;
import org.apache.cloudstack.utils.jsinterpreter.TagAsRuleHelper;
import org.apache.cloudstack.utils.jsinterpreter.GenericRuleHelper;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo;
import org.apache.commons.collections.CollectionUtils;
@ -3904,7 +3904,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
boolean result;
if (storagePoolTags.second()) {
result = TagAsRuleHelper.interpretTagAsRule(storageTagsList.get(0), diskOfferingTags, storageTagRuleExecutionTimeout.value());
result = GenericRuleHelper.interpretTagAsRule(storageTagsList.get(0), diskOfferingTags, storageTagRuleExecutionTimeout.value(),
storageTagRuleExecutionTimeout.key());
} else {
result = CollectionUtils.isSubCollection(Arrays.asList(newDiskOfferingTagsAsStringArray), storageTagsList);
}

View File

@ -257,7 +257,7 @@ public class HeuristicRuleHelper {
* @return the {@link DataStore} returned by the script.
*/
public DataStore interpretHeuristicRule(String rule, HeuristicType heuristicType, Object obj, long zoneId) {
try (JsInterpreter jsInterpreter = new JsInterpreter(HEURISTICS_SCRIPT_TIMEOUT)) {
try (JsInterpreter jsInterpreter = new JsInterpreter(HEURISTICS_SCRIPT_TIMEOUT, StorageManager.HEURISTICS_SCRIPT_TIMEOUT.key())) {
buildPresetVariables(jsInterpreter, heuristicType, zoneId, obj);
Object scriptReturn = jsInterpreter.executeScript(rule);

View File

@ -193,7 +193,7 @@ public class FirstFitAllocatorTest {
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());
Mockito.doReturn(emptyList).when(firstFitAllocatorSpy).retrieveHosts(Mockito.any(VirtualMachineProfile.class), Mockito.any(Host.Type.class), Mockito.nullable(List.class), Mockito.any(VMTemplateVO.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);
@ -208,7 +208,7 @@ public class FirstFitAllocatorTest {
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).retrieveHosts(Mockito.any(VirtualMachineProfile.class), Mockito.any(Host.Type.class), Mockito.nullable(List.class), Mockito.any(VMTemplateVO.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);
@ -226,7 +226,7 @@ public class FirstFitAllocatorTest {
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).retrieveHosts(Mockito.any(VirtualMachineProfile.class), Mockito.any(Host.Type.class), Mockito.nullable(List.class), Mockito.any(VMTemplateVO.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);
@ -245,7 +245,8 @@ public class FirstFitAllocatorTest {
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);
Mockito.doNothing().when(firstFitAllocatorSpy).filterHostsBasedOnGuestOsRules(Mockito.any(VMTemplateVO.class), Mockito.anyList());
List<HostVO> resultHosts = firstFitAllocatorSpy.retrieveHosts(virtualMachineProfile, type, emptyList, vmTemplateVO, clusterId, podId, dcId, hostTag, templateTag);
Assert.assertEquals(2, resultHosts.size());
Assert.assertEquals(host1, resultHosts.get(0));
@ -262,7 +263,8 @@ public class FirstFitAllocatorTest {
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);
Mockito.doNothing().when(firstFitAllocatorSpy).filterHostsBasedOnGuestOsRules(Mockito.any(VMTemplateVO.class), Mockito.anyList());
List<HostVO> resultHosts = firstFitAllocatorSpy.retrieveHosts(virtualMachineProfile, type, hostsToFilter, vmTemplateVO, clusterId, podId, dcId, hostTag, templateTag);
Assert.assertEquals(2, resultHosts.size());
Assert.assertEquals(host1, resultHosts.get(0));
@ -278,7 +280,8 @@ public class FirstFitAllocatorTest {
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);
Mockito.doNothing().when(firstFitAllocatorSpy).filterHostsBasedOnGuestOsRules(Mockito.any(VMTemplateVO.class), Mockito.anyList());
List<HostVO> resultHosts = firstFitAllocatorSpy.retrieveHosts(virtualMachineProfile, type, emptyList, vmTemplateVO, clusterId, podId, dcId, null, null);
Assert.assertEquals(2, resultHosts.size());
Assert.assertEquals(host1, resultHosts.get(0));
@ -293,7 +296,8 @@ public class FirstFitAllocatorTest {
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.doNothing().when(firstFitAllocatorSpy).filterHostsBasedOnGuestOsRules(Mockito.any(VMTemplateVO.class), Mockito.anyList());
firstFitAllocatorSpy.retrieveHosts(virtualMachineProfile, type, emptyList, vmTemplateVO, 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());
}

View File

@ -222,6 +222,7 @@ public class RandomAllocatorTest {
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));
Mockito.doNothing().when(randomAllocator).filterHostsBasedOnGuestOsRules(Mockito.any(VMTemplateVO.class), Mockito.anyList());
List<HostVO> availableHosts = randomAllocator.retrieveHosts(type, null, vmTemplateVO, null, clusterId, podId, zoneId);
Assert.assertEquals(1, availableHosts.size());
@ -237,6 +238,7 @@ public class RandomAllocatorTest {
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));
Mockito.doNothing().when(randomAllocator).filterHostsBasedOnGuestOsRules(Mockito.any(VMTemplateVO.class), Mockito.anyList());
List<HostVO> availableHosts = randomAllocator.retrieveHosts(type, null, vmTemplateVO, null, clusterId, podId, zoneId);
Assert.assertEquals(2, availableHosts.size());
@ -252,6 +254,7 @@ public class RandomAllocatorTest {
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));
Mockito.doNothing().when(randomAllocator).filterHostsBasedOnGuestOsRules(Mockito.any(VMTemplateVO.class), Mockito.anyList());
List<HostVO> availableHosts = randomAllocator.retrieveHosts(type, null, vmTemplateVO, hostTag, clusterId, podId, zoneId);
Assert.assertEquals(1, availableHosts.size());
@ -267,6 +270,7 @@ public class RandomAllocatorTest {
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));
Mockito.doNothing().when(randomAllocator).filterHostsBasedOnGuestOsRules(Mockito.any(VMTemplateVO.class), Mockito.anyList());
List<HostVO> availableHosts = randomAllocator.retrieveHosts(type, null, vmTemplateVO, hostTag, clusterId, podId, zoneId);
Assert.assertEquals(2, availableHosts.size());
@ -281,6 +285,7 @@ public class RandomAllocatorTest {
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));
Mockito.doNothing().when(randomAllocator).filterHostsBasedOnGuestOsRules(Mockito.any(VMTemplateVO.class), Mockito.anyList());
List<HostVO> availableHosts = randomAllocator.retrieveHosts(type, providedHosts, vmTemplateVO, null, clusterId, podId, zoneId);
Assert.assertEquals(1, availableHosts.size());
@ -295,6 +300,7 @@ public class RandomAllocatorTest {
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));
Mockito.doNothing().when(randomAllocator).filterHostsBasedOnGuestOsRules(Mockito.any(VMTemplateVO.class), Mockito.anyList());
List<HostVO> availableHosts = randomAllocator.retrieveHosts(type, providedHosts, vmTemplateVO, null, clusterId, podId, zoneId);
Assert.assertEquals(2, availableHosts.size());
@ -309,6 +315,7 @@ public class RandomAllocatorTest {
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));
Mockito.doNothing().when(randomAllocator).filterHostsBasedOnGuestOsRules(Mockito.any(VMTemplateVO.class), Mockito.anyList());
List<HostVO> availableHosts = randomAllocator.retrieveHosts(type, providedHosts, vmTemplateVO, hostTag, clusterId, podId, zoneId);
Assert.assertEquals(1, availableHosts.size());
@ -323,6 +330,7 @@ public class RandomAllocatorTest {
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));
Mockito.doNothing().when(randomAllocator).filterHostsBasedOnGuestOsRules(Mockito.any(VMTemplateVO.class), Mockito.anyList());
List<HostVO> availableHosts = randomAllocator.retrieveHosts(type, providedHosts, vmTemplateVO, hostTag, clusterId, podId, zoneId);
Assert.assertEquals(2, availableHosts.size());

View File

@ -1219,6 +1219,8 @@
"label.guestnetwork": "Guest Network",
"label.guestnetworkid": "Network ID",
"label.guestnetworkname": "Network name",
"label.guestosasrule": "Guest OS as rule",
"label.guestosrule": "Guest OS rule",
"label.gueststartip": "Guest start IP",
"label.guest.vlan": "Guest VLAN",
"label.guestvlanrange": "VLAN range(s)",

View File

@ -1090,6 +1090,8 @@
"label.guestnetwork": "Rede guest",
"label.guestnetworkid": "ID de rede",
"label.guestnetworkname": "Nome da rede",
"label.guestosasrule": "SO guest como regra JS",
"label.guestosrule": "Regra SO guest",
"label.gueststartip": "IP de in\u00edcio do guest",
"label.guestvlanrange": "Intervalo(s) de VLAN",
"label.guestvmcidr": "CIDR",

View File

@ -64,7 +64,22 @@
</a-select-option>
</a-select>
</a-form-item>
<a-form-item name="oscategoryid" ref="oscategoryid">
<a-form-item name="guestosasrule" ref="guestosasrule">
<template #label>
<tooltip-label :title="$t('label.guestosasrule')"/>
</template>
<a-switch v-model:checked="form.guestosasrule"/>
</a-form-item>
<a-form-item v-if="form.guestosasrule" name="guestosrule" ref="guestosrule">
<template #label>
<tooltip-label :title="$t('label.guestosrule')" :tooltip="apiParams.guestosrule.description"/>
</template>
<a-textarea
v-model:value="form.guestosrule"
:placeholder="apiParams.guestosrule.name">
</a-textarea>
</a-form-item>
<a-form-item v-if="!form.guestosasrule" name="oscategoryid" ref="oscategoryid">
<template #label>
<tooltip-label :title="$t('label.oscategoryid')" :tooltip="apiParams.oscategoryid.description"/>
</template>
@ -149,6 +164,7 @@ export default {
methods: {
initForm () {
this.formRef = ref()
const guestOsRule = this.resource?.guestosrule
this.form = reactive({
name: this.resource.name,
hosttags: this.resource.explicithosttags,
@ -157,7 +173,9 @@ export default {
? this.resource.storageaccessgroups.split(',')
: [],
oscategoryid: this.resource.oscategoryid,
externaldetails: this.resourceExternalDetails
externaldetails: this.resourceExternalDetails,
guestosasrule: guestOsRule !== undefined,
guestosrule: guestOsRule
})
this.rules = reactive({})
},
@ -194,7 +212,11 @@ export default {
params.id = this.resource.id
params.name = values.name
params.hosttags = values.hosttags
params.oscategoryid = values.oscategoryid
if (values.guestosasrule === true) {
params.guestosrule = values.guestosrule
} else {
params.oscategoryid = values.oscategoryid || this.osCategories.opts.filter(os => os.name === 'None')[0]?.id
}
if (values.istagarule !== undefined) {
params.istagarule = values.istagarule
}
@ -205,9 +227,11 @@ export default {
} else {
params.cleanupexternaldetails = true
}
Object.keys(params).forEach((key) => (params[key] == null) && delete params[key])
this.loading = true
postAPI('updateHost', params).then(json => {
postAPI('updateHost', params).then(() => {
this.$message.success({
content: `${this.$t('label.action.update.host')} - ${values.name}`,
duration: 2

View File

@ -16,41 +16,60 @@
//under the License.
package org.apache.cloudstack.utils.jsinterpreter;
import com.cloud.utils.StringUtils;
import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.cloud.utils.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class GenericRuleHelper {
import com.cloud.utils.exception.CloudRuntimeException;
protected static Logger LOGGER = LogManager.getLogger(GenericRuleHelper.class);
public class TagAsRuleHelper {
protected static Logger LOGGER = LogManager.getLogger(TagAsRuleHelper.class);
public static boolean interpretTagAsRule(String rule, String tags, long timeout) {
public static boolean interpretTagAsRule(String rule, String tags, long timeout, String configName) {
List<String> tagsPresetVariable = new ArrayList<>();
if (!StringUtils.isEmpty(tags)) {
tagsPresetVariable.addAll(Arrays.asList(tags.split(",")));
}
try (JsInterpreter jsInterpreter = new JsInterpreter(timeout)) {
jsInterpreter.injectVariable("tags", tagsPresetVariable);
Object scriptReturn = jsInterpreter.executeScript(rule);
if (scriptReturn instanceof Boolean) {
return (Boolean)scriptReturn;
}
} catch (IOException ex) {
String message = String.format("Error while executing script [%s].", rule);
LOGGER.error(message, ex);
throw new CloudRuntimeException(message, ex);
Boolean scriptReturn = interpretRule("tags", tagsPresetVariable, timeout, rule, configName);
if (scriptReturn != null) {
return scriptReturn;
}
LOGGER.debug("Result of tag rule [{}] was not a boolean, returning false.", rule);
return false;
}
public static boolean interpretGuestOsRule(String rule, String vmGuestOs, long timeout, String configName) {
Boolean scriptReturn = interpretRule("vmGuestOs", vmGuestOs, timeout, rule, configName);
if (scriptReturn != null) {
return scriptReturn;
}
LOGGER.debug("Result of guest OS rule [{}] was not a boolean, returning false.", rule);
return false;
}
private static Boolean interpretRule(String variableName, Object variableValue, long timeout, String script, String configName) {
try (JsInterpreter jsInterpreter = new JsInterpreter(timeout, configName)) {
jsInterpreter.injectVariable(variableName, variableValue);
Object scriptReturn = jsInterpreter.executeScript(script);
if (scriptReturn instanceof Boolean) {
return (Boolean) scriptReturn;
}
} catch (IOException ex) {
String message = String.format("Error while executing script [%s].", script);
LOGGER.error(message, ex);
throw new CloudRuntimeException(message, ex);
}
return null;
}
}

View File

@ -86,10 +86,10 @@ public class JsInterpreter implements Closeable {
*/
protected JsInterpreter() { }
public JsInterpreter(long timeout) {
public JsInterpreter(long timeout, String configName) {
this.timeout = timeout;
this.timeoutDefaultMessage = String.format(
"Timeout (in milliseconds) defined in the global setting [quota.activationrule.timeout]: [%s].", this.timeout);
this.timeoutDefaultMessage = String.format("Timeout (in milliseconds) defined in the global setting [%s]: [%s].",
configName, this.timeout);
if (System.getProperty("nashorn.args") == null) {
System.setProperty("nashorn.args", "--no-java --no-syntax-extensions");

View File

@ -161,7 +161,7 @@ public class JsInterpreterTest {
@Test
public void executeScriptTestValidScriptShouldPassWithMixedVariables() {
try (JsInterpreter jsInterpreter = new JsInterpreter(1000)) {
try (JsInterpreter jsInterpreter = new JsInterpreter(1000, "timeout.configuration")) {
jsInterpreter.injectVariable("x", 10);
jsInterpreter.injectVariable("y", "hello");
jsInterpreter.injectVariable("z", true);
@ -174,7 +174,7 @@ public class JsInterpreterTest {
}
private void runMaliciousScriptFileTest(String script, String filename) {
try (JsInterpreter jsInterpreter = new JsInterpreter(1000)) {
try (JsInterpreter jsInterpreter = new JsInterpreter(1000, "timeout.configuration")) {
jsInterpreter.executeScript(script);
} catch (CloudRuntimeException ex) {
Assert.assertTrue(ex.getMessage().contains("Unable to execute script"));