This is an automated email from the ASF dual-hosted git repository.
dahn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/main by this push:
new 6f27b1f459f Improve logs when adding components to avoid set (#7214)
6f27b1f459f is described below
commit 6f27b1f459f8347ccaff1cc780980856a446705c
Author: SadiJr <[email protected]>
AuthorDate: Wed Feb 28 04:49:10 2024 -0300
Improve logs when adding components to avoid set (#7214)
Co-authored-by: SadiJr <[email protected]>
Co-authored-by: GaOrtiga <[email protected]>
Co-authored-by: João Jandre <[email protected]>
---
.../java/com/cloud/deploy/DeploymentPlanner.java | 24 +-
.../com/cloud/vm/VirtualMachineManagerImpl.java | 14 +-
.../manager/allocator/impl/FirstFitAllocator.java | 14 +-
.../deploy/DeploymentPlanningManagerImpl.java | 783 +++++++++++----------
.../deploy/DeploymentPlanningManagerImplTest.java | 4 +-
5 files changed, 455 insertions(+), 384 deletions(-)
diff --git a/api/src/main/java/com/cloud/deploy/DeploymentPlanner.java
b/api/src/main/java/com/cloud/deploy/DeploymentPlanner.java
index e9f706ac1ce..354f9cfaac5 100644
--- a/api/src/main/java/com/cloud/deploy/DeploymentPlanner.java
+++ b/api/src/main/java/com/cloud/deploy/DeploymentPlanner.java
@@ -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)) {
diff --git
a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
index f3ec3dd4761..87399389271 100755
---
a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
+++
b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
@@ -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;
diff --git
a/server/src/main/java/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java
b/server/src/main/java/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java
index 1e92a80a280..36330d6685c 100644
---
a/server/src/main/java/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java
+++
b/server/src/main/java/com/cloud/agent/manager/allocator/impl/FirstFitAllocator.java
@@ -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;
}
diff --git
a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java
b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java
index 41a51bc7af5..d97fcef7453 100644
--- a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java
+++ b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java
@@ -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);
- }
-
- 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());
- }
+ DeployDestination deployDestination =
deployInVmLastHost(vmProfile, plan, avoids, planner, vm, dc, offering,
cpuRequested, ramRequested, volumesRequireEncryption);
+ if (deployDestination != null) {
+ return deployDestination;
}
- 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) {
@@ -636,32 +678,29 @@ StateListener<State, VirtualMachine.Event,
VirtualMachine>, Configurable {
return vmProfile == null || vmProfile.getTemplate() == null ||
!vmProfile.getTemplate().isDeployAsIs();
}
- /**
- * 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()) {
- return;
- }
-
- 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;
- }
+ /**
+ * 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()) {
+ return;
+ }
- if (isOwnerRoleIdAdmin &&
isAdminVmDeployableInDisabledResources()) {
- return;
- }
+ VMInstanceVO vm = _vmInstanceDao.findById(vmProfile.getId());
+ AccountVO owner = accountDao.findById(vm.getAccountId());
+ boolean isOwnerRoleIdAdmin = owner != null && owner.getRoleId() !=
null && owner.getRoleId() == ADMIN_ACCOUNT_ROLE_ID;
- avoidDisabledDataCenters(dc, avoids);
- avoidDisabledPods(dc, avoids);
- avoidDisabledClusters(dc, avoids);
- avoidDisabledHosts(dc, avoids);
+ if (isOwnerRoleIdAdmin && isAdminVmDeployableInDisabledResources()) {
+ return;
}
+ avoidDisabledDataCenters(dc, avoids);
+ avoidDisabledPods(dc, avoids);
+ avoidDisabledClusters(dc, avoids);
+ avoidDisabledHosts(dc, avoids);
+ }
+
/**
* Returns the value of the ConfigKey 'allow.router.on.disabled.resources'.
* @note this method allows mocking and testing with the respective
ConfigKey parameter.
@@ -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) {
- 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>();
+ findAvoiSetForRouterVM(avoids, vm, allPodsInDc, allClustersInDc,
allHostsInDc);
+ }
+ }
- //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);
+ private void findAvoiSetForRouterVM(ExcludeList avoids, VirtualMachine vm,
List<Long> allPodsInDc, List<Long> allClustersInDc, List<Long> allHostsInDc) {
+ long vmAccountId = vm.getAccountId();
+ long vmDomainId = vm.getDomainId();
- //For temporary storage and indexing.
- List<DedicatedResourceVO> tempStorage;
+ List<Long> allPodsFromDedicatedID = new ArrayList<Long>();
+ List<Long> allClustersFromDedicatedID = new ArrayList<Long>();
+ List<Long> allHostsFromDedicatedID = new ArrayList<Long>();
- if (domainGroupMappings == null || domainGroupMappings.isEmpty()) {
- //The dedicated resource belongs to VM Account ID.
+ List<AffinityGroupDomainMapVO> domainGroupMappings =
_affinityGroupDomainMapDao.listByDomain(vmDomainId);
- tempStorage = _dedicatedDao.searchDedicatedPods(null,
vmDomainId, vmAccountId, null, new Filter(DedicatedResourceVO.class, "id",
true, 0L, 1L)).first();
+ List<DedicatedResourceVO> tempStorage;
- for(DedicatedResourceVO vo : tempStorage) {
- allPodsFromDedicatedID.add(vo.getPodId());
- }
+ if (domainGroupMappings == null || domainGroupMappings.isEmpty()) {
+ tempStorage = _dedicatedDao.searchDedicatedPods(null, vmDomainId,
vmAccountId, null,
+ new Filter(DedicatedResourceVO.class, "id", true, 0L,
1L)).first();
- tempStorage.clear();
- tempStorage = _dedicatedDao.searchDedicatedClusters(null,
vmDomainId, vmAccountId, null, new Filter(DedicatedResourceVO.class, "id",
true, 0L, 1L)).first();
+ for (DedicatedResourceVO vo : tempStorage) {
+ allPodsFromDedicatedID.add(vo.getPodId());
+ }
- for(DedicatedResourceVO vo : tempStorage) {
- allClustersFromDedicatedID.add(vo.getClusterId());
- }
+ tempStorage.clear();
+ tempStorage = _dedicatedDao.searchDedicatedClusters(null,
vmDomainId, vmAccountId, null,
+ new Filter(DedicatedResourceVO.class, "id", true, 0L,
1L)).first();
- tempStorage.clear();
- tempStorage = _dedicatedDao.searchDedicatedHosts(null,
vmDomainId, vmAccountId, null, new Filter(DedicatedResourceVO.class, "id",
true, 0L, 1L)).first();
+ for (DedicatedResourceVO vo : tempStorage) {
+ allClustersFromDedicatedID.add(vo.getClusterId());
+ }
- for(DedicatedResourceVO vo : tempStorage) {
- allHostsFromDedicatedID.add(vo.getHostId());
- }
+ tempStorage.clear();
+ tempStorage = _dedicatedDao.searchDedicatedHosts(null, vmDomainId,
vmAccountId, null,
+ new Filter(DedicatedResourceVO.class, "id", true, 0L,
1L)).first();
- //Remove the dedicated ones from main list
- allPodsInDc.removeAll(allPodsFromDedicatedID);
- allClustersInDc.removeAll(allClustersFromDedicatedID);
- allHostsInDc.removeAll(allHostsFromDedicatedID);
+ for (DedicatedResourceVO vo : tempStorage) {
+ allHostsFromDedicatedID.add(vo.getHostId());
}
- else {
- //The dedicated resource belongs to VM Domain ID or No
dedication.
-
- tempStorage = _dedicatedDao.searchDedicatedPods(null,
vmDomainId, null, null, new Filter(DedicatedResourceVO.class, "id", true, 0L,
1L)).first();
- for(DedicatedResourceVO vo : tempStorage) {
- allPodsFromDedicatedID.add(vo.getPodId());
- }
+ allPodsInDc.removeAll(allPodsFromDedicatedID);
+ allClustersInDc.removeAll(allClustersFromDedicatedID);
+ allHostsInDc.removeAll(allHostsFromDedicatedID);
+ } else {
+ tempStorage = _dedicatedDao.searchDedicatedPods(null, vmDomainId,
null, null,
+ new Filter(DedicatedResourceVO.class, "id", true, 0L,
1L)).first();
- tempStorage.clear();
- tempStorage = _dedicatedDao.searchDedicatedClusters(null,
vmDomainId, null, null, new Filter(DedicatedResourceVO.class, "id", true, 0L,
1L)).first();
+ for (DedicatedResourceVO vo : tempStorage) {
+ allPodsFromDedicatedID.add(vo.getPodId());
+ }
- for(DedicatedResourceVO vo : tempStorage) {
- allClustersFromDedicatedID.add(vo.getClusterId());
- }
+ tempStorage.clear();
+ tempStorage = _dedicatedDao.searchDedicatedClusters(null,
vmDomainId, null, null,
+ new Filter(DedicatedResourceVO.class, "id", true, 0L,
1L)).first();
- tempStorage.clear();
- tempStorage = _dedicatedDao.searchDedicatedHosts(null,
vmDomainId, null, null, new Filter(DedicatedResourceVO.class, "id", true, 0L,
1L)).first();
+ for (DedicatedResourceVO vo : tempStorage) {
+ allClustersFromDedicatedID.add(vo.getClusterId());
+ }
- for(DedicatedResourceVO vo : tempStorage) {
- allHostsFromDedicatedID.add(vo.getHostId());
- }
+ tempStorage.clear();
+ tempStorage = _dedicatedDao.searchDedicatedHosts(null, vmDomainId,
null, null,
+ new Filter(DedicatedResourceVO.class, "id", true, 0L,
1L)).first();
- //Remove the dedicated ones from main list
- allPodsInDc.removeAll(allPodsFromDedicatedID);
- allClustersInDc.removeAll(allClustersFromDedicatedID);
- allHostsInDc.removeAll(allHostsFromDedicatedID);
+ for (DedicatedResourceVO vo : tempStorage) {
+ allHostsFromDedicatedID.add(vo.getHostId());
}
- //Add in avoid list or no addition if no dedication
- if (logger.isDebugEnabled()) {
- logger.debug("Adding pods to avoid lists: " + allPodsInDc);
- }
- 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);
+ allPodsInDc.removeAll(allPodsFromDedicatedID);
+ allClustersInDc.removeAll(allClustersFromDedicatedID);
+ allHostsInDc.removeAll(allHostsFromDedicatedID);
}
+
+ 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);
+ avoids.addClusterList(allClustersInDc);
+ 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,72 +1738,18 @@ 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);
- }
- 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 ((plan.getPoolId() != null || (toBeCreated.getVolumeType() ==
Volume.Type.DATADISK && toBeCreated.getPoolId() != null &&
toBeCreated.getState() == Volume.State.Ready)) &&
+ checkIfPoolCanBeReused(vmProfile, plan, avoid,
suitableVolumeStoragePools, readyAndReusedVolumes, toBeCreated)) {
+ continue;
}
- 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.
- suitableVolumeStoragePools.clear();
- break;
- }
+ 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());
DiskProfile diskProfile = new DiskProfile(toBeCreated,
diskOffering, vmProfile.getHypervisorType());
@@ -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};
diff --git
a/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java
b/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java
index ea04a09cf92..3afd3dc4a95 100644
---
a/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java
+++
b/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java
@@ -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);