You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by pr...@apache.org on 2013/07/16 22:42:13 UTC

[3/4] git commit: updated refs/heads/4.2 to f53a063

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/d70076e6
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/d70076e6
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/d70076e6

Branch: refs/heads/4.2
Commit: d70076e6a5486293000535d8b7e4e781b3f12fca
Parents: 9cb91ba
Author: Prachi Damle <pr...@cloud.com>
Authored: Tue Jul 16 00:52:52 2013 -0700
Committer: Prachi Damle <pr...@cloud.com>
Committed: Tue Jul 16 13:41:16 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/d70076e6/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/d70076e6/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/d70076e6/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/d70076e6/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/d70076e6/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 00aeb71..90c1db5 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/d70076e6/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/d70076e6/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/d70076e6/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/d70076e6/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/d70076e6/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/d70076e6/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/d70076e6/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