From 661088927d2138745109316d7688d0e1caee7de4 Mon Sep 17 00:00:00 2001 From: Prachi Damle Date: Fri, 12 Jul 2013 13:39:24 -0700 Subject: [PATCH] CLOUDSTACK-2155 Anti-Affinity -When Vm deployment is done in parallel , anti-affinity rule is not honored. Changes to check if the destination found does not conflict with any vm reservation --- .../affinity/AffinityGroupProcessor.java | 13 ++++ .../affinity/AffinityProcessorBase.java | 7 ++ .../cloud/entity/api/VMEntityManagerImpl.java | 68 +++++++++++-------- .../cloud/entity/api/db/VMComputeTagVO.java | 0 .../cloud/entity/api/db/VMEntityVO.java | 6 +- .../cloud/entity/api/db/VMNetworkMapVO.java | 0 .../cloud/entity/api/db/VMReservationVO.java | 0 .../cloud/entity/api/db/VMRootDiskTagVO.java | 0 .../entity/api/db/VolumeReservationVO.java | 0 .../entity/api/db/dao/VMComputeTagDao.java | 0 .../api/db/dao/VMComputeTagDaoImpl.java | 0 .../cloud/entity/api/db/dao/VMEntityDao.java | 0 .../entity/api/db/dao/VMEntityDaoImpl.java | 0 .../entity/api/db/dao/VMNetworkMapDao.java | 0 .../api/db/dao/VMNetworkMapDaoImpl.java | 0 .../entity/api/db/dao/VMReservationDao.java | 0 .../api/db/dao/VMReservationDaoImpl.java | 0 .../entity/api/db/dao/VMRootDiskTagDao.java | 0 .../api/db/dao/VMRootDiskTagDaoImpl.java | 0 .../api/db/dao/VolumeReservationDao.java | 0 .../api/db/dao/VolumeReservationDaoImpl.java | 0 .../affinity/HostAntiAffinityProcessor.java | 51 ++++++++++++++ .../deploy/DeploymentPlanningManager.java | 4 ++ .../deploy/DeploymentPlanningManagerImpl.java | 22 ++++++ 24 files changed, 139 insertions(+), 32 deletions(-) rename engine/{orchestration => schema}/src/org/apache/cloudstack/engine/cloud/entity/api/db/VMComputeTagVO.java (100%) rename engine/{orchestration => schema}/src/org/apache/cloudstack/engine/cloud/entity/api/db/VMEntityVO.java (99%) rename engine/{orchestration => schema}/src/org/apache/cloudstack/engine/cloud/entity/api/db/VMNetworkMapVO.java (100%) rename engine/{orchestration => schema}/src/org/apache/cloudstack/engine/cloud/entity/api/db/VMReservationVO.java (100%) rename engine/{orchestration => schema}/src/org/apache/cloudstack/engine/cloud/entity/api/db/VMRootDiskTagVO.java (100%) rename engine/{orchestration => schema}/src/org/apache/cloudstack/engine/cloud/entity/api/db/VolumeReservationVO.java (100%) rename engine/{orchestration => schema}/src/org/apache/cloudstack/engine/cloud/entity/api/db/dao/VMComputeTagDao.java (100%) rename engine/{orchestration => schema}/src/org/apache/cloudstack/engine/cloud/entity/api/db/dao/VMComputeTagDaoImpl.java (100%) rename engine/{orchestration => schema}/src/org/apache/cloudstack/engine/cloud/entity/api/db/dao/VMEntityDao.java (100%) rename engine/{orchestration => schema}/src/org/apache/cloudstack/engine/cloud/entity/api/db/dao/VMEntityDaoImpl.java (100%) rename engine/{orchestration => schema}/src/org/apache/cloudstack/engine/cloud/entity/api/db/dao/VMNetworkMapDao.java (100%) rename engine/{orchestration => schema}/src/org/apache/cloudstack/engine/cloud/entity/api/db/dao/VMNetworkMapDaoImpl.java (100%) rename engine/{orchestration => schema}/src/org/apache/cloudstack/engine/cloud/entity/api/db/dao/VMReservationDao.java (100%) rename engine/{orchestration => schema}/src/org/apache/cloudstack/engine/cloud/entity/api/db/dao/VMReservationDaoImpl.java (100%) rename engine/{orchestration => schema}/src/org/apache/cloudstack/engine/cloud/entity/api/db/dao/VMRootDiskTagDao.java (100%) rename engine/{orchestration => schema}/src/org/apache/cloudstack/engine/cloud/entity/api/db/dao/VMRootDiskTagDaoImpl.java (100%) rename engine/{orchestration => schema}/src/org/apache/cloudstack/engine/cloud/entity/api/db/dao/VolumeReservationDao.java (100%) rename engine/{orchestration => schema}/src/org/apache/cloudstack/engine/cloud/entity/api/db/dao/VolumeReservationDaoImpl.java (100%) diff --git a/api/src/org/apache/cloudstack/affinity/AffinityGroupProcessor.java b/api/src/org/apache/cloudstack/affinity/AffinityGroupProcessor.java index 60b8e4c554f..e3a9b62c6ce 100644 --- a/api/src/org/apache/cloudstack/affinity/AffinityGroupProcessor.java +++ b/api/src/org/apache/cloudstack/affinity/AffinityGroupProcessor.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.affinity; +import com.cloud.deploy.DeployDestination; import com.cloud.deploy.DeploymentPlan; import com.cloud.deploy.DeploymentPlanner.ExcludeList; import com.cloud.exception.AffinityConflictException; @@ -46,4 +47,16 @@ public interface AffinityGroupProcessor extends Adapter { * @return String Affinity/Anti-affinity type */ String getType(); + + /** + * check() is called to see if the planned destination fits the group + * requirements + * + * @param vm + * virtual machine. + * @param plannedDestination + * deployment destination where VM is planned to be deployed + */ + boolean check(VirtualMachineProfile vm, DeployDestination plannedDestination) + throws AffinityConflictException; } \ No newline at end of file diff --git a/api/src/org/apache/cloudstack/affinity/AffinityProcessorBase.java b/api/src/org/apache/cloudstack/affinity/AffinityProcessorBase.java index 70ecd08350f..325ab80e047 100644 --- a/api/src/org/apache/cloudstack/affinity/AffinityProcessorBase.java +++ b/api/src/org/apache/cloudstack/affinity/AffinityProcessorBase.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.affinity; +import com.cloud.deploy.DeployDestination; import com.cloud.deploy.DeploymentPlan; import com.cloud.deploy.DeploymentPlanner.ExcludeList; import com.cloud.exception.AffinityConflictException; @@ -41,4 +42,10 @@ public class AffinityProcessorBase extends AdapterBase implements AffinityGroupP public void setType(String type) { _type = type; } + + @Override + public boolean check(VirtualMachineProfile vm, DeployDestination plannedDestination) + throws AffinityConflictException { + return true; + } } diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManagerImpl.java b/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManagerImpl.java index bd2ee328ef3..961d08523f0 100755 --- a/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManagerImpl.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManagerImpl.java @@ -16,7 +16,6 @@ // under the License. package org.apache.cloudstack.engine.cloud.entity.api; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -181,38 +180,49 @@ public class VMEntityManagerImpl implements VMEntityManager { } - DeployDestination dest; - try { - dest = _dpMgr.planDeployment(vmProfile, plan, exclude); - } catch (AffinityConflictException e) { - throw new CloudRuntimeException("Unable to create deployment, affinity rules associted to the VM conflict"); - } - - if (dest != null) { - //save destination with VMEntityVO - VMReservationVO vmReservation = new VMReservationVO(vm.getId(), dest.getDataCenter().getId(), dest.getPod().getId(), dest.getCluster().getId(), dest.getHost().getId()); - Map volumeReservationMap = new HashMap(); - - if (vm.getHypervisorType() != HypervisorType.BareMetal) { - for(Volume vo : dest.getStorageForDisks().keySet()){ - volumeReservationMap.put(vo.getId(), dest.getStorageForDisks().get(vo).getId()); - } - vmReservation.setVolumeReservation(volumeReservationMap); + while (true) { + DeployDestination dest = null; + try { + dest = _dpMgr.planDeployment(vmProfile, plan, exclude); + } catch (AffinityConflictException e) { + throw new CloudRuntimeException( + "Unable to create deployment, affinity rules associted to the VM conflict"); } - vmEntityVO.setVmReservation(vmReservation); - _vmEntityDao.persist(vmEntityVO); + if (dest != null) { + if (_dpMgr.finalizeReservation(dest, vmProfile, plan, exclude)) { + // save destination with VMEntityVO + VMReservationVO vmReservation = new VMReservationVO(vm.getId(), dest.getDataCenter().getId(), dest + .getPod().getId(), dest.getCluster().getId(), dest.getHost().getId()); + Map volumeReservationMap = new HashMap(); - return vmReservation.getUuid(); - } else if (planChangedByReadyVolume) { - // we could not reserve in the Volume's cluster - let the deploy - // call retry it. - return UUID.randomUUID().toString(); - }else{ - throw new InsufficientServerCapacityException("Unable to create a deployment for " + vmProfile, - DataCenter.class, plan.getDataCenterId(), areAffinityGroupsAssociated(vmProfile)); + if (vm.getHypervisorType() != HypervisorType.BareMetal) { + for (Volume vo : dest.getStorageForDisks().keySet()) { + volumeReservationMap.put(vo.getId(), dest.getStorageForDisks().get(vo).getId()); + } + vmReservation.setVolumeReservation(volumeReservationMap); + } + + vmEntityVO.setVmReservation(vmReservation); + _vmEntityDao.persist(vmEntityVO); + + return vmReservation.getUuid(); + } else { + try { + Thread.sleep(10000); + } catch (final InterruptedException e) { + } + continue; + } + } else if (planChangedByReadyVolume) { + // we could not reserve in the Volume's cluster - let the deploy + // call retry it. + return UUID.randomUUID().toString(); + } else { + throw new InsufficientServerCapacityException("Unable to create a deployment for " + vmProfile, + DataCenter.class, plan.getDataCenterId(), areAffinityGroupsAssociated(vmProfile)); + } } - } @Override diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/db/VMComputeTagVO.java b/engine/schema/src/org/apache/cloudstack/engine/cloud/entity/api/db/VMComputeTagVO.java similarity index 100% rename from engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/db/VMComputeTagVO.java rename to engine/schema/src/org/apache/cloudstack/engine/cloud/entity/api/db/VMComputeTagVO.java diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/db/VMEntityVO.java b/engine/schema/src/org/apache/cloudstack/engine/cloud/entity/api/db/VMEntityVO.java similarity index 99% rename from engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/db/VMEntityVO.java rename to engine/schema/src/org/apache/cloudstack/engine/cloud/entity/api/db/VMEntityVO.java index 4748215e3c6..b1df9675894 100644 --- a/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/db/VMEntityVO.java +++ b/engine/schema/src/org/apache/cloudstack/engine/cloud/entity/api/db/VMEntityVO.java @@ -5,7 +5,7 @@ // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, @@ -181,10 +181,10 @@ public class VMEntityVO implements VirtualMachine, FiniteStateObject vmProfile, DeploymentPlan plan, ExcludeList avoid) @@ -109,4 +117,47 @@ public class HostAntiAffinityProcessor extends AffinityProcessorBase implements return true; } + @DB + @Override + public boolean check(VirtualMachineProfile vmProfile, DeployDestination plannedDestination) + throws AffinityConflictException { + + if (plannedDestination.getHost() == null) { + return true; + } + long plannedHostId = plannedDestination.getHost().getId(); + + VirtualMachine vm = vmProfile.getVirtualMachine(); + + List vmGroupMappings = _affinityGroupVMMapDao.findByVmIdType(vm.getId(), getType()); + + for (AffinityGroupVMMapVO vmGroupMapping : vmGroupMappings) { + final Transaction txn = Transaction.currentTxn(); + try { + txn.start(); + // lock the group + AffinityGroupVO group = _affinityGroupDao.lockRow(vmGroupMapping.getAffinityGroupId(), true); + // if more than 1 VM's are present in the group then check for + // conflict due to parallel deployment + List groupVMIds = _affinityGroupVMMapDao.listVmIdsByAffinityGroup(vmGroupMapping + .getAffinityGroupId()); + groupVMIds.remove(vm.getId()); + + for (Long groupVMId : groupVMIds) { + VMReservationVO vmReservation = _reservationDao.findByVmId(groupVMId); + if (vmReservation.getHostId() != null && vmReservation.getHostId().equals(plannedHostId)) { + return false; + } + } + + return true; + + } finally { + txn.commit(); + } + } + + return true; + } + } diff --git a/server/src/com/cloud/deploy/DeploymentPlanningManager.java b/server/src/com/cloud/deploy/DeploymentPlanningManager.java index 13f1c67d613..8e8dc584744 100644 --- a/server/src/com/cloud/deploy/DeploymentPlanningManager.java +++ b/server/src/com/cloud/deploy/DeploymentPlanningManager.java @@ -42,4 +42,8 @@ public interface DeploymentPlanningManager extends Manager { */ DeployDestination planDeployment(VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoids) throws InsufficientServerCapacityException, AffinityConflictException; + + boolean finalizeReservation(DeployDestination plannedDestination, + VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoids) + throws InsufficientServerCapacityException, AffinityConflictException; } diff --git a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java index 142e63f2db0..5d2d7f226a4 100644 --- a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java +++ b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java @@ -1181,4 +1181,26 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy } return false; } + + @Override + public boolean finalizeReservation(DeployDestination plannedDestination, + VirtualMachineProfile vmProfile, DeploymentPlan plan, ExcludeList avoids) + throws InsufficientServerCapacityException, AffinityConflictException { + + VirtualMachine vm = vmProfile.getVirtualMachine(); + long vmGroupCount = _affinityGroupVMMapDao.countAffinityGroupsForVm(vm.getId()); + DataCenter dc = _dcDao.findById(vm.getDataCenterId()); + + if (vmGroupCount > 0) { + // uses affinity groups. For every group check if processor flags + // that the destination is ok + for (AffinityGroupProcessor processor : _affinityProcessors) { + if (!processor.check(vmProfile, plannedDestination)) { + return false; + } + } + } + + return true; + } }