You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by mi...@apache.org on 2012/11/18 11:45:36 UTC

[2/3] submit vm snapshots related codes based on 4.0 branch

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/600fb904/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
index a19022c..43d26f9 100755
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
@@ -25,9 +25,7 @@ import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
-import java.lang.reflect.InvocationTargetException;
 import java.net.InetAddress;
-import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
 import java.net.URLConnection;
@@ -44,7 +42,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
-import java.util.Set;
+import java.util.Stack;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
@@ -82,12 +80,16 @@ import com.cloud.agent.api.Command;
 import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand;
 import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand;
 import com.cloud.agent.api.CreateStoragePoolCommand;
+import com.cloud.agent.api.CreateVMSnapshotAnswer;
+import com.cloud.agent.api.CreateVMSnapshotCommand;
 import com.cloud.agent.api.CreateVolumeFromSnapshotAnswer;
 import com.cloud.agent.api.CreateVolumeFromSnapshotCommand;
 import com.cloud.agent.api.DeleteSnapshotBackupAnswer;
 import com.cloud.agent.api.DeleteSnapshotBackupCommand;
 import com.cloud.agent.api.DeleteSnapshotsDirCommand;
 import com.cloud.agent.api.DeleteStoragePoolCommand;
+import com.cloud.agent.api.DeleteVMSnapshotAnswer;
+import com.cloud.agent.api.DeleteVMSnapshotCommand;
 import com.cloud.agent.api.FenceAnswer;
 import com.cloud.agent.api.FenceCommand;
 import com.cloud.agent.api.GetHostStatsAnswer;
@@ -124,6 +126,8 @@ import com.cloud.agent.api.ReadyCommand;
 import com.cloud.agent.api.RebootAnswer;
 import com.cloud.agent.api.RebootCommand;
 import com.cloud.agent.api.RebootRouterCommand;
+import com.cloud.agent.api.RevertToVMSnapshotAnswer;
+import com.cloud.agent.api.RevertToVMSnapshotCommand;
 import com.cloud.agent.api.SecurityGroupRuleAnswer;
 import com.cloud.agent.api.SecurityGroupRulesCmd;
 import com.cloud.agent.api.SetupGuestNetworkAnswer;
@@ -138,6 +142,7 @@ import com.cloud.agent.api.StopCommand;
 import com.cloud.agent.api.UnPlugNicAnswer;
 import com.cloud.agent.api.UnPlugNicCommand;
 import com.cloud.agent.api.UpgradeSnapshotCommand;
+import com.cloud.agent.api.VMSnapshotTO;
 import com.cloud.agent.api.VmStatsEntry;
 import com.cloud.agent.api.check.CheckSshAnswer;
 import com.cloud.agent.api.check.CheckSshCommand;
@@ -165,11 +170,18 @@ import com.cloud.agent.api.to.NicTO;
 import com.cloud.agent.api.to.StorageFilerTO;
 import com.cloud.agent.api.to.VirtualMachineTO;
 import com.cloud.agent.api.to.VolumeTO;
+import com.cloud.agent.resource.virtualnetwork.VirtualRoutingResource;
+import com.cloud.dc.Vlan;
+import com.cloud.exception.InternalErrorException;
+import com.cloud.host.Host.Type;
+import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.hypervisor.kvm.resource.KVMHABase.NfsStoragePool;
+import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ClockDef;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ConsoleDef;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.CpuTuneDef;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DevicesDef;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef;
+import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef.diskBus;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef.diskProtocol;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.FeaturesDef;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.GraphicDef;
@@ -180,16 +192,10 @@ import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef.hostNicType;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.SerialDef;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.TermPolicy;
-import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ClockDef;
-import com.cloud.agent.resource.virtualnetwork.VirtualRoutingResource;
 import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk;
 import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk.PhysicalDiskFormat;
 import com.cloud.hypervisor.kvm.storage.KVMStoragePool;
 import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager;
-import com.cloud.dc.Vlan;
-import com.cloud.exception.InternalErrorException;
-import com.cloud.host.Host.Type;
-import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.network.Networks.BroadcastDomainType;
 import com.cloud.network.Networks.IsolationType;
 import com.cloud.network.Networks.RouterPrivateIpStrategy;
@@ -218,8 +224,8 @@ import com.cloud.utils.script.Script;
 import com.cloud.vm.DiskProfile;
 import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.VirtualMachine.State;
+import com.cloud.vm.snapshot.VMSnapshot;
 import com.cloud.vm.VirtualMachineName;
-
 /**
  * LibvirtComputingResource execute requests on the computing/routing host using
  * the libvirt API
@@ -879,7 +885,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements
             if (failed) {
                 dmOld = conn.domainLookupByUUID(UUID.nameUUIDFromBytes(vmName
                         .getBytes()));
-                dmOld.undefine();
+                // undefine(2) removes any vm snapshots metadata associated with this domain
+                dmOld.undefineFlags(2);
                 dmNew = conn.domainDefineXML(domainXML);
             }
         } catch (final LibvirtException e) {
@@ -1061,7 +1068,14 @@ public class LibvirtComputingResource extends ServerResourceBase implements
                 return execute((CopyVolumeCommand) cmd);
             } else if (cmd instanceof CheckNetworkCommand) {
                 return execute((CheckNetworkCommand) cmd);
-            } else {
+            }else if (cmd instanceof DeleteVMSnapshotCommand){
+                return execute((DeleteVMSnapshotCommand)cmd);
+            }else if (cmd instanceof RevertToVMSnapshotCommand){
+            	return execute((RevertToVMSnapshotCommand)cmd);
+            }else if (cmd instanceof CreateVMSnapshotCommand){
+            	return execute((CreateVMSnapshotCommand)cmd);
+            }
+            else {
                 s_logger.warn("Unsupported command ");
                 return Answer.createUnsupportedCommandAnswer(cmd);
             }
@@ -1069,7 +1083,229 @@ public class LibvirtComputingResource extends ServerResourceBase implements
             return new Answer(cmd, false, e.getMessage());
         }
     }
+    
+    private Answer execute(DeleteVMSnapshotCommand cmd) {
+        VMSnapshotTO target = cmd.getTarget();
+        String vmName = cmd.getVmName();
+        Domain dm = null;
+        try{
+            Connect conn = LibvirtConnection.getConnection();
+            try{
+                dm = getDomain(conn,vmName);
+            }catch (Exception e) {
+                s_logger.debug("Cannot find domain:" + vmName + ", create a working VM for VM snapshot.");
+            }
+            // create a work VM with volumes path info if domain does not exist
+            if(dm == null){
+                dm = createWorkVM(vmName,cmd.getGuestOSType(),cmd.getVolumeTOs(),conn);
+            }
+                
+            DomainSnapshot snapshot = null;
+            try{
+                snapshot = dm.snapshotLookupByName(target.getSnapshotName());
+            }catch (Exception e) {
+                s_logger.debug("Can not find snapshot " + target.getSnapshotName() 
+                         +" for VM: " + vmName +", redefine it on the fly");
+            }
+            // redefine VM snapshot's meta data if it does not exist on the agent
+            if(snapshot == null){
+                snapshot = redefineSnapshot(conn,target,dm);
+            }
+            int ret = snapshot.delete(0);
+            if(ret == 0){
+                return new DeleteVMSnapshotAnswer(cmd,cmd.getVolumeTOs());
+            }
+            
+        }catch (Exception e) {
+            return new DeleteVMSnapshotAnswer(cmd,false,e.getMessage());
+        }
+        return new DeleteVMSnapshotAnswer(cmd,false,"Failed to delete snapshot " + cmd.getTarget().getSnapshotName());
+    
+    }
 
+    private Domain createWorkVM(String vmName, String osType, List<VolumeTO> volumeTOs, Connect conn) throws LibvirtException {
+        VirtualMachineTO vmTO = new VirtualMachineTO(0, vmName, com.cloud.vm.VirtualMachine.Type.User, 1, 100, 128, 128, null, osType, false, false, null);
+        LibvirtVMDef vm = createVMFromSpec(vmTO);
+        for (VolumeTO volume : volumeTOs) {
+            DiskDef disk = new DiskDef();
+            KVMStoragePool pool = _storagePoolMgr.getStoragePool(volume.getPoolUuid());
+            KVMPhysicalDisk physicalDisk = pool.getPhysicalDisk(volume.getPath());
+            disk.defFileBasedDisk(physicalDisk.getPath(), new Long(volume.getDeviceId()).intValue(), diskBus.VIRTIO, DiskDef.diskFmtType.QCOW2);
+            vm.getDevices().addDevice(disk);
+        }
+        return conn.domainDefineXML(vm.toString());
+        
+    }
+    
+    
+    
+    private Answer execute(CreateVMSnapshotCommand cmd) {
+        MessageFormat snapshotDef = new MessageFormat("<domainsnapshot><name>{0}</name><memory snapshot=\"{1}\"/> </domainsnapshot>");
+        String errorMsg = "";
+        VMSnapshotTO target = cmd.getTarget();
+        String vmName = cmd.getVmName();
+        Domain dm = null;
+        try{
+            Connect conn = LibvirtConnection.getConnection();
+            try{
+                dm = conn.domainLookupByUUID(UUID.nameUUIDFromBytes(vmName.getBytes()));
+            }catch (Exception e) {
+                s_logger.debug("Cannot find domain:" + vmName + ", create a working VM for VM snapshot.");
+            }
+            
+            // if snapshot already exists, return it
+            if(dm != null){
+                DomainSnapshot existingSnapshot = null;
+                try{
+                    existingSnapshot = dm.snapshotLookupByName(cmd.getTarget().getSnapshotName());
+                }catch (Exception e) {
+                }
+                if(existingSnapshot != null){
+                    s_logger.debug("VM snapshot:" + cmd.getTarget().getSnapshotName() +" already exists");
+                    return new CreateVMSnapshotAnswer(cmd,target,cmd.getVolumeTOs());
+                }
+            }
+            
+            // if domain does not exist, define it as a work vm
+            if(dm == null){
+                dm = createWorkVM(vmName,cmd.getGuestOSType(),cmd.getVolumeTOs(),conn);
+            }
+            DomainInfo.DomainState ps = dm.getInfo().state;
+            State state = convertToState(ps);
+            if((cmd.getVmState() == State.Running || cmd.getVmState() == State.RunningSnapshotting) &&  state != State.Running){
+                errorMsg = "Cannot create VM snapshot " + cmd.getTarget().getSnapshotName() + 
+                        " expected VM state: running/runningSnapshoting, actual state: " + state + ", for VM " + vmName;
+                s_logger.error(errorMsg);
+                return new CreateVMSnapshotAnswer(cmd, false, errorMsg);
+            }
+            
+            if((cmd.getVmState() == State.Stopped || cmd.getVmState() == State.StoppedSnapshotting) &&  state != State.Stopped){
+                errorMsg = "Cannot create VM snapshot " + cmd.getTarget().getSnapshotName() + 
+                        " expected VM state: stopped/stoppedSnapshoting, actual state: " + state + ", for VM " + vmName;
+                s_logger.error(errorMsg);
+                return new CreateVMSnapshotAnswer(cmd, false, errorMsg);
+            }
+           
+            if(cmd.getTarget().getParent() != null && cmd.getTarget().getParent().getSnapshotName() != null){ 
+                redefineSnapshot(conn, cmd.getTarget().getParent(), dm); // redefine current snapshot and its parents
+            }
+            DomainSnapshot snapshot = dm.snapshotCreateXML(snapshotDef.format(new Object[] { target.getSnapshotName(), target.getType() == VMSnapshot.Type.DiskAndMemory?"yes":"no" }));
+            if(snapshot != null)
+                return new CreateVMSnapshotAnswer(cmd,target,cmd.getVolumeTOs());
+        }catch (Exception e) {
+            s_logger.error(e.getMessage());
+            errorMsg = e.getMessage();
+        }
+        
+        return new CreateVMSnapshotAnswer(cmd, false, errorMsg);
+    }
+
+    private Answer execute(RevertToVMSnapshotCommand cmd) {
+        VMSnapshotTO target = cmd.getTarget();
+        String vmName = cmd.getVmName();
+        boolean memorySnapshot = cmd.getTarget().getType() == VMSnapshot.Type.DiskAndMemory;
+        Domain dm = null;
+        try{
+            Connect conn = LibvirtConnection.getConnection();
+            // get domain
+            try{
+                dm = getDomain(conn,vmName);
+            }catch (Exception e) {
+                s_logger.debug("Cannot find domain:" + vmName + ", create a working VM for VM snapshot.");
+            }
+            // Wait for the domain gets into running or shutoff state 
+            int retry = 30;
+            if(dm != null){
+                
+                while ((dm.getInfo().state != DomainInfo.DomainState.VIR_DOMAIN_RUNNING 
+                        || dm.getInfo().state != DomainInfo.DomainState.VIR_DOMAIN_SHUTOFF
+                        || dm.getInfo().state != DomainInfo.DomainState.VIR_DOMAIN_SHUTDOWN )
+                        && (retry >= 0)) {
+                    Thread.sleep(2000);
+                    retry--;
+                }
+            }
+            // define a work VM if domain is nll
+            if(dm == null){
+            	if(memorySnapshot)
+            		return new RevertToVMSnapshotAnswer(cmd,false,
+            				"Cannot revert to VM snapshot, expected VM state: running, actual state: stopped, for VM " + vmName);
+            	else
+            		dm = createWorkVM(vmName,cmd.getGuestOSType(),cmd.getVolumeTOs(),conn);
+            }
+            // check if snapshot exists    
+            DomainSnapshot snapshot = null;
+            try{
+                snapshot = dm.snapshotLookupByName(target.getSnapshotName());
+            }catch (Exception e) {
+                s_logger.debug("Can not find snapshot " + target.getSnapshotName() 
+                		+ " for VM: " + vmName +", redefine it on the fly");
+            }
+            // redefine snapshot if it does not exist on agent
+            if(snapshot == null){
+                snapshot = redefineSnapshot(conn,target,dm);
+            }
+            int ret = dm.revertToSnapshot(snapshot);
+            if(ret == 0){
+                DomainInfo.DomainState state = null;
+                dm = getDomain(conn, cmd.getVmName());
+                state = dm.getInfo().state;
+                State vmState = convertToState(state);
+                if(vmState == State.Running && memorySnapshot || vmState == State.Stopped && !memorySnapshot )
+                	return new RevertToVMSnapshotAnswer(cmd,cmd.getVolumeTOs(),vmState);
+            }
+            
+        }catch (Exception e) {
+        	return new RevertToVMSnapshotAnswer(cmd,false,e.getMessage());
+        }
+        return new RevertToVMSnapshotAnswer(cmd,false,"Internal error, failed to revert to snapshot");
+    }
+    
+    private DomainSnapshot redefineSnapshot(Connect conn, VMSnapshotTO vmSnapshotTo,Domain dm) throws LibvirtException {
+        String targetName = vmSnapshotTo.getSnapshotName();
+        MessageFormat parentDef = new MessageFormat("<parent><name>{0}</name></parent>");
+        MessageFormat def = new MessageFormat("<domainsnapshot><name>{0}</name><state>{1}</state><creationTime>{2}</creationTime>");
+        String defEnd ="</domainsnapshot>";
+        VMSnapshotTO snapshotTO = null;
+        // Pair<snapshotDef, isCurrent>
+        Stack<Pair<String,Boolean>> SnapshotDefs = new Stack<Pair<String,Boolean>>();
+        do{
+            snapshotTO = (snapshotTO == null ? vmSnapshotTo : snapshotTO.getParent());
+            String snapshotName = snapshotTO.getSnapshotName();
+            
+            String snapshotState = snapshotTO.getType() == VMSnapshot.Type.DiskAndMemory?"running":"shutoff";
+            
+            DomainInfo.DomainState ps = dm.getInfo().state;
+            State dmState = convertToState(ps);
+           
+            // libvirt does not allow to re-define a running state snapshot as current on a stopped VM
+            if(dmState == State.Stopped && snapshotTO.getType() == VMSnapshot.Type.DiskAndMemory && snapshotTO.getCurrent())
+                snapshotState = "shutdown";
+            
+            Long creationTime = snapshotTO.getCreateTime();
+            Object[] args = new Object[] { snapshotName, snapshotState, creationTime/1000 + "" };
+            String snapshotDef = def.format(args);
+            if(snapshotTO.getParent()!=null){
+                String parentUUID = snapshotTO.getParent().getSnapshotName();
+                snapshotDef = snapshotDef + parentDef.format(new Object[] {parentUUID});
+            }
+            String domainDef = dm.getXMLDesc(0);
+            Pair<String,Boolean> pair = new Pair<String, Boolean>(snapshotDef + domainDef + defEnd, snapshotTO.getCurrent());
+            SnapshotDefs.push(pair);
+        }while(snapshotTO.getParent() != null);
+       
+        while(!SnapshotDefs.isEmpty()){
+            Pair<String,Boolean> snapshotDef = SnapshotDefs.pop();
+            
+            if(snapshotDef.second())
+                dm.snapshotCreateXML(snapshotDef.first(),3);
+            else
+                dm.snapshotCreateXML(snapshotDef.first(),1);
+        }
+        
+        return dm.snapshotLookupByName(targetName);
+    }
+    
     private CheckNetworkAnswer execute(CheckNetworkCommand cmd) {
         List<PhysicalNetworkSetupInfo> phyNics = cmd
                 .getPhysicalNetworkInfoList();
@@ -3704,7 +3940,8 @@ public class LibvirtComputingResource extends ServerResourceBase implements
             try {
                 dm = conn.domainLookupByUUID(UUID.nameUUIDFromBytes(vmName
                         .getBytes()));
-                dm.undefine();
+                // undefineFlags(2) removes any vm snapshots metadata associated with this domain
+                dm.undefineFlags(2);
             } catch (LibvirtException e) {
 
             } finally {

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/600fb904/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManager.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManager.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManager.java
old mode 100644
new mode 100755
index f27e026..a2e517d
--- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManager.java
+++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManager.java
@@ -20,15 +20,21 @@ import com.cloud.agent.api.Answer;
 import com.cloud.agent.api.BackupSnapshotCommand;
 import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand;
 import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand;
+import com.cloud.agent.api.CreateVMSnapshotCommand;
 import com.cloud.agent.api.CreateVolumeFromSnapshotCommand;
+import com.cloud.agent.api.DeleteVMSnapshotCommand;
+import com.cloud.agent.api.RevertToVMSnapshotCommand;
 import com.cloud.agent.api.storage.CopyVolumeCommand;
 import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand;
 
 public interface VmwareStorageManager {
     Answer execute(VmwareHostService hostService, PrimaryStorageDownloadCommand cmd);
-	Answer execute(VmwareHostService hostService, BackupSnapshotCommand cmd);
-	Answer execute(VmwareHostService hostService, CreatePrivateTemplateFromVolumeCommand cmd);
-	Answer execute(VmwareHostService hostService, CreatePrivateTemplateFromSnapshotCommand cmd);
-	Answer execute(VmwareHostService hostService, CopyVolumeCommand cmd);
-	Answer execute(VmwareHostService hostService, CreateVolumeFromSnapshotCommand cmd);
+    Answer execute(VmwareHostService hostService, BackupSnapshotCommand cmd);
+    Answer execute(VmwareHostService hostService, CreatePrivateTemplateFromVolumeCommand cmd);
+    Answer execute(VmwareHostService hostService, CreatePrivateTemplateFromSnapshotCommand cmd);
+    Answer execute(VmwareHostService hostService, CopyVolumeCommand cmd);
+    Answer execute(VmwareHostService hostService, CreateVolumeFromSnapshotCommand cmd);
+    Answer execute(VmwareHostService hostService, CreateVMSnapshotCommand cmd);
+    Answer execute(VmwareHostService hostService, DeleteVMSnapshotCommand cmd);
+    Answer execute(VmwareHostService hostService, RevertToVMSnapshotCommand cmd);
 }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/600fb904/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java
old mode 100644
new mode 100755
index 8650274..c7b7cd3
--- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java
+++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/manager/VmwareStorageManagerImpl.java
@@ -22,6 +22,7 @@ import java.io.FileOutputStream;
 import java.io.OutputStreamWriter;
 import java.rmi.RemoteException;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
@@ -32,18 +33,27 @@ import com.cloud.agent.api.BackupSnapshotAnswer;
 import com.cloud.agent.api.BackupSnapshotCommand;
 import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand;
 import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand;
+import com.cloud.agent.api.CreateVMSnapshotAnswer;
+import com.cloud.agent.api.CreateVMSnapshotCommand;
 import com.cloud.agent.api.CreateVolumeFromSnapshotAnswer;
 import com.cloud.agent.api.CreateVolumeFromSnapshotCommand;
+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.storage.CopyVolumeAnswer;
 import com.cloud.agent.api.storage.CopyVolumeCommand;
 import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer;
 import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer;
 import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand;
 import com.cloud.agent.api.to.StorageFilerTO;
+import com.cloud.agent.api.to.VolumeTO;
 import com.cloud.hypervisor.vmware.mo.CustomFieldConstants;
 import com.cloud.hypervisor.vmware.mo.DatacenterMO;
 import com.cloud.hypervisor.vmware.mo.DatastoreMO;
+import com.cloud.hypervisor.vmware.mo.HostMO;
 import com.cloud.hypervisor.vmware.mo.HypervisorHostHelper;
+import com.cloud.hypervisor.vmware.mo.TaskMO;
 import com.cloud.hypervisor.vmware.mo.VirtualMachineMO;
 import com.cloud.hypervisor.vmware.mo.VmwareHypervisorHost;
 import com.cloud.hypervisor.vmware.util.VmwareContext;
@@ -57,7 +67,11 @@ import com.cloud.utils.Pair;
 import com.cloud.utils.StringUtils;
 import com.cloud.utils.Ternary;
 import com.cloud.utils.script.Script;
+import com.cloud.vm.VirtualMachine;
+import com.cloud.vm.snapshot.VMSnapshot;
 import com.vmware.vim25.ManagedObjectReference;
+import com.vmware.vim25.TaskEvent;
+import com.vmware.vim25.TaskInfo;
 import com.vmware.vim25.VirtualDeviceConfigSpec;
 import com.vmware.vim25.VirtualDeviceConfigSpecOperation;
 import com.vmware.vim25.VirtualDisk;
@@ -222,8 +236,12 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager {
                 }
 				
 			} finally {
-                if(vmMo != null)
-                    vmMo.removeAllSnapshots();
+                if(vmMo != null){
+                    ManagedObjectReference snapshotMor = vmMo.getSnapshotMor(snapshotUuid);
+                    if (snapshotMor != null){
+                        vmMo.removeSnapshot(snapshotUuid, false);
+                    }
+                }
 			    
 				try {
 		            if (workerVm != null) {
@@ -377,47 +395,47 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager {
 	}
     
     @Override
-	public Answer execute(VmwareHostService hostService, CreateVolumeFromSnapshotCommand cmd) {
+    public Answer execute(VmwareHostService hostService, CreateVolumeFromSnapshotCommand cmd) {
 
-		String primaryStorageNameLabel = cmd.getPrimaryStoragePoolNameLabel();
-		Long accountId = cmd.getAccountId();
-		Long volumeId = cmd.getVolumeId();
-        String secondaryStorageUrl = cmd.getSecondaryStorageUrl();
-		String backedUpSnapshotUuid = cmd.getSnapshotUuid();
+            String primaryStorageNameLabel = cmd.getPrimaryStoragePoolNameLabel();
+            Long accountId = cmd.getAccountId();
+            Long volumeId = cmd.getVolumeId();
+            String secondaryStorageUrl = cmd.getSecondaryStorageUrl();
+            String backedUpSnapshotUuid = cmd.getSnapshotUuid();
 
-		String details = null;
-		boolean success = false;
-		String newVolumeName = UUID.randomUUID().toString().replaceAll("-", "");
+            String details = null;
+            boolean success = false;
+            String newVolumeName = UUID.randomUUID().toString().replaceAll("-", "");
 
-		VmwareContext context = hostService.getServiceContext(cmd);
-		try {
-			VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd);
-			
-			ManagedObjectReference morPrimaryDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, primaryStorageNameLabel);
-			if (morPrimaryDs == null) {
-				String msg = "Unable to find datastore: " + primaryStorageNameLabel;
-				s_logger.error(msg);
-				throw new Exception(msg);
-			}
+            VmwareContext context = hostService.getServiceContext(cmd);
+            try {
+                    VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd);
+                    ManagedObjectReference morPrimaryDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost,
+                            primaryStorageNameLabel);
+                    if (morPrimaryDs == null) {
+                        String msg = "Unable to find datastore: " + primaryStorageNameLabel;
+                        s_logger.error(msg);
+                        throw new Exception(msg);
+                    }
 
-			DatastoreMO primaryDsMo = new DatastoreMO(hyperHost.getContext(), morPrimaryDs);
-			details = createVolumeFromSnapshot(hyperHost, primaryDsMo,
-					newVolumeName, accountId, volumeId, secondaryStorageUrl, backedUpSnapshotUuid);
-			if (details == null) {
-				success = true;
-			}
-		} catch (Throwable e) {
-			if (e instanceof RemoteException) {
-				hostService.invalidateServiceContext(context);
-			}
+                    DatastoreMO primaryDsMo = new DatastoreMO(hyperHost.getContext(), morPrimaryDs);
+                    details = createVolumeFromSnapshot(hyperHost, primaryDsMo,
+                            newVolumeName, accountId, volumeId, secondaryStorageUrl, backedUpSnapshotUuid);
+                    if (details == null) {
+                        success = true;
+                    }
+            } catch (Throwable e) {
+                if (e instanceof RemoteException) {
+                    hostService.invalidateServiceContext(context);
+                }
+                
+                s_logger.error("Unexpecpted exception ", e);
+                details = "CreateVolumeFromSnapshotCommand exception: " + StringUtils.getExceptionStackInfo(e);
+            }
 
-			s_logger.error("Unexpecpted exception ", e);
-			details = "CreateVolumeFromSnapshotCommand exception: " + StringUtils.getExceptionStackInfo(e);
-		}
+            return new CreateVolumeFromSnapshotAnswer(cmd, success, details, newVolumeName);
+    }
 
-		return new CreateVolumeFromSnapshotAnswer(cmd, success, details, newVolumeName);
-	}
-	
     // templateName: name in secondary storage
     // templateUuid: will be used at hypervisor layer
     private void copyTemplateFromSecondaryToPrimary(VmwareHypervisorHost hyperHost, DatastoreMO datastoreMo, String secondaryStorageUrl,
@@ -881,4 +899,244 @@ public class VmwareStorageManagerImpl implements VmwareStorageManager {
     private static String getSnapshotRelativeDirInSecStorage(long accountId, long volumeId) {
         return "snapshots/" + accountId + "/" + volumeId;
     }
+
+    @Override
+    public CreateVMSnapshotAnswer execute(VmwareHostService hostService, CreateVMSnapshotCommand cmd) {
+        List<VolumeTO> volumeTOs = cmd.getVolumeTOs();
+        String vmName = cmd.getVmName();
+        String vmSnapshotName = cmd.getTarget().getSnapshotName();
+        String vmSnapshotDesc = cmd.getTarget().getDescription();
+        boolean snapshotMemory = cmd.getTarget().getType() == VMSnapshot.Type.DiskAndMemory;
+        VirtualMachineMO vmMo = null;
+        VmwareContext context = hostService.getServiceContext(cmd);
+        Map<String, String> mapNewDisk = new HashMap<String, String>();
+        try {
+            VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd);
+            
+            // wait if there are already VM snapshot task running
+            ManagedObjectReference taskmgr = context.getServiceContent().getTaskManager();
+            ManagedObjectReference[] tasks =  (ManagedObjectReference[]) context.getServiceUtil().getDynamicProperty(taskmgr, "recentTask");
+            for (ManagedObjectReference taskMor : tasks) {
+                TaskInfo info = (TaskInfo) (context.getServiceUtil().getDynamicProperty(taskMor, "info"));
+                if(info.getEntityName().equals(cmd.getVmName()) && info.getName().equalsIgnoreCase("CreateSnapshot_Task")){
+                    s_logger.debug("There is already a VM snapshot task running, wait for it");
+                    context.getServiceUtil().waitForTask(taskMor);
+                }
+            }
+            
+            vmMo = hyperHost.findVmOnHyperHost(vmName);
+            if(vmMo == null)
+                vmMo = hyperHost.findVmOnPeerHyperHost(vmName);
+            if (vmMo == null) {
+                String msg = "Unable to find VM for CreateVMSnapshotCommand";
+                s_logger.debug(msg);
+                return new CreateVMSnapshotAnswer(cmd, false, msg);
+            } else {
+                if (vmMo.getSnapshotMor(vmSnapshotName) != null){
+                    s_logger.debug("VM snapshot " + vmSnapshotName + " already exists");
+                }else if (!vmMo.createSnapshot(vmSnapshotName, vmSnapshotDesc, snapshotMemory, true)) {
+                    return new CreateVMSnapshotAnswer(cmd, false,
+                            "Unable to create snapshot due to esxi internal failed");
+                }
+                // find VM disk file path after creating snapshot
+                VirtualDisk[] vdisks = vmMo.getAllDiskDevice();
+                for (int i = 0; i < vdisks.length; i ++){
+                    @SuppressWarnings("deprecation")
+                    List<Pair<String, ManagedObjectReference>> vmdkFiles = vmMo.getDiskDatastorePathChain(vdisks[i], false);
+                    for(Pair<String, ManagedObjectReference> fileItem : vmdkFiles) {
+                        String vmdkName = fileItem.first().split(" ")[1];
+                        if ( vmdkName.endsWith(".vmdk")){
+                            vmdkName = vmdkName.substring(0, vmdkName.length() - (".vmdk").length());
+                        }
+                        String[] s = vmdkName.split("-");
+                        mapNewDisk.put(s[0], vmdkName);
+                    }
+                }
+                
+                // update volume path using maps
+                for (VolumeTO volumeTO : volumeTOs) {
+                    String parentUUID = volumeTO.getPath();
+                    String[] s = parentUUID.split("-");
+                    String key = s[0];
+                    volumeTO.setPath(mapNewDisk.get(key));
+                }
+                return new CreateVMSnapshotAnswer(cmd, cmd.getTarget(), volumeTOs);
+            }
+        } catch (Exception e) {
+            String msg = e.getMessage();
+            s_logger.error("failed to create snapshot for vm:" + vmName + " due to " + msg);
+            try {
+                if (vmMo.getSnapshotMor(vmSnapshotName) != null) {
+                    vmMo.removeSnapshot(vmSnapshotName, false);
+                }
+            } catch (Exception e1) {
+            }
+            return new CreateVMSnapshotAnswer(cmd, false, e.getMessage());
+        }
+    }
+
+	@Override
+    public DeleteVMSnapshotAnswer execute(VmwareHostService hostService, DeleteVMSnapshotCommand cmd) {
+        List<VolumeTO> listVolumeTo = cmd.getVolumeTOs();
+        VirtualMachineMO vmMo = null;
+        VmwareContext context = hostService.getServiceContext(cmd);
+        Map<String, String> mapNewDisk = new HashMap<String, String>();
+        String vmName = cmd.getVmName();
+        String vmSnapshotName = cmd.getTarget().getSnapshotName();
+        try {
+            VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd);
+            vmMo = hyperHost.findVmOnHyperHost(vmName);
+            if(vmMo == null)
+                vmMo = hyperHost.findVmOnPeerHyperHost(vmName);
+            if (vmMo == null) {
+                String msg = "Unable to find VM for RevertToVMSnapshotCommand";
+                s_logger.debug(msg);
+                return new DeleteVMSnapshotAnswer(cmd, false, msg);
+            } else {
+                if (vmMo.getSnapshotMor(vmSnapshotName) == null) {
+                    s_logger.debug("can not find the snapshot " + vmSnapshotName + ", assume it is already removed");
+                } else {
+                    if (!vmMo.removeSnapshot(vmSnapshotName, false)) {
+                        String msg = "delete vm snapshot " + vmSnapshotName + " due to error occured in vmware";
+                        s_logger.error(msg);
+                        return new DeleteVMSnapshotAnswer(cmd, false, msg);
+                    }
+                }
+                s_logger.debug("snapshot: " + vmSnapshotName + " is removed");
+                // after removed snapshot, the volumes' paths have been changed for the VM, needs to report new paths to manager 
+                VirtualDisk[] vdisks = vmMo.getAllDiskDevice();
+                for (int i = 0; i < vdisks.length; i++) {
+                    @SuppressWarnings("deprecation")
+                    List<Pair<String, ManagedObjectReference>> vmdkFiles = vmMo.getDiskDatastorePathChain(vdisks[i], false);
+                    for (Pair<String, ManagedObjectReference> fileItem : vmdkFiles) {
+                        String vmdkName = fileItem.first().split(" ")[1];
+                        if (vmdkName.endsWith(".vmdk")) {
+                            vmdkName = vmdkName.substring(0, vmdkName.length() - (".vmdk").length());
+                        }
+                        String[] s = vmdkName.split("-");
+                        mapNewDisk.put(s[0], vmdkName);
+                    }
+                }
+                for (VolumeTO volumeTo : listVolumeTo) {
+                    String key = null;
+                    String parentUUID = volumeTo.getPath();
+                    String[] s = parentUUID.split("-");
+                    key = s[0];
+                    volumeTo.setPath(mapNewDisk.get(key));
+                }
+                return new DeleteVMSnapshotAnswer(cmd, listVolumeTo);
+            }
+        } catch (Exception e) {
+            String msg = e.getMessage();
+            s_logger.error("failed to delete vm snapshot " + vmSnapshotName + " of vm " + vmName + " due to " + msg);
+            return new DeleteVMSnapshotAnswer(cmd, false, msg);
+        }
+    }
+
+	@Override
+    public RevertToVMSnapshotAnswer execute(VmwareHostService hostService, RevertToVMSnapshotCommand cmd) {
+        String snapshotName = cmd.getTarget().getSnapshotName();
+        String vmName = cmd.getVmName();
+        Boolean snapshotMemory = cmd.getTarget().getType() == VMSnapshot.Type.DiskAndMemory;
+        List<VolumeTO> listVolumeTo = cmd.getVolumeTOs();
+        VirtualMachine.State vmState = VirtualMachine.State.Running;
+        VirtualMachineMO vmMo = null;
+        VmwareContext context = hostService.getServiceContext(cmd);
+        Map<String, String> mapNewDisk = new HashMap<String, String>();
+        try {
+            VmwareHypervisorHost hyperHost = hostService.getHyperHost(context, cmd);
+            
+            // wait if there are already VM revert task running
+            ManagedObjectReference taskmgr = context.getServiceContent().getTaskManager();
+            ManagedObjectReference[] tasks =  (ManagedObjectReference[]) context.getServiceUtil().getDynamicProperty(taskmgr, "recentTask");
+            for (ManagedObjectReference taskMor : tasks) {
+                TaskInfo info = (TaskInfo) (context.getServiceUtil().getDynamicProperty(taskMor, "info"));
+                if(info.getEntityName().equals(cmd.getVmName()) && info.getName().equalsIgnoreCase("RevertToSnapshot_Task")){
+                    s_logger.debug("There is already a VM snapshot task running, wait for it");
+                    context.getServiceUtil().waitForTask(taskMor);
+                }
+            }
+            
+            HostMO hostMo = (HostMO) hyperHost;
+            vmMo = hyperHost.findVmOnHyperHost(vmName);
+            if(vmMo == null)
+                vmMo = hyperHost.findVmOnPeerHyperHost(vmName);
+            if (vmMo == null) {
+                String msg = "Unable to find VM for RevertToVMSnapshotCommand";
+                s_logger.debug(msg);
+                return new RevertToVMSnapshotAnswer(cmd, false, msg);
+            } else {
+                boolean result = false;
+                if (snapshotName != null) {
+                    ManagedObjectReference morSnapshot = vmMo.getSnapshotMor(snapshotName);
+                    result = hostMo.revertToSnapshot(morSnapshot);
+                } else {
+                    return new RevertToVMSnapshotAnswer(cmd, false, "Unable to find the snapshot by name " + snapshotName);
+                }
+
+                if (result) {
+                    VirtualDisk[] vdisks = vmMo.getAllDiskDevice();
+                    // build a map<volumeName, vmdk>
+                    for (int i = 0; i < vdisks.length; i++) {
+                        @SuppressWarnings("deprecation")
+                        List<Pair<String, ManagedObjectReference>> vmdkFiles = vmMo.getDiskDatastorePathChain(
+                                vdisks[i], false);
+                        for (Pair<String, ManagedObjectReference> fileItem : vmdkFiles) {
+                            String vmdkName = fileItem.first().split(" ")[1];
+                            if (vmdkName.endsWith(".vmdk")) {
+                                vmdkName = vmdkName.substring(0, vmdkName.length() - (".vmdk").length());
+                            }
+                            String[] s = vmdkName.split("-");
+                            mapNewDisk.put(s[0], vmdkName);
+                        }
+                    }
+                    String key = null;
+                    for (VolumeTO volumeTo : listVolumeTo) {
+                        String parentUUID = volumeTo.getPath();
+                        String[] s = parentUUID.split("-");
+                        key = s[0];
+                        volumeTo.setPath(mapNewDisk.get(key));
+                    }
+                    if (!snapshotMemory) {
+                        vmState = VirtualMachine.State.Stopped;
+                    }
+                    return new RevertToVMSnapshotAnswer(cmd, listVolumeTo, vmState);
+                } else {
+                    return new RevertToVMSnapshotAnswer(cmd, false,
+                            "Error while reverting to snapshot due to execute in esxi");
+                }
+            }
+        } catch (Exception e) {
+            String msg = "revert vm " + vmName + " to snapshot " + snapshotName + " failed due to " + e.getMessage();
+            s_logger.error(msg);
+            return new RevertToVMSnapshotAnswer(cmd, false, msg);
+        }
+    }
+
+ 
+    private VirtualMachineMO createWorkingVM(DatastoreMO dsMo, VmwareHypervisorHost hyperHost) throws Exception {
+        String uniqueName = UUID.randomUUID().toString();
+        VirtualMachineMO workingVM = null;
+        VirtualMachineConfigSpec vmConfig = new VirtualMachineConfigSpec();
+        vmConfig.setName(uniqueName);
+        vmConfig.setMemoryMB((long) 4);
+        vmConfig.setNumCPUs(1);
+        vmConfig.setGuestId(VirtualMachineGuestOsIdentifier._otherGuest.toString());
+        VirtualMachineFileInfo fileInfo = new VirtualMachineFileInfo();
+        fileInfo.setVmPathName(String.format("[%s]", dsMo.getName()));
+        vmConfig.setFiles(fileInfo);
+
+        VirtualLsiLogicController scsiController = new VirtualLsiLogicController();
+        scsiController.setSharedBus(VirtualSCSISharing.noSharing);
+        scsiController.setBusNumber(0);
+        scsiController.setKey(1);
+        VirtualDeviceConfigSpec scsiControllerSpec = new VirtualDeviceConfigSpec();
+        scsiControllerSpec.setDevice(scsiController);
+        scsiControllerSpec.setOperation(VirtualDeviceConfigSpecOperation.add);
+
+        vmConfig.setDeviceChange(new VirtualDeviceConfigSpec[] { scsiControllerSpec });
+        hyperHost.createVm(vmConfig);
+        workingVM = hyperHost.findVmOnHyperHost(uniqueName);
+        return workingVM;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/600fb904/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java
index 0cdc3f3..08e7b72 100755
--- a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java
+++ b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java
@@ -65,9 +65,13 @@ import com.cloud.agent.api.Command;
 import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand;
 import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand;
 import com.cloud.agent.api.CreateStoragePoolCommand;
+import com.cloud.agent.api.CreateVMSnapshotAnswer;
+import com.cloud.agent.api.CreateVMSnapshotCommand;
 import com.cloud.agent.api.CreateVolumeFromSnapshotAnswer;
 import com.cloud.agent.api.CreateVolumeFromSnapshotCommand;
 import com.cloud.agent.api.DeleteStoragePoolCommand;
+import com.cloud.agent.api.DeleteVMSnapshotAnswer;
+import com.cloud.agent.api.DeleteVMSnapshotCommand;
 import com.cloud.agent.api.GetDomRVersionAnswer;
 import com.cloud.agent.api.GetDomRVersionCmd;
 import com.cloud.agent.api.GetHostStatsAnswer;
@@ -103,6 +107,8 @@ import com.cloud.agent.api.ReadyCommand;
 import com.cloud.agent.api.RebootAnswer;
 import com.cloud.agent.api.RebootCommand;
 import com.cloud.agent.api.RebootRouterCommand;
+import com.cloud.agent.api.RevertToVMSnapshotAnswer;
+import com.cloud.agent.api.RevertToVMSnapshotCommand;
 import com.cloud.agent.api.SetupAnswer;
 import com.cloud.agent.api.SetupCommand;
 import com.cloud.agent.api.SetupGuestNetworkAnswer;
@@ -138,8 +144,8 @@ import com.cloud.agent.api.routing.SetNetworkACLCommand;
 import com.cloud.agent.api.routing.SetPortForwardingRulesAnswer;
 import com.cloud.agent.api.routing.SetPortForwardingRulesCommand;
 import com.cloud.agent.api.routing.SetPortForwardingRulesVpcCommand;
-import com.cloud.agent.api.routing.SetSourceNatCommand;
 import com.cloud.agent.api.routing.SetSourceNatAnswer;
+import com.cloud.agent.api.routing.SetSourceNatCommand;
 import com.cloud.agent.api.routing.SetStaticNatRulesAnswer;
 import com.cloud.agent.api.routing.SetStaticNatRulesCommand;
 import com.cloud.agent.api.routing.Site2SiteVpnCfgCommand;
@@ -250,9 +256,6 @@ import com.vmware.vim25.VirtualMachineGuestOsIdentifier;
 import com.vmware.vim25.VirtualMachinePowerState;
 import com.vmware.vim25.VirtualMachineRuntimeInfo;
 import com.vmware.vim25.VirtualSCSISharing;
-import com.xensource.xenapi.Connection;
-import com.xensource.xenapi.VIF;
-import com.xensource.xenapi.VM;
 
 public class VmwareResource implements StoragePoolResource, ServerResource, VmwareHostService {
     private static final Logger s_logger = Logger.getLogger(VmwareResource.class);
@@ -445,7 +448,13 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
                 answer = execute((SetSourceNatCommand) cmd);
             } else if (clz == SetNetworkACLCommand.class) {
                 answer = execute((SetNetworkACLCommand) cmd);
-            } else if (clz == SetPortForwardingRulesVpcCommand.class) {
+            } else if (cmd instanceof CreateVMSnapshotCommand) {
+            	return execute((CreateVMSnapshotCommand)cmd);
+            } else if(cmd instanceof DeleteVMSnapshotCommand){
+            	return execute((DeleteVMSnapshotCommand)cmd);
+            } else if(cmd instanceof RevertToVMSnapshotCommand){
+            	return execute((RevertToVMSnapshotCommand)cmd);
+        	}else if (clz == SetPortForwardingRulesVpcCommand.class) {
                 answer = execute((SetPortForwardingRulesVpcCommand) cmd);
             } else if (clz == Site2SiteVpnCfgCommand.class) {
                 answer = execute((Site2SiteVpnCfgCommand) cmd);
@@ -2721,7 +2730,6 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
                         // before we stop VM, remove all possible snapshots on the VM to let
                         // disk chain be collapsed
                         s_logger.info("Remove all snapshot before stopping VM " + cmd.getVmName());
-                        vmMo.removeAllSnapshots();
                         if (vmMo.safePowerOff(_shutdown_waitMs)) {
                             state = State.Stopped;
                             return new StopAnswer(cmd, "Stop VM " + cmd.getVmName() + " Succeed", 0, true);
@@ -3273,7 +3281,42 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
         }
     }
 
+    protected Answer execute(CreateVMSnapshotCommand cmd) {
+        try {
+            VmwareContext context = getServiceContext();
+            VmwareManager mgr = context
+                    .getStockObject(VmwareManager.CONTEXT_STOCK_NAME);
 
+            return mgr.getStorageManager().execute(this, cmd);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return new CreateVMSnapshotAnswer(cmd, false, "");
+        }
+    }
+    
+    protected Answer execute(DeleteVMSnapshotCommand cmd) {
+        try {
+            VmwareContext context = getServiceContext();
+            VmwareManager mgr = context
+                    .getStockObject(VmwareManager.CONTEXT_STOCK_NAME);
+
+            return mgr.getStorageManager().execute(this, cmd);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return new DeleteVMSnapshotAnswer(cmd, false, "");
+        }
+    }
+    
+    protected Answer execute(RevertToVMSnapshotCommand cmd){
+    	try{
+    		VmwareContext context = getServiceContext();
+			VmwareManager mgr = context.getStockObject(VmwareManager.CONTEXT_STOCK_NAME);
+			return mgr.getStorageManager().execute(this, cmd);
+    	}catch (Exception e){
+    		e.printStackTrace();
+    		return new RevertToVMSnapshotAnswer(cmd,false,"");
+    	}
+    }
     protected Answer execute(CreateVolumeFromSnapshotCommand cmd) {
         if (s_logger.isInfoEnabled()) {
             s_logger.info("Executing resource CreateVolumeFromSnapshotCommand: " + _gson.toJson(cmd));

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/600fb904/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
old mode 100644
new mode 100755
index d2db85c..543b7e3
--- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
+++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
@@ -81,9 +81,13 @@ import com.cloud.agent.api.Command;
 import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand;
 import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand;
 import com.cloud.agent.api.CreateStoragePoolCommand;
+import com.cloud.agent.api.CreateVMSnapshotAnswer;
+import com.cloud.agent.api.CreateVMSnapshotCommand;
 import com.cloud.agent.api.CreateVolumeFromSnapshotAnswer;
 import com.cloud.agent.api.CreateVolumeFromSnapshotCommand;
 import com.cloud.agent.api.DeleteStoragePoolCommand;
+import com.cloud.agent.api.DeleteVMSnapshotAnswer;
+import com.cloud.agent.api.DeleteVMSnapshotCommand;
 import com.cloud.agent.api.GetDomRVersionAnswer;
 import com.cloud.agent.api.GetDomRVersionCmd;
 import com.cloud.agent.api.GetHostStatsAnswer;
@@ -120,6 +124,8 @@ import com.cloud.agent.api.ReadyCommand;
 import com.cloud.agent.api.RebootAnswer;
 import com.cloud.agent.api.RebootCommand;
 import com.cloud.agent.api.RebootRouterCommand;
+import com.cloud.agent.api.RevertToVMSnapshotAnswer;
+import com.cloud.agent.api.RevertToVMSnapshotCommand;
 import com.cloud.agent.api.SecurityGroupRuleAnswer;
 import com.cloud.agent.api.SecurityGroupRulesCmd;
 import com.cloud.agent.api.SetupAnswer;
@@ -223,6 +229,7 @@ import com.cloud.utils.net.NetUtils;
 import com.cloud.vm.DiskProfile;
 import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.VirtualMachine.State;
+import com.cloud.vm.snapshot.VMSnapshot;
 import com.trilead.ssh2.SCPClient;
 import com.xensource.xenapi.Bond;
 import com.xensource.xenapi.Connection;
@@ -242,6 +249,10 @@ import com.xensource.xenapi.Types;
 import com.xensource.xenapi.Types.BadServerResponse;
 import com.xensource.xenapi.Types.ConsoleProtocol;
 import com.xensource.xenapi.Types.IpConfigurationMode;
+import com.xensource.xenapi.Types.OperationNotAllowed;
+import com.xensource.xenapi.Types.SrFull;
+import com.xensource.xenapi.Types.VbdType;
+import com.xensource.xenapi.Types.VmBadPowerState;
 import com.xensource.xenapi.Types.VmPowerState;
 import com.xensource.xenapi.Types.XenAPIException;
 import com.xensource.xenapi.VBD;
@@ -557,11 +568,109 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
             return execute((Site2SiteVpnCfgCommand) cmd);
         } else if (clazz == CheckS2SVpnConnectionsCommand.class) {
             return execute((CheckS2SVpnConnectionsCommand) cmd);
+        } else if (clazz == CreateVMSnapshotCommand.class) {
+            return execute((CreateVMSnapshotCommand)cmd);
+        } else if (clazz == DeleteVMSnapshotCommand.class) {
+            return execute((DeleteVMSnapshotCommand)cmd);
+        } else if (clazz == RevertToVMSnapshotCommand.class) {
+            return execute((RevertToVMSnapshotCommand)cmd);
         } else {
             return Answer.createUnsupportedCommandAnswer(cmd);
         }
     }
 
+
+
+    private Answer execute(RevertToVMSnapshotCommand cmd) {
+        String vmName = cmd.getVmName();
+        List<VolumeTO> listVolumeTo = cmd.getVolumeTOs();
+        VMSnapshot.Type vmSnapshotType = cmd.getTarget().getType();
+        Boolean snapshotMemory = vmSnapshotType == VMSnapshot.Type.DiskAndMemory;
+        Connection conn = getConnection();
+        VirtualMachine.State vmState = null;
+        VM vm = null;
+        try {
+
+            // remove vm from s_vms, for delta sync
+            s_vms.remove(_cluster, _name, vmName);
+
+            Set<VM> vmSnapshots = VM.getByNameLabel(conn, cmd.getTarget().getSnapshotName());
+            if(vmSnapshots.size() == 0)
+                return new RevertToVMSnapshotAnswer(cmd, false, "Cannot find vmSnapshot with name: " + cmd.getTarget().getSnapshotName());
+            
+            VM vmSnapshot = vmSnapshots.iterator().next();
+            
+            // find target VM or creating a work VM
+            try {
+                vm = getVM(conn, vmName);
+            } catch (Exception e) {
+                vm = createWorkingVM(conn, vmName, cmd.getGuestOSType(), listVolumeTo);
+            }
+
+            if (vm == null) {
+                return new RevertToVMSnapshotAnswer(cmd, false,
+                        "Revert to VM Snapshot Failed due to can not find vm: " + vmName);
+            }
+            
+            // call plugin to execute revert
+            revertToSnapshot(conn, vmSnapshot, vmName, vm.getUuid(conn), snapshotMemory, _host.uuid);
+            vm = getVM(conn, vmName);
+            Set<VBD> vbds = vm.getVBDs(conn);
+            Map<String, VDI> vdiMap = new HashMap<String, VDI>();
+            // get vdi:vbdr to a map
+            for (VBD vbd : vbds) {
+                VBD.Record vbdr = vbd.getRecord(conn);
+                if (vbdr.type == Types.VbdType.DISK) {
+                    VDI vdi = vbdr.VDI;
+                    vdiMap.put(vbdr.userdevice, vdi);
+                }
+            }
+
+            if (!snapshotMemory) {
+                vm.destroy(conn);
+                vmState = VirtualMachine.State.Stopped;
+            } else {
+                s_vms.put(_cluster, _name, vmName, State.Running);
+                vmState = VirtualMachine.State.Running;
+            }
+
+            // after revert, VM's volumes path have been changed, need to report to manager
+            for (VolumeTO volumeTo : listVolumeTo) {
+                Long deviceId = volumeTo.getDeviceId();
+                VDI vdi = vdiMap.get(deviceId.toString());
+                volumeTo.setPath(vdi.getUuid(conn));
+            }
+
+            return new RevertToVMSnapshotAnswer(cmd, listVolumeTo,vmState);
+        } catch (Exception e) {
+            s_logger.error("revert vm " + vmName
+                    + " to snapshot " + cmd.getTarget().getSnapshotName() + " failed due to " + e.getMessage());
+            return new RevertToVMSnapshotAnswer(cmd, false, e.getMessage());
+        } 
+    }
+
+    private String revertToSnapshot(Connection conn, VM vmSnapshot,
+            String vmName, String oldVmUuid, Boolean snapshotMemory, String hostUUID)
+            throws XenAPIException, XmlRpcException {
+ 
+        String results = callHostPluginAsync(conn, "vmopsSnapshot",
+                "revert_memory_snapshot", 10 * 60 * 1000, "snapshotUUID",
+                vmSnapshot.getUuid(conn), "vmName", vmName, "oldVmUuid",
+                oldVmUuid, "snapshotMemory", snapshotMemory.toString(), "hostUUID", hostUUID);
+        String errMsg = null;
+        if (results == null || results.isEmpty()) {
+            errMsg = "revert_memory_snapshot return null";
+        } else {
+            if (results.equals("0")) {
+                return results;
+            } else {
+                errMsg = "revert_memory_snapshot exception";
+            }
+        }
+        s_logger.warn(errMsg);
+        throw new CloudRuntimeException(errMsg);
+    }
+
     protected XsLocalNetwork getNativeNetworkForTraffic(Connection conn, TrafficType type, String name) throws XenAPIException, XmlRpcException {
         if (name != null) {
             if (s_logger.isDebugEnabled()) {
@@ -6105,6 +6214,199 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
 
     }
 
+    protected Answer execute(final CreateVMSnapshotCommand cmd) {
+        String vmName = cmd.getVmName();
+        String vmSnapshotName = cmd.getTarget().getSnapshotName();
+        List<VolumeTO> listVolumeTo = cmd.getVolumeTOs();
+        VirtualMachine.State vmState = cmd.getVmState();
+        String guestOSType = cmd.getGuestOSType();
+
+        boolean snapshotMemory = cmd.getTarget().getType() == VMSnapshot.Type.DiskAndMemory;
+        long timeout = 600;
+
+        Connection conn = getConnection();
+        VM vm = null;
+        VM vmSnapshot = null;
+        boolean success = false;
+
+        try {
+            // check if VM snapshot already exists
+            Set<VM> vmSnapshots = VM.getByNameLabel(conn, cmd.getTarget().getSnapshotName());
+            if(vmSnapshots.size() > 0)
+                return new CreateVMSnapshotAnswer(cmd, cmd.getTarget(), cmd.getVolumeTOs());
+            
+            // check if there is already a task for this VM snapshot
+            Task task = null;
+            Set<Task> tasks = Task.getByNameLabel(conn, "Async.VM.snapshot");
+            tasks.addAll(Task.getByNameLabel(conn, "Async.VM.checkpoint"));
+            for (Task taskItem : tasks) {
+                if(taskItem.getOtherConfig(conn).containsKey("CS_VM_SNAPSHOT_KEY")){
+                    String vmSnapshotTaskName = taskItem.getOtherConfig(conn).get("CS_VM_SNAPSHOT_KEY");
+                    if(vmSnapshotTaskName != null && vmSnapshotTaskName.equals(cmd.getTarget().getSnapshotName())){
+                        task = taskItem;
+                    }
+                }
+            }
+            
+            // create a new task if there is no existing task for this VM snapshot
+            if(task == null){
+                try {
+                    vm = getVM(conn, vmName);
+                } catch (Exception e) {
+                    if (!snapshotMemory) {
+                        vm = createWorkingVM(conn, vmName, guestOSType, listVolumeTo);
+                    }
+                }
+    
+                if (vm == null) {
+                    return new CreateVMSnapshotAnswer(cmd, false,
+                            "Creating VM Snapshot Failed due to can not find vm: "
+                                    + vmName);
+                }
+                
+                // call Xenserver API
+                if (!snapshotMemory) {
+                    task = vm.snapshotAsync(conn, vmSnapshotName);
+                } else {
+                    Set<VBD> vbds = vm.getVBDs(conn);
+                    Pool pool = Pool.getByUuid(conn, _host.pool);
+                    for (VBD vbd: vbds){
+                        VBD.Record vbdr = vbd.getRecord(conn);
+                        if (vbdr.userdevice.equals("0")){
+                            VDI vdi = vbdr.VDI;
+                            SR sr = vdi.getSR(conn);
+                            // store memory image on the same SR with ROOT volume
+                            pool.setSuspendImageSR(conn, sr);
+                        }
+                    }
+                    task = vm.checkpointAsync(conn, vmSnapshotName);
+                }
+                task.addToOtherConfig(conn, "CS_VM_SNAPSHOT_KEY", vmSnapshotName);
+            }
+            
+            waitForTask(conn, task, 1000, timeout * 1000);
+            checkForSuccess(conn, task);
+            String result = task.getResult(conn);
+            
+            // extract VM snapshot ref from result 
+            String ref = result.substring("<value>".length(), result.length() - "</value>".length());
+            vmSnapshot = Types.toVM(ref);
+            
+            success = true;
+            return new CreateVMSnapshotAnswer(cmd, cmd.getTarget(), cmd.getVolumeTOs());
+        } catch (Exception e) {
+            String msg = e.getMessage();
+            s_logger.error("Creating VM Snapshot " + cmd.getTarget().getSnapshotName() + " failed due to: " + msg);
+            return new CreateVMSnapshotAnswer(cmd, false, msg);
+        } finally {
+            try {
+                if (!success) {
+                    if (vmSnapshot != null) {
+                        s_logger.debug("Delete exsisting VM Snapshot "
+                                + vmSnapshotName
+                                + " after making VolumeTO failed");
+                        Set<VBD> vbds = vmSnapshot.getVBDs(conn);
+                        for (VBD vbd : vbds) {
+                            VBD.Record vbdr = vbd.getRecord(conn);
+                            if (vbdr.type == VbdType.DISK) {
+                                VDI vdi = vbdr.VDI;
+                                vdi.destroy(conn);
+                            }
+                        }
+                        vmSnapshot.destroy(conn);
+                    }
+                }
+                if (vmState == VirtualMachine.State.Stopped) {
+                    if (vm != null) {
+                        vm.destroy(conn);
+                    }
+                }
+            } catch (Exception e2) {
+                s_logger.error("delete snapshot error due to "
+                        + e2.getMessage());
+            }
+        }
+    }
+    
+    private VM createWorkingVM(Connection conn, String vmName,
+            String guestOSType, List<VolumeTO> listVolumeTo)
+            throws BadServerResponse, VmBadPowerState, SrFull,
+            OperationNotAllowed, XenAPIException, XmlRpcException {
+        String guestOsTypeName = getGuestOsType(guestOSType, false);
+        if (guestOsTypeName == null) {
+            String msg = " Hypervisor " + this.getClass().getName()
+                    + " doesn't support guest OS type " + guestOSType
+                    + ". you can choose 'Other install media' to run it as HVM";
+            s_logger.warn(msg);
+            throw new CloudRuntimeException(msg);
+        }
+        VM template = getVM(conn, guestOsTypeName);
+        VM vm = template.createClone(conn, vmName);
+        vm.setIsATemplate(conn, false);
+        Map<VDI, VolumeTO> vdiMap = new HashMap<VDI, VolumeTO>();
+        for (VolumeTO volume : listVolumeTo) {
+            String vdiUuid = volume.getPath();
+            try {
+                VDI vdi = VDI.getByUuid(conn, vdiUuid);
+                vdiMap.put(vdi, volume);
+            } catch (Types.UuidInvalid e) {
+                s_logger.warn("Unable to find vdi by uuid: " + vdiUuid
+                        + ", skip it");
+            }
+        }
+        for (VDI vdi : vdiMap.keySet()) {
+            VolumeTO volumeTO = vdiMap.get(vdi);
+            VBD.Record vbdr = new VBD.Record();
+            vbdr.VM = vm;
+            vbdr.VDI = vdi;
+            if (volumeTO.getType() == Volume.Type.ROOT) {
+                vbdr.bootable = true;
+                vbdr.unpluggable = false;
+            } else {
+                vbdr.bootable = false;
+                vbdr.unpluggable = true;
+            }
+            vbdr.userdevice = new Long(volumeTO.getDeviceId()).toString();
+            vbdr.mode = Types.VbdMode.RW;
+            vbdr.type = Types.VbdType.DISK;
+            VBD.create(conn, vbdr);
+        }
+        return vm;
+    }
+
+    protected Answer execute(final DeleteVMSnapshotCommand cmd) {
+        String snapshotName = cmd.getTarget().getSnapshotName();
+        Connection conn = getConnection();
+                
+        try {
+            List<VDI> vdiList = new ArrayList<VDI>();
+            Set<VM> snapshots = VM.getByNameLabel(conn, snapshotName);
+            if(snapshots.size() == 0){
+                s_logger.warn("VM snapshot with name " + snapshotName + " does not exist, assume it is already deleted");
+                return new DeleteVMSnapshotAnswer(cmd, cmd.getVolumeTOs());
+            }
+            VM snapshot = snapshots.iterator().next();
+            Set<VBD> vbds = snapshot.getVBDs(conn);
+            for (VBD vbd : vbds) {
+                if (vbd.getType(conn) == VbdType.DISK) {
+                    VDI vdi = vbd.getVDI(conn);
+                    vdiList.add(vdi);
+                }
+            }
+            if(cmd.getTarget().getType() == VMSnapshot.Type.DiskAndMemory)
+                vdiList.add(snapshot.getSuspendVDI(conn));
+            snapshot.destroy(conn);
+            for (VDI vdi : vdiList) {
+                vdi.destroy(conn);
+            }
+            return new DeleteVMSnapshotAnswer(cmd, cmd.getVolumeTOs());
+        } catch (Exception e) {
+            s_logger.warn("Catch Exception: " + e.getClass().toString()
+                    + " due to " + e.toString(), e);
+            return new DeleteVMSnapshotAnswer(cmd, false, e.getMessage());
+        }
+    }
+    
     protected Answer execute(final AttachIsoCommand cmd) {
         Connection conn = getConnection();
         boolean attach = cmd.isAttach();

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/600fb904/scripts/vm/hypervisor/xenserver/vmopsSnapshot
----------------------------------------------------------------------
diff --git a/scripts/vm/hypervisor/xenserver/vmopsSnapshot b/scripts/vm/hypervisor/xenserver/vmopsSnapshot
index 80e21f8..d183bec 100755
--- a/scripts/vm/hypervisor/xenserver/vmopsSnapshot
+++ b/scripts/vm/hypervisor/xenserver/vmopsSnapshot
@@ -556,6 +556,33 @@ def deleteSnapshotBackup(session, args):
 
     return "1"
    
+@echo
+def revert_memory_snapshot(session, args):
+    util.SMlog("Calling revert_memory_snapshot with " + str(args))
+    vmName = args['vmName']
+    snapshotUUID = args['snapshotUUID']
+    oldVmUuid = args['oldVmUuid']
+    snapshotMemory = args['snapshotMemory']
+    hostUUID = args['hostUUID']
+    try:
+        cmd = '''xe vbd-list vm-uuid=%s | grep 'vdi-uuid' | grep -v 'not in database' | sed -e 's/vdi-uuid ( RO)://g' ''' % oldVmUuid
+        vdiUuids = os.popen(cmd).read().split()
+        cmd2 = '''xe vm-param-get param-name=power-state uuid=''' + oldVmUuid
+        if os.popen(cmd2).read().split()[0] != 'halted':
+            os.system("xe vm-shutdown force=true vm=" + vmName)
+        os.system("xe vm-destroy uuid=" + oldVmUuid)
+        os.system("xe snapshot-revert snapshot-uuid=" + snapshotUUID)
+        if snapshotMemory == 'true':
+            os.system("xe vm-resume vm=" + vmName + " on=" + hostUUID)
+        for vdiUuid in vdiUuids:
+            os.system("xe vdi-destroy uuid=" + vdiUuid)
+    except OSError, (errno, strerror):
+        errMsg = "OSError while reverting vm " + vmName + " to snapshot " + snapshotUUID + " with errno: " + str(errno) + " and strerr: " + strerror
+        util.SMlog(errMsg)
+        raise xs_errors.XenError(errMsg)
+    return "0"
+
 if __name__ == "__main__":
-    XenAPIPlugin.dispatch({"getVhdParent":getVhdParent,  "create_secondary_storage_folder":create_secondary_storage_folder, "delete_secondary_storage_folder":delete_secondary_storage_folder, "post_create_private_template":post_create_private_template, "backupSnapshot": backupSnapshot, "deleteSnapshotBackup": deleteSnapshotBackup, "unmountSnapshotsDir": unmountSnapshotsDir})
+    XenAPIPlugin.dispatch({"getVhdParent":getVhdParent,  "create_secondary_storage_folder":create_secondary_storage_folder, "delete_secondary_storage_folder":delete_secondary_storage_folder, "post_create_private_template":post_create_private_template, "backupSnapshot": backupSnapshot, "deleteSnapshotBackup": deleteSnapshotBackup, "unmountSnapshotsDir": unmountSnapshotsDir, "revert_memory_snapshot":revert_memory_snapshot})
+    
 

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/600fb904/server/src/com/cloud/api/ApiDBUtils.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiDBUtils.java b/server/src/com/cloud/api/ApiDBUtils.java
index 3fac7a7..5958d6c 100755
--- a/server/src/com/cloud/api/ApiDBUtils.java
+++ b/server/src/com/cloud/api/ApiDBUtils.java
@@ -62,17 +62,17 @@ import com.cloud.network.NetworkManager;
 import com.cloud.network.NetworkProfile;
 import com.cloud.network.NetworkRuleConfigVO;
 import com.cloud.network.NetworkVO;
-import com.cloud.network.Site2SiteVpnGatewayVO;
-import com.cloud.network.Site2SiteCustomerGatewayVO;
 import com.cloud.network.Networks.TrafficType;
+import com.cloud.network.Site2SiteCustomerGatewayVO;
+import com.cloud.network.Site2SiteVpnGatewayVO;
 import com.cloud.network.dao.FirewallRulesCidrsDao;
 import com.cloud.network.dao.IPAddressDao;
 import com.cloud.network.dao.LoadBalancerDao;
 import com.cloud.network.dao.NetworkDao;
 import com.cloud.network.dao.NetworkDomainDao;
 import com.cloud.network.dao.NetworkRuleConfigDao;
-import com.cloud.network.dao.Site2SiteVpnGatewayDao;
 import com.cloud.network.dao.Site2SiteCustomerGatewayDao;
+import com.cloud.network.dao.Site2SiteVpnGatewayDao;
 import com.cloud.network.security.SecurityGroup;
 import com.cloud.network.security.SecurityGroupManager;
 import com.cloud.network.security.SecurityGroupVO;
@@ -153,6 +153,8 @@ import com.cloud.vm.dao.UserVmDao;
 import com.cloud.vm.dao.UserVmData;
 import com.cloud.vm.dao.UserVmDetailsDao;
 import com.cloud.vm.dao.VMInstanceDao;
+import com.cloud.vm.snapshot.VMSnapshot;
+import com.cloud.vm.snapshot.dao.VMSnapshotDao;
 
 public class ApiDBUtils {
     private static ManagementServer _ms;
@@ -213,6 +215,7 @@ public class ApiDBUtils {
     private static UserVmDetailsDao _userVmDetailsDao;
     private static SSHKeyPairDao _sshKeyPairDao;
 
+    private static VMSnapshotDao _vmSnapshotDao;
     static {
         _ms = (ManagementServer) ComponentLocator.getComponent(ManagementServer.Name);
          ComponentLocator locator = ComponentLocator.getLocator(ManagementServer.Name);
@@ -271,7 +274,7 @@ public class ApiDBUtils {
         _taggedResourceService = locator.getManager(TaggedResourceService.class);
         _sshKeyPairDao = locator.getDao(SSHKeyPairDao.class);
         _userVmDetailsDao = locator.getDao(UserVmDetailsDao.class);
-
+        _vmSnapshotDao = locator.getDao(VMSnapshotDao.class);
         // Note: stats collector should already have been initialized by this time, otherwise a null instance is returned
         _statsCollector = StatsCollector.getInstance();
     }
@@ -802,7 +805,12 @@ public class ApiDBUtils {
     public static boolean canUseForDeploy(Network network) {
         return _networkMgr.canUseForDeploy(network);
     }
-    
+
+    public static VMSnapshot getVMSnapshotById(Long vmSnapshotId) {
+        VMSnapshot vmSnapshot = _vmSnapshotDao.findById(vmSnapshotId);
+        return vmSnapshot;
+    }
+
     public static String getUuid(String resourceId, TaggedResourceType resourceType) {
         return _taggedResourceService.getUuid(resourceId, resourceType);
     }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/600fb904/server/src/com/cloud/api/ApiResponseHelper.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java
index 2b6a31b..543165e 100755
--- a/server/src/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/com/cloud/api/ApiResponseHelper.java
@@ -99,6 +99,7 @@ import com.cloud.api.response.TemplateResponse;
 import com.cloud.api.response.TrafficTypeResponse;
 import com.cloud.api.response.UserResponse;
 import com.cloud.api.response.UserVmResponse;
+import com.cloud.api.response.VMSnapshotResponse;
 import com.cloud.api.response.VirtualRouterProviderResponse;
 import com.cloud.api.response.VlanIpRangeResponse;
 import com.cloud.api.response.VolumeResponse;
@@ -126,7 +127,6 @@ import com.cloud.dc.Vlan.VlanType;
 import com.cloud.dc.VlanVO;
 import com.cloud.domain.Domain;
 import com.cloud.event.Event;
-import com.cloud.exception.InvalidParameterValueException;
 import com.cloud.host.Host;
 import com.cloud.host.HostStats;
 import com.cloud.host.HostVO;
@@ -220,6 +220,7 @@ import com.cloud.vm.VmStats;
 import com.cloud.vm.dao.UserVmData;
 import com.cloud.vm.dao.UserVmData.NicData;
 import com.cloud.vm.dao.UserVmData.SecurityGroupData;
+import com.cloud.vm.snapshot.VMSnapshot;
 
 public class ApiResponseHelper implements ResponseGenerator {
 
@@ -579,6 +580,23 @@ public class ApiResponseHelper implements ResponseGenerator {
     }
 
     @Override
+    public VMSnapshotResponse createVMSnapshotResponse(VMSnapshot vmSnapshot) {
+        VMSnapshotResponse vmSnapshotResponse = new VMSnapshotResponse();
+        vmSnapshotResponse.setId(vmSnapshot.getId());
+        vmSnapshotResponse.setName(vmSnapshot.getName());
+        vmSnapshotResponse.setState(vmSnapshot.getState());
+        vmSnapshotResponse.setCreated(vmSnapshot.getCreated());
+        vmSnapshotResponse.setDescription(vmSnapshot.getDescription());
+        vmSnapshotResponse.setDisplayName(vmSnapshot.getDisplayName());
+        vmSnapshotResponse.setVmId(vmSnapshot.getVmId());
+        if(vmSnapshot.getParent() != null)
+            vmSnapshotResponse.setParentName(ApiDBUtils.getVMSnapshotById(vmSnapshot.getParent()).getDisplayName());
+        vmSnapshotResponse.setCurrent(vmSnapshot.getCurrent());
+        vmSnapshotResponse.setType(vmSnapshot.getType().toString());
+        return vmSnapshotResponse;
+    }
+    
+    @Override
     public SnapshotPolicyResponse createSnapshotPolicyResponse(SnapshotPolicy policy) {
         SnapshotPolicyResponse policyResponse = new SnapshotPolicyResponse();
         policyResponse.setId(policy.getId());
@@ -3915,4 +3933,9 @@ public class ApiResponseHelper implements ResponseGenerator {
         response.setObjectName("vpnconnection");
         return response;
     }
+
+    @Override
+    public VMSnapshot getVMSnapshotById(Long vmSnapshotId) {
+        return ApiDBUtils.getVMSnapshotById(vmSnapshotId);
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/600fb904/server/src/com/cloud/configuration/Config.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java
index dbcc97a..33a7c49 100755
--- a/server/src/com/cloud/configuration/Config.java
+++ b/server/src/com/cloud/configuration/Config.java
@@ -33,6 +33,7 @@ import com.cloud.storage.secondary.SecondaryStorageVmManager;
 import com.cloud.storage.snapshot.SnapshotManager;
 import com.cloud.template.TemplateManager;
 import com.cloud.vm.UserVmManager;
+import com.cloud.vm.snapshot.VMSnapshotManager;
 
 public enum Config {
 	
@@ -349,8 +350,14 @@ public enum Config {
 	SecondaryStorageServiceOffering("Advanced", ManagementServer.class, Long.class, "secstorage.service.offering", null, "Service offering used by secondary storage; if NULL - system offering will be used", null),
 	HaTag("Advanced", ManagementServer.class, String.class, "ha.tag", null, "HA tag defining that the host marked with this tag can be used for HA purposes only", null),
 	VpcCleanupInterval("Advanced", ManagementServer.class, Integer.class, "vpc.cleanup.interval", "3600", "The interval (in seconds) between cleanup for Inactive VPCs", null),
-    VpcMaxNetworks("Advanced", ManagementServer.class, Integer.class, "vpc.max.networks", "3", "Maximum number of networks per vpc", null);
+    VpcMaxNetworks("Advanced", ManagementServer.class, Integer.class, "vpc.max.networks", "3", "Maximum number of networks per vpc", null),
 	
+	// VMSnapshots
+    VMSnapshotMax("Advanced", VMSnapshotManager.class, Integer.class, "vmsnapshot.max", "10", "Maximum vm snapshots for a vm", null),
+    VMSnapshotCreateWait("Advanced", VMSnapshotManager.class, Integer.class, "vmsnapshot.create.wait", "600", "In second, timeout for create vm snapshot", null),
+    VMSnapshotExpungeInterval("Advanced", VMSnapshotManager.class, Integer.class, "vmsnapshot.expunge.interval", "60", "The interval (in seconds) to wait before running the expunge thread.", null),
+    VMSnapshotExpungeWorkers("Advanced", VMSnapshotManager.class, Integer.class, "vmsnapshot.expunge.workers",  "1", "Number of workers performing expunge ", null);
+    
 	
 	private final String _category;
 	private final Class<?> _componentClass;

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/600fb904/server/src/com/cloud/configuration/DefaultComponentLibrary.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/configuration/DefaultComponentLibrary.java b/server/src/com/cloud/configuration/DefaultComponentLibrary.java
index 4edd402..ab7d6dd 100755
--- a/server/src/com/cloud/configuration/DefaultComponentLibrary.java
+++ b/server/src/com/cloud/configuration/DefaultComponentLibrary.java
@@ -197,7 +197,8 @@ import com.cloud.vm.dao.SecondaryStorageVmDaoImpl;
 import com.cloud.vm.dao.UserVmDaoImpl;
 import com.cloud.vm.dao.UserVmDetailsDaoImpl;
 import com.cloud.vm.dao.VMInstanceDaoImpl;
-
+import com.cloud.vm.snapshot.VMSnapshotManagerImpl;
+import com.cloud.vm.snapshot.dao.VMSnapshotDaoImpl;
 
 public class DefaultComponentLibrary extends ComponentLibraryBase implements ComponentLibrary {
     protected void populateDaos() {
@@ -337,6 +338,7 @@ public class DefaultComponentLibrary extends ComponentLibraryBase implements Com
         addDao("Site2SiteVpnGatewayDao", Site2SiteVpnGatewayDaoImpl.class);
         addDao("Site2SiteCustomerGatewayDao", Site2SiteCustomerGatewayDaoImpl.class);
         addDao("Site2SiteVpnConnnectionDao", Site2SiteVpnConnectionDaoImpl.class);
+        addDao("VMSnapshotDao", VMSnapshotDaoImpl.class);
     }
 
     @Override
@@ -395,6 +397,7 @@ public class DefaultComponentLibrary extends ComponentLibraryBase implements Com
         addManager("NetworkACLManager", NetworkACLManagerImpl.class);
         addManager("TaggedResourcesManager", TaggedResourceManagerImpl.class);
         addManager("Site2SiteVpnManager", Site2SiteVpnManagerImpl.class);
+        addManager("VMSnapshot Manager", VMSnapshotManagerImpl.class);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/600fb904/server/src/com/cloud/resource/ResourceManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/resource/ResourceManagerImpl.java b/server/src/com/cloud/resource/ResourceManagerImpl.java
index ced601b..20684bc 100755
--- a/server/src/com/cloud/resource/ResourceManagerImpl.java
+++ b/server/src/com/cloud/resource/ResourceManagerImpl.java
@@ -1278,7 +1278,7 @@ public class ResourceManagerImpl implements ResourceManager, ResourceService, Ma
 
         for (ClusterVO cluster : clustersForZone) {
             HypervisorType hType = cluster.getHypervisorType();
-            if (!forVirtualRouter || (forVirtualRouter && hType != HypervisorType.BareMetal && hType != HypervisorType.Ovm)) {
+            if ( (cluster.getAllocationState() == AllocationState.Enabled) && (!forVirtualRouter || (forVirtualRouter && hType != HypervisorType.BareMetal && hType != HypervisorType.Ovm))) {
                 hypervisorTypes.add(hType);
             }
         }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/600fb904/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java
index d89a6d9..8e3c285 100755
--- a/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java
+++ b/server/src/com/cloud/storage/snapshot/SnapshotManagerImpl.java
@@ -123,6 +123,9 @@ import com.cloud.vm.VMInstanceVO;
 import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.VirtualMachine.State;
 import com.cloud.vm.dao.UserVmDao;
+import com.cloud.vm.snapshot.VMSnapshot;
+import com.cloud.vm.snapshot.VMSnapshotVO;
+import com.cloud.vm.snapshot.dao.VMSnapshotDao;
 
 @Local(value = { SnapshotManager.class, SnapshotService.class })
 public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Manager {
@@ -181,7 +184,8 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
     private VolumeDao _volumeDao;
     @Inject
     private ResourceTagDao _resourceTagDao;
-    
+    @Inject
+    protected VMSnapshotDao _vmSnapshotDao;
     String _name;
     private int _totalRetries;
     private int _pauseInterval;
@@ -383,6 +387,7 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
             }
 
             // if volume is attached to a vm in destroyed or expunging state; disallow
+            // if volume is attached to a vm in taking vm snapshot; disallow
             if (volume.getInstanceId() != null) {
                 UserVmVO userVm = _vmDao.findById(volume.getInstanceId());
                 if (userVm != null) {
@@ -396,6 +401,12 @@ public class SnapshotManagerImpl implements SnapshotManager, SnapshotService, Ma
                         if(activeSnapshots.size() > 1)
                             throw new CloudRuntimeException("There is other active snapshot tasks on the instance to which the volume is attached, please try again later");
                     }
+                    List<VMSnapshotVO> activeVMSnapshots = _vmSnapshotDao.listByInstanceId(userVm.getId(),
+                            VMSnapshot.State.Creating, VMSnapshot.State.Reverting, VMSnapshot.State.Expunging);
+                    if (activeVMSnapshots.size() > 0) {
+                        throw new CloudRuntimeException(
+                                "There is other active vm snapshot tasks on the instance to which the volume is attached, please try again later");
+                    }			
                 }
             }
 

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/600fb904/server/src/com/cloud/tags/TaggedResourceManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/tags/TaggedResourceManagerImpl.java b/server/src/com/cloud/tags/TaggedResourceManagerImpl.java
old mode 100644
new mode 100755
index db32f2c..3bbc946
--- a/server/src/com/cloud/tags/TaggedResourceManagerImpl.java
+++ b/server/src/com/cloud/tags/TaggedResourceManagerImpl.java
@@ -68,6 +68,7 @@ import com.cloud.utils.db.Transaction;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.uuididentity.dao.IdentityDao;
 import com.cloud.vm.dao.UserVmDao;
+import com.cloud.vm.snapshot.dao.VMSnapshotDao;
 
 
 @Local(value = { TaggedResourceService.class})
@@ -114,6 +115,8 @@ public class TaggedResourceManagerImpl implements TaggedResourceService, Manager
     VpcDao _vpcDao;
     @Inject
     StaticRouteDao _staticRouteDao;
+    @Inject
+    VMSnapshotDao _vmSnapshotDao;
 
     @Override
     public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
@@ -133,6 +136,7 @@ public class TaggedResourceManagerImpl implements TaggedResourceService, Manager
         _daoMap.put(TaggedResourceType.Vpc, _vpcDao);
         _daoMap.put(TaggedResourceType.NetworkACL, _firewallDao);
         _daoMap.put(TaggedResourceType.StaticRoute, _staticRouteDao);
+        _daoMap.put(TaggedResourceType.VMSnapshot, _vmSnapshotDao);
 
         return true;
     }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/600fb904/server/src/com/cloud/vm/UserVmManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java
index dbcbeb8..37ae98a 100755
--- a/server/src/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/com/cloud/vm/UserVmManagerImpl.java
@@ -233,6 +233,9 @@ import com.cloud.vm.dao.NicDao;
 import com.cloud.vm.dao.UserVmDao;
 import com.cloud.vm.dao.UserVmDetailsDao;
 import com.cloud.vm.dao.VMInstanceDao;
+import com.cloud.vm.snapshot.VMSnapshot;
+import com.cloud.vm.snapshot.VMSnapshotVO;
+import com.cloud.vm.snapshot.dao.VMSnapshotDao;
 
 @Local(value = { UserVmManager.class, UserVmService.class })
 public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager {
@@ -362,7 +365,8 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager
     PhysicalNetworkDao _physicalNetworkDao;
     @Inject
     VpcManager _vpcMgr;
-
+    @Inject
+    VMSnapshotDao _vmSnapshotDao;
     protected ScheduledExecutorService _executor = null;
     protected int _expungeInterval;
     protected int _expungeDelay;
@@ -604,6 +608,9 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager
         //permission check
         _accountMgr.checkAccess(caller, null, true, volume, vm);
 
+        //check if vm has snapshot, if true: can't attache volume
+        boolean attach = true;
+        checkVMSnapshots(vm, volumeId, attach);
         //Check if volume is stored on secondary Storage.
         boolean isVolumeOnSec = false;
         VolumeHostVO  volHostVO = _volumeHostDao.findByVolumeId(volume.getId());
@@ -812,8 +819,19 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager
         }
     }
 
-    @Override
-    @ActionEvent(eventType = EventTypes.EVENT_VOLUME_DETACH, eventDescription = "detaching volume", async = true)
+    private void checkVMSnapshots(UserVmVO vm, Long volumeId, boolean attach) {
+        // Check that if vm has any VM snapshot
+        Long vmId = vm.getId();
+        List<VMSnapshotVO> listSnapshot = _vmSnapshotDao.listByInstanceId(vmId,
+                VMSnapshot.State.Ready, VMSnapshot.State.Creating, VMSnapshot.State.Reverting, VMSnapshot.State.Expunging);
+        if (listSnapshot != null && listSnapshot.size() != 0) {
+            throw new InvalidParameterValueException(
+                        "The VM has VM snapshots, do not allowed to attach volume. Please delete the VM snapshots first.");
+        }
+    }
+
+	@Override
+    @ActionEvent(eventType = EventTypes.EVENT_VOLUME_DETACH, eventDescription = "event_detaching_volume1", async = true)
     public Volume detachVolumeFromVM(DetachVolumeCmd cmmd) {
         Account caller = UserContext.current().getCaller();
         if ((cmmd.getId() == null && cmmd.getDeviceId() == null && cmmd.getVirtualMachineId() == null) || (cmmd.getId() != null && (cmmd.getDeviceId() != null || cmmd.getVirtualMachineId() != null))
@@ -862,6 +880,9 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager
             throw new InvalidParameterValueException("Please specify a VM that is either running or stopped.");
         }
 
+        // Check that if the volume has snapshot
+        boolean attach = false;
+        checkVMSnapshots(vm, volumeId, attach);
         AsyncJobExecutor asyncExecutor = BaseAsyncJobExecutor.getCurrentExecutor();
         if (asyncExecutor != null) {
             AsyncJobVO job = asyncExecutor.getJob();
@@ -1380,7 +1401,6 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager
             }
 
             volume = _volsDao.findById(snapshot.getVolumeId());
-            VolumeVO snapshotVolume = _volsDao.findByIdIncludingRemoved(snapshot.getVolumeId());     
 
             //check permissions
             _accountMgr.checkAccess(caller, null, true, snapshot);