This is an automated email from the ASF dual-hosted git repository.
dahn pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/master by this push:
new a3cdd1f Allow deploy Admin VMs and VRs in disabled
zones/pods/clusters (#3600)
a3cdd1f is described below
commit a3cdd1f836e40a4b4444af738780a337ee7aac1d
Author: Gabriel Beims Bräscher <[email protected]>
AuthorDate: Fri May 28 05:45:30 2021 -0300
Allow deploy Admin VMs and VRs in disabled zones/pods/clusters (#3600)
---
.../cloud/deploy/DeploymentPlanningManager.java | 8 +
.../src/main/java/com/cloud/host/dao/HostDao.java | 13 ++
.../main/java/com/cloud/host/dao/HostDaoImpl.java | 20 +-
.../src/main/java/com/cloud/vm/VMInstanceVO.java | 2 +-
.../allocator/impl/RecreateHostAllocator.java | 15 --
.../deploy/DeploymentPlanningManagerImpl.java | 153 +++++++++++---
.../java/com/cloud/deploy/FirstFitPlanner.java | 39 ----
.../DeploymentPlanningManagerImplTest.java | 221 ++++++++++++++++++++-
8 files changed, 372 insertions(+), 99 deletions(-)
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 2266cd5..d3a5f7f 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<Boolean> allowRouterOnDisabledResource = new
ConfigKey<Boolean>("Advanced", Boolean.class,
"allow.router.on.disabled.resources", "false",
+ "Allow deploying VR in disabled Zones, Pods, and Clusters", true);
+
+ static final ConfigKey<Boolean> allowAdminVmOnDisabledResource = new
ConfigKey<Boolean>("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 3d76c8b..af06fcc 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<HostVO, Long>,
StateDao<Status, Stat
List<HostVO> findByClusterId(Long clusterId);
+ /**
+ * Returns hosts that are 'Up' and 'Enabled' from the given Data
Center/Zone
+ */
List<HostVO> 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<HostVO> listByDataCenterIdAndState(long id, ResourceState state);
+
+ /**
+ * Returns hosts that are 'Up' and 'Disabled' from the given Data
Center/Zone
+ */
+ List<HostVO> listDisabledByDataCenterId(long id);
+
List<HostVO> listByDataCenterIdAndHypervisorType(long zoneId,
Hypervisor.HypervisorType hypervisorType);
List<Long> 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 e931f65..e58df1d 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<HostVO,
Long> implements HostDao
@Override
public List<HostVO> listByDataCenterId(long id) {
+ return listByDataCenterIdAndState(id, ResourceState.Enabled);
+ }
+
+ @Override
+ public List<HostVO> listByDataCenterIdAndState(long id, ResourceState
state) {
+ SearchCriteria<HostVO> sc = scHostsFromZoneUpRouting(id);
+ sc.setParameters("resourceState", state);
+ return listBy(sc);
+ }
+
+ @Override
+ public List<HostVO> listDisabledByDataCenterId(long id) {
+ return listByDataCenterIdAndState(id, ResourceState.Disabled);
+ }
+
+ private SearchCriteria<HostVO> scHostsFromZoneUpRouting(long id) {
SearchCriteria<HostVO> 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 47932c9..0e8dd4e 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<State, Vi
this.diskOfferingId = diskOfferingId;
}
- protected VMInstanceVO() {
+ public VMInstanceVO() {
}
public Date getRemoved() {
diff --git
a/server/src/main/java/com/cloud/agent/manager/allocator/impl/RecreateHostAllocator.java
b/server/src/main/java/com/cloud/agent/manager/allocator/impl/RecreateHostAllocator.java
index a4797c1..be6f401 100644
---
a/server/src/main/java/com/cloud/agent/manager/allocator/impl/RecreateHostAllocator.java
+++
b/server/src/main/java/com/cloud/agent/manager/allocator/impl/RecreateHostAllocator.java
@@ -45,7 +45,6 @@ import com.cloud.deploy.DeploymentPlanner.ExcludeList;
import com.cloud.host.Host;
import com.cloud.host.Host.Type;
import com.cloud.host.dao.HostDao;
-import com.cloud.org.Grouping;
import com.cloud.resource.ResourceManager;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.VolumeDao;
@@ -122,21 +121,7 @@ public class RecreateHostAllocator extends
FirstFitRoutingAllocator {
}
for (PodCluster p : pcs) {
- if (p.getPod().getAllocationState() !=
Grouping.AllocationState.Enabled) {
- if (s_logger.isDebugEnabled()) {
- s_logger.debug("Pod name: " + p.getPod().getName() + ",
podId: " + p.getPod().getId() + " is in " +
p.getPod().getAllocationState().name() +
- " state, skipping this and trying other pods");
- }
- continue;
- }
Long clusterId = p.getCluster() == null ? null :
p.getCluster().getId();
- if (p.getCluster() != null && p.getCluster().getAllocationState()
!= Grouping.AllocationState.Enabled) {
- if (s_logger.isDebugEnabled()) {
- s_logger.debug("Cluster name: " + p.getCluster().getName()
+ ", clusterId: " + clusterId + " is in " +
p.getCluster().getAllocationState().name() +
- " state, skipping this and trying other pod-clusters");
- }
- continue;
- }
DataCenterDeployment newPlan = new
DataCenterDeployment(plan.getDataCenterId(), p.getPod().getId(), clusterId,
null, null, null);
hosts = super.allocateTo(vm, newPlan, type, avoid, returnUpTo);
if (hosts != null && !hosts.isEmpty()) {
diff --git
a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java
b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java
index fed29aa..7a939ae 100644
--- a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java
+++ b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java
@@ -31,6 +31,20 @@ import javax.inject.Inject;
import javax.naming.ConfigurationException;
import org.apache.cloudstack.affinity.AffinityGroupDomainMapVO;
+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.utils.StringUtils;
+import com.cloud.exception.StorageUnavailableException;
+import com.cloud.utils.db.Filter;
+import com.cloud.utils.fsm.StateMachine2;
+
+import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.framework.config.Configurable;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.MapUtils;
+import org.apache.log4j.Logger;
import org.apache.cloudstack.affinity.AffinityGroupProcessor;
import org.apache.cloudstack.affinity.AffinityGroupService;
import org.apache.cloudstack.affinity.AffinityGroupVMMapVO;
@@ -50,9 +64,6 @@ import
org.apache.cloudstack.managed.context.ManagedContextTimerTask;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.utils.identity.ManagementServerNode;
-import org.apache.commons.collections.CollectionUtils;
-import org.apache.commons.collections.MapUtils;
-import org.apache.log4j.Logger;
import com.cloud.agent.AgentManager;
import com.cloud.agent.Listener;
@@ -84,7 +95,6 @@ import com.cloud.deploy.dao.PlannerHostReservationDao;
import com.cloud.exception.AffinityConflictException;
import com.cloud.exception.ConnectionException;
import com.cloud.exception.InsufficientServerCapacityException;
-import com.cloud.exception.StorageUnavailableException;
import com.cloud.gpu.GPU;
import com.cloud.host.DetailVO;
import com.cloud.host.Host;
@@ -97,7 +107,6 @@ import com.cloud.offering.ServiceOffering;
import com.cloud.org.Cluster;
import com.cloud.org.Grouping;
import com.cloud.resource.ResourceManager;
-import com.cloud.resource.ResourceState;
import com.cloud.service.ServiceOfferingDetailsVO;
import com.cloud.service.dao.ServiceOfferingDetailsDao;
import com.cloud.storage.DiskOfferingVO;
@@ -107,31 +116,26 @@ import com.cloud.storage.Storage;
import com.cloud.storage.StorageManager;
import com.cloud.storage.StoragePool;
import com.cloud.storage.StoragePoolHostVO;
-import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.Volume;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.DiskOfferingDao;
import com.cloud.storage.dao.GuestOSCategoryDao;
import com.cloud.storage.dao.GuestOSDao;
import com.cloud.storage.dao.StoragePoolHostDao;
-import com.cloud.storage.dao.VMTemplateDao;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.user.AccountManager;
import com.cloud.utils.DateUtil;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.Pair;
-import com.cloud.utils.StringUtils;
import com.cloud.utils.component.Manager;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.db.DB;
-import com.cloud.utils.db.Filter;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionCallback;
import com.cloud.utils.db.TransactionStatus;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.fsm.StateListener;
-import com.cloud.utils.fsm.StateMachine2;
import com.cloud.vm.DiskProfile;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.VirtualMachine;
@@ -144,12 +148,14 @@ import com.cloud.vm.dao.VMInstanceDao;
import static com.cloud.utils.NumbersUtil.toHumanReadableSize;
public class DeploymentPlanningManagerImpl extends ManagerBase implements
DeploymentPlanningManager, Manager, Listener,
-StateListener<State, VirtualMachine.Event, VirtualMachine> {
+StateListener<State, VirtualMachine.Event, VirtualMachine>, 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<State, VirtualMachine.Event, VirtualMachine> {
@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<State, VirtualMachine.Event, VirtualMachine> {
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<State, VirtualMachine.Event, VirtualMachine>
{
}
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<State, VirtualMachine.Event, VirtualMachine>
{
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<State, VirtualMachine.Event, VirtualMachine>
{
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<HostVO> 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<Long> pods = _podDao.listAllPods(dc.getId());
+ for (Long podId : pods) {
+ List<Long> 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<Long> 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<State, VirtualMachine.Event,
VirtualMachine> {
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<State, VirtualMachine.Event,
VirtualMachine> {
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<Host> suitableHosts = findSuitableHosts(vmProfile,
potentialPlan, avoid, HostAllocator.RETURN_UPTO_ALL);
@@ -1151,9 +1235,6 @@ StateListener<State, VirtualMachine.Event,
VirtualMachine> {
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<State, VirtualMachine.Event,
VirtualMachine> {
}
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 a93da71..dbcfe4d 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<Long> 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<Long> disabledClusters = new ArrayList<Long>();
- 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<Long> listDisabledClusters(long zoneId, Long podId) {
- List<Long> disabledClusters = clusterDao.listDisabledClusters(zoneId,
podId);
- if (podId == null) {
- //list all disabled clusters under this zone + clusters under any
disabled pod of this zone
- List<Long> clustersWithDisabledPods =
clusterDao.listClustersWithDisabledPods(zoneId);
- disabledClusters.addAll(clustersWithDisabledPods);
- }
- return disabledClusters;
- }
-
- private List<Long> listDisabledPods(long zoneId) {
- List<Long> disabledPods = podDao.listDisabledPods(zoneId);
- return disabledPods;
- }
-
protected Pair<List<Long>, Map<Long, Double>> 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 d356570..ab73e63 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
@@ -120,6 +127,12 @@ public class DeploymentPlanningManagerImplTest {
VirtualMachineProfileImpl vmProfile;
@Inject
+ private AccountDao accountDao;
+
+ @Inject
+ private VMInstanceDao vmInstanceDao;
+
+ @Inject
AffinityGroupVMMapDao _affinityGroupVMMapDao;
@Inject
@@ -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<Long> 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<Long> 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<Long> 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<Long> 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,11 +657,16 @@ 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);
}