incubator-cloudstack-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From m...@apache.org
Subject [1/3] CLOUDSTACK-684 Support VM Snapshot
Date Wed, 30 Jan 2013 02:12:22 GMT
http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/bb7cd90b/server/src/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java b/server/src/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java
new file mode 100644
index 0000000..49a4eec
--- /dev/null
+++ b/server/src/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java
@@ -0,0 +1,828 @@
+// 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 com.cloud.vm.snapshot;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.ejb.Local;
+import javax.naming.ConfigurationException;
+
+import org.apache.cloudstack.api.command.user.vmsnapshot.CreateVMSnapshotCmd;
+import org.apache.cloudstack.api.command.user.vmsnapshot.DeleteVMSnapshotCmd;
+import org.apache.cloudstack.api.command.user.vmsnapshot.ListVMSnapshotCmd;
+import org.apache.cloudstack.api.command.user.vmsnapshot.RevertToSnapshotCmd;
+import org.apache.log4j.Logger;
+
+import com.cloud.agent.AgentManager;
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.Command;
+import com.cloud.agent.api.CreateVMSnapshotAnswer;
+import com.cloud.agent.api.CreateVMSnapshotCommand;
+import com.cloud.agent.api.DeleteVMSnapshotAnswer;
+import com.cloud.agent.api.DeleteVMSnapshotCommand;
+import com.cloud.agent.api.RevertToVMSnapshotAnswer;
+import com.cloud.agent.api.RevertToVMSnapshotCommand;
+import com.cloud.agent.api.VMSnapshotTO;
+import com.cloud.agent.api.to.VolumeTO;
+import com.cloud.configuration.dao.ConfigurationDao;
+import com.cloud.event.ActionEvent;
+import com.cloud.event.EventTypes;
+import com.cloud.exception.AgentUnavailableException;
+import com.cloud.exception.ConcurrentOperationException;
+import com.cloud.exception.InsufficientCapacityException;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.exception.OperationTimedoutException;
+import com.cloud.exception.ResourceAllocationException;
+import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.host.Host;
+import com.cloud.host.HostVO;
+import com.cloud.host.dao.HostDao;
+import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.hypervisor.HypervisorGuruManager;
+import com.cloud.projects.Project.ListProjectResourcesCriteria;
+import com.cloud.service.dao.ServiceOfferingDao;
+import com.cloud.storage.GuestOSVO;
+import com.cloud.storage.Snapshot;
+import com.cloud.storage.SnapshotVO;
+import com.cloud.storage.StoragePoolVO;
+import com.cloud.storage.VolumeVO;
+import com.cloud.storage.dao.GuestOSDao;
+import com.cloud.storage.dao.SnapshotDao;
+import com.cloud.storage.dao.StoragePoolDao;
+import com.cloud.storage.dao.VolumeDao;
+import com.cloud.user.Account;
+import com.cloud.user.AccountManager;
+import com.cloud.user.UserContext;
+import com.cloud.user.UserVO;
+import com.cloud.user.dao.AccountDao;
+import com.cloud.user.dao.UserDao;
+import com.cloud.uservm.UserVm;
+import com.cloud.utils.DateUtil;
+import com.cloud.utils.NumbersUtil;
+import com.cloud.utils.Pair;
+import com.cloud.utils.Ternary;
+import com.cloud.utils.component.ComponentLocator;
+import com.cloud.utils.component.Inject;
+import com.cloud.utils.component.Manager;
+import com.cloud.utils.db.DB;
+import com.cloud.utils.db.Filter;
+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.NoTransitionException;
+import com.cloud.utils.fsm.StateMachine2;
+import com.cloud.vm.UserVmVO;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.VirtualMachine;
+import com.cloud.vm.VirtualMachine.State;
+import com.cloud.vm.VirtualMachineManager;
+import com.cloud.vm.VirtualMachineProfile;
+import com.cloud.vm.dao.UserVmDao;
+import com.cloud.vm.dao.VMInstanceDao;
+import com.cloud.vm.snapshot.dao.VMSnapshotDao;
+
+@Local(value = { VMSnapshotManager.class, VMSnapshotService.class })
+public class VMSnapshotManagerImpl implements VMSnapshotManager, VMSnapshotService, Manager {
+    private static final Logger s_logger = Logger.getLogger(VMSnapshotManagerImpl.class);
+    String _name;
+    @Inject VMSnapshotDao _vmSnapshotDao;
+    @Inject VolumeDao _volumeDao;
+    @Inject AccountDao _accountDao;
+    @Inject VMInstanceDao _vmInstanceDao;
+    @Inject UserVmDao _userVMDao;
+    @Inject HostDao _hostDao;
+    @Inject UserDao _userDao;
+    @Inject AgentManager _agentMgr;
+    @Inject HypervisorGuruManager _hvGuruMgr;
+    @Inject AccountManager _accountMgr;
+    @Inject GuestOSDao _guestOSDao;
+    @Inject StoragePoolDao _storagePoolDao;
+    @Inject SnapshotDao _snapshotDao;
+    @Inject VirtualMachineManager _itMgr;
+    @Inject ConfigurationDao _configDao;
+    int _vmSnapshotMax;
+    StateMachine2<State, VirtualMachine.Event, VirtualMachine> _stateMachine;
+    StateMachine2<VMSnapshot.State, VMSnapshot.Event, VMSnapshot> _vmSnapshottateMachine ;
+
+    @Override
+    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
+        _name = name;
+
+        ComponentLocator locator = ComponentLocator.getCurrentLocator();
+        _configDao = locator.getDao(ConfigurationDao.class);
+        if (_configDao == null) {
+            throw new ConfigurationException("Unable to get the configuration dao.");
+        }
+        s_logger.info("Snapshot Manager is configured.");
+
+        _vmSnapshotMax = NumbersUtil.parseInt(_configDao.getValue("vmsnapshot.max"), VMSNAPSHOTMAX);
+
+        _stateMachine = VirtualMachine.State.getStateMachine();
+        _vmSnapshottateMachine   = VMSnapshot.State.getStateMachine();
+        return true;
+    }
+
+    @Override
+    public boolean start() {
+        return true;
+    }
+
+    @Override
+    public boolean stop() {
+        return true;
+    }
+
+    @Override
+    public List<VMSnapshotVO> listVMSnapshots(ListVMSnapshotCmd cmd) {
+        Account caller = getCaller();
+        List<Long> permittedAccounts = new ArrayList<Long>();
+
+        boolean listAll = cmd.listAll();
+        Long id = cmd.getId();
+        Long vmId = cmd.getVmId();
+        
+        String state = cmd.getState();
+        String keyword = cmd.getKeyword();
+        String name = cmd.getVmSnapshotName();
+        String accountName = cmd.getAccountName();
+
+        Ternary<Long, Boolean, ListProjectResourcesCriteria> domainIdRecursiveListProject = new Ternary<Long, Boolean, ListProjectResourcesCriteria>(
+                cmd.getDomainId(), cmd.isRecursive(), null);
+        _accountMgr.buildACLSearchParameters(caller, id, cmd.getAccountName(), cmd.getProjectId(), permittedAccounts, domainIdRecursiveListProject, listAll,
+                false);
+        Long domainId = domainIdRecursiveListProject.first();
+        Boolean isRecursive = domainIdRecursiveListProject.second();
+        ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
+
+        Filter searchFilter = new Filter(VMSnapshotVO.class, "created", false, cmd.getStartIndex(), cmd.getPageSizeVal());
+        SearchBuilder<VMSnapshotVO> sb = _vmSnapshotDao.createSearchBuilder();
+        _accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
+
+        sb.and("vm_id", sb.entity().getVmId(), SearchCriteria.Op.EQ);
+        sb.and("domain_id", sb.entity().getDomainId(), SearchCriteria.Op.EQ);
+        sb.and("status", sb.entity().getState(), SearchCriteria.Op.IN);
+        sb.and("state", sb.entity().getState(), SearchCriteria.Op.EQ);
+        sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ);
+        sb.and("display_name", sb.entity().getDisplayName(), SearchCriteria.Op.EQ);
+        sb.and("account_id", sb.entity().getAccountId(), SearchCriteria.Op.EQ);
+        sb.done();
+
+        SearchCriteria<VMSnapshotVO> sc = sb.create();
+        _accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
+
+        if (accountName != null && cmd.getDomainId() != null) {
+            Account account = _accountMgr.getActiveAccountByName(accountName, cmd.getDomainId());
+            sc.setParameters("account_id", account.getId());
+        }
+
+        if (vmId != null) {
+            sc.setParameters("vm_id", vmId);
+        }
+
+        if (domainId != null) {
+            sc.setParameters("domain_id", domainId);
+        }
+
+        if (state == null) {
+            VMSnapshot.State[] status = { VMSnapshot.State.Ready, VMSnapshot.State.Creating, VMSnapshot.State.Allocated, 
+                    VMSnapshot.State.Error, VMSnapshot.State.Expunging, VMSnapshot.State.Reverting };
+            sc.setParameters("status", (Object[]) status);
+        } else {
+            sc.setParameters("state", state);
+        }
+
+        if (name != null) {
+            sc.setParameters("display_name", name);
+        }
+
+        if (keyword != null) {
+            SearchCriteria<VMSnapshotVO> ssc = _vmSnapshotDao.createSearchCriteria();
+            ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%");
+            ssc.addOr("display_name", SearchCriteria.Op.LIKE, "%" + keyword + "%");
+            ssc.addOr("description", SearchCriteria.Op.LIKE, "%" + keyword + "%");
+            sc.addAnd("name", SearchCriteria.Op.SC, ssc);
+        }
+
+        if (id != null) {
+            sc.setParameters("id", id);
+        }
+
+        return _vmSnapshotDao.search(sc, searchFilter);
+    
+    }
+
+    protected Account getCaller(){
+        return UserContext.current().getCaller();
+    }
+    
+    @Override
+    public VMSnapshot allocVMSnapshot(CreateVMSnapshotCmd cmd) throws ResourceAllocationException {
+        Long vmId = cmd.getVmId();
+        String vsDisplayName = cmd.getDisplayName();
+        String vsDescription = cmd.getDescription();
+        Boolean snapshotMemory = cmd.snapshotMemory();
+
+        Account caller = getCaller();
+
+        // check if VM exists
+        UserVmVO userVmVo = _userVMDao.findById(vmId);
+        if (userVmVo == null) {
+            throw new InvalidParameterValueException("Creating VM snapshot failed due to VM:" + vmId + " is a system VM or does not exist");
+        }
+        
+        // VM snapshot display name must be unique for a VM
+        String timeString = DateUtil.getDateDisplayString(DateUtil.GMT_TIMEZONE, new Date(), DateUtil.YYYYMMDD_FORMAT);
+        String vmSnapshotName = userVmVo.getInstanceName() + "_VS_" + timeString;
+        if (vsDisplayName == null) {
+            vsDisplayName = vmSnapshotName;
+        }
+        if(_vmSnapshotDao.findByName(vmId,vsDisplayName) != null){
+            throw new InvalidParameterValueException("Creating VM snapshot failed due to VM snapshot with name" + vsDisplayName + "  already exists"); 
+        }
+        
+        // check VM state
+        if (userVmVo.getState() != VirtualMachine.State.Running && userVmVo.getState() != VirtualMachine.State.Stopped) {
+            throw new InvalidParameterValueException("Creating vm snapshot failed due to VM:" + vmId + " is not in the running or Stopped state");
+        }
+        
+        // for KVM, only allow snapshot with memory when VM is in running state
+        if(userVmVo.getHypervisorType() == HypervisorType.KVM && userVmVo.getState() == State.Running && !snapshotMemory){
+            throw new InvalidParameterValueException("KVM VM does not allow to take a disk-only snapshot when VM is in running state");
+        }
+        
+        // check access
+        _accountMgr.checkAccess(caller, null, true, userVmVo);
+
+        // check max snapshot limit for per VM
+        if (_vmSnapshotDao.findByVm(vmId).size() >= _vmSnapshotMax) {
+            throw new CloudRuntimeException("Creating vm snapshot failed due to a VM can just have : " + _vmSnapshotMax
+                    + " VM snapshots. Please delete old ones");
+        }
+
+        // check if there are active volume snapshots tasks
+        List<VolumeVO> listVolumes = _volumeDao.findByInstance(vmId);
+        for (VolumeVO volume : listVolumes) {
+            List<SnapshotVO> activeSnapshots = _snapshotDao.listByInstanceId(volume.getInstanceId(), Snapshot.Status.Creating,
+                    Snapshot.Status.CreatedOnPrimary, Snapshot.Status.BackingUp);
+            if (activeSnapshots.size() > 0) {
+                throw new CloudRuntimeException(
+                        "There is other active volume snapshot tasks on the instance to which the volume is attached, please try again later.");
+            }
+        }
+        
+        // check if there are other active VM snapshot tasks
+        if (checkActiveVMSnapshotTasks(vmId)) {
+            throw new CloudRuntimeException("There is other active vm snapshot tasks on the instance, please try again later");
+        }
+        
+        VMSnapshot.Type vmSnapshotType = VMSnapshot.Type.Disk;
+        if(snapshotMemory && userVmVo.getState() == VirtualMachine.State.Running)
+            vmSnapshotType = VMSnapshot.Type.DiskAndMemory;
+        
+        try {
+            VMSnapshotVO vmSnapshotVo = new VMSnapshotVO(userVmVo.getAccountId(), userVmVo.getDomainId(), vmId, vsDescription, vmSnapshotName,
+                    vsDisplayName, userVmVo.getServiceOfferingId(), vmSnapshotType, null);
+            VMSnapshot vmSnapshot = _vmSnapshotDao.persist(vmSnapshotVo);
+            if (vmSnapshot == null) {
+                throw new CloudRuntimeException("Failed to create snapshot for vm: " + vmId);
+            }
+            return vmSnapshot;
+        } catch (Exception e) {
+            String msg = e.getMessage();
+            s_logger.error("Create vm snapshot record failed for vm: " + vmId + " due to: " + msg);
+        }  
+        return null;
+    }
+
+    @Override
+    public String getName() {
+        return _name;
+    }
+
+    @Override
+    @ActionEvent(eventType = EventTypes.EVENT_VM_SNAPSHOT_CREATE, eventDescription = "creating_vm_snapshot", async = true)
+    public VMSnapshot creatVMSnapshot(CreateVMSnapshotCmd cmd) {
+        Long vmId = cmd.getVmId();
+        UserVmVO userVm = _userVMDao.findById(vmId);
+        VMSnapshotVO vmSnapshot = _vmSnapshotDao.findById(cmd.getEntityId());
+        Long hostId = pickRunningHost(vmId);
+        transitState(vmSnapshot, VMSnapshot.Event.CreateRequested, userVm,  VirtualMachine.Event.SnapshotRequested, hostId);
+        return createVmSnapshotInternal(userVm, vmSnapshot, hostId);
+    }
+
+    protected VMSnapshot createVmSnapshotInternal(UserVmVO userVm, VMSnapshotVO vmSnapshot, Long hostId) {
+        try { 
+            CreateVMSnapshotAnswer answer = null;
+            GuestOSVO guestOS = _guestOSDao.findById(userVm.getGuestOSId());
+            
+            // prepare snapshotVolumeTos
+            List<VolumeTO> volumeTOs = getVolumeTOList(userVm.getId());
+            
+            // prepare target snapshotTO and its parent snapshot (current snapshot)
+            VMSnapshotTO current = null;
+            VMSnapshotVO currentSnapshot = _vmSnapshotDao.findCurrentSnapshotByVmId(userVm.getId());
+            if (currentSnapshot != null)
+                current = getSnapshotWithParents(currentSnapshot);
+            VMSnapshotTO target = new VMSnapshotTO(vmSnapshot.getId(),  vmSnapshot.getName(), vmSnapshot.getType(), null, vmSnapshot.getDescription(), false,
+                    current);
+            if (current == null)
+                vmSnapshot.setParent(null);
+            else
+                vmSnapshot.setParent(current.getId());
+
+            CreateVMSnapshotCommand ccmd = new CreateVMSnapshotCommand(userVm.getInstanceName(),target ,volumeTOs, guestOS.getDisplayName(),userVm.getState());
+            
+            answer = (CreateVMSnapshotAnswer) sendToPool(hostId, ccmd);
+            if (answer != null && answer.getResult()) {
+                processAnswer(vmSnapshot, userVm, answer, hostId);
+                transitState(vmSnapshot, VMSnapshot.Event.OperationSucceeded, userVm, VirtualMachine.Event.OperationSucceeded, hostId);
+                s_logger.debug("Create vm snapshot " + vmSnapshot.getName() + " succeeded for vm: " + userVm.getInstanceName());
+            }else{
+                String errMsg = answer.getDetails();
+                s_logger.error("Agent reports creating vm snapshot " + vmSnapshot.getName() + " failed for vm: " + userVm.getInstanceName() + " due to " + errMsg);
+                transitState(vmSnapshot, VMSnapshot.Event.OperationFailed, userVm,  VirtualMachine.Event.OperationFailed, hostId);
+            }
+            return vmSnapshot;
+        } catch (Exception e) {
+            String msg = e.getMessage();
+            s_logger.error("Create vm snapshot " + vmSnapshot.getName() + " failed for vm: " + userVm.getInstanceName() + " due to " + msg);
+            throw new CloudRuntimeException(msg);
+        } finally{
+            if(vmSnapshot.getState() == VMSnapshot.State.Allocated){
+                s_logger.warn("Create vm snapshot " + vmSnapshot.getName() + " failed for vm: " + userVm.getInstanceName());
+                _vmSnapshotDao.remove(vmSnapshot.getId());
+            }
+        }
+    }
+
+    protected List<VolumeTO> getVolumeTOList(Long vmId) {
+        List<VolumeTO> volumeTOs = new ArrayList<VolumeTO>();
+        List<VolumeVO> volumeVos = _volumeDao.findByInstance(vmId);
+        
+        for (VolumeVO volume : volumeVos) {
+            StoragePoolVO pool = _storagePoolDao.findById(volume.getPoolId());
+            VolumeTO volumeTO = new VolumeTO(volume, pool);
+            volumeTOs.add(volumeTO);
+        }
+        return volumeTOs;
+    }
+
+    // get snapshot and its parents recursively
+    private VMSnapshotTO getSnapshotWithParents(VMSnapshotVO snapshot) {
+        Map<Long, VMSnapshotVO> snapshotMap = new HashMap<Long, VMSnapshotVO>();
+        List<VMSnapshotVO> allSnapshots = _vmSnapshotDao.findByVm(snapshot.getVmId());
+        for (VMSnapshotVO vmSnapshotVO : allSnapshots) {
+            snapshotMap.put(vmSnapshotVO.getId(), vmSnapshotVO);
+        }
+
+        VMSnapshotTO currentTO = convert2VMSnapshotTO(snapshot);
+        VMSnapshotTO result = currentTO;
+        VMSnapshotVO current = snapshot;
+        while (current.getParent() != null) {
+            VMSnapshotVO parent = snapshotMap.get(current.getParent());
+            currentTO.setParent(convert2VMSnapshotTO(parent));
+            current = snapshotMap.get(current.getParent());
+            currentTO = currentTO.getParent();
+        }
+        return result;
+    }
+
+    private VMSnapshotTO convert2VMSnapshotTO(VMSnapshotVO vo) {
+        return new VMSnapshotTO(vo.getId(), vo.getName(),  vo.getType(), vo.getCreated().getTime(), vo.getDescription(),
+                vo.getCurrent(), null);
+    }
+
+    private boolean vmSnapshotStateTransitTo(VMSnapshotVO vsnp, VMSnapshot.Event event) throws NoTransitionException {
+        return _vmSnapshottateMachine.transitTo(vsnp, event, null, _vmSnapshotDao);
+    }
+    
+    @DB
+    protected boolean transitState(VMSnapshotVO vsnp, VMSnapshot.Event e1, VMInstanceVO vm, VirtualMachine.Event e2, Long hostId){
+        final Transaction txn = Transaction.currentTxn();
+        try {
+            txn.start();
+            vmSnapshotStateTransitTo(vsnp, e1);
+            vmStateTransitTo(vm, e2, hostId, null);
+            return txn.commit();
+        } catch (NoTransitionException e) {
+            txn.rollback();
+            return false;
+        }finally{
+            txn.close();
+        }
+    }
+    
+    @DB
+    protected void processAnswer(VMSnapshotVO vmSnapshot, UserVmVO  userVm, Answer as, Long hostId) {
+        final Transaction txn = Transaction.currentTxn();
+        try {
+            txn.start();
+            if (as instanceof CreateVMSnapshotAnswer) {
+                CreateVMSnapshotAnswer answer = (CreateVMSnapshotAnswer) as;
+                finalizeCreate(vmSnapshot, answer.getVolumeTOs());
+            } else if (as instanceof RevertToVMSnapshotAnswer) {
+                RevertToVMSnapshotAnswer answer = (RevertToVMSnapshotAnswer) as;
+                finalizeRevert(vmSnapshot, answer.getVolumeTOs());
+            } else if (as instanceof DeleteVMSnapshotAnswer) {
+                DeleteVMSnapshotAnswer answer = (DeleteVMSnapshotAnswer) as;
+                finalizeDelete(vmSnapshot, answer.getVolumeTOs());
+                _vmSnapshotDao.remove(vmSnapshot.getId());
+            }
+            txn.commit();
+        } catch (Exception e) {
+            String errMsg = "Error while process answer: " + as.getClass() + " due to " + e.getMessage();
+            s_logger.error(errMsg, e);
+            txn.rollback();
+            throw new CloudRuntimeException(errMsg);
+        } finally {
+            txn.close();
+        }
+    }
+
+    protected void finalizeDelete(VMSnapshotVO vmSnapshot, List<VolumeTO> VolumeTOs) {
+        // update volumes path
+        updateVolumePath(VolumeTOs);
+        
+        // update children's parent snapshots
+        List<VMSnapshotVO> children= _vmSnapshotDao.listByParent(vmSnapshot.getId());
+        for (VMSnapshotVO child : children) {
+            child.setParent(vmSnapshot.getParent());
+            _vmSnapshotDao.persist(child);
+        }
+        
+        // update current snapshot 
+        VMSnapshotVO current = _vmSnapshotDao.findCurrentSnapshotByVmId(vmSnapshot.getVmId());
+        if(current != null && current.getId() == vmSnapshot.getId() && vmSnapshot.getParent() != null){
+            VMSnapshotVO parent = _vmSnapshotDao.findById(vmSnapshot.getParent());
+            parent.setCurrent(true);
+            _vmSnapshotDao.persist(parent);
+        }
+        vmSnapshot.setCurrent(false);
+        _vmSnapshotDao.persist(vmSnapshot);
+    }
+
+    protected void finalizeCreate(VMSnapshotVO vmSnapshot, List<VolumeTO> VolumeTOs) {
+        // update volumes path
+        updateVolumePath(VolumeTOs);
+        
+        vmSnapshot.setCurrent(true);
+        
+        // change current snapshot
+        if (vmSnapshot.getParent() != null) {
+            VMSnapshotVO previousCurrent = _vmSnapshotDao.findById(vmSnapshot.getParent());
+            previousCurrent.setCurrent(false);
+            _vmSnapshotDao.persist(previousCurrent);
+        }
+        _vmSnapshotDao.persist(vmSnapshot);
+    }
+
+    protected void finalizeRevert(VMSnapshotVO vmSnapshot, List<VolumeTO> volumeToList) {
+        // update volumes path
+        updateVolumePath(volumeToList);
+
+        // update current snapshot, current snapshot is the one reverted to
+        VMSnapshotVO previousCurrent = _vmSnapshotDao.findCurrentSnapshotByVmId(vmSnapshot.getVmId());
+        previousCurrent.setCurrent(false);
+        vmSnapshot.setCurrent(true);
+        _vmSnapshotDao.persist(previousCurrent);
+        _vmSnapshotDao.persist(vmSnapshot);
+    }
+
+    private void updateVolumePath(List<VolumeTO> volumeTOs) {
+        for (VolumeTO volume : volumeTOs) {
+            if (volume.getPath() != null) {
+                VolumeVO volumeVO = _volumeDao.findById(volume.getId());
+                volumeVO.setPath(volume.getPath());
+                _volumeDao.persist(volumeVO);
+            }
+        }
+    }
+    
+    public VMSnapshotManagerImpl() {
+        
+    }
+    
+    protected Answer sendToPool(Long hostId, Command cmd) throws AgentUnavailableException, OperationTimedoutException {
+        long targetHostId = _hvGuruMgr.getGuruProcessedCommandTargetHost(hostId, cmd);
+        Answer answer = _agentMgr.send(targetHostId, cmd);
+        return answer;
+    }
+    
+    private boolean checkActiveVMSnapshotTasks(Long vmId){
+        List<VMSnapshotVO> activeVMSnapshots = _vmSnapshotDao.listByInstanceId(vmId, 
+                VMSnapshot.State.Creating, VMSnapshot.State.Expunging,VMSnapshot.State.Reverting,VMSnapshot.State.Allocated);
+        return activeVMSnapshots.size() > 0;
+    }
+
+    @Override
+    @ActionEvent(eventType = EventTypes.EVENT_VM_SNAPSHOT_DELETE, eventDescription = "delete_vm_snapshot", async=true)
+    public boolean deleteVMSnapshot(DeleteVMSnapshotCmd cmd) {
+        Long vmSnapshotId = cmd.getId();
+        Account caller = getCaller();
+
+        VMSnapshotVO vmSnapshot = _vmSnapshotDao.findById(vmSnapshotId);
+        if (vmSnapshot == null) {
+            throw new InvalidParameterValueException("unable to find the vm snapshot with id " + vmSnapshotId);
+        }
+
+        _accountMgr.checkAccess(caller, null, true, vmSnapshot);
+        
+        // check VM snapshot states, only allow to delete vm snapshots in created and error state
+        if (VMSnapshot.State.Ready != vmSnapshot.getState() && VMSnapshot.State.Error != vmSnapshot.getState()) {
+            throw new InvalidParameterValueException("Can't delete the vm snapshotshot " + vmSnapshotId + " due to it is not in Created or Error State");
+        }
+        
+        // check if there are other active VM snapshot tasks
+        if (checkActiveVMSnapshotTasks(vmSnapshot.getVmId())) {
+            throw new InvalidParameterValueException("There is other active vm snapshot tasks on the instance, please try again later");
+        }
+
+        if(vmSnapshot.getState() == VMSnapshot.State.Allocated){
+            return _vmSnapshotDao.remove(vmSnapshot.getId());
+        }else{
+            return deleteSnapshotInternal(vmSnapshot);
+        }
+    }
+
+    @DB
+    protected boolean deleteSnapshotInternal(VMSnapshotVO vmSnapshot) {
+        UserVmVO userVm = _userVMDao.findById(vmSnapshot.getVmId());
+        
+        try {
+            vmSnapshotStateTransitTo(vmSnapshot,VMSnapshot.Event.ExpungeRequested);
+            Long hostId = pickRunningHost(vmSnapshot.getVmId());
+
+            // prepare snapshotVolumeTos
+            List<VolumeTO> volumeTOs = getVolumeTOList(vmSnapshot.getVmId());
+            
+            // prepare DeleteVMSnapshotCommand
+            String vmInstanceName = userVm.getInstanceName();
+            VMSnapshotTO parent = getSnapshotWithParents(vmSnapshot).getParent();
+            VMSnapshotTO vmSnapshotTO = new VMSnapshotTO(vmSnapshot.getId(), vmSnapshot.getName(), vmSnapshot.getType(), 
+                    vmSnapshot.getCreated().getTime(), vmSnapshot.getDescription(), vmSnapshot.getCurrent(), parent);
+            GuestOSVO guestOS = _guestOSDao.findById(userVm.getGuestOSId());
+            DeleteVMSnapshotCommand deleteSnapshotCommand = new DeleteVMSnapshotCommand(vmInstanceName, vmSnapshotTO, volumeTOs,guestOS.getDisplayName());
+            
+            DeleteVMSnapshotAnswer answer = (DeleteVMSnapshotAnswer) sendToPool(hostId, deleteSnapshotCommand);
+           
+            if (answer != null && answer.getResult()) {
+                processAnswer(vmSnapshot, userVm, answer, hostId);
+                return true;
+            } else {
+                s_logger.error("Delete vm snapshot " + vmSnapshot.getName() + " of vm " + userVm.getInstanceName() + " failed due to " + answer.getDetails());
+                return false;
+            }
+        } catch (Exception e) {
+            String msg = "Delete vm snapshot " + vmSnapshot.getName() + " of vm " + userVm.getInstanceName() + " failed due to " + e.getMessage();
+            s_logger.error(msg , e);
+            throw new CloudRuntimeException(e.getMessage());
+        }
+    }
+
+    @Override
+    @ActionEvent(eventType = EventTypes.EVENT_VM_SNAPSHOT_REVERT, eventDescription = "revert_vm", async = true)
+    public UserVm revertToSnapshot(RevertToSnapshotCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException {
+
+        Long vmSnapshotId = cmd.getVmSnapShotId();
+        
+        // check if VM snapshot exists in DB
+        VMSnapshotVO vmSnapshotVo = _vmSnapshotDao.findById(vmSnapshotId);
+        if (vmSnapshotVo == null) {
+            throw new InvalidParameterValueException(
+                    "unable to find the vm snapshot with id " + vmSnapshotId);
+        }
+        Long vmId = vmSnapshotVo.getVmId();
+        UserVmVO userVm = _userVMDao.findById(vmId);
+        // check if VM exists
+        if (userVm == null) {
+            throw new InvalidParameterValueException("Revert vm to snapshot: "
+                    + vmSnapshotId + " failed due to vm: " + vmId
+                    + " is not found");
+        }
+        
+        // check if there are other active VM snapshot tasks
+        if (checkActiveVMSnapshotTasks(vmId)) {
+            throw new InvalidParameterValueException("There is other active vm snapshot tasks on the instance, please try again later");
+        }
+        
+        Account caller = getCaller();
+        _accountMgr.checkAccess(caller, null, true, vmSnapshotVo);
+
+        // VM should be in running or stopped states
+        if (userVm.getState() != VirtualMachine.State.Running
+                && userVm.getState() != VirtualMachine.State.Stopped) {
+            throw new InvalidParameterValueException(
+                    "VM Snapshot reverting failed due to vm is not in the state of Running or Stopped.");
+        }
+        
+        // if snapshot is not created, error out
+        if (vmSnapshotVo.getState() != VMSnapshot.State.Ready) {
+            throw new InvalidParameterValueException(
+                    "VM Snapshot reverting failed due to vm snapshot is not in the state of Created.");
+        }
+
+        UserVO callerUser = _userDao.findById(UserContext.current().getCallerUserId());
+        
+        UserVmVO vm = null;
+        Long hostId = null;
+        Account owner = _accountDao.findById(vmSnapshotVo.getAccountId());
+        
+        // start or stop VM first, if revert from stopped state to running state, or from running to stopped
+        if(userVm.getState() == VirtualMachine.State.Stopped && vmSnapshotVo.getType() == VMSnapshot.Type.DiskAndMemory){
+            try {
+        	    vm = _itMgr.advanceStart(userVm, new HashMap<VirtualMachineProfile.Param, Object>(), callerUser, owner);
+        	    hostId = vm.getHostId();
+        	} catch (Exception e) {
+        	    s_logger.error("Start VM " + userVm.getInstanceName() + " before reverting failed due to " + e.getMessage());
+        	    throw new CloudRuntimeException(e.getMessage());
+        	}
+        }else {
+            if(userVm.getState() == VirtualMachine.State.Running && vmSnapshotVo.getType() == VMSnapshot.Type.Disk){
+                try {
+    			    _itMgr.advanceStop(userVm, true, callerUser, owner);
+                } catch (Exception e) {
+                    s_logger.error("Stop VM " + userVm.getInstanceName() + " before reverting failed due to " + e.getMessage());
+    			    throw new CloudRuntimeException(e.getMessage());
+                }
+            }
+            hostId = pickRunningHost(userVm.getId());
+        }
+        
+        if(hostId == null)
+            throw new CloudRuntimeException("Can not find any host to revert snapshot " + vmSnapshotVo.getName());
+        
+        // check if there are other active VM snapshot tasks
+        if (checkActiveVMSnapshotTasks(userVm.getId())) {
+            throw new InvalidParameterValueException("There is other active vm snapshot tasks on the instance, please try again later");
+        }
+        
+        userVm = _userVMDao.findById(userVm.getId());
+        transitState(vmSnapshotVo, VMSnapshot.Event.RevertRequested, userVm, VirtualMachine.Event.RevertRequested, hostId);
+        return revertInternal(userVm, vmSnapshotVo, hostId);
+    }
+
+    private UserVm revertInternal(UserVmVO userVm, VMSnapshotVO vmSnapshotVo, Long hostId) {
+
+        try {
+            
+            VMSnapshotVO snapshot = _vmSnapshotDao.findById(vmSnapshotVo.getId());
+            // prepare RevertToSnapshotCommand
+            List<VolumeTO> volumeTOs = getVolumeTOList(userVm.getId());
+            String vmInstanceName = userVm.getInstanceName();
+            VMSnapshotTO parent = getSnapshotWithParents(snapshot).getParent();
+            VMSnapshotTO vmSnapshotTO = new VMSnapshotTO(snapshot.getId(), snapshot.getName(), snapshot.getType(), 
+            		snapshot.getCreated().getTime(), snapshot.getDescription(), snapshot.getCurrent(), parent);
+            
+            GuestOSVO guestOS = _guestOSDao.findById(userVm.getGuestOSId());
+            RevertToVMSnapshotCommand revertToSnapshotCommand = new RevertToVMSnapshotCommand(vmInstanceName, vmSnapshotTO, volumeTOs, guestOS.getDisplayName());
+           
+            RevertToVMSnapshotAnswer answer = (RevertToVMSnapshotAnswer) sendToPool(hostId, revertToSnapshotCommand);
+            if (answer.getResult()) {
+                processAnswer(vmSnapshotVo, userVm, answer, hostId);
+                transitState(vmSnapshotVo, VMSnapshot.Event.OperationSucceeded, userVm, VirtualMachine.Event.OperationSucceeded, hostId);
+            } else {
+                String errMsg = "Revert VM: " + userVm.getInstanceName() + " to snapshot: "+ vmSnapshotVo.getName() + " failed";
+                if(answer != null && answer.getDetails() != null)
+                    errMsg = errMsg + " due to " + answer.getDetails();
+                s_logger.error(errMsg);
+                // agent report revert operation fails
+                transitState(vmSnapshotVo, VMSnapshot.Event.OperationFailed, userVm, VirtualMachine.Event.OperationFailed, hostId);
+                throw new CloudRuntimeException(errMsg);
+            }
+        } catch (Exception e) {
+            // for other exceptions, do not change VM state, leave it for snapshotSync
+            String errMsg = "revert vm: " + userVm.getInstanceName() + " to snapshot " + vmSnapshotVo.getName() + " failed due to " + e.getMessage();
+            s_logger.error(errMsg);
+            throw new CloudRuntimeException(e.getMessage());
+        } 
+        return userVm;
+    }
+
+    private boolean vmStateTransitTo(VMInstanceVO vm, VirtualMachine.Event e, Long hostId, String reservationId) throws NoTransitionException {
+        vm.setReservationId(reservationId);
+        return _stateMachine.transitTo(vm, e, new Pair<Long, Long>(vm.getHostId(), hostId), _vmInstanceDao);
+    }
+
+    @Override
+    public VMSnapshot getVMSnapshotById(long id) {
+        VMSnapshotVO vmSnapshot = _vmSnapshotDao.findById(id);
+        return vmSnapshot;
+    }
+
+    private Long pickRunningHost(Long vmId) {
+        UserVmVO vm = _userVMDao.findById(vmId);
+        // use VM's host if VM is running
+        if(vm.getState() == State.Running)
+            return vm.getHostId();
+        
+        // check if lastHostId is available
+        if(vm.getLastHostId() != null){
+           HostVO lastHost =  _hostDao.findById(vm.getLastHostId());
+           if(lastHost.getStatus() == com.cloud.host.Status.Up && !lastHost.isInMaintenanceStates())
+               return lastHost.getId();
+        }
+        
+        List<VolumeVO> listVolumes = _volumeDao.findByInstance(vmId);
+        if (listVolumes == null || listVolumes.size() == 0) {
+            throw new InvalidParameterValueException("vmInstance has no volumes");
+        }
+        VolumeVO volume = listVolumes.get(0);
+        Long poolId = volume.getPoolId();
+        if (poolId == null) {
+            throw new InvalidParameterValueException("pool id is not found");
+        }
+        StoragePoolVO storagePool = _storagePoolDao.findById(poolId);
+        if (storagePool == null) {
+            throw new InvalidParameterValueException("storage pool is not found");
+        }
+        List<HostVO> listHost = _hostDao.listAllUpAndEnabledNonHAHosts(Host.Type.Routing, storagePool.getClusterId(), storagePool.getPodId(),
+                storagePool.getDataCenterId(), null);
+        if (listHost == null || listHost.size() == 0) {
+            throw new InvalidParameterValueException("no host in up state is found");
+        }
+        return listHost.get(0).getId();
+    }
+
+    @Override
+    public VirtualMachine getVMBySnapshotId(Long id) {
+        VMSnapshotVO vmSnapshot = _vmSnapshotDao.findById(id);
+        Long vmId = vmSnapshot.getVmId();
+        UserVmVO vm = _userVMDao.findById(vmId);
+        return vm;
+    }
+
+    @Override
+    public boolean deleteAllVMSnapshots(long vmId, VMSnapshot.Type type) {
+        boolean result = true;
+        List<VMSnapshotVO> listVmSnapshots = _vmSnapshotDao.findByVm(vmId);
+        if (listVmSnapshots == null || listVmSnapshots.isEmpty()) {
+            return true;
+        }
+        for (VMSnapshotVO snapshot : listVmSnapshots) {
+            if(type != null && snapshot.getType() != type)
+                continue;
+            if (!deleteSnapshotInternal(snapshot)) {
+                result = false;
+                break;
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public boolean syncVMSnapshot(VMInstanceVO vm, VMSnapshotVO vmSnapshot, Long hostId) {
+        try{
+            
+            UserVmVO userVm = _userVMDao.findById(vm.getId());
+            if(userVm == null)
+                return false;
+            if(userVm.getState() == State.RevertingToRunning || userVm.getState() == State.RevertingToStopped){
+                List<VMSnapshotVO> vmSnapshots = _vmSnapshotDao.listByInstanceId(userVm.getId(), VMSnapshot.State.Reverting);
+                if(vmSnapshots.size() != 1){
+                    s_logger.error("VM:" + userVm.getInstanceName()+ " expected to have one VM snapshot in reverting state but actually has " + vmSnapshots.size());
+                    return false;
+                }
+                // send revert command again
+                revertInternal(userVm, vmSnapshots.get(0), hostId);
+                return true;
+            }else if (userVm.getState() == State.RunningSnapshotting || userVm.getState() == State.StoppedSnapshotting){
+                List<VMSnapshotVO> vmSnapshots = _vmSnapshotDao.listByInstanceId(userVm.getId(), VMSnapshot.State.Creating);
+                if(vmSnapshots.size() != 1){
+                    s_logger.error("VM:" + userVm.getInstanceName()+ " expected to have one VM snapshot in creating state but actually has " + vmSnapshots.size());
+                    return false;
+                }
+                // send create vm snapshot command again
+                createVmSnapshotInternal(userVm, vmSnapshots.get(0), hostId);
+                return true;
+                
+            }else if (vmSnapshot != null && vmSnapshot.getState() == VMSnapshot.State.Expunging){
+                return deleteSnapshotInternal(vmSnapshot);
+            }
+        }catch (Exception e) {
+            s_logger.error(e.getMessage(),e);
+            return false;
+        }
+        return false;
+    }
+ 
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/bb7cd90b/server/src/com/cloud/vm/snapshot/dao/VMSnapshotDao.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/vm/snapshot/dao/VMSnapshotDao.java b/server/src/com/cloud/vm/snapshot/dao/VMSnapshotDao.java
new file mode 100644
index 0000000..7532edf
--- /dev/null
+++ b/server/src/com/cloud/vm/snapshot/dao/VMSnapshotDao.java
@@ -0,0 +1,39 @@
+// 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 com.cloud.vm.snapshot.dao;
+
+import java.util.List;
+
+import com.cloud.utils.db.GenericDao;
+import com.cloud.utils.fsm.StateDao;
+import com.cloud.vm.snapshot.VMSnapshot;
+import com.cloud.vm.snapshot.VMSnapshotVO;
+
+public interface VMSnapshotDao extends GenericDao<VMSnapshotVO, Long>, StateDao<VMSnapshot.State, VMSnapshot.Event, VMSnapshot> {
+
+    List<VMSnapshotVO> findByVm(Long vmId);
+
+    List<VMSnapshotVO> listExpungingSnapshot();
+
+    List<VMSnapshotVO> listByInstanceId(Long vmId, VMSnapshot.State... status);
+
+	VMSnapshotVO findCurrentSnapshotByVmId(Long vmId);
+	
+	List<VMSnapshotVO> listByParent(Long vmSnapshotId);
+	
+	VMSnapshotVO findByName(Long vm_id, String name);
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/bb7cd90b/server/src/com/cloud/vm/snapshot/dao/VMSnapshotDaoImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/vm/snapshot/dao/VMSnapshotDaoImpl.java b/server/src/com/cloud/vm/snapshot/dao/VMSnapshotDaoImpl.java
new file mode 100644
index 0000000..5e592d0
--- /dev/null
+++ b/server/src/com/cloud/vm/snapshot/dao/VMSnapshotDaoImpl.java
@@ -0,0 +1,160 @@
+// 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 com.cloud.vm.snapshot.dao;
+
+import java.util.Date;
+import java.util.List;
+
+import javax.ejb.Local;
+
+import org.apache.log4j.Logger;
+
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.SearchCriteria.Op;
+import com.cloud.utils.db.UpdateBuilder;
+import com.cloud.vm.snapshot.VMSnapshot;
+import com.cloud.vm.snapshot.VMSnapshot.Event;
+import com.cloud.vm.snapshot.VMSnapshot.State;
+import com.cloud.vm.snapshot.VMSnapshotVO;
+
+@Local(value = { VMSnapshotDao.class })
+public class VMSnapshotDaoImpl extends GenericDaoBase<VMSnapshotVO, Long>
+        implements VMSnapshotDao {
+    private static final Logger s_logger = Logger.getLogger(VMSnapshotDaoImpl.class);
+    private final SearchBuilder<VMSnapshotVO> SnapshotSearch;
+    private final SearchBuilder<VMSnapshotVO> ExpungingSnapshotSearch;
+    private final SearchBuilder<VMSnapshotVO> SnapshotStatusSearch;
+    private final SearchBuilder<VMSnapshotVO> AllFieldsSearch;
+
+    protected VMSnapshotDaoImpl() {
+        AllFieldsSearch = createSearchBuilder();
+        AllFieldsSearch.and("state", AllFieldsSearch.entity().getState(), Op.EQ);
+        AllFieldsSearch.and("accountId", AllFieldsSearch.entity().getAccountId(), Op.EQ);
+        AllFieldsSearch.and("vm_id", AllFieldsSearch.entity().getVmId(), Op.EQ);
+        AllFieldsSearch.and("deviceId", AllFieldsSearch.entity().getVmId(), Op.EQ);
+        AllFieldsSearch.and("id", AllFieldsSearch.entity().getId(), Op.EQ);
+        AllFieldsSearch.and("removed", AllFieldsSearch.entity().getState(), Op.EQ);
+        AllFieldsSearch.and("parent", AllFieldsSearch.entity().getParent(), Op.EQ);
+        AllFieldsSearch.and("current", AllFieldsSearch.entity().getCurrent(), Op.EQ);
+        AllFieldsSearch.and("vm_snapshot_type", AllFieldsSearch.entity().getType(), Op.EQ);
+        AllFieldsSearch.and("updatedCount", AllFieldsSearch.entity().getUpdatedCount(), Op.EQ);
+        AllFieldsSearch.and("display_name", AllFieldsSearch.entity().getDisplayName(), SearchCriteria.Op.EQ);
+        AllFieldsSearch.and("name", AllFieldsSearch.entity().getName(), SearchCriteria.Op.EQ);
+        AllFieldsSearch.done();
+        
+        SnapshotSearch = createSearchBuilder();
+        SnapshotSearch.and("vm_id", SnapshotSearch.entity().getVmId(),
+                SearchCriteria.Op.EQ);
+        SnapshotSearch.done();
+
+        ExpungingSnapshotSearch = createSearchBuilder();
+        ExpungingSnapshotSearch.and("state", ExpungingSnapshotSearch.entity()
+                .getState(), SearchCriteria.Op.EQ);
+        ExpungingSnapshotSearch.and("removed", ExpungingSnapshotSearch.entity()
+                .getRemoved(), SearchCriteria.Op.NULL);
+        ExpungingSnapshotSearch.done();
+
+        SnapshotStatusSearch = createSearchBuilder();
+        SnapshotStatusSearch.and("vm_id", SnapshotStatusSearch.entity()
+                .getVmId(), SearchCriteria.Op.EQ);
+        SnapshotStatusSearch.and("state", SnapshotStatusSearch.entity()
+                .getState(), SearchCriteria.Op.IN);
+        SnapshotStatusSearch.done();
+    }
+
+    @Override
+    public List<VMSnapshotVO> findByVm(Long vmId) {
+        SearchCriteria<VMSnapshotVO> sc = SnapshotSearch.create();
+        sc.setParameters("vm_id", vmId);
+        return listBy(sc, null);
+    }
+
+    @Override
+    public List<VMSnapshotVO> listExpungingSnapshot() {
+        SearchCriteria<VMSnapshotVO> sc = ExpungingSnapshotSearch.create();
+        sc.setParameters("state", State.Expunging);
+        return listBy(sc, null);
+    }
+
+    @Override
+    public List<VMSnapshotVO> listByInstanceId(Long vmId, State... status) {
+        SearchCriteria<VMSnapshotVO> sc = SnapshotStatusSearch.create();
+        sc.setParameters("vm_id", vmId);
+        sc.setParameters("state", (Object[]) status);
+        return listBy(sc, null);
+    }
+
+	@Override
+	public VMSnapshotVO findCurrentSnapshotByVmId(Long vmId) {
+        SearchCriteria<VMSnapshotVO> sc = AllFieldsSearch.create();
+        sc.setParameters("vm_id", vmId);
+        sc.setParameters("current", 1);
+        return findOneBy(sc);
+	}
+
+    @Override
+    public List<VMSnapshotVO> listByParent(Long vmSnapshotId) {
+        SearchCriteria<VMSnapshotVO> sc = AllFieldsSearch.create();
+        sc.setParameters("parent", vmSnapshotId);
+        sc.setParameters("state", State.Ready );
+        return listBy(sc, null);
+    }
+
+    @Override
+    public VMSnapshotVO findByName(Long vm_id, String name) {
+        SearchCriteria<VMSnapshotVO> sc = AllFieldsSearch.create();
+        sc.setParameters("vm_id", vm_id);
+        sc.setParameters("display_name", name );
+        return null;
+    }
+
+    @Override
+    public boolean updateState(State currentState, Event event, State nextState, VMSnapshot vo, Object data) {
+        
+        Long oldUpdated = vo.getUpdatedCount();
+        Date oldUpdatedTime = vo.getUpdated();
+        
+        SearchCriteria<VMSnapshotVO> sc = AllFieldsSearch.create();
+        sc.setParameters("id", vo.getId());
+        sc.setParameters("state", currentState);
+        sc.setParameters("updatedCount", vo.getUpdatedCount());
+        
+        vo.incrUpdatedCount();
+        
+        UpdateBuilder builder = getUpdateBuilder(vo);
+        builder.set(vo, "state", nextState);
+        builder.set(vo, "updated", new Date());
+        
+        int rows = update((VMSnapshotVO)vo, sc);
+        if (rows == 0 && s_logger.isDebugEnabled()) {
+            VMSnapshotVO dbVol = findByIdIncludingRemoved(vo.getId()); 
+            if (dbVol != null) {
+                StringBuilder str = new StringBuilder("Unable to update ").append(vo.toString());
+                str.append(": DB Data={id=").append(dbVol.getId()).append("; state=").append(dbVol.getState()).append("; updatecount=").append(dbVol.getUpdatedCount()).append(";updatedTime=").append(dbVol.getUpdated());
+                str.append(": New Data={id=").append(vo.getId()).append("; state=").append(nextState).append("; event=").append(event).append("; updatecount=").append(vo.getUpdatedCount()).append("; updatedTime=").append(vo.getUpdated());
+                str.append(": stale Data={id=").append(vo.getId()).append("; state=").append(currentState).append("; event=").append(event).append("; updatecount=").append(oldUpdated).append("; updatedTime=").append(oldUpdatedTime);
+            } else {
+                s_logger.debug("Unable to update VM snapshot: id=" + vo.getId() + ", as there is no such snapshot exists in the database anymore");
+            }
+        }
+        return rows > 0;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/bb7cd90b/server/test/com/cloud/vm/snapshot/VMSnapshotManagerTest.java
----------------------------------------------------------------------
diff --git a/server/test/com/cloud/vm/snapshot/VMSnapshotManagerTest.java b/server/test/com/cloud/vm/snapshot/VMSnapshotManagerTest.java
new file mode 100644
index 0000000..fe49207
--- /dev/null
+++ b/server/test/com/cloud/vm/snapshot/VMSnapshotManagerTest.java
@@ -0,0 +1,182 @@
+// 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 com.cloud.vm.snapshot;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.cloudstack.acl.ControlledEntity;
+import org.apache.cloudstack.acl.SecurityChecker.AccessType;
+import org.apache.cloudstack.api.command.user.vmsnapshot.CreateVMSnapshotCmd;
+import org.junit.Test;
+
+import com.cloud.agent.AgentManager;
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.Command;
+import com.cloud.agent.api.CreateVMSnapshotAnswer;
+import com.cloud.agent.api.CreateVMSnapshotCommand;
+import com.cloud.agent.api.to.VolumeTO;
+import com.cloud.configuration.dao.ConfigurationDao;
+import com.cloud.exception.AgentUnavailableException;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.exception.OperationTimedoutException;
+import com.cloud.exception.ResourceAllocationException;
+import com.cloud.host.dao.HostDao;
+import com.cloud.hypervisor.HypervisorGuruManager;
+import com.cloud.storage.GuestOSVO;
+import com.cloud.storage.Snapshot;
+import com.cloud.storage.SnapshotVO;
+import com.cloud.storage.VolumeVO;
+import com.cloud.storage.dao.GuestOSDao;
+import com.cloud.storage.dao.SnapshotDao;
+import com.cloud.storage.dao.StoragePoolDao;
+import com.cloud.storage.dao.VolumeDao;
+import com.cloud.user.Account;
+import com.cloud.user.AccountManager;
+import com.cloud.user.dao.AccountDao;
+import com.cloud.user.dao.UserDao;
+import com.cloud.uservm.UserVm;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.UserVmVO;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.VirtualMachine;
+import com.cloud.vm.VirtualMachine.Event;
+import com.cloud.vm.VirtualMachine.State;
+import com.cloud.vm.VirtualMachineManager;
+import com.cloud.vm.dao.UserVmDao;
+import com.cloud.vm.dao.VMInstanceDao;
+import com.cloud.vm.snapshot.dao.VMSnapshotDao;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+import org.junit.Before;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+
+
+
+public class VMSnapshotManagerTest {
+    @Spy VMSnapshotManagerImpl _vmSnapshotMgr = new VMSnapshotManagerImpl();
+    @Mock Account admin;
+    @Mock VMSnapshotDao _vmSnapshotDao;
+    @Mock VolumeDao _volumeDao;
+    @Mock AccountDao _accountDao;
+    @Mock VMInstanceDao _vmInstanceDao;
+    @Mock UserVmDao _userVMDao;
+    @Mock HostDao _hostDao;
+    @Mock UserDao _userDao;
+    @Mock AgentManager _agentMgr;
+    @Mock HypervisorGuruManager _hvGuruMgr;
+    @Mock AccountManager _accountMgr;
+    @Mock GuestOSDao _guestOSDao;
+    @Mock StoragePoolDao _storagePoolDao;
+    @Mock SnapshotDao _snapshotDao;
+    @Mock VirtualMachineManager _itMgr;
+    @Mock ConfigurationDao _configDao;
+    int _vmSnapshotMax = 10;
+    
+    @Mock CreateVMSnapshotCmd createCmd;
+    
+    private static long TEST_VM_ID = 3L;
+    @Mock UserVmVO vmMock;
+    @Mock VolumeVO volumeMock;
+    
+    @Before
+    public void setup(){
+        MockitoAnnotations.initMocks(this);
+        doReturn(admin).when(_vmSnapshotMgr).getCaller();
+        _vmSnapshotMgr._accountDao = _accountDao;
+        _vmSnapshotMgr._userVMDao = _userVMDao;
+        _vmSnapshotMgr._vmSnapshotDao = _vmSnapshotDao;
+        _vmSnapshotMgr._volumeDao = _volumeDao;
+        _vmSnapshotMgr._accountMgr = _accountMgr;
+        _vmSnapshotMgr._snapshotDao = _snapshotDao;
+        _vmSnapshotMgr._guestOSDao = _guestOSDao;
+        
+        doNothing().when(_accountMgr).checkAccess(any(Account.class), any(AccessType.class), 
+                any(Boolean.class), any(ControlledEntity.class));
+        
+        _vmSnapshotMgr._vmSnapshotMax = _vmSnapshotMax;
+        
+        when(createCmd.getVmId()).thenReturn(TEST_VM_ID);
+        when(createCmd.getDisplayName()).thenReturn("testName");
+        
+        when(_userVMDao.findById(anyLong())).thenReturn(vmMock);
+        when(_vmSnapshotDao.findByName(anyLong(), anyString())).thenReturn(null);
+        when(_vmSnapshotDao.findByVm(anyLong())).thenReturn(new ArrayList<VMSnapshotVO>());
+       
+        List<VolumeVO> mockVolumeList = new ArrayList<VolumeVO>();
+        mockVolumeList.add(volumeMock);
+        when(volumeMock.getInstanceId()).thenReturn(TEST_VM_ID);
+        when(_volumeDao.findByInstance(anyLong())).thenReturn(mockVolumeList);
+        
+        when(vmMock.getInstanceName()).thenReturn("i-3-VM-TEST");
+        when(vmMock.getState()).thenReturn(State.Running);
+        
+        when(_guestOSDao.findById(anyLong())).thenReturn(mock(GuestOSVO.class));
+    } 
+    
+    // vmId null case
+    @Test(expected=InvalidParameterValueException.class)
+    public void testAllocVMSnapshotF1() throws ResourceAllocationException{
+        when(_userVMDao.findById(TEST_VM_ID)).thenReturn(null);
+        _vmSnapshotMgr.allocVMSnapshot(createCmd);
+    }
+
+    // vm state not in [running, stopped] case
+    @Test(expected=InvalidParameterValueException.class)
+    public void testAllocVMSnapshotF2() throws ResourceAllocationException{
+        when(vmMock.getState()).thenReturn(State.Starting);
+        _vmSnapshotMgr.allocVMSnapshot(createCmd);
+    }
+    
+    // max snapshot limit case
+    @SuppressWarnings("unchecked")
+    @Test(expected=CloudRuntimeException.class)
+    public void testAllocVMSnapshotF3() throws ResourceAllocationException{
+        List<VMSnapshotVO> mockList = mock(List.class);
+        when(mockList.size()).thenReturn(10);
+        when(_vmSnapshotDao.findByVm(TEST_VM_ID)).thenReturn(mockList);
+        _vmSnapshotMgr.allocVMSnapshot(createCmd);
+    }
+    
+    // active volume snapshots case
+    @SuppressWarnings("unchecked")
+    @Test(expected=CloudRuntimeException.class)
+    public void testAllocVMSnapshotF4() throws ResourceAllocationException{
+        List<SnapshotVO> mockList = mock(List.class);
+        when(mockList.size()).thenReturn(1);
+        when(_snapshotDao.listByInstanceId(TEST_VM_ID,Snapshot.Status.Creating,
+                Snapshot.Status.CreatedOnPrimary, Snapshot.Status.BackingUp)).thenReturn(mockList);
+        _vmSnapshotMgr.allocVMSnapshot(createCmd);
+    }
+    
+    @Test
+    public void testCreateVMSnapshot() throws AgentUnavailableException, OperationTimedoutException{
+        when(_vmSnapshotDao.findCurrentSnapshotByVmId(anyLong())).thenReturn(null);
+        doReturn(new ArrayList<VolumeTO>()).when(_vmSnapshotMgr).getVolumeTOList(anyLong());
+        doReturn(new CreateVMSnapshotAnswer(null,true,"")).when(_vmSnapshotMgr).sendToPool(anyLong(), any(CreateVMSnapshotCommand.class));
+        doReturn(true).when(_vmSnapshotMgr).transitState(any(VMSnapshotVO.class), 
+                any(VMSnapshot.Event.class), any(VMInstanceVO.class), any(VirtualMachine.Event.class), anyLong());
+        doNothing().when(_vmSnapshotMgr).processAnswer(any(VMSnapshotVO.class), 
+                any(UserVmVO.class), any(Answer.class), anyLong());
+        _vmSnapshotMgr.createVmSnapshotInternal(vmMock, mock(VMSnapshotVO.class), 5L);
+        
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/bb7cd90b/setup/db/create-schema.sql
----------------------------------------------------------------------
diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql
index ead98a5..42d86bf 100755
--- a/setup/db/create-schema.sql
+++ b/setup/db/create-schema.sql
@@ -2543,6 +2543,36 @@ INSERT INTO `cloud`.`counter` (id, uuid, source, name, value,created) VALUES (1,
 INSERT INTO `cloud`.`counter` (id, uuid, source, name, value,created) VALUES (2, UUID(), 'snmp','Linux System CPU - percentage', '1.3.6.1.4.1.2021.11.10.0', now());
 INSERT INTO `cloud`.`counter` (id, uuid, source, name, value,created) VALUES (3, UUID(), 'snmp','Linux CPU Idle - percentage', '1.3.6.1.4.1.2021.11.11.0', now());
 INSERT INTO `cloud`.`counter` (id, uuid, source, name, value,created) VALUES (100, UUID(), 'netscaler','Response Time - microseconds', 'RESPTIME', now());
+CREATE TABLE `cloud`.`vm_snapshots` (
+  `id` bigint(20) unsigned NOT NULL auto_increment COMMENT 'Primary Key',
+  `uuid` varchar(40) NOT NULL,
+  `name` varchar(255) NOT NULL,
+  `display_name` varchar(255) default NULL,
+  `description` varchar(255) default NULL,
+  `vm_id` bigint(20) unsigned NOT NULL,
+  `account_id` bigint(20) unsigned NOT NULL,
+  `domain_id` bigint(20) unsigned NOT NULL,
+  `vm_snapshot_type` varchar(32) default NULL,
+  `state` varchar(32) NOT NULL,
+  `parent` bigint unsigned default NULL,
+  `current` int(1) unsigned default NULL,
+  `update_count` bigint unsigned NOT NULL DEFAULT 0,
+  `updated` datetime default NULL,
+  `created` datetime default NULL,
+  `removed` datetime default NULL,
+  PRIMARY KEY  (`id`),
+  CONSTRAINT UNIQUE KEY `uc_vm_snapshots_uuid` (`uuid`),
+  INDEX `vm_snapshots_name` (`name`),
+  INDEX `vm_snapshots_vm_id` (`vm_id`),
+  INDEX `vm_snapshots_account_id` (`account_id`),
+  INDEX `vm_snapshots_display_name` (`display_name`),
+  INDEX `vm_snapshots_removed` (`removed`),
+  INDEX `vm_snapshots_parent` (`parent`),
+  CONSTRAINT `fk_vm_snapshots_vm_id__vm_instance_id` FOREIGN KEY `fk_vm_snapshots_vm_id__vm_instance_id` (`vm_id`) REFERENCES `vm_instance` (`id`),
+  CONSTRAINT `fk_vm_snapshots_account_id__account_id` FOREIGN KEY `fk_vm_snapshots_account_id__account_id` (`account_id`) REFERENCES `account` (`id`),
+  CONSTRAINT `fk_vm_snapshots_domain_id__domain_id` FOREIGN KEY `fk_vm_snapshots_domain_id__domain_id` (`domain_id`) REFERENCES `domain` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
 
 SET foreign_key_checks = 1;
 

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/bb7cd90b/ui/css/cloudstack3.css
----------------------------------------------------------------------
diff --git a/ui/css/cloudstack3.css b/ui/css/cloudstack3.css
index eb42843..bca4b10 100644
--- a/ui/css/cloudstack3.css
+++ b/ui/css/cloudstack3.css
@@ -1931,7 +1931,7 @@ div.detail-group.actions td {
 }
 
 .detail-group table td.detail-actions {
-  width: 55%;
+  width: 59%;
   height: 26px;
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/bb7cd90b/ui/dictionary.jsp
----------------------------------------------------------------------
diff --git a/ui/dictionary.jsp b/ui/dictionary.jsp
index 203c9da..2a08e6d 100644
--- a/ui/dictionary.jsp
+++ b/ui/dictionary.jsp
@@ -1431,6 +1431,16 @@ dictionary = {
 'label.delete.NiciraNvp': '<fmt:message key="label.delete.NiciraNvp" />',
 'label.nicira.controller.address': '<fmt:message key="label.nicira.controller.address" />',
 'label.nicira.transportzoneuuid': '<fmt:message key="label.nicira.transportzoneuuid" />',
-'label.nicira.l3gatewayserviceuuid': '<fmt:message key="label.nicira.l3gatewayserviceuuid" />'
+'label.nicira.l3gatewayserviceuuid': '<fmt:message key="label.nicira.l3gatewayserviceuuid" />',
+'label.vmsnapshot.current': '<fmt:message key="label.vmsnapshot.current" />',
+'label.vmsnapshot.parentname': '<fmt:message key="label.vmsnapshot.parentname" />',
+'label.vmsnapshot.type': '<fmt:message key="label.vmsnapshot.type" />',
+'label.vmsnapshot.memory': '<fmt:message key="label.vmsnapshot.memory" />',
+'label.vmsnapshot': '<fmt:message key="label.vmsnapshot" />',
+'label.action.vmsnapshot.create': '<fmt:message key="label.action.vmsnapshot.create" />',
+'label.action.vmsnapshot.delete': '<fmt:message key="label.action.vmsnapshot.delete" />',
+'label.action.vmsnapshot.revert': '<fmt:message key="label.action.vmsnapshot.revert" />',
+'message.action.vmsnapshot.delete': '<fmt:message key="message.action.vmsnapshot.delete" />',
+'message.action.vmsnapshot.revert': '<fmt:message key="message.action.vmsnapshot.revert" />'
 };
 </script>

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/bb7cd90b/ui/index.jsp
----------------------------------------------------------------------
diff --git a/ui/index.jsp b/ui/index.jsp
index 5481da8..5821937 100644
--- a/ui/index.jsp
+++ b/ui/index.jsp
@@ -1673,6 +1673,8 @@ under the License.
     <script type="text/javascript" src="scripts/system.js?t=<%=now%>"></script>
     <script type="text/javascript" src="scripts/domains.js?t=<%=now%>"></script>
     <script type="text/javascript" src="scripts/docs.js?t=<%=now%>"></script>
+    <script type="text/javascript" src="scripts/domains.js?t=<%=now%>"></script>   
+    <script type="text/javascript" src="scripts/vm_snapshots.js?t=<%=now%>"></script>  
   </body>
 </html>
 <jsp:include page="dictionary.jsp" />

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/bb7cd90b/ui/scripts/instances.js
----------------------------------------------------------------------
diff --git a/ui/scripts/instances.js b/ui/scripts/instances.js
index 5b5ed18..d9542a1 100644
--- a/ui/scripts/instances.js
+++ b/ui/scripts/instances.js
@@ -48,7 +48,15 @@
 				displayname: { label: 'label.display.name' },
         zonename: { label: 'label.zone.name' },
         state: {
-          label: 'label.state',         
+          label: 'label.state',
+          converter: function(data) {
+            if(data == 'StoppedSnapshotting' || data == 'RunningSnapshotting')
+              return  'Snapshotting';
+            if(data == 'RevertingToRunning' || data == 'RevertingToStopped'){
+              return 'Reverting';
+            } 
+            return data;
+          },          
           indicator: {
             'Running': 'on',
             'Stopped': 'off',
@@ -153,6 +161,194 @@
           notification: {
             poll: pollAsyncJobResult
           }
+        },
+        start: {
+          label: 'label.action.start.instance' ,
+          action: function(args) {
+            $.ajax({
+              url: createURL("startVirtualMachine&id=" + args.context.instances[0].id),
+              dataType: "json",
+              async: true,
+              success: function(json) {
+                var jid = json.startvirtualmachineresponse.jobid;
+                args.response.success(
+                  {_custom:
+                   {jobId: jid,
+                    getUpdatedItem: function(json) {
+                      return json.queryasyncjobresultresponse.jobresult.virtualmachine;
+                    },
+                    getActionFilter: function() {
+                      return vmActionfilter;
+                    }
+                   }
+                  }
+                );
+              }
+            });
+          },
+          messages: {
+            confirm: function(args) {
+              return 'message.action.start.instance';
+            },
+            notification: function(args) {
+              return 'label.action.start.instance';
+            },
+						complete: function(args) {						  
+							if(args.password != null) {
+								alert('Password of the VM is ' + args.password);
+							}
+							return 'label.action.start.instance';
+						}			
+          },
+          notification: {
+            poll: pollAsyncJobResult
+          }
+        },
+        stop: {
+          label: 'label.action.stop.instance',
+          addRow: 'false',
+          createForm: {
+            title: 'label.action.stop.instance',
+            desc: 'message.action.stop.instance',
+            fields: {
+              forced: {
+                label: 'force.stop',
+                isBoolean: true,
+                isChecked: false
+              }
+            }
+          },
+          action: function(args) {
+            var array1 = [];
+            array1.push("&forced=" + (args.data.forced == "on"));
+            $.ajax({
+              url: createURL("stopVirtualMachine&id=" + args.context.instances[0].id + array1.join("")),
+              dataType: "json",
+              async: true,
+              success: function(json) {
+                var jid = json.stopvirtualmachineresponse.jobid;
+                args.response.success(
+                  {_custom:
+                   {jobId: jid,
+                    getUpdatedItem: function(json) {
+                      return json.queryasyncjobresultresponse.jobresult.virtualmachine;
+                    },
+                    getActionFilter: function() {
+                      return vmActionfilter;
+                    }
+                   }
+                  }
+                );
+              }
+            });
+          },
+          messages: {
+            confirm: function(args) {
+              return 'message.action.stop.instance';
+            },
+
+            notification: function(args) {
+              return 'label.action.stop.instance';
+            }
+          },
+          notification: {
+            poll: pollAsyncJobResult
+          }
+        },
+        restart: {
+          label: 'instances.actions.reboot.label',
+          action: function(args) {
+            $.ajax({
+              url: createURL("rebootVirtualMachine&id=" + args.context.instances[0].id),
+              dataType: "json",
+              async: true,
+              success: function(json) {
+                var jid = json.rebootvirtualmachineresponse.jobid;
+                args.response.success(
+                  {_custom:
+                   {jobId: jid,
+                    getUpdatedItem: function(json) {
+                      return json.queryasyncjobresultresponse.jobresult.virtualmachine;
+                    },
+                    getActionFilter: function() {
+                      return vmActionfilter;
+                    }
+                   }
+                  }
+                );
+              }
+            });
+          },
+          messages: {
+            confirm: function(args) {
+              return 'message.action.reboot.instance';
+            },
+            notification: function(args) {
+              return 'instances.actions.reboot.label';
+            }
+          },
+          notification: {
+            poll: pollAsyncJobResult
+          }
+        },
+
+        destroy: {
+          label: 'label.action.destroy.instance',
+          messages: {
+            confirm: function(args) {
+              return 'message.action.destroy.instance';
+            },            
+            notification: function(args) {
+              return 'label.action.destroy.instance';
+            }
+          },
+          action: function(args) {
+            $.ajax({
+              url: createURL("destroyVirtualMachine&id=" + args.context.instances[0].id),
+              dataType: "json",
+              async: true,
+              success: function(json) {
+                var jid = json.destroyvirtualmachineresponse.jobid;
+                args.response.success(
+                  {_custom:
+                   {jobId: jid,
+                    getUpdatedItem: function(json) {
+                      return json.queryasyncjobresultresponse.jobresult.virtualmachine;
+                    },
+                    getActionFilter: function() {
+                      return vmActionfilter;
+                    }
+                   }
+                  }
+                );
+              }
+            });
+          },
+          notification: {
+            poll: pollAsyncJobResult
+          }
+        },
+        restore: {     
+					label: 'label.action.restore.instance',
+					messages: {
+						confirm: function(args) {
+							return 'message.action.restore.instance';
+						},
+						notification: function(args) {
+							return 'label.action.restore.instance';
+						}
+					},					
+          action: function(args) {
+            $.ajax({
+              url: createURL("recoverVirtualMachine&id=" + args.context.instances[0].id),
+              dataType: "json",
+              async: true,
+              success: function(json) {
+                var item = json.recovervirtualmachineresponse.virtualmachine;
+                args.response.success({data:item});
+              }
+            });
+          }
         }
       },
 
@@ -231,7 +427,7 @@
 
       detailView: {
         name: 'Instance details',
-        viewAll: { path: 'storage.volumes', label: 'label.volumes' },
+        viewAll: [{ path: 'storage.volumes', label: 'label.volumes' }, { path: 'vmsnapshots', label: 'Snapshots' } ],
         tabFilter: function(args) {
           var hiddenTabs = [];
 					
@@ -409,6 +605,70 @@
               poll: pollAsyncJobResult
             }
           },
+
+          snapshot: {
+            messages: {
+              notification: function(args) {
+                return 'label.action.vmsnapshot.create';
+              }
+            },
+            label: 'label.action.vmsnapshot.create',
+            addRow: 'false',
+            createForm: {
+              title: 'label.action.vmsnapshot.create',
+              fields: {
+                name: {
+                  label: 'label.name',
+                  isInput: true
+                },
+                description: {
+                  label: 'label.description',
+                  isTextarea: true
+                },
+                snapshotMemory: {
+                  label: 'label.vmsnapshot.memory',
+                  isBoolean: true,
+                  isChecked: false
+                }
+              }
+            },
+            action: function(args) {
+              var array1 = [];
+              array1.push("&snapshotmemory=" + (args.data.snapshotMemory == "on"));
+              var displayname = args.data.name;
+              if (displayname != null && displayname.length > 0) {
+                array1.push("&name=" + todb(displayname));
+              }
+              var description = args.data.description;
+              if (description != null && description.length > 0) {
+                array1.push("&description=" + todb(description));
+              }
+              $.ajax({
+                url: createURL("createVMSnapshot&virtualmachineid=" + args.context.instances[0].id + array1.join("")),
+                dataType: "json",
+                async: true,
+                success: function(json) {
+                  var jid = json.createvmsnapshotresponse.jobid;
+                  args.response.success({
+                    _custom: {
+                      jobId: jid,
+                      getUpdatedItem: function(json) {
+                        return json.queryasyncjobresultresponse.jobresult.virtualmachine;
+                      },
+                      getActionFilter: function() {
+                        return vmActionfilter;
+                      }
+                    }
+                  });
+                }
+              });
+          
+            },
+            notification: {
+              pool: pollAsyncJobResult
+            }
+          },          
+
           destroy: {
             label: 'label.action.destroy.instance',
             compactLabel: 'label.destroy',
@@ -1232,6 +1492,7 @@
     else if (jsonObj.state == 'Running') {     
       allowedActions.push("stop");
       allowedActions.push("restart");
+      allowedActions.push("snapshot");
       allowedActions.push("destroy");
       allowedActions.push("changeService");
       allowedActions.push("reset");
@@ -1257,7 +1518,7 @@
       allowedActions.push("start");
       allowedActions.push("destroy");
       allowedActions.push("reset");
-
+      allowedActions.push("snapshot");
       if(isAdmin())
         allowedActions.push("migrateToAnotherStorage");
 

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/bb7cd90b/ui/scripts/ui/widgets/detailView.js
----------------------------------------------------------------------
diff --git a/ui/scripts/ui/widgets/detailView.js b/ui/scripts/ui/widgets/detailView.js
index 4b4fbd5..5f0c0f0 100644
--- a/ui/scripts/ui/widgets/detailView.js
+++ b/ui/scripts/ui/widgets/detailView.js
@@ -892,14 +892,20 @@
         $actions.prependTo($firstRow.closest('div.detail-group').closest('.details'));
       }
       if (detailViewArgs.viewAll && showViewAll) {
+       
+      if( !(detailViewArgs.viewAll instanceof Array)){
+       	detailViewArgs.viewAll = [detailViewArgs.viewAll];
+      }
+      $.each(detailViewArgs.viewAll, function(n, view){
         $('<div>')
           .addClass('view-all')
           .append(
             $('<a>')
               .attr({ href: '#' })
-              .data('detail-view-link-view-all', detailViewArgs.viewAll)
+              .css('padding','0 1px')
+              .data('detail-view-link-view-all', view)
               .append(
-                $('<span>').html(_l('label.view') + ' ' + _l(detailViewArgs.viewAll.label))
+                $('<span>').html(_l('label.view') + ' ' + _l(view.label))
               )
           )
           .append(
@@ -908,9 +914,10 @@
           .appendTo(
             $('<td>')
               .addClass('view-all')
+              .css('padding','9px 3px 8px 0')
               .appendTo($actions.find('tr'))
           );
-
+        });
       }
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/bb7cd90b/ui/scripts/vm_snapshots.js
----------------------------------------------------------------------
diff --git a/ui/scripts/vm_snapshots.js b/ui/scripts/vm_snapshots.js
new file mode 100644
index 0000000..0d6305b
--- /dev/null
+++ b/ui/scripts/vm_snapshots.js
@@ -0,0 +1,196 @@
+// 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.
+(function($, cloudStack) {
+  cloudStack.sections.vmsnapshots = {
+    title: 'label.vmsnapshot',
+    id: 'vmsnapshots',
+    listView: {
+      id: 'vmsnapshots',
+      isMaximized: true,
+      fields: {
+        displayname: {
+          label: 'label.name'
+        },
+        state: {
+          label: 'label.state',
+          indicator: {
+            'Ready': 'on',
+            'Error': 'off'
+          }
+        },
+        type:{
+          label: 'label.vmsnapshot.type'
+        },
+        current:{
+          label: 'label.vmsnapshot.current',
+          converter: cloudStack.converters.toBooleanText
+        },
+        parentName:{
+          label: 'label.vmsnapshot.parentname'
+        },
+        created: {
+          label: 'label.date',
+          converter: cloudStack.converters.toLocalDate
+        }
+      },
+
+      dataProvider: function(args) {
+        var apiCmd = "listVMSnapshot&listAll=true";
+        if (args.context != null) {
+          if ("instances" in args.context) {
+            apiCmd += "&virtualmachineid=" + args.context.instances[0].id;
+          }
+        }
+        $.ajax({
+          url: createURL(apiCmd),
+          dataType: "json",
+          async: true,
+          success: function(json) {
+            var jsonObj;
+            jsonObj = json.listvmsnapshotresponse.vmSnapshot;
+            args.response.success({
+              data: jsonObj
+            });
+          }
+        });
+      },
+      //dataProvider end
+      detailView: {
+        tabs: {
+          details: {
+            title: 'label.details',
+            fields: {
+              id: {
+                label: 'label.id'
+              },
+              name: {
+                label: 'label.name'
+              },
+              displayname: {
+                label: 'label.display.name',
+              },
+              type: {
+                label: 'label.vmsnapshot.type',
+              },
+              description: {
+                label: 'label.description',
+              },
+              state: {
+                label: 'label.state',
+                indicator: {
+                  'Ready': 'on',
+                  'Error': 'off'
+                }
+              },
+              current: {
+              	label: 'label.vmsnapshot.current',
+              	converter: cloudStack.converters.toBooleanText
+              },              
+              parentName: {
+              	label: 'label.vmsnapshot.parentname'
+              },
+              created: {
+                label: 'label.date',
+                converter: cloudStack.converters.toLocalDate
+              },
+
+            },
+            dataProvider: function(args) {
+              $.ajax({
+                url: createURL("listVMSnapshot&listAll=true&id=" + args.context.vmsnapshots[0].id),
+                dataType: "json",
+                async: true,
+                success: function(json) {
+                  var jsonObj;
+                  jsonObj = json.listvmsnapshotresponse.vmSnapshot[0];
+                  args.response.success({
+                    //actionFilter: vmActionfilter,
+                    data: jsonObj
+                  });
+                }
+              });
+            },
+            tags: cloudStack.api.tags({ resourceType: 'VMSnapshot', contextId: 'vmsnapshots' })
+          }
+        },
+        actions: {
+          //delete a snapshot
+          remove: {
+            label: 'label.action.vmsnapshot.delete',
+            messages: {
+              confirm: function(args) {
+                return 'message.action.vmsnapshot.delete';
+              },
+              notification: function(args) {
+                return 'label.action.vmsnapshot.delete';
+              }
+            },
+            action: function(args) {
+              $.ajax({
+                url: createURL("deleteVMSnapshot&vmsnapshotid=" + args.context.vmsnapshots[0].id),
+                dataType: "json",
+                async: true,
+                success: function(json) {
+                  var jid = json.deletevmsnapshotresponse.jobid;
+                  args.response.success(
+                    {_custom:
+                     {jobId: jid}
+                    }
+                  );
+                }
+              });
+            },
+            notification: {
+              poll: pollAsyncJobResult
+            }
+          },
+          restart: {
+            label: 'label.action.vmsnapshot.revert',
+            messages: {
+              confirm: function(args) {
+                return 'label.action.vmsnapshot.revert';
+              },
+              notification: function(args) {
+                return 'message.action.vmsnapshot.revert';
+              }
+            },
+            action: function(args) {
+              $.ajax({
+                url: createURL("revertToSnapshot&vmsnapshotid=" + args.context.vmsnapshots[0].id),
+                dataType: "json",
+                async: true,
+                success: function(json) {
+                  var jid = json.reverttosnapshotresponse.jobid;
+                  args.response.success({
+                    _custom: {
+                      jobId: jid
+                    }
+                  });
+                }
+              });
+
+            },
+            notification: {
+              poll: pollAsyncJobResult
+            }
+          }
+        }
+      }
+      //detailview end
+    }
+  }
+})(jQuery, cloudStack);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/bb7cd90b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java
----------------------------------------------------------------------
diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java
index a765b42..40343b6 100755
--- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java
+++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/HostMO.java
@@ -58,6 +58,8 @@ import com.vmware.vim25.PropertySpec;
 import com.vmware.vim25.SelectionSpec;
 import com.vmware.vim25.TraversalSpec;
 import com.vmware.vim25.VirtualMachineConfigSpec;
+import com.vmware.vim25.VirtualMachineSnapshotInfo;
+import com.vmware.vim25.VirtualMachineSnapshotTree;
 import com.vmware.vim25.VirtualNicManagerNetConfig;
 import com.vmware.vim25.NasDatastoreInfo;
 
@@ -949,4 +951,20 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost {
     	HostRuntimeInfo runtimeInfo = (HostRuntimeInfo)_context.getServiceUtil().getDynamicProperty(_mor, "runtime");
     	return runtimeInfo.getConnectionState() == HostSystemConnectionState.connected;
 	}
+	
+    public boolean revertToSnapshot(ManagedObjectReference morSnapshot)
+            throws Exception {
+        ManagedObjectReference morTask = _context.getService()
+                .revertToSnapshot_Task(morSnapshot, _mor, false);
+        String result = _context.getServiceUtil().waitForTask(morTask);
+        if (result.equals("sucess")) {
+            _context.waitForTaskProgressDone(morTask);
+            return true;
+        } else {
+            s_logger.error("VMware revert to snapshot failed due to "
+                    + TaskMO.getTaskFailureInfo(_context, morTask));
+        }
+
+        return false;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/bb7cd90b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java
----------------------------------------------------------------------
diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java
index cd54127..d36814d 100644
--- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java
+++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java
@@ -405,6 +405,26 @@ public class VirtualMachineMO extends BaseMO {
 		return false;
 	}
 	
+    public boolean revertToSnapshot(String snapshotName) throws Exception {
+        ManagedObjectReference morSnapshot = getSnapshotMor(snapshotName);
+        if (morSnapshot == null) {
+            s_logger.warn("Unable to find snapshot: " + snapshotName);
+            return false;
+        }
+        ManagedObjectReference morTask = _context.getService()
+                .revertToSnapshot_Task(morSnapshot, _mor, null);
+        String result = _context.getServiceUtil().waitForTask(morTask);
+        if (result.equals("sucess")) {
+            _context.waitForTaskProgressDone(morTask);
+            return true;
+        } else {
+            s_logger.error("VMware revert to snapshot failed due to "
+                    + TaskMO.getTaskFailureInfo(_context, morTask));
+        }
+
+        return false;
+    }
+	
 	public boolean removeAllSnapshots() throws Exception {
 		VirtualMachineSnapshotInfo snapshotInfo = getSnapshotInfo();
 		


Mime
View raw message