From a3cdd1f836e40a4b4444af738780a337ee7aac1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Beims=20Br=C3=A4scher?= Date: Fri, 28 May 2021 05:45:30 -0300 Subject: [PATCH] Allow deploy Admin VMs and VRs in disabled zones/pods/clusters (#3600) --- .../deploy/DeploymentPlanningManager.java | 8 + .../main/java/com/cloud/host/dao/HostDao.java | 13 ++ .../java/com/cloud/host/dao/HostDaoImpl.java | 20 +- .../main/java/com/cloud/vm/VMInstanceVO.java | 2 +- .../allocator/impl/RecreateHostAllocator.java | 15 -- .../deploy/DeploymentPlanningManagerImpl.java | 153 +++++++++--- .../com/cloud/deploy/FirstFitPlanner.java | 39 ---- .../DeploymentPlanningManagerImplTest.java | 221 +++++++++++++++++- 8 files changed, 372 insertions(+), 99 deletions(-) rename server/src/test/java/com/cloud/{vm => deploy}/DeploymentPlanningManagerImplTest.java (64%) diff --git a/engine/components-api/src/main/java/com/cloud/deploy/DeploymentPlanningManager.java b/engine/components-api/src/main/java/com/cloud/deploy/DeploymentPlanningManager.java index 2266cd50fe6..d3a5f7f7376 100644 --- a/engine/components-api/src/main/java/com/cloud/deploy/DeploymentPlanningManager.java +++ b/engine/components-api/src/main/java/com/cloud/deploy/DeploymentPlanningManager.java @@ -22,9 +22,17 @@ import com.cloud.exception.AffinityConflictException; import com.cloud.exception.InsufficientServerCapacityException; import com.cloud.utils.component.Manager; import com.cloud.vm.VirtualMachineProfile; +import org.apache.cloudstack.framework.config.ConfigKey; public interface DeploymentPlanningManager extends Manager { + + static final ConfigKey allowRouterOnDisabledResource = new ConfigKey("Advanced", Boolean.class, "allow.router.on.disabled.resources", "false", + "Allow deploying VR in disabled Zones, Pods, and Clusters", true); + + static final ConfigKey allowAdminVmOnDisabledResource = new ConfigKey("Advanced", Boolean.class, "allow.admin.vm.on.disabled.resources", "false", + "Allow deploying VMs owned by the admin account in disabled Clusters, Pods, and Zones", true); + /** * Manages vm deployment stages: First Process Affinity/Anti-affinity - Call * the chain of AffinityGroupProcessor adapters to set deploymentplan scope diff --git a/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java b/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java index 3d76c8b38c1..af06fcc31f7 100644 --- a/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java +++ b/engine/schema/src/main/java/com/cloud/host/dao/HostDao.java @@ -83,8 +83,21 @@ public interface HostDao extends GenericDao, StateDao findByClusterId(Long clusterId); + /** + * Returns hosts that are 'Up' and 'Enabled' from the given Data Center/Zone + */ List listByDataCenterId(long id); + /** + * Returns hosts that are from the given Data Center/Zone and at a given state (e.g. Creating, Enabled, Disabled, etc). + */ + List listByDataCenterIdAndState(long id, ResourceState state); + + /** + * Returns hosts that are 'Up' and 'Disabled' from the given Data Center/Zone + */ + List listDisabledByDataCenterId(long id); + List listByDataCenterIdAndHypervisorType(long zoneId, Hypervisor.HypervisorType hypervisorType); List listAllHosts(long zoneId); diff --git a/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java b/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java index e931f651571..e58df1defd0 100644 --- a/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/host/dao/HostDaoImpl.java @@ -463,13 +463,27 @@ public class HostDaoImpl extends GenericDaoBase implements HostDao @Override public List listByDataCenterId(long id) { + return listByDataCenterIdAndState(id, ResourceState.Enabled); + } + + @Override + public List listByDataCenterIdAndState(long id, ResourceState state) { + SearchCriteria sc = scHostsFromZoneUpRouting(id); + sc.setParameters("resourceState", state); + return listBy(sc); + } + + @Override + public List listDisabledByDataCenterId(long id) { + return listByDataCenterIdAndState(id, ResourceState.Disabled); + } + + private SearchCriteria scHostsFromZoneUpRouting(long id) { SearchCriteria sc = DcSearch.create(); sc.setParameters("dc", id); sc.setParameters("status", Status.Up); sc.setParameters("type", Host.Type.Routing); - sc.setParameters("resourceState", ResourceState.Enabled); - - return listBy(sc); + return sc; } @Override diff --git a/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java b/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java index 47932c9fbbb..0e8dd4ee44a 100644 --- a/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java +++ b/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java @@ -238,7 +238,7 @@ public class VMInstanceVO implements VirtualMachine, FiniteStateObject { +StateListener, Configurable { private static final Logger s_logger = Logger.getLogger(DeploymentPlanningManagerImpl.class); @Inject AgentManager _agentMgr; @Inject + private AccountDao accountDao; + @Inject protected UserVmDao _vmDao; @Inject protected VMInstanceDao _vmInstanceDao; @@ -177,6 +183,7 @@ StateListener { @Inject private VMTemplateDao templateDao; + private static final long ADMIN_ACCOUNT_ROLE_ID = 1l; private static final long INITIAL_RESERVATION_RELEASE_CHECKER_DELAY = 30L * 1000L; // thirty seconds expressed in milliseconds protected long _nodeId = -1; @@ -283,6 +290,8 @@ StateListener { s_logger.debug("Is ROOT volume READY (pool already allocated)?: " + (plan.getPoolId() != null ? "Yes" : "No")); } + avoidDisabledResources(vmProfile, dc, avoids); + String haVmTag = (String)vmProfile.getParameter(VirtualMachineProfile.Param.HaTag); String uefiFlag = (String)vmProfile.getParameter(VirtualMachineProfile.Param.UefiFlag); @@ -311,17 +320,8 @@ StateListener { } Pod pod = _podDao.findById(host.getPodId()); - // check if the cluster or the pod is disabled - if (pod.getAllocationState() != Grouping.AllocationState.Enabled) { - s_logger.warn("The Pod containing this host is in disabled state, PodId= " + pod.getId()); - return null; - } Cluster cluster = _clusterDao.findById(host.getClusterId()); - if (cluster.getAllocationState() != Grouping.AllocationState.Enabled) { - s_logger.warn("The Cluster containing this host is in disabled state, PodId= " + cluster.getId()); - return null; - } boolean displayStorage = getDisplayStorageFromVmProfile(vmProfile); if (vm.getHypervisorType() == HypervisorType.BareMetal) { @@ -422,8 +422,15 @@ StateListener { s_logger.debug("The last host of this VM does not have required GPU devices available"); } } else { - if (host.getStatus() == Status.Up && host.getResourceState() == ResourceState.Enabled) { - if (checkVmProfileAndHost(vmProfile, host)) { + if (host.getStatus() == Status.Up) { + boolean hostTagsMatch = true; + if(offering.getHostTag() != null){ + _hostDao.loadHostTags(host); + if (!(host.getHostTags() != null && host.getHostTags().contains(offering.getHostTag()))) { + hostTagsMatch = false; + } + } + if (hostTagsMatch) { long cluster_id = host.getClusterId(); ClusterDetailsVO cluster_detail_cpu = _clusterDetailsDao.findDetail(cluster_id, "cpuOvercommitRatio"); @@ -573,6 +580,86 @@ StateListener { 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; + } + + 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. + */ + protected boolean isRouterDeployableInDisabledResources() { + return allowRouterOnDisabledResource.value(); + } + + /** + * Returns the value of the ConfigKey 'allow.admin.vm.on.disabled.resources'. + * @note this method allows mocking and testing with the respective ConfigKey parameter. + */ + protected boolean isAdminVmDeployableInDisabledResources() { + return allowAdminVmOnDisabledResource.value(); + } + + /** + * Adds disabled Hosts to the ExcludeList in order to avoid them at the deployment planner. + */ + protected void avoidDisabledHosts(DataCenter dc, ExcludeList avoids) { + List disabledHosts = _hostDao.listDisabledByDataCenterId(dc.getId()); + for (HostVO host : disabledHosts) { + avoids.addHost(host.getId()); + } + } + + /** + * Adds disabled Clusters to the ExcludeList in order to avoid them at the deployment planner. + */ + protected void avoidDisabledClusters(DataCenter dc, ExcludeList avoids) { + List pods = _podDao.listAllPods(dc.getId()); + for (Long podId : pods) { + List disabledClusters = _clusterDao.listDisabledClusters(dc.getId(), podId); + avoids.addClusterList(disabledClusters); + } + } + + /** + * Adds disabled Pods to the ExcludeList in order to avoid them at the deployment planner. + */ + protected void avoidDisabledPods(DataCenter dc, ExcludeList avoids) { + List disabledPods = _podDao.listDisabledPods(dc.getId()); + avoids.addPodList(disabledPods); + } + + /** + * Adds disabled Data Centers (Zones) to the ExcludeList in order to avoid them at the deployment planner. + */ + protected void avoidDisabledDataCenters(DataCenter dc, ExcludeList avoids) { + if (dc.getAllocationState() == Grouping.AllocationState.Disabled) { + avoids.addDataCenter(dc.getId()); + } + } + @Override public DeploymentPlanner getDeploymentPlannerByName(String plannerName) { if (plannerName != null) { @@ -1092,11 +1179,6 @@ StateListener { for (Long clusterId : clusterList) { ClusterVO clusterVO = _clusterDao.findById(clusterId); - if (clusterVO.getAllocationState() == Grouping.AllocationState.Disabled && !plan.isMigrationPlan()) { - s_logger.debug("Cannot deploy in disabled cluster " + clusterId + ", skipping this cluster"); - avoid.addCluster(clusterVO.getId()); - } - if (clusterVO.getHypervisorType() != vmProfile.getHypervisorType()) { s_logger.debug("Cluster: " + clusterId + " has HyperVisorType that does not match the VM, skipping this cluster"); avoid.addCluster(clusterVO.getId()); @@ -1110,7 +1192,9 @@ StateListener { new DataCenterDeployment(plan.getDataCenterId(), clusterVO.getPodId(), clusterVO.getId(), null, plan.getPoolId(), null, plan.getReservationContext()); Pod pod = _podDao.findById(clusterVO.getPodId()); - if (pod.getAllocationState() == Grouping.AllocationState.Enabled ) { + if (CollectionUtils.isNotEmpty(avoid.getPodsToAvoid()) && avoid.getPodsToAvoid().contains(pod.getId())) { + s_logger.debug("The cluster is in a disabled pod : " + pod.getId()); + } else { // find suitable hosts under this cluster, need as many hosts as we // get. List suitableHosts = findSuitableHosts(vmProfile, potentialPlan, avoid, HostAllocator.RETURN_UPTO_ALL); @@ -1151,9 +1235,6 @@ StateListener { s_logger.debug("No suitable hosts found under this Cluster: " + clusterId); } } - else { - s_logger.debug("The cluster is in a disabled pod : " + pod.getId()); - } if (canAvoidCluster(clusterVO, avoid, plannerAvoidOutput, vmProfile)) { avoid.addCluster(clusterVO.getId()); @@ -1739,4 +1820,14 @@ StateListener { } return true; } + + @Override + public ConfigKey[] getConfigKeys() { + return new ConfigKey[] {allowRouterOnDisabledResource, allowAdminVmOnDisabledResource}; + } + + @Override + public String getConfigComponentName() { + return DeploymentPlanningManager.class.getSimpleName(); + } } diff --git a/server/src/main/java/com/cloud/deploy/FirstFitPlanner.java b/server/src/main/java/com/cloud/deploy/FirstFitPlanner.java index a93da7102b4..dbcfe4d1b17 100644 --- a/server/src/main/java/com/cloud/deploy/FirstFitPlanner.java +++ b/server/src/main/java/com/cloud/deploy/FirstFitPlanner.java @@ -260,15 +260,6 @@ public class FirstFitPlanner extends AdapterBase implements DeploymentClusterPla } podsWithCapacity.removeAll(avoid.getPodsToAvoid()); } - if (!isRootAdmin(vmProfile)) { - List disabledPods = listDisabledPods(plan.getDataCenterId()); - if (!disabledPods.isEmpty()) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Removing from the podId list these pods that are disabled: " + disabledPods); - } - podsWithCapacity.removeAll(disabledPods); - } - } } else { if (s_logger.isDebugEnabled()) { s_logger.debug("No pods found having a host with enough capacity, returning."); @@ -402,21 +393,6 @@ public class FirstFitPlanner extends AdapterBase implements DeploymentClusterPla prioritizedClusterIds.removeAll(avoid.getClustersToAvoid()); } - if (!isRootAdmin(vmProfile)) { - List disabledClusters = new ArrayList(); - if (isZone) { - disabledClusters = listDisabledClusters(plan.getDataCenterId(), null); - } else { - disabledClusters = listDisabledClusters(plan.getDataCenterId(), id); - } - if (!disabledClusters.isEmpty()) { - if (s_logger.isDebugEnabled()) { - s_logger.debug("Removing from the clusterId list these clusters that are disabled/clusters under disabled pods: " + disabledClusters); - } - prioritizedClusterIds.removeAll(disabledClusters); - } - } - removeClustersCrossingThreshold(prioritizedClusterIds, avoid, vmProfile, plan); String hostTagOnOffering = offering.getHostTag(); if (hostTagOnOffering != null) { @@ -465,21 +441,6 @@ public class FirstFitPlanner extends AdapterBase implements DeploymentClusterPla return podIdsByCapacity; } - private List listDisabledClusters(long zoneId, Long podId) { - List disabledClusters = clusterDao.listDisabledClusters(zoneId, podId); - if (podId == null) { - //list all disabled clusters under this zone + clusters under any disabled pod of this zone - List clustersWithDisabledPods = clusterDao.listClustersWithDisabledPods(zoneId); - disabledClusters.addAll(clustersWithDisabledPods); - } - return disabledClusters; - } - - private List listDisabledPods(long zoneId) { - List disabledPods = podDao.listDisabledPods(zoneId); - return disabledPods; - } - protected Pair, Map> listClustersByCapacity(long id, int requiredCpu, long requiredRam, ExcludeList avoid, boolean isZone) { //look at the aggregate available cpu and ram per cluster //although an aggregate value may be false indicator that a cluster can host a vm, it will at the least eliminate those clusters which definitely cannot diff --git a/server/src/test/java/com/cloud/vm/DeploymentPlanningManagerImplTest.java b/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java similarity index 64% rename from server/src/test/java/com/cloud/vm/DeploymentPlanningManagerImplTest.java rename to server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java index d356570b633..ab73e63f287 100644 --- a/server/src/test/java/com/cloud/vm/DeploymentPlanningManagerImplTest.java +++ b/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java @@ -14,7 +14,7 @@ // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. -package com.cloud.vm; +package com.cloud.deploy; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; @@ -28,18 +28,30 @@ import java.util.List; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.dc.DataCenter; import com.cloud.host.Host; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.dao.VMTemplateDao; +import com.cloud.user.AccountVO; +import com.cloud.user.dao.AccountDao; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachine.Type; +import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.VirtualMachineProfileImpl; import org.apache.cloudstack.affinity.dao.AffinityGroupDomainMapDao; +import org.apache.commons.collections.CollectionUtils; +import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InjectMocks; import org.mockito.Matchers; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import org.mockito.Spy; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan.Filter; @@ -73,15 +85,8 @@ import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; import com.cloud.dc.dao.DedicatedResourceDao; import com.cloud.dc.dao.HostPodDao; -import com.cloud.deploy.DataCenterDeployment; -import com.cloud.deploy.DeployDestination; -import com.cloud.deploy.DeploymentClusterPlanner; -import com.cloud.deploy.DeploymentPlanner; import com.cloud.deploy.DeploymentPlanner.ExcludeList; import com.cloud.deploy.DeploymentPlanner.PlannerResourceUsage; -import com.cloud.deploy.DeploymentPlanningManagerImpl; -import com.cloud.deploy.FirstFitPlanner; -import com.cloud.deploy.PlannerHostReservationVO; import com.cloud.deploy.dao.PlannerHostReservationDao; import com.cloud.exception.AffinityConflictException; import com.cloud.exception.InsufficientServerCapacityException; @@ -90,6 +95,7 @@ import com.cloud.host.dao.HostDao; import com.cloud.host.dao.HostTagsDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.resource.ResourceManager; +import com.cloud.org.Grouping.AllocationState; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDetailsDao; import com.cloud.storage.Storage.ProvisioningType; @@ -110,7 +116,8 @@ import com.cloud.host.dao.HostDetailsDao; @ContextConfiguration(loader = AnnotationConfigContextLoader.class) public class DeploymentPlanningManagerImplTest { - @Inject + @Spy + @InjectMocks DeploymentPlanningManagerImpl _dpm; @Inject @@ -119,6 +126,12 @@ public class DeploymentPlanningManagerImplTest { @Inject VirtualMachineProfileImpl vmProfile; + @Inject + private AccountDao accountDao; + + @Inject + private VMInstanceDao vmInstanceDao; + @Inject AffinityGroupVMMapDao _affinityGroupVMMapDao; @@ -146,11 +159,15 @@ public class DeploymentPlanningManagerImplTest { @Inject VMTemplateDao templateDao; + @Inject + HostPodDao hostPodDao; + @Mock Host host; private static long dataCenterId = 1L; private static long hostId = 1l; + private static final long ADMIN_ACCOUNT_ROLE_ID = 1l; @BeforeClass public static void setUp() throws ConfigurationException { @@ -189,6 +206,7 @@ public class DeploymentPlanningManagerImplTest { _dpm.setPlanners(planners); Mockito.when(host.getId()).thenReturn(hostId); + Mockito.doNothing().when(_dpm).avoidDisabledResources(vmProfile, dc, avoids); } @Test @@ -259,6 +277,184 @@ public class DeploymentPlanningManagerImplTest { assertFalse(_dpm.checkAffinity(host, Arrays.asList(3l, 4l, 2l))); } + @Test + public void routerInDisabledResourceAssertFalse() { + Assert.assertFalse(DeploymentPlanningManager.allowRouterOnDisabledResource.value()); + } + + @Test + public void adminVmInDisabledResourceAssertFalse() { + Assert.assertFalse(DeploymentPlanningManager.allowAdminVmOnDisabledResource.value()); + } + + @Test + public void avoidDisabledResourcesTestAdminAccount() { + Type[] vmTypes = VirtualMachine.Type.values(); + for (int i = 0; i < vmTypes.length - 1; ++i) { + Mockito.when(vmProfile.getType()).thenReturn(vmTypes[i]); + if (vmTypes[i].isUsedBySystem()) { + prepareAndVerifyAvoidDisabledResourcesTest(1, 0, 0, ADMIN_ACCOUNT_ROLE_ID, vmTypes[i], true, false); + } else { + prepareAndVerifyAvoidDisabledResourcesTest(0, 1, 1, ADMIN_ACCOUNT_ROLE_ID, vmTypes[i], true, false); + } + } + } + + @Test + public void avoidDisabledResourcesTestUserAccounAdminCannotDeployOnDisabled() { + Type[] vmTypes = VirtualMachine.Type.values(); + for (int i = 0; i < vmTypes.length - 1; ++i) { + Mockito.when(vmProfile.getType()).thenReturn(vmTypes[i]); + long userAccountId = ADMIN_ACCOUNT_ROLE_ID + 1; + if (vmTypes[i].isUsedBySystem()) { + prepareAndVerifyAvoidDisabledResourcesTest(1, 0, 0, userAccountId, vmTypes[i], true, false); + } else { + prepareAndVerifyAvoidDisabledResourcesTest(0, 0, 1, userAccountId, vmTypes[i], true, false); + } + } + } + + @Test + public void avoidDisabledResourcesTestUserAccounAdminCanDeployOnDisabled() { + Type[] vmTypes = VirtualMachine.Type.values(); + for (int i = 0; i < vmTypes.length - 1; ++i) { + Mockito.when(vmProfile.getType()).thenReturn(vmTypes[i]); + long userAccountId = ADMIN_ACCOUNT_ROLE_ID + 1; + if (vmTypes[i].isUsedBySystem()) { + prepareAndVerifyAvoidDisabledResourcesTest(1, 0, 0, userAccountId, vmTypes[i], true, true); + } else { + prepareAndVerifyAvoidDisabledResourcesTest(0, 0, 1, userAccountId, vmTypes[i], true, true); + } + } + } + + private void prepareAndVerifyAvoidDisabledResourcesTest(int timesRouter, int timesAdminVm, int timesDisabledResource, long roleId, Type vmType, boolean isSystemDepolyable, + boolean isAdminVmDeployable) { + Mockito.doReturn(isSystemDepolyable).when(_dpm).isRouterDeployableInDisabledResources(); + Mockito.doReturn(isAdminVmDeployable).when(_dpm).isAdminVmDeployableInDisabledResources(); + + VirtualMachineProfile vmProfile = Mockito.mock(VirtualMachineProfile.class); + DataCenter dc = Mockito.mock(DataCenter.class); + ExcludeList avoids = Mockito.mock(ExcludeList.class); + + Mockito.when(vmProfile.getType()).thenReturn(vmType); + Mockito.when(vmProfile.getId()).thenReturn(1l); + + Mockito.doNothing().when(_dpm).avoidDisabledDataCenters(dc, avoids); + Mockito.doNothing().when(_dpm).avoidDisabledPods(dc, avoids); + Mockito.doNothing().when(_dpm).avoidDisabledClusters(dc, avoids); + Mockito.doNothing().when(_dpm).avoidDisabledHosts(dc, avoids); + + VMInstanceVO vmInstanceVO = Mockito.mock(VMInstanceVO.class); + Mockito.when(vmInstanceDao.findById(Mockito.anyLong())).thenReturn(vmInstanceVO); + AccountVO owner = Mockito.mock(AccountVO.class); + Mockito.when(owner.getRoleId()).thenReturn(roleId); + Mockito.when(accountDao.findById(Mockito.anyLong())).thenReturn(owner); + + _dpm.avoidDisabledResources(vmProfile, dc, avoids); + + Mockito.verify(_dpm, Mockito.times(timesRouter)).isRouterDeployableInDisabledResources(); + Mockito.verify(_dpm, Mockito.times(timesAdminVm)).isAdminVmDeployableInDisabledResources(); + Mockito.verify(_dpm, Mockito.times(timesDisabledResource)).avoidDisabledDataCenters(dc, avoids); + Mockito.verify(_dpm, Mockito.times(timesDisabledResource)).avoidDisabledPods(dc, avoids); + Mockito.verify(_dpm, Mockito.times(timesDisabledResource)).avoidDisabledClusters(dc, avoids); + Mockito.verify(_dpm, Mockito.times(timesDisabledResource)).avoidDisabledHosts(dc, avoids); + Mockito.reset(_dpm); + } + + @Test + public void avoidDisabledDataCentersTest() { + DataCenter dc = Mockito.mock(DataCenter.class); + Mockito.when(dc.getId()).thenReturn(123l); + + ExcludeList avoids = new ExcludeList(); + AllocationState[] allocationStates = AllocationState.values(); + for (int i = 0; i < allocationStates.length - 1; ++i) { + Mockito.when(dc.getAllocationState()).thenReturn(allocationStates[i]); + + _dpm.avoidDisabledDataCenters(dc, avoids); + + if (allocationStates[i] == AllocationState.Disabled) { + assertAvoidIsEmpty(avoids, false, true, true, true); + Assert.assertTrue(avoids.getDataCentersToAvoid().size() == 1); + Assert.assertTrue(avoids.getDataCentersToAvoid().contains(dc.getId())); + } else { + assertAvoidIsEmpty(avoids, true, true, true, true); + } + } + } + + @Test + public void avoidDisabledPodsTestNoDisabledPod() { + DataCenter dc = Mockito.mock(DataCenter.class); + List podIds = new ArrayList<>(); + long expectedPodId = 123l; + podIds.add(expectedPodId); + Mockito.doReturn(new ArrayList<>()).when(hostPodDao).listDisabledPods(Mockito.anyLong()); + ExcludeList avoids = new ExcludeList(); + + _dpm.avoidDisabledPods(dc, avoids); + assertAvoidIsEmpty(avoids, true, true, true, true); + } + + @Test + public void avoidDisabledPodsTestHasDisabledPod() { + DataCenter dc = Mockito.mock(DataCenter.class); + List podIds = new ArrayList<>(); + long expectedPodId = 123l; + podIds.add(expectedPodId); + Mockito.doReturn(podIds).when(hostPodDao).listDisabledPods(Mockito.anyLong()); + + ExcludeList avoids = new ExcludeList(); + + _dpm.avoidDisabledPods(dc, avoids); + assertAvoidIsEmpty(avoids, true, false, true, true); + Assert.assertTrue(avoids.getPodsToAvoid().size() == 1); + Assert.assertTrue(avoids.getPodsToAvoid().contains(expectedPodId)); + } + + @Test + public void avoidDisabledClustersTestNoDisabledCluster() { + DataCenter dc = prepareAvoidDisabledTests(); + Mockito.doReturn(new ArrayList<>()).when(_clusterDao).listDisabledClusters(Mockito.anyLong(), Mockito.anyLong()); + ExcludeList avoids = new ExcludeList(); + + _dpm.avoidDisabledClusters(dc, avoids); + assertAvoidIsEmpty(avoids, true, true, true, true); + } + + @Test + public void avoidDisabledClustersTestHasDisabledCluster() { + DataCenter dc = prepareAvoidDisabledTests(); + long expectedClusterId = 123l; + List disabledClusters = new ArrayList<>(); + disabledClusters.add(expectedClusterId); + Mockito.doReturn(disabledClusters).when(_clusterDao).listDisabledClusters(Mockito.anyLong(), Mockito.anyLong()); + ExcludeList avoids = new ExcludeList(); + + _dpm.avoidDisabledClusters(dc, avoids); + + assertAvoidIsEmpty(avoids, true, true, false, true); + Assert.assertTrue(avoids.getClustersToAvoid().size() == 1); + Assert.assertTrue(avoids.getClustersToAvoid().contains(expectedClusterId)); + } + + private DataCenter prepareAvoidDisabledTests() { + DataCenter dc = Mockito.mock(DataCenter.class); + Mockito.when(dc.getId()).thenReturn(123l); + List podIds = new ArrayList<>(); + podIds.add(1l); + Mockito.doReturn(podIds).when(hostPodDao).listAllPods(Mockito.anyLong()); + return dc; + } + + private void assertAvoidIsEmpty(ExcludeList avoids, boolean isDcEmpty, boolean isPodsEmpty, boolean isClustersEmpty, boolean isHostsEmpty) { + Assert.assertEquals(isDcEmpty, CollectionUtils.isEmpty(avoids.getDataCentersToAvoid())); + Assert.assertEquals(isPodsEmpty, CollectionUtils.isEmpty(avoids.getPodsToAvoid())); + Assert.assertEquals(isClustersEmpty, CollectionUtils.isEmpty(avoids.getClustersToAvoid())); + Assert.assertEquals(isHostsEmpty, CollectionUtils.isEmpty(avoids.getHostsToAvoid())); + } + @Configuration @ComponentScan(basePackageClasses = {DeploymentPlanningManagerImpl.class}, includeFilters = {@Filter(value = TestConfiguration.Library.class, type = FilterType.CUSTOM)}, useDefaultFilters = false) @@ -461,10 +657,15 @@ public class DeploymentPlanningManagerImplTest { } @Bean - public HostGpuGroupsDao hostGpuGroupsDap() { + public HostGpuGroupsDao hostGpuGroupsDao() { return Mockito.mock(HostGpuGroupsDao.class); } + @Bean + public AccountDao accountDao() { + return Mockito.mock(AccountDao.class); + } + @Bean public VMTemplateDao vmTemplateDao() { return Mockito.mock(VMTemplateDao.class);