Improve logs when adding components to avoid set (#7214)

Co-authored-by: SadiJr <sadi@scclouds.com.br>
Co-authored-by: GaOrtiga <49285692+GaOrtiga@users.noreply.github.com>
Co-authored-by: João Jandre <48719461+JoaoJandre@users.noreply.github.com>
This commit is contained in:
SadiJr 2024-02-28 04:49:10 -03:00 committed by GitHub
parent c8a4575bcd
commit 6f27b1f459
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 476 additions and 405 deletions

View File

@ -21,8 +21,12 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import com.cloud.dc.DataCenter;
import com.cloud.dc.Pod;
import com.cloud.exception.CloudException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.InsufficientServerCapacityException;
import com.cloud.exception.ResourceUnavailableException;
@ -75,7 +79,7 @@ public interface DeploymentPlanner extends Adapter {
public static class ExcludeList implements Serializable {
private static final long serialVersionUID = -482175549460148301L;
protected static Logger LOGGER = LogManager.getLogger(ExcludeList.class);
private Set<Long> _dcIds;
private Set<Long> _podIds;
private Set<Long> _clusterIds;
@ -104,13 +108,26 @@ public interface DeploymentPlanner extends Adapter {
}
}
private void logAvoid(Class<?> scope, CloudException e) {
Long id = null;
if (e instanceof InsufficientCapacityException) {
id = ((InsufficientCapacityException) e).getId();
} else if (e instanceof ResourceUnavailableException) {
id = ((ResourceUnavailableException) e).getResourceId();
} else {
LOGGER.debug("Failed to log avoided component due to unexpected exception type [{}].", e.getMessage());
return;
}
LOGGER.debug("Adding {} [{}] to the avoid set due to [{}].", scope.getSimpleName(), id, e.getMessage());
}
public boolean add(InsufficientCapacityException e) {
Class<?> scope = e.getScope();
if (scope == null) {
return false;
}
logAvoid(scope, e);
if (Host.class.isAssignableFrom(scope)) {
addHost(e.getId());
} else if (Pod.class.isAssignableFrom(scope)) {
@ -128,13 +145,14 @@ public interface DeploymentPlanner extends Adapter {
return true;
}
public boolean add(ResourceUnavailableException e) {
Class<?> scope = e.getScope();
if (scope == null) {
return false;
}
logAvoid(scope, e);
if (Host.class.isAssignableFrom(scope)) {
addHost(e.getResourceId());
} else if (Pod.class.isAssignableFrom(scope)) {

View File

@ -165,6 +165,7 @@ import com.cloud.deploy.DeploymentPlan;
import com.cloud.deploy.DeploymentPlanner;
import com.cloud.deploy.DeploymentPlanner.ExcludeList;
import com.cloud.deploy.DeploymentPlanningManager;
import com.cloud.deploy.DeploymentPlanningManagerImpl;
import com.cloud.deployasis.dao.UserVmDeployAsIsDetailsDao;
import com.cloud.event.EventTypes;
import com.cloud.event.UsageEventUtils;
@ -237,6 +238,7 @@ import com.cloud.user.User;
import com.cloud.uservm.UserVm;
import com.cloud.utils.DateUtil;
import com.cloud.utils.Journal;
import com.cloud.utils.LogUtils;
import com.cloud.utils.Pair;
import com.cloud.utils.Predicate;
import com.cloud.utils.ReflectionUse;
@ -1093,6 +1095,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
public void orchestrateStart(final String vmUuid, final Map<VirtualMachineProfile.Param, Object> params, final DeploymentPlan planToDeploy, final DeploymentPlanner planner)
throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException {
logger.debug(() -> LogUtils.logGsonWithoutException("Trying to start VM [%s] using plan [%s] and planner [%s].", vmUuid, planToDeploy, planner));
final CallContext cctxt = CallContext.current();
final Account account = cctxt.getCallingAccount();
final User caller = cctxt.getCallingUser();
@ -1116,10 +1119,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
DataCenterDeployment plan = new DataCenterDeployment(vm.getDataCenterId(), vm.getPodIdToDeployIn(), null, null, null, null, ctx);
if (planToDeploy != null && planToDeploy.getDataCenterId() != 0) {
if (logger.isDebugEnabled()) {
logger.debug("advanceStart: DeploymentPlan is provided, using dcId:" + planToDeploy.getDataCenterId() + ", podId: " + planToDeploy.getPodId() +
", clusterId: " + planToDeploy.getClusterId() + ", hostId: " + planToDeploy.getHostId() + ", poolId: " + planToDeploy.getPoolId());
}
VMInstanceVO finalVm = vm;
logger.debug(() -> DeploymentPlanningManagerImpl.logDeploymentWithoutException(finalVm, planToDeploy, planToDeploy.getAvoids(), planner));
plan =
new DataCenterDeployment(planToDeploy.getDataCenterId(), planToDeploy.getPodId(), planToDeploy.getClusterId(), planToDeploy.getHostId(),
planToDeploy.getPoolId(), planToDeploy.getPhysicalNetworkId(), ctx);
@ -1140,13 +1141,12 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
if (planToDeploy != null) {
avoids = planToDeploy.getAvoids();
ExcludeList finalAvoids = avoids;
logger.debug(() -> LogUtils.logGsonWithoutException("Avoiding components [%s] in deployment of VM [%s].", finalAvoids, vmUuid));
}
if (avoids == null) {
avoids = new ExcludeList();
}
if (logger.isDebugEnabled()) {
logger.debug("Deploy avoids pods: " + avoids.getPodsToAvoid() + ", clusters: " + avoids.getClustersToAvoid() + ", hosts: " + avoids.getHostsToAvoid());
}
boolean planChangedByVolume = false;
boolean reuseVolume = true;

View File

@ -27,6 +27,7 @@ import javax.naming.ConfigurationException;
import com.cloud.utils.exception.CloudRuntimeException;
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;
@ -210,6 +211,10 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator {
// 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());
}
@ -325,10 +330,8 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator {
//find number of guest VMs occupying capacity on this host.
if (_capacityMgr.checkIfHostReachMaxGuestLimit(host)) {
if (logger.isDebugEnabled()) {
logger.debug("Host name: " + host.getName() + ", hostId: " + host.getId() +
" already has max Running VMs(count includes system VMs), skipping this and trying other available hosts");
}
logger.debug(() -> String.format("Adding host [%s] to the avoid set because this host already has the max number of running (user and/or system) VMs.",
ReflectionToStringBuilderUtils.reflectOnlySelectedFields(host, "uuid", "name")));
avoid.addHost(host.getId());
continue;
}
@ -337,7 +340,8 @@ public class FirstFitAllocator extends AdapterBase implements HostAllocator {
if ((offeringDetails = _serviceOfferingDetailsDao.findDetail(serviceOfferingId, GPU.Keys.vgpuType.toString())) != null) {
ServiceOfferingDetailsVO groupName = _serviceOfferingDetailsDao.findDetail(serviceOfferingId, GPU.Keys.pciDevice.toString());
if(!_resourceMgr.isGPUDeviceAvailable(host.getId(), groupName.getValue(), offeringDetails.getValue())){
logger.info("Host name: " + host.getName() + ", hostId: "+ host.getId() +" does not have required GPU devices available");
logger.debug(String.format("Adding host [%s] to avoid set, because this host does not have required GPU devices available.",
ReflectionToStringBuilderUtils.reflectOnlySelectedFields(host, "uuid", "name")));
avoid.addHost(host.getId());
continue;
}

View File

@ -46,7 +46,9 @@ import com.cloud.utils.fsm.StateMachine2;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.Configurable;
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.cloudstack.affinity.AffinityGroupProcessor;
import org.apache.cloudstack.affinity.AffinityGroupService;
@ -128,6 +130,7 @@ import com.cloud.storage.dao.VolumeDao;
import com.cloud.template.VirtualMachineTemplate;
import com.cloud.user.AccountManager;
import com.cloud.utils.DateUtil;
import com.cloud.utils.LogUtils;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.Pair;
import com.cloud.utils.component.Manager;
@ -299,25 +302,30 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
@Override
public DeployDestination planDeployment(VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoids, DeploymentPlanner planner)
throws InsufficientServerCapacityException, AffinityConflictException {
logger.debug(logDeploymentWithoutException(vmProfile.getVirtualMachine(), plan, avoids, planner));
ServiceOffering offering = vmProfile.getServiceOffering();
int cpu_requested = offering.getCpu() * offering.getSpeed();
long ram_requested = offering.getRamSize() * 1024L * 1024L;
int cpuRequested = offering.getCpu() * offering.getSpeed();
long ramRequested = offering.getRamSize() * 1024L * 1024L;
VirtualMachine vm = vmProfile.getVirtualMachine();
DataCenter dc = _dcDao.findById(vm.getDataCenterId());
boolean volumesRequireEncryption = anyVolumeRequiresEncryption(_volsDao.findByInstance(vm.getId()));
if (vm.getType() == VirtualMachine.Type.User || vm.getType() == VirtualMachine.Type.DomainRouter) {
logger.debug("Checking non dedicated resources to deploy VM [{}].", () -> ReflectionToStringBuilderUtils.reflectOnlySelectedFields(vm, "uuid", "type", "instanceName"));
checkForNonDedicatedResources(vmProfile, dc, avoids);
}
if (logger.isDebugEnabled()) {
logger.debug("DeploymentPlanner allocation algorithm: " + planner);
logger.debug("Trying to allocate a host and storage pools from dc:" + plan.getDataCenterId() + ", pod:" + plan.getPodId() + ",cluster:" +
plan.getClusterId() + ", requested cpu: " + cpu_requested + ", requested ram: " + toHumanReadableSize(ram_requested));
logger.debug(() -> {
String datacenter = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(dc, "uuid", "name");
String podVO = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(_podDao.findById(plan.getPodId()), "uuid", "name");
String clusterVO = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(_clusterDao.findById(plan.getClusterId()), "uuid", "name");
String vmDetails = ReflectionToStringBuilderUtils.reflectOnlySelectedFields(vm, "uuid", "type", "instanceName");
return String.format("Trying to allocate a host and storage pools from datacenter [%s], pod [%s], cluster [%s], to deploy VM [%s] "
+ "with requested CPU [%s] and requested RAM [%s].", datacenter, podVO, clusterVO, vmDetails, cpuRequested, toHumanReadableSize(ramRequested));
});
logger.debug("Is ROOT volume READY (pool already allocated)?: " + (plan.getPoolId() != null ? "Yes" : "No"));
}
logger.debug("ROOT volume [{}] {} to deploy VM [{}].", () -> getRootVolumeUuid(_volsDao.findByInstance(vm.getId())), () -> plan.getPoolId() != null ? "is ready" : "is not ready", vm::getUuid);
avoidDisabledResources(vmProfile, dc, avoids);
@ -325,81 +333,7 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
String uefiFlag = (String)vmProfile.getParameter(VirtualMachineProfile.Param.UefiFlag);
if (plan.getHostId() != null && haVmTag == null) {
Long hostIdSpecified = plan.getHostId();
if (logger.isDebugEnabled()) {
logger.debug("DeploymentPlan has host_id specified, choosing this host: " + hostIdSpecified);
}
HostVO host = _hostDao.findById(hostIdSpecified);
if (host != null && StringUtils.isNotBlank(uefiFlag) && "yes".equalsIgnoreCase(uefiFlag)) {
DetailVO uefiHostDetail = _hostDetailsDao.findDetail(host.getId(), Host.HOST_UEFI_ENABLE);
if (uefiHostDetail == null || "false".equalsIgnoreCase(uefiHostDetail.getValue())) {
logger.debug("Cannot deploy to specified host as host does n't support uefi vm deployment, returning.");
return null;
}
}
if (host == null) {
logger.debug("The specified host cannot be found");
} else if (avoids.shouldAvoid(host)) {
logger.debug("The specified host is in avoid set");
} else {
if (logger.isDebugEnabled()) {
logger.debug(
"Looking for suitable pools for this host under zone: " + host.getDataCenterId() + ", pod: " + host.getPodId() + ", cluster: " + host.getClusterId());
}
Pod pod = _podDao.findById(host.getPodId());
Cluster cluster = _clusterDao.findById(host.getClusterId());
boolean displayStorage = getDisplayStorageFromVmProfile(vmProfile);
if (vm.getHypervisorType() == HypervisorType.BareMetal) {
DeployDestination dest = new DeployDestination(dc, pod, cluster, host, new HashMap<Volume, StoragePool>(), displayStorage);
logger.debug("Returning Deployment Destination: " + dest);
return dest;
}
// search for storage under the zone, pod, cluster of the host.
DataCenterDeployment lastPlan =
new DataCenterDeployment(host.getDataCenterId(), host.getPodId(), host.getClusterId(), hostIdSpecified, plan.getPoolId(), null,
plan.getReservationContext());
Pair<Map<Volume, List<StoragePool>>, List<Volume>> result = findSuitablePoolsForVolumes(vmProfile, lastPlan, avoids, HostAllocator.RETURN_UPTO_ALL);
Map<Volume, List<StoragePool>> suitableVolumeStoragePools = result.first();
List<Volume> readyAndReusedVolumes = result.second();
_hostDao.loadDetails(host);
if (volumesRequireEncryption && !Boolean.parseBoolean(host.getDetail(Host.HOST_VOLUME_ENCRYPTION))) {
logger.warn(String.format("VM's volumes require encryption support, and provided host %s can't handle it", host));
return null;
} else {
logger.debug(String.format("Volume encryption requirements are met by provided host %s", host));
}
// choose the potential pool for this VM for this host
if (!suitableVolumeStoragePools.isEmpty()) {
List<Host> suitableHosts = new ArrayList<Host>();
suitableHosts.add(host);
Pair<Host, Map<Volume, StoragePool>> potentialResources = findPotentialDeploymentResources(
suitableHosts, suitableVolumeStoragePools, avoids,
getPlannerUsage(planner, vmProfile, plan, avoids), readyAndReusedVolumes, plan.getPreferredHosts(), vm);
if (potentialResources != null) {
pod = _podDao.findById(host.getPodId());
cluster = _clusterDao.findById(host.getClusterId());
Map<Volume, StoragePool> storageVolMap = potentialResources.second();
// remove the reused vol<->pool from destination, since
// we don't have to prepare this volume.
for (Volume vol : readyAndReusedVolumes) {
storageVolMap.remove(vol);
}
DeployDestination dest = new DeployDestination(dc, pod, cluster, host, storageVolMap, displayStorage);
logger.debug("Returning Deployment Destination: " + dest);
return dest;
}
}
}
logger.debug("Cannot deploy to specified host, returning.");
return null;
return deployInSpecifiedHostWithoutHA(vmProfile, plan, avoids, planner, vm, dc, uefiFlag);
}
// call affinitygroup chain
@ -410,11 +344,14 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
processor.process(vmProfile, plan, avoids);
}
}
logger.debug("DeploymentPlan [{}] has not specified host. Trying to find another destination to deploy VM [{}], avoiding pods [{}], clusters [{}] and hosts [{}].",
() -> plan.getClass().getSimpleName(), vmProfile::getUuid, () -> StringUtils.join(avoids.getPodsToAvoid(), ", "), () -> StringUtils.join(avoids.getClustersToAvoid(), ", "),
() -> StringUtils.join(avoids.getHostsToAvoid(), ", "));
logger.debug("Deploy avoids pods: {}, clusters: {}, hosts: {}.", avoids.getPodsToAvoid(), avoids.getClustersToAvoid(), avoids.getHostsToAvoid());
logger.debug("Deploy hosts with priorities {}, hosts have NORMAL priority by default", plan.getHostPriorities());
if (logger.isDebugEnabled()) {
logger.debug("Deploy avoids pods: " + avoids.getPodsToAvoid() + ", clusters: " + avoids.getClustersToAvoid() + ", hosts: " + avoids.getHostsToAvoid());
logger.debug("Deploy hosts with priorities " + plan.getHostPriorities() + " , hosts have NORMAL priority by default");
}
// call planners
// DataCenter dc = _dcDao.findById(vm.getDataCenterId());
@ -448,108 +385,11 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
HostVO host = _hostDao.findById(vm.getLastHostId());
lastHost = host;
_hostDao.loadHostTags(host);
_hostDao.loadDetails(host);
ServiceOfferingDetailsVO offeringDetails = null;
if (host == null) {
logger.debug("The last host of this VM cannot be found");
} else if (avoids.shouldAvoid(host)) {
logger.debug("The last host of this VM is in avoid set");
} else if (plan.getClusterId() != null && host.getClusterId() != null
&& !plan.getClusterId().equals(host.getClusterId())) {
logger.debug("The last host of this VM cannot be picked as the plan specifies different clusterId: "
+ plan.getClusterId());
} else if (_capacityMgr.checkIfHostReachMaxGuestLimit(host)) {
logger.debug("The last Host, hostId: " + host.getId() +
" already has max Running VMs(count includes system VMs), skipping this and trying other available hosts");
} else if ((offeringDetails = _serviceOfferingDetailsDao.findDetail(offering.getId(), GPU.Keys.vgpuType.toString())) != null) {
ServiceOfferingDetailsVO groupName = _serviceOfferingDetailsDao.findDetail(offering.getId(), GPU.Keys.pciDevice.toString());
if(!_resourceMgr.isGPUDeviceAvailable(host.getId(), groupName.getValue(), offeringDetails.getValue())){
logger.debug("The last host of this VM does not have required GPU devices available");
}
} else if (volumesRequireEncryption && !Boolean.parseBoolean(host.getDetail(Host.HOST_VOLUME_ENCRYPTION))) {
logger.warn(String.format("The last host of this VM %s does not support volume encryption, which is required by this VM.", host));
} else {
if (host.getStatus() == Status.Up) {
if (checkVmProfileAndHost(vmProfile, host)) {
long cluster_id = host.getClusterId();
ClusterDetailsVO cluster_detail_cpu = _clusterDetailsDao.findDetail(cluster_id,
"cpuOvercommitRatio");
ClusterDetailsVO cluster_detail_ram = _clusterDetailsDao.findDetail(cluster_id,
"memoryOvercommitRatio");
Float cpuOvercommitRatio = Float.parseFloat(cluster_detail_cpu.getValue());
Float memoryOvercommitRatio = Float.parseFloat(cluster_detail_ram.getValue());
boolean hostHasCpuCapability, hostHasCapacity = false;
hostHasCpuCapability = _capacityMgr.checkIfHostHasCpuCapability(host.getId(), offering.getCpu(), offering.getSpeed());
if (hostHasCpuCapability) {
// first check from reserved capacity
hostHasCapacity = _capacityMgr.checkIfHostHasCapacity(host.getId(), cpu_requested, ram_requested, true, cpuOvercommitRatio, memoryOvercommitRatio, true);
// if not reserved, check the free capacity
if (!hostHasCapacity)
hostHasCapacity = _capacityMgr.checkIfHostHasCapacity(host.getId(), cpu_requested, ram_requested, false, cpuOvercommitRatio, memoryOvercommitRatio, true);
DeployDestination deployDestination = deployInVmLastHost(vmProfile, plan, avoids, planner, vm, dc, offering, cpuRequested, ramRequested, volumesRequireEncryption);
if (deployDestination != null) {
return deployDestination;
}
boolean displayStorage = getDisplayStorageFromVmProfile(vmProfile);
if (hostHasCapacity
&& hostHasCpuCapability) {
logger.debug("The last host of this VM is UP and has enough capacity");
logger.debug("Now checking for suitable pools under zone: " + host.getDataCenterId()
+ ", pod: " + host.getPodId() + ", cluster: " + host.getClusterId());
Pod pod = _podDao.findById(host.getPodId());
Cluster cluster = _clusterDao.findById(host.getClusterId());
if (vm.getHypervisorType() == HypervisorType.BareMetal) {
DeployDestination dest = new DeployDestination(dc, pod, cluster, host, new HashMap<Volume, StoragePool>(), displayStorage);
logger.debug("Returning Deployment Destination: " + dest);
return dest;
}
// search for storage under the zone, pod, cluster
// of
// the last host.
DataCenterDeployment lastPlan = new DataCenterDeployment(host.getDataCenterId(),
host.getPodId(), host.getClusterId(), host.getId(), plan.getPoolId(), null);
Pair<Map<Volume, List<StoragePool>>, List<Volume>> result = findSuitablePoolsForVolumes(
vmProfile, lastPlan, avoids, HostAllocator.RETURN_UPTO_ALL);
Map<Volume, List<StoragePool>> suitableVolumeStoragePools = result.first();
List<Volume> readyAndReusedVolumes = result.second();
// choose the potential pool for this VM for this
// host
if (!suitableVolumeStoragePools.isEmpty()) {
List<Host> suitableHosts = new ArrayList<Host>();
suitableHosts.add(host);
Pair<Host, Map<Volume, StoragePool>> potentialResources = findPotentialDeploymentResources(
suitableHosts, suitableVolumeStoragePools, avoids,
getPlannerUsage(planner, vmProfile, plan, avoids), readyAndReusedVolumes, plan.getPreferredHosts(), vm);
if (potentialResources != null) {
Map<Volume, StoragePool> storageVolMap = potentialResources.second();
// remove the reused vol<->pool from
// destination, since we don't have to
// prepare
// this volume.
for (Volume vol : readyAndReusedVolumes) {
storageVolMap.remove(vol);
}
DeployDestination dest = new DeployDestination(dc, pod, cluster, host,
storageVolMap, displayStorage);
logger.debug("Returning Deployment Destination: " + dest);
return dest;
}
}
} else {
logger.debug("The last host of this VM does not have enough capacity");
}
}
} else {
logger.debug("The last host of this VM is not UP or is not enabled, host status is: " + host.getStatus().name() + ", host resource state is: " +
host.getResourceState());
}
}
logger.debug("Cannot choose the last host to deploy this VM ");
}
avoidOtherClustersForDeploymentIfMigrationDisabled(vm, lastHost, avoids);
@ -614,6 +454,208 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
return dest;
}
private DeployDestination deployInVmLastHost(VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoids,
DeploymentPlanner planner, VirtualMachine vm, DataCenter dc, ServiceOffering offering, int cpuRequested, long ramRequested,
boolean volumesRequireEncryption) throws InsufficientServerCapacityException {
HostVO host = _hostDao.findById(vm.getLastHostId());
_hostDao.loadHostTags(host);
_hostDao.loadDetails(host);
if (canUseLastHost(host, avoids, plan, vm, offering, volumesRequireEncryption)) {
if (host.getStatus() != Status.Up) {
logger.debug("Cannot deploy VM [{}] to the last host [{}] because this host is not in UP state or is not enabled. Host current status [{}] and resource status [{}].",
vm.getUuid(), host.getUuid(), host.getState().name(), host.getResourceState());
return null;
}
if (checkVmProfileAndHost(vmProfile, host)) {
long cluster_id = host.getClusterId();
ClusterDetailsVO cluster_detail_cpu = _clusterDetailsDao.findDetail(cluster_id, "cpuOvercommitRatio");
ClusterDetailsVO cluster_detail_ram = _clusterDetailsDao.findDetail(cluster_id, "memoryOvercommitRatio");
float cpuOvercommitRatio = Float.parseFloat(cluster_detail_cpu.getValue());
float memoryOvercommitRatio = Float.parseFloat(cluster_detail_ram.getValue());
boolean hostHasCpuCapability, hostHasCapacity = false;
hostHasCpuCapability = _capacityMgr.checkIfHostHasCpuCapability(host.getId(), offering.getCpu(), offering.getSpeed());
if (hostHasCpuCapability) {
// first check from reserved capacity
hostHasCapacity = _capacityMgr.checkIfHostHasCapacity(host.getId(), cpuRequested, ramRequested, true, cpuOvercommitRatio, memoryOvercommitRatio, true);
// if not reserved, check the free capacity
if (!hostHasCapacity)
hostHasCapacity = _capacityMgr.checkIfHostHasCapacity(host.getId(), cpuRequested, ramRequested, false, cpuOvercommitRatio, memoryOvercommitRatio, true);
}
boolean displayStorage = getDisplayStorageFromVmProfile(vmProfile);
if (!hostHasCapacity || !hostHasCpuCapability) {
logger.debug("Cannot deploy VM [{}] to the last host [{}] because this host does not have enough capacity to deploy this VM.", vm.getUuid(), host.getUuid());
return null;
}
logger.debug("Last host [{}] of VM [{}] is UP and has enough capacity. Checking for suitable pools for this host under zone [{}], pod [{}] and cluster [{}].",
host.getUuid(), vm.getUuid(), host.getDataCenterId(), host.getPodId(), host.getClusterId());
Pod pod = _podDao.findById(host.getPodId());
Cluster cluster = _clusterDao.findById(host.getClusterId());
if (vm.getHypervisorType() == HypervisorType.BareMetal) {
DeployDestination dest = new DeployDestination(dc, pod, cluster, host, new HashMap<Volume, StoragePool>(), displayStorage);
logger.debug("Returning Deployment Destination: {}.", dest);
return dest;
}
// search for storage under the zone, pod, cluster
// of
// the last host.
DataCenterDeployment lastPlan = new DataCenterDeployment(host.getDataCenterId(),
host.getPodId(), host.getClusterId(), host.getId(), plan.getPoolId(), null);
Pair<Map<Volume, List<StoragePool>>, List<Volume>> result = findSuitablePoolsForVolumes(
vmProfile, lastPlan, avoids, HostAllocator.RETURN_UPTO_ALL);
Map<Volume, List<StoragePool>> suitableVolumeStoragePools = result.first();
List<Volume> readyAndReusedVolumes = result.second();
// choose the potential pool for this VM for this
// host
if (suitableVolumeStoragePools.isEmpty()) {
logger.debug("Cannot find suitable storage pools in host [{}] to deploy VM [{}]", host.getUuid(), vm.getUuid());
return null;
}
List<Host> suitableHosts = new ArrayList<Host>();
suitableHosts.add(host);
Pair<Host, Map<Volume, StoragePool>> potentialResources = findPotentialDeploymentResources(
suitableHosts, suitableVolumeStoragePools, avoids,
getPlannerUsage(planner, vmProfile, plan, avoids), readyAndReusedVolumes, plan.getPreferredHosts(), vm);
if (potentialResources != null) {
Map<Volume, StoragePool> storageVolMap = potentialResources.second();
// remove the reused vol<->pool from
// destination, since we don't have to
// prepare
// this volume.
for (Volume vol : readyAndReusedVolumes) {
storageVolMap.remove(vol);
}
DeployDestination dest = new DeployDestination(dc, pod, cluster, host, storageVolMap, displayStorage);
logger.debug("Returning Deployment Destination: {}", dest);
return dest;
}
}
}
logger.debug("Cannot choose the last host to deploy this VM {}.", vm);
return null;
}
private boolean canUseLastHost(HostVO host, ExcludeList avoids, DeploymentPlan plan, VirtualMachine vm, ServiceOffering offering, boolean volumesRequireEncryption) {
if (host == null) {
logger.warn("Could not find last host of VM [{}] with id [{}]. Skipping this and trying other available hosts.", vm.getUuid(), vm.getLastHostId());
return false;
}
if (avoids.shouldAvoid(host)) {
logger.warn("The last host [{}] of VM [{}] is in the avoid set. Skipping this and trying other available hosts.", host.getUuid(), vm.getUuid());
return false;
}
if (plan.getClusterId() != null && host.getClusterId() != null && !plan.getClusterId().equals(host.getClusterId())) {
logger.debug(() -> String.format("The last host [%s] of VM [%s] cannot be picked, as the plan [%s] specifies a different cluster [%s] to deploy this VM. Skipping this and trying other available hosts.",
ReflectionToStringBuilderUtils.reflectOnlySelectedFields(host, "uuid", "clusterId"), vm.getUuid(), plan.getClass().getSimpleName(), plan.getClusterId()));
return false;
}
if (_capacityMgr.checkIfHostReachMaxGuestLimit(host)) {
logger.debug("Cannot deploy VM [{}] in the last host [{}] because this host already has the max number of running VMs (users and system VMs). Skipping this and trying other available hosts.",
vm.getUuid(), host.getUuid());
return false;
}
ServiceOfferingDetailsVO offeringDetails = _serviceOfferingDetailsDao.findDetail(offering.getId(), GPU.Keys.vgpuType.toString());
ServiceOfferingDetailsVO groupName = _serviceOfferingDetailsDao.findDetail(offering.getId(), GPU.Keys.pciDevice.toString());
if (offeringDetails != null && !_resourceMgr.isGPUDeviceAvailable(host.getId(), groupName.getValue(), offeringDetails.getValue())) {
logger.debug("Cannot deploy VM [{}] in the last host [{}] because this host does not have the required GPU devices available. Skipping this and trying other available hosts.",
vm.getUuid(), host.getUuid());
return false;
}
if (volumesRequireEncryption && !Boolean.parseBoolean(host.getDetail(Host.HOST_VOLUME_ENCRYPTION))) {
logger.warn("The last host of this VM {} does not support volume encryption, which is required by this VM.", host);
return false;
}
return true;
}
private DeployDestination deployInSpecifiedHostWithoutHA(VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoids,
DeploymentPlanner planner, VirtualMachine vm, DataCenter dc, String uefiFlag)
throws InsufficientServerCapacityException {
Long hostIdSpecified = plan.getHostId();
logger.debug("DeploymentPlan [{}] has specified host [{}] without HA flag. Choosing this host to deploy VM [{}].", plan.getClass().getSimpleName(), hostIdSpecified, vm.getUuid());
HostVO host = _hostDao.findById(hostIdSpecified);
if (host != null && StringUtils.isNotBlank(uefiFlag) && "yes".equalsIgnoreCase(uefiFlag)) {
_hostDao.loadDetails(host);
if (MapUtils.isNotEmpty(host.getDetails()) && host.getDetails().containsKey(Host.HOST_UEFI_ENABLE) && "false".equalsIgnoreCase(host.getDetails().get(Host.HOST_UEFI_ENABLE))) {
logger.debug("Cannot deploy VM [{}] to specified host [{}] because this host does not support UEFI VM deployment, returning.", vm.getUuid(), host.getUuid());
return null;
}
}
if (host == null) {
logger.debug("Cannot deploy VM [{}] to host [{}] because this host cannot be found.", vm.getUuid(), hostIdSpecified);
return null;
}
if (avoids.shouldAvoid(host)) {
logger.debug("Cannot deploy VM [{}] to host [{}] because this host is in the avoid set.", vm.getUuid(), host.getUuid());
return null;
}
logger.debug("Trying to find suitable pools for host [{}] under pod [{}], cluster [{}] and zone [{}], to deploy VM [{}].",
host.getUuid(), host.getDataCenterId(), host.getPodId(), host.getClusterId(), vm.getUuid());
Pod pod = _podDao.findById(host.getPodId());
Cluster cluster = _clusterDao.findById(host.getClusterId());
boolean displayStorage = getDisplayStorageFromVmProfile(vmProfile);
if (vm.getHypervisorType() == HypervisorType.BareMetal) {
DeployDestination dest = new DeployDestination(dc, pod, cluster, host, new HashMap<Volume, StoragePool>(),
displayStorage);
logger.debug("Returning Deployment Destination: {}.", dest);
return dest;
}
DataCenterDeployment lastPlan = new DataCenterDeployment(host.getDataCenterId(), host.getPodId(),
host.getClusterId(), hostIdSpecified, plan.getPoolId(), null, plan.getReservationContext());
Pair<Map<Volume, List<StoragePool>>, List<Volume>> result = findSuitablePoolsForVolumes(vmProfile, lastPlan,
avoids, HostAllocator.RETURN_UPTO_ALL);
Map<Volume, List<StoragePool>> suitableVolumeStoragePools = result.first();
List<Volume> readyAndReusedVolumes = result.second();
if (!suitableVolumeStoragePools.isEmpty()) {
List<Host> suitableHosts = new ArrayList<Host>();
suitableHosts.add(host);
Pair<Host, Map<Volume, StoragePool>> potentialResources = findPotentialDeploymentResources(suitableHosts,
suitableVolumeStoragePools, avoids, getPlannerUsage(planner, vmProfile, plan, avoids),
readyAndReusedVolumes, plan.getPreferredHosts(), vm);
if (potentialResources != null) {
pod = _podDao.findById(host.getPodId());
cluster = _clusterDao.findById(host.getClusterId());
Map<Volume, StoragePool> storageVolMap = potentialResources.second();
for (Volume vol : readyAndReusedVolumes) {
storageVolMap.remove(vol);
}
DeployDestination dest = new DeployDestination(dc, pod, cluster, host, storageVolMap, displayStorage);
logger.debug("Returning Deployment Destination: {}", dest);
return dest;
}
}
logger.debug("Cannot deploy VM [{}] under host [{}], because no suitable pools were found.", vmProfile.getUuid(), host.getUuid());
return null;
}
protected String getRootVolumeUuid(List<? extends Volume> volumes) {
for (Volume volume : volumes) {
if (volume.getVolumeType() == Volume.Type.ROOT) {
return volume.getUuid();
}
}
return null;
}
protected boolean anyVolumeRequiresEncryption(List<? extends Volume> volumes) {
for (Volume volume : volumes) {
if (volume.getPassphraseId() != null) {
@ -637,7 +679,8 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
}
/**
* Adds disabled resources (Data centers, Pods, Clusters, and hosts) to exclude list (avoid) in case of disabled state.
* Adds disabled resources (Data centers, Pods, Clusters, and hosts) to exclude
* list (avoid) in case of disabled state.
*/
public void avoidDisabledResources(VirtualMachineProfile vmProfile, DataCenter dc, ExcludeList avoids) {
if (vmProfile.getType().isUsedBySystem() && isRouterDeployableInDisabledResources()) {
@ -646,11 +689,7 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
VMInstanceVO vm = _vmInstanceDao.findById(vmProfile.getId());
AccountVO owner = accountDao.findById(vm.getAccountId());
boolean isOwnerRoleIdAdmin = false;
if (owner != null && owner.getRoleId() != null && owner.getRoleId() == ADMIN_ACCOUNT_ROLE_ID) {
isOwnerRoleIdAdmin = true;
}
boolean isOwnerRoleIdAdmin = owner != null && owner.getRoleId() != null && owner.getRoleId() == ADMIN_ACCOUNT_ROLE_ID;
if (isOwnerRoleIdAdmin && isAdminVmDeployableInDisabledResources()) {
return;
@ -683,6 +722,8 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
*/
protected void avoidDisabledHosts(DataCenter dc, ExcludeList avoids) {
List<HostVO> disabledHosts = _hostDao.listDisabledByDataCenterId(dc.getId());
logger.debug(() -> String.format("Adding hosts [%s] of datacenter [%s] to the avoid set, because these hosts are in the Disabled state.",
disabledHosts.stream().map(HostVO::getUuid).collect(Collectors.joining(", ")), dc.getUuid()));
for (HostVO host : disabledHosts) {
avoids.addHost(host.getId());
}
@ -695,6 +736,7 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
List<Long> pods = _podDao.listAllPods(dc.getId());
for (Long podId : pods) {
List<Long> disabledClusters = _clusterDao.listDisabledClusters(dc.getId(), podId);
logger.debug(() -> String.format("Adding clusters [%s] of pod [%s] to the void set because these clusters are in the Disabled state.", StringUtils.join(disabledClusters, ", "), podId));
avoids.addClusterList(disabledClusters);
}
}
@ -704,6 +746,7 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
*/
protected void avoidDisabledPods(DataCenter dc, ExcludeList avoids) {
List<Long> disabledPods = _podDao.listDisabledPods(dc.getId());
logger.debug(() -> String.format("Adding pods [%s] to the avoid set because these pods are in the Disabled state.", StringUtils.join(disabledPods, ", ")));
avoids.addPodList(disabledPods);
}
@ -712,6 +755,7 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
*/
protected void avoidDisabledDataCenters(DataCenter dc, ExcludeList avoids) {
if (dc.getAllocationState() == Grouping.AllocationState.Disabled) {
logger.debug("Adding datacenter [{}] to the avoid set because this datacenter is in Disabled state.", dc.getUuid());
avoids.addDataCenter(dc.getId());
}
}
@ -765,6 +809,8 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
if (dedicatedZone != null && !_accountMgr.isRootAdmin(vmProfile.getOwner().getId())) {
long accountDomainId = vmProfile.getOwner().getDomainId();
long accountId = vmProfile.getOwner().getAccountId();
logger.debug("Zone [{}] is dedicated. Checking if account [{}] in domain [{}] can use this zone to deploy VM [{}].",
dedicatedZone.getUuid(), accountId, accountDomainId, vmProfile.getUuid());
// If a zone is dedicated to an account then all hosts in this zone
// will be explicitly dedicated to
@ -784,7 +830,6 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
if (!_affinityGroupService.isAffinityGroupAvailableInDomain(dedicatedZone.getAffinityGroupId(), accountDomainId)) {
throw new CloudRuntimeException("Failed to deploy VM, Zone " + dc.getName() + " not available for the user domain " + vmProfile.getOwner());
}
}
// check affinity group of type Explicit dedication exists. If No put
@ -809,109 +854,97 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
//Only when the type is instance VM and not explicitly dedicated.
if (vm.getType() == VirtualMachine.Type.User && !isExplicit) {
//add explicitly dedicated resources in avoidList
if (logger.isDebugEnabled()) {
logger.debug("Adding pods to avoid lists for non-explicit VM deployment: " + allPodsInDc);
}
avoids.addPodList(allPodsInDc);
if (logger.isDebugEnabled()) {
logger.debug("Adding clusters to avoid lists for non-explicit VM deployment: " + allClustersInDc);
}
avoids.addClusterList(allClustersInDc);
if (logger.isDebugEnabled()) {
logger.debug("Adding hosts to avoid lists for non-explicit VM deployment: " + allHostsInDc);
}
avoids.addHostList(allHostsInDc);
findAvoidSetForNonExplicitUserVM(avoids, vm, allPodsInDc, allClustersInDc, allHostsInDc);
}
//Handle the Virtual Router Case
//No need to check the isExplicit. As both the cases are handled.
if (vm.getType() == VirtualMachine.Type.DomainRouter) {
findAvoiSetForRouterVM(avoids, vm, allPodsInDc, allClustersInDc, allHostsInDc);
}
}
private void findAvoiSetForRouterVM(ExcludeList avoids, VirtualMachine vm, List<Long> allPodsInDc, List<Long> allClustersInDc, List<Long> allHostsInDc) {
long vmAccountId = vm.getAccountId();
long vmDomainId = vm.getDomainId();
//Lists all explicitly dedicated resources from vm account ID or domain ID.
List<Long> allPodsFromDedicatedID = new ArrayList<Long>();
List<Long> allClustersFromDedicatedID = new ArrayList<Long>();
List<Long> allHostsFromDedicatedID = new ArrayList<Long>();
//Whether the dedicated resources belong to Domain or not. If not, it may belongs to Account or no dedication.
List<AffinityGroupDomainMapVO> domainGroupMappings = _affinityGroupDomainMapDao.listByDomain(vmDomainId);
//For temporary storage and indexing.
List<DedicatedResourceVO> tempStorage;
if (domainGroupMappings == null || domainGroupMappings.isEmpty()) {
//The dedicated resource belongs to VM Account ID.
tempStorage = _dedicatedDao.searchDedicatedPods(null, vmDomainId, vmAccountId, null,
new Filter(DedicatedResourceVO.class, "id", true, 0L, 1L)).first();
tempStorage = _dedicatedDao.searchDedicatedPods(null, vmDomainId, vmAccountId, null, new Filter(DedicatedResourceVO.class, "id", true, 0L, 1L)).first();
for(DedicatedResourceVO vo : tempStorage) {
for (DedicatedResourceVO vo : tempStorage) {
allPodsFromDedicatedID.add(vo.getPodId());
}
tempStorage.clear();
tempStorage = _dedicatedDao.searchDedicatedClusters(null, vmDomainId, vmAccountId, null, new Filter(DedicatedResourceVO.class, "id", true, 0L, 1L)).first();
tempStorage = _dedicatedDao.searchDedicatedClusters(null, vmDomainId, vmAccountId, null,
new Filter(DedicatedResourceVO.class, "id", true, 0L, 1L)).first();
for(DedicatedResourceVO vo : tempStorage) {
for (DedicatedResourceVO vo : tempStorage) {
allClustersFromDedicatedID.add(vo.getClusterId());
}
tempStorage.clear();
tempStorage = _dedicatedDao.searchDedicatedHosts(null, vmDomainId, vmAccountId, null, new Filter(DedicatedResourceVO.class, "id", true, 0L, 1L)).first();
tempStorage = _dedicatedDao.searchDedicatedHosts(null, vmDomainId, vmAccountId, null,
new Filter(DedicatedResourceVO.class, "id", true, 0L, 1L)).first();
for(DedicatedResourceVO vo : tempStorage) {
for (DedicatedResourceVO vo : tempStorage) {
allHostsFromDedicatedID.add(vo.getHostId());
}
//Remove the dedicated ones from main list
allPodsInDc.removeAll(allPodsFromDedicatedID);
allClustersInDc.removeAll(allClustersFromDedicatedID);
allHostsInDc.removeAll(allHostsFromDedicatedID);
}
else {
//The dedicated resource belongs to VM Domain ID or No dedication.
} else {
tempStorage = _dedicatedDao.searchDedicatedPods(null, vmDomainId, null, null,
new Filter(DedicatedResourceVO.class, "id", true, 0L, 1L)).first();
tempStorage = _dedicatedDao.searchDedicatedPods(null, vmDomainId, null, null, new Filter(DedicatedResourceVO.class, "id", true, 0L, 1L)).first();
for(DedicatedResourceVO vo : tempStorage) {
for (DedicatedResourceVO vo : tempStorage) {
allPodsFromDedicatedID.add(vo.getPodId());
}
tempStorage.clear();
tempStorage = _dedicatedDao.searchDedicatedClusters(null, vmDomainId, null, null, new Filter(DedicatedResourceVO.class, "id", true, 0L, 1L)).first();
tempStorage = _dedicatedDao.searchDedicatedClusters(null, vmDomainId, null, null,
new Filter(DedicatedResourceVO.class, "id", true, 0L, 1L)).first();
for(DedicatedResourceVO vo : tempStorage) {
for (DedicatedResourceVO vo : tempStorage) {
allClustersFromDedicatedID.add(vo.getClusterId());
}
tempStorage.clear();
tempStorage = _dedicatedDao.searchDedicatedHosts(null, vmDomainId, null, null, new Filter(DedicatedResourceVO.class, "id", true, 0L, 1L)).first();
tempStorage = _dedicatedDao.searchDedicatedHosts(null, vmDomainId, null, null,
new Filter(DedicatedResourceVO.class, "id", true, 0L, 1L)).first();
for(DedicatedResourceVO vo : tempStorage) {
for (DedicatedResourceVO vo : tempStorage) {
allHostsFromDedicatedID.add(vo.getHostId());
}
//Remove the dedicated ones from main list
allPodsInDc.removeAll(allPodsFromDedicatedID);
allClustersInDc.removeAll(allClustersFromDedicatedID);
allHostsInDc.removeAll(allHostsFromDedicatedID);
}
//Add in avoid list or no addition if no dedication
if (logger.isDebugEnabled()) {
logger.debug("Adding pods to avoid lists: " + allPodsInDc);
}
logger.debug(() -> LogUtils.logGsonWithoutException("Adding pods [%s], clusters [%s] and hosts [%s] to the avoid list in the deploy process of VR VM [%s], "
+ "because this VM is not dedicated to this components.", allPodsInDc, allClustersInDc, allHostsInDc, vm.getUuid()));
avoids.addPodList(allPodsInDc);
if (logger.isDebugEnabled()) {
logger.debug("Adding clusters to avoid lists: " + allClustersInDc);
}
avoids.addClusterList(allClustersInDc);
if (logger.isDebugEnabled()) {
logger.debug("Adding hosts to avoid lists: " + allHostsInDc);
}
avoids.addHostList(allHostsInDc);
}
private void findAvoidSetForNonExplicitUserVM(ExcludeList avoids, VirtualMachine vm, List<Long> allPodsInDc, List<Long> allClustersInDc, List<Long> allHostsInDc) {
logger.debug(() -> LogUtils.logGsonWithoutException("Adding pods [%s], clusters [%s] and hosts [%s] to the avoid list in the deploy process of user VM [%s], "
+ "because this VM is not explicitly dedicated to these components.", allPodsInDc, allClustersInDc, allHostsInDc, vm.getUuid()));
avoids.addPodList(allPodsInDc);
avoids.addClusterList(allClustersInDc);
avoids.addHostList(allHostsInDc);
}
private void resetAvoidSet(ExcludeList avoidSet, ExcludeList removeSet) {
@ -1246,14 +1279,15 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
DeploymentPlanner.PlannerResourceUsage resourceUsageRequired, ExcludeList plannerAvoidOutput) {
if (logger.isTraceEnabled()) {
logger.trace("ClusterId List to consider: " + clusterList);
logger.trace("ClusterId List to consider: {}.", clusterList);
}
for (Long clusterId : clusterList) {
ClusterVO clusterVO = _clusterDao.findById(clusterId);
if (clusterVO.getHypervisorType() != vmProfile.getHypervisorType()) {
logger.debug("Cluster: " + clusterId + " has HyperVisorType that does not match the VM, skipping this cluster");
logger.debug("Adding cluster [{}] to the avoid set because the cluster's hypervisor [{}] does not match the VM [{}] hypervisor: [{}]. Skipping this cluster.",
clusterVO.getUuid(), clusterVO.getHypervisorType().name(), vmProfile.getUuid(), vmProfile.getHypervisorType().name());
avoid.addCluster(clusterVO.getId());
continue;
}
@ -1564,14 +1598,17 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
boolean hostHasEncryption = Boolean.parseBoolean(potentialHostVO.getDetail(Host.HOST_VOLUME_ENCRYPTION));
boolean hostMeetsEncryptionRequirements = !anyVolumeRequiresEncryption(new ArrayList<>(volumesOrderBySizeDesc)) || hostHasEncryption;
boolean plannerUsageFits = checkIfHostFitsPlannerUsage(potentialHost.getId(), resourceUsageRequired);
boolean hostFitsPlannerUsage = checkIfHostFitsPlannerUsage(potentialHost.getId(), resourceUsageRequired);
if (hostCanAccessPool && haveEnoughSpace && hostAffinityCheck && hostMeetsEncryptionRequirements && plannerUsageFits) {
if (hostCanAccessPool && haveEnoughSpace && hostAffinityCheck && hostMeetsEncryptionRequirements && hostFitsPlannerUsage) {
logger.debug("Found a potential host " + "id: " + potentialHost.getId() + " name: " + potentialHost.getName() +
" and associated storage pools for this VM");
volumeAllocationMap.clear();
return new Pair<Host, Map<Volume, StoragePool>>(potentialHost, storage);
} else {
logger.debug("Adding host [{}] to the avoid set because: can access Pool [{}], has enough space [{}], affinity check [{}], fits planner [{}] usage [{}].",
potentialHost.getUuid(), hostCanAccessPool, haveEnoughSpace, hostAffinityCheck, resourceUsageRequired.getClass().getSimpleName(), hostFitsPlannerUsage);
if (!hostMeetsEncryptionRequirements) {
logger.debug("Potential host " + potentialHost + " did not meet encryption requirements of all volumes");
}
@ -1668,12 +1705,12 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
// There should be at least the ROOT volume of the VM in usable state
if (volumesTobeCreated.isEmpty()) {
// OfflineVmwareMigration: find out what is wrong with the id of the vm we try to start
throw new CloudRuntimeException("Unable to create deployment, no usable volumes found for the VM: " + vmProfile.getId());
throw new CloudRuntimeException("Unable to create deployment, no usable volumes found for the VM: " + vmProfile.getUuid());
}
// don't allow to start vm that doesn't have a root volume
if (_volsDao.findByInstanceAndType(vmProfile.getId(), Volume.Type.ROOT).isEmpty()) {
throw new CloudRuntimeException("Unable to prepare volumes for vm as ROOT volume is missing");
throw new CloudRuntimeException(String.format("Unable to deploy VM [%s] because the ROOT volume is missing.", vmProfile.getUuid()));
}
// for each volume find list of suitable storage pools by calling the
@ -1685,7 +1722,7 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
Set<Long> poolsToAvoidOutput = new HashSet<>(originalAvoidPoolSet);
for (VolumeVO toBeCreated : volumesTobeCreated) {
logger.debug("Checking suitable pools for volume (Id, Type): (" + toBeCreated.getId() + "," + toBeCreated.getVolumeType().name() + ")");
logger.debug("Checking suitable pools for volume [{}, {}] of VM [{}].", toBeCreated.getUuid(), toBeCreated.getVolumeType().name(), vmProfile.getUuid());
if (toBeCreated.getState() == Volume.State.Allocated && toBeCreated.getPoolId() != null) {
toBeCreated.setPoolId(null);
@ -1701,71 +1738,17 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
// volume is ready and the pool should be reused.
// In this case, also check if rest of the volumes are ready and can
// be reused.
if (plan.getPoolId() != null || (toBeCreated.getVolumeType() == Volume.Type.DATADISK && toBeCreated.getPoolId() != null && toBeCreated.getState() == Volume.State.Ready)) {
logger.debug("Volume has pool already allocated, checking if pool can be reused, poolId: " + toBeCreated.getPoolId());
List<StoragePool> suitablePools = new ArrayList<StoragePool>();
StoragePool pool = null;
if (toBeCreated.getPoolId() != null) {
pool = (StoragePool)dataStoreMgr.getPrimaryDataStore(toBeCreated.getPoolId());
} else {
pool = (StoragePool)dataStoreMgr.getPrimaryDataStore(plan.getPoolId());
}
if (!pool.isInMaintenance()) {
if (!avoid.shouldAvoid(pool)) {
long exstPoolDcId = pool.getDataCenterId();
long exstPoolPodId = pool.getPodId() != null ? pool.getPodId() : -1;
long exstPoolClusterId = pool.getClusterId() != null ? pool.getClusterId() : -1;
boolean canReusePool = false;
if (plan.getDataCenterId() == exstPoolDcId && plan.getPodId() == exstPoolPodId && plan.getClusterId() == exstPoolClusterId) {
canReusePool = true;
} else if (plan.getDataCenterId() == exstPoolDcId) {
DataStore dataStore = dataStoreMgr.getPrimaryDataStore(pool.getId());
if (dataStore != null && dataStore.getScope() != null && dataStore.getScope().getScopeType() == ScopeType.ZONE) {
canReusePool = true;
}
} else {
logger.debug("Pool of the volume does not fit the specified plan, need to reallocate a pool for this volume");
canReusePool = false;
}
if (canReusePool) {
logger.debug("Planner need not allocate a pool for this volume since its READY");
suitablePools.add(pool);
suitableVolumeStoragePools.put(toBeCreated, suitablePools);
if (!(toBeCreated.getState() == Volume.State.Allocated || toBeCreated.getState() == Volume.State.Creating)) {
readyAndReusedVolumes.add(toBeCreated);
}
if ((plan.getPoolId() != null || (toBeCreated.getVolumeType() == Volume.Type.DATADISK && toBeCreated.getPoolId() != null && toBeCreated.getState() == Volume.State.Ready)) &&
checkIfPoolCanBeReused(vmProfile, plan, avoid, suitableVolumeStoragePools, readyAndReusedVolumes, toBeCreated)) {
continue;
}
} else {
logger.debug("Pool of the volume is in avoid set, need to reallocate a pool for this volume");
}
} else {
logger.debug("Pool of the volume is in maintenance, need to reallocate a pool for this volume");
}
}
if (logger.isDebugEnabled()) {
logger.debug("We need to allocate new storagepool for this volume");
}
if (!isRootAdmin(vmProfile)) {
if (!isEnabledForAllocation(plan.getDataCenterId(), plan.getPodId(), plan.getClusterId())) {
if (logger.isDebugEnabled()) {
logger.debug("Cannot allocate new storagepool for this volume in this cluster, allocation state is disabled");
logger.debug("Cannot deploy to this specified plan, allocation state is disabled, returning.");
}
// Cannot find suitable storage pools under this cluster for
// this volume since allocation_state is disabled.
// - remove any suitable pools found for other volumes.
// All volumes should get suitable pools under this cluster;
// else we can't use this cluster.
if (!isRootAdmin(vmProfile) && !isEnabledForAllocation(plan.getDataCenterId(), plan.getPodId(), plan.getClusterId())) {
logger.debug(String.format("Cannot find new storage pool to deploy volume [{}] of VM [{}] in cluster [{}] because allocation state is disabled. Returning.",
toBeCreated.getUuid(), vmProfile.getUuid(), plan.getClusterId()));
suitableVolumeStoragePools.clear();
break;
}
}
logger.debug("Calling StoragePoolAllocators to find suitable pools");
DiskOfferingVO diskOffering = _diskOfferingDao.findById(toBeCreated.getDiskOfferingId());
@ -1783,16 +1766,8 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
useLocalStorage = diskOffering.isUseLocalStorage();
}
diskProfile.setUseLocalStorage(useLocalStorage);
boolean foundPotentialPools = false;
for (StoragePoolAllocator allocator : _storagePoolAllocators) {
final List<StoragePool> suitablePools = allocator.allocateToPool(diskProfile, vmProfile, plan, avoid, returnUpTo);
if (suitablePools != null && !suitablePools.isEmpty()) {
checkForPreferredStoragePool(suitablePools, vmProfile.getVirtualMachine(), suitableVolumeStoragePools, toBeCreated);
foundPotentialPools = true;
break;
}
}
logger.debug(String.format("Calling StoragePoolAllocators to find suitable pools to allocate volume [{}] necessary to deploy VM [{}].", toBeCreated.getUuid(), vmProfile.getUuid()));
boolean foundPotentialPools = tryToFindPotentialPoolsToAlocateVolume(vmProfile, plan, avoid, returnUpTo, suitableVolumeStoragePools, toBeCreated, diskProfile);
if (avoid.getPoolsToAvoid() != null) {
poolsToAvoidOutput.addAll(avoid.getPoolsToAvoid());
@ -1800,7 +1775,7 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
}
if (!foundPotentialPools) {
logger.debug("No suitable pools found for volume: " + toBeCreated + " under cluster: " + plan.getClusterId());
logger.debug(String.format("No suitable pools found for volume [{}] used by VM [{}] under cluster: [{}].", toBeCreated.getUuid(), vmProfile.getUuid(), plan.getClusterId()));
// No suitable storage pools found under this cluster for this
// volume. - remove any suitable pools found for other volumes.
// All volumes should get suitable pools under this cluster;
@ -1829,6 +1804,75 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
return new Pair<Map<Volume, List<StoragePool>>, List<Volume>>(suitableVolumeStoragePools, readyAndReusedVolumes);
}
private boolean tryToFindPotentialPoolsToAlocateVolume(VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid, int returnUpTo,
Map<Volume, List<StoragePool>> suitableVolumeStoragePools, VolumeVO toBeCreated, DiskProfile diskProfile) {
for (StoragePoolAllocator allocator : _storagePoolAllocators) {
logger.debug("Trying to find suitable pools to allocate volume [{}] necessary to deploy VM [{}], using StoragePoolAllocator: [{}].",
toBeCreated.getUuid(), vmProfile.getUuid(), allocator.getClass().getSimpleName());
final List<StoragePool> suitablePools = allocator.allocateToPool(diskProfile, vmProfile, plan, avoid, returnUpTo);
if (suitablePools != null && !suitablePools.isEmpty()) {
logger.debug("StoragePoolAllocator [{}] found {} suitable pools to allocate volume [{}] necessary to deploy VM [{}].",
allocator.getClass().getSimpleName(), suitablePools.size(), toBeCreated.getUuid(), vmProfile.getUuid());
checkForPreferredStoragePool(suitablePools, vmProfile.getVirtualMachine(), suitableVolumeStoragePools, toBeCreated);
return true;
}
}
return false;
}
private boolean checkIfPoolCanBeReused(VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoid,
Map<Volume, List<StoragePool>> suitableVolumeStoragePools, List<Volume> readyAndReusedVolumes,
VolumeVO toBeCreated) {
logger.debug("Volume [{}] of VM [{}] has pool [{}] already specified. Checking if this pool can be reused.", toBeCreated.getUuid(), vmProfile.getUuid(), toBeCreated.getPoolId());
List<StoragePool> suitablePools = new ArrayList<StoragePool>();
StoragePool pool = null;
if (toBeCreated.getPoolId() != null) {
pool = (StoragePool)dataStoreMgr.getPrimaryDataStore(toBeCreated.getPoolId());
} else {
pool = (StoragePool)dataStoreMgr.getPrimaryDataStore(plan.getPoolId());
}
if (!pool.isInMaintenance()) {
if (!avoid.shouldAvoid(pool)) {
return canReusePool(vmProfile, plan, suitableVolumeStoragePools, readyAndReusedVolumes, toBeCreated, suitablePools, pool);
} else {
logger.debug("Pool [{}] of volume [{}] used by VM [{}] is in the avoid set. Need to reallocate a pool for this volume.",
pool.getUuid(), toBeCreated.getUuid(), vmProfile.getUuid());
}
} else {
logger.debug("Pool [{}] of volume [{}] used by VM [{}] is in maintenance. Need to reallocate a pool for this volume.",
pool.getUuid(), toBeCreated.getUuid(), vmProfile.getUuid());
}
return false;
}
private boolean canReusePool(VirtualMachineProfile vmProfile, DeploymentPlan plan,
Map<Volume, List<StoragePool>> suitableVolumeStoragePools, List<Volume> readyAndReusedVolumes,
VolumeVO toBeCreated, List<StoragePool> suitablePools, StoragePool pool) {
DataStore dataStore = dataStoreMgr.getPrimaryDataStore(pool.getId());
long exstPoolDcId = pool.getDataCenterId();
long exstPoolPodId = pool.getPodId() != null ? pool.getPodId() : -1;
long exstPoolClusterId = pool.getClusterId() != null ? pool.getClusterId() : -1;
if (plan.getDataCenterId() == exstPoolDcId && ((plan.getPodId() == exstPoolPodId && plan.getClusterId() == exstPoolClusterId) ||
(dataStore != null && dataStore.getScope() != null && dataStore.getScope().getScopeType() == ScopeType.ZONE))) {
logger.debug("Pool [{}] of volume [{}] used by VM [{}] fits the specified plan. No need to reallocate a pool for this volume.",
pool.getUuid(), toBeCreated.getUuid(), vmProfile.getUuid());
suitablePools.add(pool);
suitableVolumeStoragePools.put(toBeCreated, suitablePools);
if (!(toBeCreated.getState() == Volume.State.Allocated || toBeCreated.getState() == Volume.State.Creating)) {
readyAndReusedVolumes.add(toBeCreated);
}
return true;
}
logger.debug("Pool [{}] of volume [{}] used by VM [{}] does not fit the specified plan. Need to reallocate a pool for this volume.",
pool.getUuid(), toBeCreated.getUuid(), vmProfile.getUuid());
return false;
}
private void checkForPreferredStoragePool(List<StoragePool> suitablePools,
VirtualMachine vm,
Map<Volume, List<StoragePool>> suitableVolumeStoragePools,
@ -1972,6 +2016,9 @@ StateListener<State, VirtualMachine.Event, VirtualMachine>, Configurable {
return true;
}
public static String logDeploymentWithoutException(VirtualMachine vm, DeploymentPlan plan, ExcludeList avoids, DeploymentPlanner planner) {
return LogUtils.logGsonWithoutException("Trying to deploy VM [%s] and details: Plan [%s]; avoid list [%s] and planner: [%s].", vm, plan, avoids, planner);
}
@Override
public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] {allowRouterOnDisabledResource, allowAdminVmOnDisabledResource};

View File

@ -252,7 +252,9 @@ public class DeploymentPlanningManagerImplTest {
Mockito.when(template.isDeployAsIs()).thenReturn(false);
Mockito.when(templateDao.findById(Mockito.anyLong())).thenReturn(template);
VMInstanceVO vm = new VMInstanceVO();
VMInstanceVO vm = Mockito.mock(VMInstanceVO.class);
Mockito.when(vm.getType()).thenReturn(Type.Instance);
Mockito.when(vm.getLastHostId()).thenReturn(null);
Mockito.when(vmProfile.getVirtualMachine()).thenReturn(vm);
Mockito.when(vmProfile.getId()).thenReturn(instanceId);