diff --git a/api/src/main/java/com/cloud/deploy/DeploymentClusterPlanner.java b/api/src/main/java/com/cloud/deploy/DeploymentClusterPlanner.java index 9471c3d5c84c..3d250b63cd3a 100644 --- a/api/src/main/java/com/cloud/deploy/DeploymentClusterPlanner.java +++ b/api/src/main/java/com/cloud/deploy/DeploymentClusterPlanner.java @@ -68,6 +68,14 @@ public interface DeploymentClusterPlanner extends DeploymentPlanner { ConfigKey.Kind.Select, "random,firstfit,userdispersing,firstfitleastconsumed"); + ConfigKey allowRoutersOnDedicatedResources = new ConfigKey<>( + "Advanced", + Boolean.class, + "allow.routers.on.dedicated.resources", + "false", + "Allow deploying virtual routers on dedicated Hosts, Clusters, Pods, and Zones", + true); + /** * This is called to determine list of possible clusters where a virtual * machine can be deployed. diff --git a/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDao.java b/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDao.java index 42313efa512f..9e2bb0f0039c 100644 --- a/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDao.java +++ b/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDao.java @@ -26,6 +26,7 @@ import com.cloud.utils.db.GenericDao; public interface CapacityDao extends GenericDao { + CapacityVO findByHostIdType(Long hostId, short capacityType); List listByHostIdTypes(Long hostId, List capacityTypes); @@ -40,7 +41,7 @@ public interface CapacityDao extends GenericDao { List findNonSharedStorageForClusterPodZone(Long zoneId, Long podId, Long clusterId); - Pair, Map> orderClustersByAggregateCapacity(long id, long vmId, short capacityType, boolean isZone); + Pair, Map> orderClustersByAggregateCapacity(long id, long vmId, Long ownerId, short capacityType, boolean isVr, boolean allowRoutersOnDedicatedResources, boolean isZone); Ternary findCapacityByZoneAndHostTag(Long zoneId, String hostTag); diff --git a/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDaoImpl.java b/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDaoImpl.java index f65c3cf188cd..455fc929f09e 100644 --- a/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/capacity/dao/CapacityDaoImpl.java @@ -87,6 +87,13 @@ public class CapacityDaoImpl extends GenericDaoBase implements "LEFT JOIN dedicated_resources dr_cluster ON dr_cluster.cluster_id IS NOT NULL AND dr_cluster.cluster_id = host.cluster_id " + "LEFT JOIN dedicated_resources dr_host ON dr_host.host_id IS NOT NULL AND dr_host.host_id = host.id "; + private static final String ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_INCLUDE_DEDICATED_JOIN_1 = + "JOIN host ON capacity.host_id = host.id " + + "LEFT JOIN (SELECT affinity_group.id, agvm.instance_id FROM affinity_group_vm_map agvm JOIN affinity_group ON agvm.affinity_group_id = affinity_group.id AND affinity_group.type='ExplicitDedication') AS ag ON ag.instance_id = ? " + + "LEFT JOIN dedicated_resources dr_pod ON dr_pod.pod_id IS NOT NULL AND dr_pod.pod_id = host.pod_id AND dr_pod.account_id IS NOT NULL AND dr_pod.account_id != ownerId " + + "LEFT JOIN dedicated_resources dr_cluster ON dr_cluster.cluster_id IS NOT NULL AND dr_cluster.cluster_id = host.cluster_id AND dr_cluster.account_id IS NOT NULL AND dr_cluster.account_id != ownerId " + + "LEFT JOIN dedicated_resources dr_host ON dr_host.host_id IS NOT NULL AND dr_host.host_id = host.id AND dr_host.account_id IS NOT NULL AND dr_host.account_id != ownerId "; + private static final String ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_JOIN_2 = " AND ((ag.id IS NULL AND dr_pod.pod_id IS NULL AND dr_cluster.cluster_id IS NULL AND dr_host.host_id IS NULL) OR " + "(dr_pod.affinity_group_id = ag.id OR dr_cluster.affinity_group_id = ag.id OR dr_host.affinity_group_id = ag.id))"; @@ -986,7 +993,7 @@ public boolean removeBy(Short capacityType, Long zoneId, Long podId, Long cluste } @Override - public Pair, Map> orderClustersByAggregateCapacity(long id, long vmId, short capacityTypeForOrdering, boolean isZone) { + public Pair, Map> orderClustersByAggregateCapacity(long id, long vmId, Long ownerId, short capacityTypeForOrdering, boolean isVr, boolean allowRoutersOnDedicatedResources, boolean isZone) { TransactionLegacy txn = TransactionLegacy.currentTxn(); PreparedStatement pstmt = null; List result = new ArrayList(); @@ -998,7 +1005,12 @@ public Pair, Map> orderClustersByAggregateCapacity(long sql.append(ORDER_CLUSTERS_BY_AGGREGATE_OVERCOMMIT_CAPACITY_PART1); } - sql.append(ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_JOIN_1); + if (isVr && allowRoutersOnDedicatedResources) { + sql.append(ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_INCLUDE_DEDICATED_JOIN_1.replace("ownerId", ownerId.toString())); + } else { + sql.append(ORDER_CLUSTERS_BY_AGGREGATE_CAPACITY_JOIN_1); + } + if (isZone) { sql.append("WHERE capacity.capacity_state = 'Enabled' AND capacity.data_center_id = ?"); } else { @@ -1290,5 +1302,4 @@ public float findClusterConsumption(Long clusterId, short capacityType, long com } return 0; } - } diff --git a/engine/schema/src/test/java/com/cloud/capacity/dao/CapacityDaoImplTest.java b/engine/schema/src/test/java/com/cloud/capacity/dao/CapacityDaoImplTest.java index f9528d5d57ff..1ececb2ea769 100644 --- a/engine/schema/src/test/java/com/cloud/capacity/dao/CapacityDaoImplTest.java +++ b/engine/schema/src/test/java/com/cloud/capacity/dao/CapacityDaoImplTest.java @@ -250,7 +250,7 @@ public void testOrderClustersByAggregateCapacityEmptyResult() throws Exception { when(pstmt.executeQuery()).thenReturn(resultSet); when(resultSet.next()).thenReturn(false); - Pair, Map> result = capacityDao.orderClustersByAggregateCapacity(1L, 1L, (short) 1, true); + Pair, Map> result = capacityDao.orderClustersByAggregateCapacity(1L, 1L, 1L, (short) 1, false, false, true); assertNotNull(result); assertTrue(result.first().isEmpty()); assertTrue(result.second().isEmpty()); diff --git a/plugins/deployment-planners/implicit-dedication/src/test/java/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java b/plugins/deployment-planners/implicit-dedication/src/test/java/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java index d859ebd0ffba..03b45c1c9a7d 100644 --- a/plugins/deployment-planners/implicit-dedication/src/test/java/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java +++ b/plugins/deployment-planners/implicit-dedication/src/test/java/org/apache/cloudstack/implicitplanner/ImplicitPlannerTest.java @@ -366,7 +366,7 @@ private void initializeForTest(VirtualMachineProfileImpl vmProfile, DataCenterDe clusterCapacityMap.put(2L, 2048D); clusterCapacityMap.put(3L, 2048D); Pair, Map> clustersOrderedByCapacity = new Pair, Map>(clustersWithEnoughCapacity, clusterCapacityMap); - when(capacityDao.orderClustersByAggregateCapacity(dataCenterId, 12L, Capacity.CAPACITY_TYPE_CPU, true)).thenReturn(clustersOrderedByCapacity); + when(capacityDao.orderClustersByAggregateCapacity(dataCenterId, 12L, 1L, Capacity.CAPACITY_TYPE_CPU, false, false, true)).thenReturn(clustersOrderedByCapacity); List disabledClusters = new ArrayList(); List clustersWithDisabledPods = new ArrayList(); diff --git a/server/src/main/java/com/cloud/deploy/FirstFitPlanner.java b/server/src/main/java/com/cloud/deploy/FirstFitPlanner.java index 3aab852ba7fc..25e62225900f 100644 --- a/server/src/main/java/com/cloud/deploy/FirstFitPlanner.java +++ b/server/src/main/java/com/cloud/deploy/FirstFitPlanner.java @@ -398,9 +398,11 @@ private List scanClustersForDestinationInZoneOrPod(long id, boolean isZone DataCenter dc = dcDao.findById(vm.getDataCenterId()); int requiredCpu = offering.getCpu() * offering.getSpeed(); long requiredRam = offering.getRamSize() * 1024L * 1024L; + boolean isVr = VirtualMachine.Type.DomainRouter.equals(vmProfile.getType()); + Long ownerId = vm.getAccountId(); //list clusters under this zone by cpu and ram capacity - Pair, Map> clusterCapacityInfo = listClustersByCapacity(id, vmProfile.getId(), requiredCpu, requiredRam, avoid, isZone); + Pair, Map> clusterCapacityInfo = listClustersByCapacity(id, vmProfile.getId(), ownerId, requiredCpu, requiredRam, isVr, isZone); List prioritizedClusterIds = clusterCapacityInfo.first(); if (!prioritizedClusterIds.isEmpty()) { if (avoid.getClustersToAvoid() != null) { @@ -458,7 +460,7 @@ protected List reorderPods(Pair, Map> podCapacity return podIdsByCapacity; } - protected Pair, Map> listClustersByCapacity(long id, long vmId, int requiredCpu, long requiredRam, ExcludeList avoid, boolean isZone) { + protected Pair, Map> listClustersByCapacity(long id, long vmId, Long ownerId, int requiredCpu, long requiredRam, boolean isVr, 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 @@ -474,7 +476,7 @@ protected Pair, Map> listClustersByCapacity(long id, lo } - Pair, Map> result = getOrderedClustersByCapacity(id, vmId, isZone); + Pair, Map> result = getOrderedClustersByCapacity(id, vmId, ownerId, isVr, isZone); List clusterIdsOrderedByAggregateCapacity = result.first(); //only keep the clusters that have enough capacity to host this VM if (logger.isTraceEnabled()) { @@ -554,14 +556,14 @@ public Map getPodByCombinedCapacities(List capacities, } - private Pair, Map> getOrderedClustersByCapacity(long id, long vmId, boolean isZone) { + private Pair, Map> getOrderedClustersByCapacity(long id, long vmId, Long ownerId, boolean isVr, boolean isZone) { double cpuToMemoryWeight = ConfigurationManager.HostCapacityTypeCpuMemoryWeight.value(); short capacityType = getHostCapacityTypeToOrderCluster( configDao.getValue(Config.HostCapacityTypeToOrderClusters.key()), cpuToMemoryWeight); logger.debug("CapacityType: {} is used for Cluster ordering", getCapacityTypeName(capacityType)); if (capacityType >= 0) { // for capacityType other than COMBINED - return capacityDao.orderClustersByAggregateCapacity(id, vmId, capacityType, isZone); + return capacityDao.orderClustersByAggregateCapacity(id, vmId, ownerId, capacityType, isVr, allowRoutersOnDedicatedResources.value(), isZone); } Long zoneId = isZone ? id : null; @@ -694,6 +696,6 @@ public String getConfigComponentName() { @Override public ConfigKey[] getConfigKeys() { - return new ConfigKey[] {ClusterCPUCapacityDisableThreshold, ClusterMemoryCapacityDisableThreshold, ClusterThresholdEnabled, VmAllocationAlgorithm}; + return new ConfigKey[] {ClusterCPUCapacityDisableThreshold, ClusterMemoryCapacityDisableThreshold, ClusterThresholdEnabled, VmAllocationAlgorithm, allowRoutersOnDedicatedResources}; } } diff --git a/server/src/test/java/com/cloud/vm/FirstFitPlannerTest.java b/server/src/test/java/com/cloud/vm/FirstFitPlannerTest.java index 5b877cc66169..43b917886b03 100644 --- a/server/src/test/java/com/cloud/vm/FirstFitPlannerTest.java +++ b/server/src/test/java/com/cloud/vm/FirstFitPlannerTest.java @@ -373,7 +373,7 @@ private void initializeForTest(VirtualMachineProfileImpl vmProfile, DataCenterDe clusterCapacityMap.put(6L, 2048D); Pair, Map> clustersOrderedByCapacity = new Pair, Map>(clustersWithEnoughCapacity, clusterCapacityMap); - when(capacityDao.orderClustersByAggregateCapacity(dataCenterId, 12L, Capacity.CAPACITY_TYPE_CPU, true)).thenReturn(clustersOrderedByCapacity); + when(capacityDao.orderClustersByAggregateCapacity(dataCenterId, 12L, 1L, Capacity.CAPACITY_TYPE_CPU, false, false, true)).thenReturn(clustersOrderedByCapacity); List disabledClusters = new ArrayList(); List clustersWithDisabledPods = new ArrayList();