CLOUDSTACK-2155: Anti-Affinity -When Vm deployment is done in parallel , anti-affinity rule is not honored.
Changes: - Locking the group and save reservation mechanism done by DPM - Added admin operation to cleanup VM reservations - DPM will also cleanup VM reservations on startup Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/fa9ca72f Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/fa9ca72f Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/fa9ca72f Branch: refs/heads/master Commit: fa9ca72f360959d84ea61b341c83bf0a1cd0ae6e Parents: 6610889 Author: Prachi Damle <pra...@cloud.com> Authored: Tue Jul 16 00:52:52 2013 -0700 Committer: Prachi Damle <pra...@cloud.com> Committed: Tue Jul 16 10:53:06 2013 -0700 ---------------------------------------------------------------------- api/src/com/cloud/event/EventTypes.java | 4 +- api/src/com/cloud/server/ManagementService.java | 14 +-- .../admin/resource/CleanVMReservationsCmd.java | 80 ++++++++++++++++ client/tomcatconf/commands.properties.in | 1 + .../cloud/entity/api/VMEntityManagerImpl.java | 33 ++----- .../affinity/dao/AffinityGroupVMMapDao.java | 3 +- .../affinity/dao/AffinityGroupVMMapDaoImpl.java | 17 +++- .../affinity/HostAntiAffinityProcessor.java | 36 +++---- .../cloud/deploy/DeploymentPlanningManager.java | 4 +- .../deploy/DeploymentPlanningManagerImpl.java | 98 ++++++++++++++++++-- .../com/cloud/server/ManagementServerImpl.java | 14 +++ .../vm/DeploymentPlanningManagerImplTest.java | 6 ++ 12 files changed, 239 insertions(+), 71 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cloudstack/blob/fa9ca72f/api/src/com/cloud/event/EventTypes.java ---------------------------------------------------------------------- diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index ed4ba12..ca764e9 100755 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -424,7 +424,7 @@ public class EventTypes { public static final String EVENT_AFFINITY_GROUP_ASSIGN = "AG.ASSIGN"; public static final String EVENT_AFFINITY_GROUP_REMOVE = "AG.REMOVE"; public static final String EVENT_VM_AFFINITY_GROUP_UPDATE = "VM.AG.UPDATE"; - + public static final String EVENT_INTERNAL_LB_VM_START = "INTERNALLBVM.START"; public static final String EVENT_INTERNAL_LB_VM_STOP = "INTERNALLBVM.STOP"; @@ -442,6 +442,8 @@ public class EventTypes { public static final String EVENT_DEDICATE_RESOURCE = "DEDICATE.RESOURCE"; public static final String EVENT_DEDICATE_RESOURCE_RELEASE = "DEDICATE.RESOURCE.RELEASE"; + public static final String EVENT_CLEANUP_VM_RESERVATION = "VM.RESERVATION.CLEANUP"; + static { // TODO: need a way to force author adding event types to declare the entity details as well, with out braking http://git-wip-us.apache.org/repos/asf/cloudstack/blob/fa9ca72f/api/src/com/cloud/server/ManagementService.java ---------------------------------------------------------------------- diff --git a/api/src/com/cloud/server/ManagementService.java b/api/src/com/cloud/server/ManagementService.java index e943a8c..7591ab1 100755 --- a/api/src/com/cloud/server/ManagementService.java +++ b/api/src/com/cloud/server/ManagementService.java @@ -16,12 +16,9 @@ // under the License. package com.cloud.server; -import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.Set; - import com.cloud.exception.*; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.command.admin.cluster.ListClustersCmd; @@ -43,18 +40,12 @@ import org.apache.cloudstack.api.command.user.event.ArchiveEventsCmd; import org.apache.cloudstack.api.command.user.event.DeleteEventsCmd; import org.apache.cloudstack.api.command.user.guest.ListGuestOsCategoriesCmd; import org.apache.cloudstack.api.command.user.guest.ListGuestOsCmd; -import org.apache.cloudstack.api.command.user.iso.ListIsosCmd; -import org.apache.cloudstack.api.command.user.iso.UpdateIsoCmd; import org.apache.cloudstack.api.command.user.ssh.CreateSSHKeyPairCmd; import org.apache.cloudstack.api.command.user.ssh.DeleteSSHKeyPairCmd; import org.apache.cloudstack.api.command.user.ssh.ListSSHKeyPairsCmd; import org.apache.cloudstack.api.command.user.ssh.RegisterSSHKeyPairCmd; -import org.apache.cloudstack.api.command.user.template.ListTemplatesCmd; -import org.apache.cloudstack.api.command.user.template.UpdateTemplateCmd; import org.apache.cloudstack.api.command.user.vm.GetVMPasswordCmd; import org.apache.cloudstack.api.command.user.vmgroup.UpdateVMGroupCmd; -import org.apache.cloudstack.api.command.user.volume.ExtractVolumeCmd; - import com.cloud.alert.Alert; import com.cloud.capacity.Capacity; import com.cloud.configuration.Configuration; @@ -69,7 +60,6 @@ import com.cloud.org.Cluster; import com.cloud.storage.GuestOS; import com.cloud.storage.GuestOsCategory; import com.cloud.storage.StoragePool; -import com.cloud.template.VirtualMachineTemplate; import com.cloud.user.SSHKeyPair; import com.cloud.utils.Pair; import com.cloud.utils.Ternary; @@ -371,6 +361,8 @@ public interface ManagementService { List<String> listDeploymentPlanners(); VirtualMachine upgradeSystemVM(ScaleSystemVMCmd cmd) throws ResourceUnavailableException, ManagementServerException, VirtualMachineMigrationException, ConcurrentOperationException; - + boolean getExecuteInSequence(); + + void cleanupVMReservations(); } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/fa9ca72f/api/src/org/apache/cloudstack/api/command/admin/resource/CleanVMReservationsCmd.java ---------------------------------------------------------------------- diff --git a/api/src/org/apache/cloudstack/api/command/admin/resource/CleanVMReservationsCmd.java b/api/src/org/apache/cloudstack/api/command/admin/resource/CleanVMReservationsCmd.java new file mode 100644 index 0000000..b0be7b2 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/admin/resource/CleanVMReservationsCmd.java @@ -0,0 +1,80 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// 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, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.admin.resource; + +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.log4j.Logger; + +import com.cloud.event.EventTypes; +import com.cloud.user.Account; +import com.cloud.user.UserContext; + +@APICommand(name = "cleanVMReservations", description = "Cleanups VM reservations in the database.", responseObject = SuccessResponse.class) +public class CleanVMReservationsCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(CleanVMReservationsCmd.class.getName()); + + private static final String s_name = "cleanvmreservationresponse"; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + @Override + public String getCommandName() { + return s_name; + } + + @Override + public long getEntityOwnerId() { + Account account = UserContext.current().getCaller(); + if (account != null) { + return account.getId(); + } + + return Account.ACCOUNT_ID_SYSTEM; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_CLEANUP_VM_RESERVATION; + } + + @Override + public String getEventDescription() { + return "cleaning vm reservations in database"; + } + + @Override + public void execute(){ + try { + _mgr.cleanupVMReservations(); + SuccessResponse response = new SuccessResponse(getCommandName()); + this.setResponseObject(response); + } catch (Exception ex) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to clean vm reservations"); + } + } +} http://git-wip-us.apache.org/repos/asf/cloudstack/blob/fa9ca72f/client/tomcatconf/commands.properties.in ---------------------------------------------------------------------- diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index ad70471..d8d176f 100644 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -214,6 +214,7 @@ ldapConfig=1 ldapRemove=1 listCapabilities=15 listDeploymentPlanners=1 +cleanVMReservations=1 #### pod commands createPod=1 http://git-wip-us.apache.org/repos/asf/cloudstack/blob/fa9ca72f/engine/orchestration/src/org/apache/cloudstack/engine/cloud/entity/api/VMEntityManagerImpl.java ---------------------------------------------------------------------- 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 961d085..0822b92 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.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; @@ -30,6 +29,7 @@ import org.apache.cloudstack.engine.cloud.entity.api.db.dao.VMEntityDao; import org.apache.cloudstack.engine.cloud.entity.api.db.dao.VMReservationDao; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; +import org.apache.log4j.Logger; import org.springframework.stereotype.Component; import com.cloud.dc.DataCenter; @@ -46,12 +46,10 @@ import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InsufficientServerCapacityException; import com.cloud.exception.OperationTimedoutException; import com.cloud.exception.ResourceUnavailableException; -import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.network.dao.NetworkDao; import com.cloud.org.Cluster; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.StoragePool; -import com.cloud.storage.Volume; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.dao.VMTemplateDao; @@ -69,6 +67,8 @@ import com.cloud.vm.dao.VMInstanceDao; @Component public class VMEntityManagerImpl implements VMEntityManager { + private static final Logger s_logger = Logger.getLogger(VMEntityManagerImpl.class); + @Inject protected VMInstanceDao _vmDao; @Inject @@ -190,28 +190,15 @@ public class VMEntityManagerImpl implements VMEntityManager { } 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<Long, Long> volumeReservationMap = new HashMap<Long, Long>(); - - 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(); + String reservationId = _dpMgr.finalizeReservation(dest, vmProfile, plan, exclude); + if(reservationId != null){ + return reservationId; } else { - try { - Thread.sleep(10000); - } catch (final InterruptedException e) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Cannot finalize the VM reservation for this destination found, retrying"); } + + exclude.addHost(dest.getHost().getId()); continue; } } else if (planChangedByReadyVolume) { http://git-wip-us.apache.org/repos/asf/cloudstack/blob/fa9ca72f/engine/schema/src/org/apache/cloudstack/affinity/dao/AffinityGroupVMMapDao.java ---------------------------------------------------------------------- diff --git a/engine/schema/src/org/apache/cloudstack/affinity/dao/AffinityGroupVMMapDao.java b/engine/schema/src/org/apache/cloudstack/affinity/dao/AffinityGroupVMMapDao.java index f2951bc..5e62b17 100644 --- a/engine/schema/src/org/apache/cloudstack/affinity/dao/AffinityGroupVMMapDao.java +++ b/engine/schema/src/org/apache/cloudstack/affinity/dao/AffinityGroupVMMapDao.java @@ -23,7 +23,6 @@ import org.apache.cloudstack.affinity.AffinityGroupVMMapVO; import com.cloud.utils.Pair; import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDao; -import com.cloud.vm.VirtualMachine.State; public interface AffinityGroupVMMapDao extends GenericDao<AffinityGroupVMMapVO, Long> { @@ -44,4 +43,6 @@ public interface AffinityGroupVMMapDao extends GenericDao<AffinityGroupVMMapVO, List<AffinityGroupVMMapVO> findByVmIdType(long instanceId, String type); void updateMap(Long vmId, List<Long> affinityGroupIds); + + List<Long> listAffinityGroupIdsByVmId(long instanceId); } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/fa9ca72f/engine/schema/src/org/apache/cloudstack/affinity/dao/AffinityGroupVMMapDaoImpl.java ---------------------------------------------------------------------- diff --git a/engine/schema/src/org/apache/cloudstack/affinity/dao/AffinityGroupVMMapDaoImpl.java b/engine/schema/src/org/apache/cloudstack/affinity/dao/AffinityGroupVMMapDaoImpl.java index e03e73c..89dfa5a 100644 --- a/engine/schema/src/org/apache/cloudstack/affinity/dao/AffinityGroupVMMapDaoImpl.java +++ b/engine/schema/src/org/apache/cloudstack/affinity/dao/AffinityGroupVMMapDaoImpl.java @@ -24,9 +24,6 @@ import javax.inject.Inject; import org.apache.cloudstack.affinity.AffinityGroupVMMapVO; import org.apache.cloudstack.affinity.AffinityGroupVO; -import org.springframework.stereotype.Component; - -import com.cloud.host.HostTagVO; import com.cloud.utils.Pair; import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; @@ -46,6 +43,7 @@ public class AffinityGroupVMMapDaoImpl extends GenericDaoBase<AffinityGroupVMMap private GenericSearchBuilder<AffinityGroupVMMapVO, Long> ListVmIdByAffinityGroup; private SearchBuilder<AffinityGroupVMMapVO> ListByAffinityGroup; private SearchBuilder<AffinityGroupVMMapVO> ListByVmIdType; + private GenericSearchBuilder<AffinityGroupVMMapVO, Long> ListAffinityGroupIdByVm; @Inject protected AffinityGroupDao _affinityGroupDao; @@ -87,6 +85,12 @@ public class AffinityGroupVMMapDaoImpl extends GenericDaoBase<AffinityGroupVMMap CountSGForVm.select(null, Func.COUNT, null); CountSGForVm.and("vmId", CountSGForVm.entity().getInstanceId(), SearchCriteria.Op.EQ); CountSGForVm.done(); + + ListAffinityGroupIdByVm = createSearchBuilder(Long.class); + ListAffinityGroupIdByVm.and("instanceId", ListAffinityGroupIdByVm.entity().getInstanceId(), + SearchCriteria.Op.EQ); + ListAffinityGroupIdByVm.selectField(ListAffinityGroupIdByVm.entity().getAffinityGroupId()); + ListAffinityGroupIdByVm.done(); } @Override @@ -148,6 +152,13 @@ public class AffinityGroupVMMapDaoImpl extends GenericDaoBase<AffinityGroupVMMap } @Override + public List<Long> listAffinityGroupIdsByVmId(long instanceId) { + SearchCriteria<Long> sc = ListAffinityGroupIdByVm.create(); + sc.setParameters("instanceId", instanceId); + return customSearchIncludingRemoved(sc, null); + } + + @Override public void updateMap(Long vmId, List<Long> affinityGroupIds) { Transaction txn = Transaction.currentTxn(); txn.start(); http://git-wip-us.apache.org/repos/asf/cloudstack/blob/fa9ca72f/plugins/affinity-group-processors/host-anti-affinity/src/org/apache/cloudstack/affinity/HostAntiAffinityProcessor.java ---------------------------------------------------------------------- diff --git a/plugins/affinity-group-processors/host-anti-affinity/src/org/apache/cloudstack/affinity/HostAntiAffinityProcessor.java b/plugins/affinity-group-processors/host-anti-affinity/src/org/apache/cloudstack/affinity/HostAntiAffinityProcessor.java index e3c27f0..1569c7e 100644 --- a/plugins/affinity-group-processors/host-anti-affinity/src/org/apache/cloudstack/affinity/HostAntiAffinityProcessor.java +++ b/plugins/affinity-group-processors/host-anti-affinity/src/org/apache/cloudstack/affinity/HostAntiAffinityProcessor.java @@ -117,7 +117,6 @@ public class HostAntiAffinityProcessor extends AffinityProcessorBase implements return true; } - @DB @Override public boolean check(VirtualMachineProfile<? extends VirtualMachine> vmProfile, DeployDestination plannedDestination) throws AffinityConflictException { @@ -132,31 +131,24 @@ public class HostAntiAffinityProcessor extends AffinityProcessorBase implements List<AffinityGroupVMMapVO> 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<Long> 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; + // if more than 1 VM's are present in the group then check for + // conflict due to parallel deployment + List<Long> groupVMIds = _affinityGroupVMMapDao + .listVmIdsByAffinityGroup(vmGroupMapping.getAffinityGroupId()); + groupVMIds.remove(vm.getId()); + + for (Long groupVMId : groupVMIds) { + VMReservationVO vmReservation = _reservationDao.findByVmId(groupVMId); + if (vmReservation != null && vmReservation.getHostId() != null + && vmReservation.getHostId().equals(plannedHostId)) { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Planned destination for VM " + vm.getId() + " conflicts with an existing VM " + + vmReservation.getVmId() + " reserved on the same host " + plannedHostId); } + return false; } - - return true; - - } finally { - txn.commit(); } } - return true; } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/fa9ca72f/server/src/com/cloud/deploy/DeploymentPlanningManager.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/deploy/DeploymentPlanningManager.java b/server/src/com/cloud/deploy/DeploymentPlanningManager.java index 8e8dc58..9458df2 100644 --- a/server/src/com/cloud/deploy/DeploymentPlanningManager.java +++ b/server/src/com/cloud/deploy/DeploymentPlanningManager.java @@ -43,7 +43,9 @@ public interface DeploymentPlanningManager extends Manager { DeployDestination planDeployment(VirtualMachineProfile<? extends VirtualMachine> vmProfile, DeploymentPlan plan, ExcludeList avoids) throws InsufficientServerCapacityException, AffinityConflictException; - boolean finalizeReservation(DeployDestination plannedDestination, + String finalizeReservation(DeployDestination plannedDestination, VirtualMachineProfile<? extends VirtualMachine> vmProfile, DeploymentPlan plan, ExcludeList avoids) throws InsufficientServerCapacityException, AffinityConflictException; + + void cleanupVMReservations(); } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/fa9ca72f/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java index 5d2d7f2..ebf2b0c 100644 --- a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java +++ b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java @@ -31,9 +31,12 @@ import javax.naming.ConfigurationException; import org.apache.cloudstack.affinity.AffinityGroupProcessor; import org.apache.cloudstack.affinity.AffinityGroupVMMapVO; +import org.apache.cloudstack.affinity.AffinityGroupVO; import org.apache.cloudstack.affinity.dao.AffinityGroupDao; import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; +import org.apache.cloudstack.engine.cloud.entity.api.db.VMReservationVO; +import org.apache.cloudstack.engine.cloud.entity.api.db.dao.VMReservationDao; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator; import org.apache.cloudstack.framework.messagebus.MessageBus; @@ -94,12 +97,17 @@ import com.cloud.utils.Pair; import com.cloud.utils.component.Manager; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.db.DB; +import com.cloud.utils.db.JoinBuilder; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.fsm.StateListener; import com.cloud.vm.DiskProfile; import com.cloud.vm.ReservationContext; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachine.Event; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.dao.UserVmDao; @@ -116,7 +124,8 @@ import com.cloud.agent.manager.allocator.HostAllocator; @Local(value = { DeploymentPlanningManager.class }) -public class DeploymentPlanningManagerImpl extends ManagerBase implements DeploymentPlanningManager, Manager, Listener { +public class DeploymentPlanningManagerImpl extends ManagerBase implements DeploymentPlanningManager, Manager, Listener, + StateListener<State, VirtualMachine.Event, VirtualMachine> { private static final Logger s_logger = Logger.getLogger(DeploymentPlanningManagerImpl.class); @Inject @@ -138,6 +147,8 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy MessageBus _messageBus; private Timer _timer = null; private long _hostReservationReleasePeriod = 60L * 60L * 1000L; // one hour by default + @Inject + protected VMReservationDao _reservationDao; private static final long INITIAL_RESERVATION_RELEASE_CHECKER_DELAY = 30L * 1000L; // thirty seconds expressed in milliseconds protected long _nodeId = -1; @@ -766,6 +777,7 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy @Override public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException { _agentMgr.registerForHostEvents(this, true, false, true); + VirtualMachine.State.getStateMachine().registerListener(this); _messageBus.subscribe("VM_ReservedCapacity_Free", new MessageSubscriber() { @Override public void onPublishMessage(String senderAddress, String subject, Object obj) { @@ -798,6 +810,7 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy public boolean start() { _timer.schedule(new HostReservationReleaseChecker(), INITIAL_RESERVATION_RELEASE_CHECKER_DELAY, _hostReservationReleasePeriod); + cleanupVMReservations(); return true; } @@ -807,6 +820,26 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy return true; } + @Override + public void cleanupVMReservations() { + List<VMReservationVO> reservations = _reservationDao.listAll(); + + for (VMReservationVO reserv : reservations) { + VMInstanceVO vm = _vmInstanceDao.findById(reserv.getVmId()); + if (vm != null) { + if (vm.getState() == State.Starting || (vm.getState() == State.Stopped && vm.getLastHostId() == null)) { + continue; + } else { + // delete reservation + _reservationDao.remove(reserv.getId()); + } + } else { + // delete reservation + _reservationDao.remove(reserv.getId()); + } + } + } + // /refactoring planner methods private DeployDestination checkClustersforDestination(List<Long> clusterList, VirtualMachineProfile<? extends VirtualMachine> vmProfile, DeploymentPlan plan, ExcludeList avoid, @@ -1182,25 +1215,72 @@ public class DeploymentPlanningManagerImpl extends ManagerBase implements Deploy return false; } + @DB @Override - public boolean finalizeReservation(DeployDestination plannedDestination, + public String finalizeReservation(DeployDestination plannedDestination, VirtualMachineProfile<? extends VirtualMachine> 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; + boolean saveReservation = true; + final Transaction txn = Transaction.currentTxn(); + try { + txn.start(); + if (vmGroupCount > 0) { + List<Long> groupIds = _affinityGroupVMMapDao.listAffinityGroupIdsByVmId(vm.getId()); + SearchCriteria<AffinityGroupVO> criteria = _affinityGroupDao.createSearchCriteria(); + criteria.addAnd("id", SearchCriteria.Op.IN, groupIds.toArray(new Object[groupIds.size()])); + List<AffinityGroupVO> groups = _affinityGroupDao.lockRows(criteria, null, true); + + for (AffinityGroupProcessor processor : _affinityProcessors) { + if (!processor.check(vmProfile, plannedDestination)) { + saveReservation = false; + break; + } } } + + if (saveReservation) { + VMReservationVO vmReservation = new VMReservationVO(vm.getId(), plannedDestination.getDataCenter() + .getId(), plannedDestination.getPod().getId(), plannedDestination.getCluster().getId(), + plannedDestination.getHost().getId()); + Map<Long, Long> volumeReservationMap = new HashMap<Long, Long>(); + + if (vm.getHypervisorType() != HypervisorType.BareMetal) { + for (Volume vo : plannedDestination.getStorageForDisks().keySet()) { + volumeReservationMap.put(vo.getId(), plannedDestination.getStorageForDisks().get(vo).getId()); + } + vmReservation.setVolumeReservation(volumeReservationMap); + } + _reservationDao.persist(vmReservation); + return vmReservation.getUuid(); + } + } finally { + txn.commit(); } + return null; + } + @Override + public boolean preStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, + boolean status, Object opaque) { + return true; + } + + @Override + public boolean postStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, + boolean status, Object opaque) { + if (!status) { + return false; + } + if ((oldState == State.Starting) && (newState != State.Starting)) { + // cleanup all VM reservation entries + SearchCriteria<VMReservationVO> sc = _reservationDao.createSearchCriteria(); + sc.addAnd("vmId", SearchCriteria.Op.EQ, vo.getId()); + _reservationDao.expunge(sc); + } return true; } } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/fa9ca72f/server/src/com/cloud/server/ManagementServerImpl.java ---------------------------------------------------------------------- diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java index 29272dd..f7fc834 100755 --- a/server/src/com/cloud/server/ManagementServerImpl.java +++ b/server/src/com/cloud/server/ManagementServerImpl.java @@ -126,6 +126,7 @@ import org.apache.cloudstack.api.command.admin.pod.DeletePodCmd; import org.apache.cloudstack.api.command.admin.pod.ListPodsByCmd; import org.apache.cloudstack.api.command.admin.pod.UpdatePodCmd; import org.apache.cloudstack.api.command.admin.resource.ArchiveAlertsCmd; +import org.apache.cloudstack.api.command.admin.resource.CleanVMReservationsCmd; import org.apache.cloudstack.api.command.admin.resource.DeleteAlertsCmd; import org.apache.cloudstack.api.command.admin.resource.ListAlertsCmd; import org.apache.cloudstack.api.command.admin.resource.ListCapacityCmd; @@ -442,6 +443,7 @@ import com.cloud.dc.dao.VlanDao; import com.cloud.deploy.DataCenterDeployment; import com.cloud.deploy.DeploymentPlanner; import com.cloud.deploy.DeploymentPlanner.ExcludeList; +import com.cloud.deploy.DeploymentPlanningManager; import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; import com.cloud.event.ActionEvent; @@ -682,6 +684,9 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe @Inject AccountService _accountService; + @Inject + DeploymentPlanningManager _dpMgr; + private final ScheduledExecutorService _eventExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("EventChecker")); private final ScheduledExecutorService _alertExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("AlertChecker")); @Inject private KeystoreManager _ksMgr; @@ -2807,6 +2812,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe cmdList.add(ListNetworkACLListsCmd.class); cmdList.add(ReplaceNetworkACLListCmd.class); cmdList.add(UpdateNetworkACLItemCmd.class); + cmdList.add(CleanVMReservationsCmd.class); return cmdList; } @@ -3787,4 +3793,12 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe return plannersAvailable; } + @Override + public void cleanupVMReservations() { + if (s_logger.isDebugEnabled()) { + s_logger.debug("Processing cleanupVMReservations"); + } + + _dpMgr.cleanupVMReservations(); + } } http://git-wip-us.apache.org/repos/asf/cloudstack/blob/fa9ca72f/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java ---------------------------------------------------------------------- diff --git a/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java b/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java index 442c2be..10e23d7 100644 --- a/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java +++ b/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java @@ -54,6 +54,7 @@ import com.cloud.deploy.dao.PlannerHostReservationDao; import org.apache.cloudstack.affinity.AffinityGroupProcessor; import org.apache.cloudstack.affinity.dao.AffinityGroupDao; import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; +import org.apache.cloudstack.engine.cloud.entity.api.db.dao.VMReservationDao; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.framework.messagebus.MessageBus; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; @@ -356,6 +357,11 @@ public class DeploymentPlanningManagerImplTest { return Mockito.mock(DataCenterDao.class); } + @Bean + public VMReservationDao reservationDao() { + return Mockito.mock(VMReservationDao.class); + } + public static class Library implements TypeFilter { @Override