cloudstack-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From m...@apache.org
Subject [1/2] fix CLOUDSTACK-3591 add usage recording for VM snapshots
Date Thu, 01 Aug 2013 02:42:10 GMT
Updated Branches:
  refs/heads/master 7e9e80448 -> 51e4f597e


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/51e4f597/server/src/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java.orig
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java.orig b/server/src/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java.orig
new file mode 100644
index 0000000..b258fa7
--- /dev/null
+++ b/server/src/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java.orig
@@ -0,0 +1,913 @@
+// 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.inject.Inject;
+import javax.naming.ConfigurationException;
+
+import org.apache.log4j.Logger;
+import org.springframework.stereotype.Component;
+
+import org.apache.cloudstack.api.command.user.vmsnapshot.ListVMSnapshotCmd;
+import org.apache.cloudstack.context.CallContext;
+import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
+import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
+
+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.event.UsageEventUtils;
+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.hypervisor.dao.HypervisorCapabilitiesDao;
+import com.cloud.projects.Project.ListProjectResourcesCriteria;
+import com.cloud.service.dao.ServiceOfferingDao;
+import com.cloud.storage.DiskOfferingVO;
+import com.cloud.storage.GuestOSVO;
+import com.cloud.storage.Snapshot;
+import com.cloud.storage.SnapshotVO;
+import com.cloud.storage.StoragePool;
+import com.cloud.storage.VolumeVO;
+import com.cloud.storage.dao.DiskOfferingDao;
+import com.cloud.storage.dao.GuestOSDao;
+import com.cloud.storage.dao.SnapshotDao;
+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.DateUtil;
+import com.cloud.utils.NumbersUtil;
+import com.cloud.utils.Ternary;
+import com.cloud.utils.component.ManagerBase;
+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;
+
+@Component
+@Local(value = { VMSnapshotManager.class, VMSnapshotService.class })
+public class VMSnapshotManagerImpl extends ManagerBase implements VMSnapshotManager, VMSnapshotService {
+    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 PrimaryDataStoreDao _storagePoolDao;
+    @Inject SnapshotDao _snapshotDao;
+    @Inject VirtualMachineManager _itMgr;
+    @Inject DataStoreManager dataStoreMgr;
+    @Inject ConfigurationDao _configDao;
+    @Inject HypervisorCapabilitiesDao _hypervisorCapabilitiesDao;
+    @Inject DiskOfferingDao _diskOfferingDao;
+    @Inject ServiceOfferingDao _serviceOfferingDao;
+    int _vmSnapshotMax;
+    int _wait;
+    StateMachine2<VMSnapshot.State, VMSnapshot.Event, VMSnapshot> _vmSnapshottateMachine ;
+
+    @Override
+    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
+        _name = name;
+        if (_configDao == null) {
+            throw new ConfigurationException(
+                    "Unable to get the configuration dao.");
+        }
+
+        _vmSnapshotMax = NumbersUtil.parseInt(_configDao.getValue("vmsnapshot.max"), VMSNAPSHOTMAX);
+        
+        String value = _configDao.getValue("vmsnapshot.create.wait");
+        _wait = NumbersUtil.parseInt(value, 1800);
+
+        _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 CallContext.current().getCallingAccount();
+    }
+    
+    @Override
+    public VMSnapshot allocVMSnapshot(Long vmId, String vsDisplayName, String vsDescription, Boolean snapshotMemory)
+            throws ResourceAllocationException {
+
+        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");
+        }
+
+        // check hypervisor capabilities
+        if(!_hypervisorCapabilitiesDao.isVmSnapshotEnabled(userVmVo.getHypervisorType(), "default"))
+            throw new InvalidParameterValueException("VM snapshot is not enabled for hypervisor type: " + userVmVo.getHypervisorType());
+
+        // parameter length check
+        if(vsDisplayName != null && vsDisplayName.length()>255)
+            throw new InvalidParameterValueException("Creating VM snapshot failed due to length of VM snapshot vsDisplayName should not exceed 255");
+        if(vsDescription != null && vsDescription.length()>255)
+            throw new InvalidParameterValueException("Creating VM snapshot failed due to length of VM snapshot vsDescription should not exceed 255");
+        
+        // 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");
+        }
+        
+        if(snapshotMemory && userVmVo.getState() == VirtualMachine.State.Stopped){
+            throw new InvalidParameterValueException("Can not snapshot memory when VM is in 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.State.Creating,
+                    Snapshot.State.CreatedOnPrimary, Snapshot.State.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 (hasActiveVMSnapshotTasks(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(Long vmId, Long vmSnapshotId) {
+        UserVmVO userVm = _userVMDao.findById(vmId);
+        if (userVm == null) {
+            throw new InvalidParameterValueException("Create vm to snapshot failed due to vm: " + vmId + " is not found");
+        }
+        VMSnapshotVO vmSnapshot = _vmSnapshotDao.findById(vmSnapshotId);
+        if(vmSnapshot == null){
+            throw new CloudRuntimeException("VM snapshot id: " + vmSnapshotId + " can not be found");
+        }
+        Long hostId = pickRunningHost(vmId);
+        try {
+            vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.CreateRequested);
+        } catch (NoTransitionException e) {
+            throw new CloudRuntimeException(e.getMessage());
+        }
+        return createVmSnapshotInternal(userVm, vmSnapshot, hostId);
+    }
+
+    protected VMSnapshot createVmSnapshotInternal(UserVmVO userVm, VMSnapshotVO vmSnapshot, Long hostId) {
+<<<<<<< HEAD
+        try {
+            CreateVMSnapshotAnswer answer = null;
+=======
+        CreateVMSnapshotAnswer answer = null;
+        try {
+>>>>>>> acd2396... fix CLOUDSTACK-3591 add usage recording for VM snapshots
+            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());
+            ccmd.setWait(_wait);
+            
+            answer = (CreateVMSnapshotAnswer) sendToPool(hostId, ccmd);
+            if (answer != null && answer.getResult()) {
+                processAnswer(vmSnapshot, userVm, answer, hostId);
+                s_logger.debug("Create vm snapshot " + vmSnapshot.getName() + " succeeded for vm: " + userVm.getInstanceName());
+            }else{
+                
+                String errMsg = "Creating VM snapshot: " + vmSnapshot.getName() + " failed";
+                if(answer != null && answer.getDetails() != null)
+                    errMsg = errMsg + " due to " + answer.getDetails();
+                s_logger.error(errMsg);
+                vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationFailed);
+                throw new CloudRuntimeException(errMsg);
+            }
+            return vmSnapshot;
+        } catch (Exception e) {
+            if(e instanceof AgentUnavailableException){
+                try {
+                    vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationFailed);
+                } catch (NoTransitionException e1) {
+                    s_logger.error("Cannot set vm snapshot state due to: " + e1.getMessage());
+                }
+            }
+            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());
+            }
+            if(vmSnapshot.getState() == VMSnapshot.State.Ready && answer != null){
+                for (VolumeTO volumeTo : answer.getVolumeTOs()){
+                    publishUsageEvent(EventTypes.EVENT_VM_SNAPSHOT_CREATE,vmSnapshot,userVm,volumeTo);
+                }
+            }
+        }
+    }
+
+    private void publishUsageEvent(String type, VMSnapshot vmSnapshot, UserVm userVm, VolumeTO volumeTo){
+        VolumeVO volume = _volumeDao.findById(volumeTo.getId());
+        Long diskOfferingId = volume.getDiskOfferingId();
+        Long offeringId = null;
+        if (diskOfferingId != null) {
+            DiskOfferingVO offering = _diskOfferingDao.findById(diskOfferingId);
+            if (offering != null
+                    && (offering.getType() == DiskOfferingVO.Type.Disk)) {
+                offeringId = offering.getId();
+            }
+        }
+        UsageEventUtils.publishUsageEvent(
+                type,
+                vmSnapshot.getAccountId(),
+                userVm.getDataCenterId(),
+                userVm.getId(),
+                vmSnapshot.getName(),
+                offeringId,     
+                volume.getId(), // save volume's id into templateId field
+                volumeTo.getChainSize(),
+                VMSnapshot.class.getName(), vmSnapshot.getUuid());       
+    }
+    
+    protected List<VolumeTO> getVolumeTOList(Long vmId) {
+        List<VolumeTO> volumeTOs = new ArrayList<VolumeTO>();
+        List<VolumeVO> volumeVos = _volumeDao.findByInstance(vmId);
+        
+        for (VolumeVO volume : volumeVos) {
+            StoragePool pool = (StoragePool)dataStoreMgr.getPrimaryDataStore(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);
+    }
+
+    protected boolean vmSnapshotStateTransitTo(VMSnapshotVO vsnp, VMSnapshot.Event event) throws NoTransitionException {
+        return _vmSnapshottateMachine.transitTo(vsnp, event, null, _vmSnapshotDao);
+    }
+    
+    @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());
+                vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationSucceeded);
+            } else if (as instanceof RevertToVMSnapshotAnswer) {
+                RevertToVMSnapshotAnswer answer = (RevertToVMSnapshotAnswer) as;
+                finalizeRevert(vmSnapshot, answer.getVolumeTOs());
+                vmSnapshotStateTransitTo(vmSnapshot, VMSnapshot.Event.OperationSucceeded);
+            } 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());
+        if(previousCurrent != null){
+            previousCurrent.setCurrent(false);
+            _vmSnapshotDao.persist(previousCurrent);
+        }
+        vmSnapshot.setCurrent(true);
+        _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());
+                volumeVO.setVmSnapshotChainSize(volume.getChainSize());
+                _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;
+    }
+    
+    @Override
+    public boolean hasActiveVMSnapshotTasks(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 snapshots", async=true)
+    public boolean deleteVMSnapshot(Long vmSnapshotId) {
+        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.Expunging != 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, or Expunging State");
+        }
+        
+        // check if there are other active VM snapshot tasks
+        if (hasActiveVMSnapshotTasks(vmSnapshot.getVmId())) {
+            List<VMSnapshotVO> expungingSnapshots = _vmSnapshotDao.listByInstanceId(vmSnapshot.getVmId(), VMSnapshot.State.Expunging);
+            if(expungingSnapshots.size() > 0 && expungingSnapshots.get(0).getId() == vmSnapshot.getId())
+                s_logger.debug("Target VM snapshot already in expunging state, go on deleting it: " + vmSnapshot.getDisplayName());
+            else
+                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());
+        DeleteVMSnapshotAnswer answer = null;
+        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());
+            
+            answer = (DeleteVMSnapshotAnswer) sendToPool(hostId, deleteSnapshotCommand);
+           
+            if (answer != null && answer.getResult()) {
+                processAnswer(vmSnapshot, userVm, answer, hostId);
+                s_logger.debug("Delete VM snapshot " + vmSnapshot.getName() + " succeeded for vm: " + userVm.getInstanceName());
+                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());
+        } finally{
+            if(answer != null && answer.getResult()){
+                for (VolumeTO volumeTo : answer.getVolumeTOs()){
+                    publishUsageEvent(EventTypes.EVENT_VM_SNAPSHOT_DELETE,vmSnapshot,userVm,volumeTo);
+                }
+            }
+        }
+    }
+
+    @Override
+    @ActionEvent(eventType = EventTypes.EVENT_VM_SNAPSHOT_REVERT, eventDescription = "revert to VM snapshot", async = true)
+    public UserVm revertToSnapshot(Long vmSnapshotId) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException {
+
+        // 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 (hasActiveVMSnapshotTasks(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.");
+        }
+
+        UserVmVO vm = null;
+        Long hostId = null;
+        
+        // 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 {
+<<<<<<< HEAD
+                _itMgr.advanceStart(userVm.getUuid(), new HashMap<VirtualMachineProfile.Param, Object>());
+                vm = _userVMDao.findById(userVm.getId());
+        	    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.getUuid(), true);
+=======
+                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);
+>>>>>>> acd2396... fix CLOUDSTACK-3591 add usage recording for VM snapshots
+                } 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 (hasActiveVMSnapshotTasks(userVm.getId())) {
+            throw new InvalidParameterValueException("There is other active vm snapshot tasks on the instance, please try again later");
+        }
+        
+        userVm = _userVMDao.findById(userVm.getId());
+        try {
+            vmSnapshotStateTransitTo(vmSnapshotVo, VMSnapshot.Event.RevertRequested);
+        } catch (NoTransitionException e) {
+            throw new CloudRuntimeException(e.getMessage());
+        }
+        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();
+<<<<<<< HEAD
+            VMSnapshotTO vmSnapshotTO = new VMSnapshotTO(snapshot.getId(), snapshot.getName(), snapshot.getType(),
+            		snapshot.getCreated().getTime(), snapshot.getDescription(), snapshot.getCurrent(), parent);
+=======
+            VMSnapshotTO vmSnapshotTO = new VMSnapshotTO(snapshot.getId(), snapshot.getName(), snapshot.getType(), 
+                    snapshot.getCreated().getTime(), snapshot.getDescription(), snapshot.getCurrent(), parent);
+>>>>>>> acd2396... fix CLOUDSTACK-3591 add usage recording for VM snapshots
+            
+            GuestOSVO guestOS = _guestOSDao.findById(userVm.getGuestOSId());
+            RevertToVMSnapshotCommand revertToSnapshotCommand = new RevertToVMSnapshotCommand(vmInstanceName, vmSnapshotTO, volumeTOs, guestOS.getDisplayName());
+           
+            RevertToVMSnapshotAnswer answer = (RevertToVMSnapshotAnswer) sendToPool(hostId, revertToSnapshotCommand);
+            if (answer != null && answer.getResult()) {
+                processAnswer(vmSnapshotVo, userVm, answer, hostId);
+                s_logger.debug("RevertTo " + vmSnapshotVo.getName() + " succeeded for vm: " + userVm.getInstanceName());
+            } 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
+                vmSnapshotStateTransitTo(vmSnapshotVo, VMSnapshot.Event.OperationFailed);
+                throw new CloudRuntimeException(errMsg);
+            }
+        } catch (Exception e) {
+            if(e instanceof AgentUnavailableException){
+                try {
+                    vmSnapshotStateTransitTo(vmSnapshotVo, VMSnapshot.Event.OperationFailed);
+                } catch (NoTransitionException e1) {
+                    s_logger.error("Cannot set vm snapshot state due to: " + e1.getMessage());
+                }
+            }
+            // for other exceptions, do not change VM snapshot 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;
+    }
+
+
+    @Override
+    public VMSnapshot getVMSnapshotById(Long id) {
+        VMSnapshotVO vmSnapshot = _vmSnapshotDao.findById(id);
+        return vmSnapshot;
+    }
+
+    protected 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);
+        if(vmSnapshot == null){
+            throw new InvalidParameterValueException("unable to find the vm snapshot with id " + 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) {
+            VMSnapshotVO target = _vmSnapshotDao.findById(snapshot.getId());
+            if(type != null && target.getType() != type)
+                continue;
+            if (!deleteSnapshotInternal(target)) {
+                result = false;
+                break;
+            }
+        }
+        return result;
+    }
+
+    @Override
+    public boolean syncVMSnapshot(VMInstanceVO vm, Long hostId) {
+        try{
+            
+            UserVmVO userVm = _userVMDao.findById(vm.getId());
+            if(userVm == null)
+                return false;
+            
+            List<VMSnapshotVO> vmSnapshotsInExpungingStates = _vmSnapshotDao.listByInstanceId(vm.getId(), VMSnapshot.State.Expunging, VMSnapshot.State.Reverting, VMSnapshot.State.Creating);
+            for (VMSnapshotVO vmSnapshotVO : vmSnapshotsInExpungingStates) {
+                if(vmSnapshotVO.getState() == VMSnapshot.State.Expunging){
+                    return deleteSnapshotInternal(vmSnapshotVO);
+                }else if(vmSnapshotVO.getState() == VMSnapshot.State.Creating){
+                    return createVmSnapshotInternal(userVm, vmSnapshotVO, hostId) != null;
+                }else if(vmSnapshotVO.getState() == VMSnapshot.State.Reverting){
+                    return revertInternal(userVm, vmSnapshotVO, hostId) != null;
+                }
+            }
+        }catch (Exception e) {
+            s_logger.error(e.getMessage(),e);
+            if(_vmSnapshotDao.listByInstanceId(vm.getId(), VMSnapshot.State.Expunging).size() == 0)
+                return true;
+            else
+                return false;
+        }
+        return false;
+    }
+ 
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/51e4f597/setup/db/db/schema-410to420.sql
----------------------------------------------------------------------
diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql
index aa2eeb7..99a0208 100644
--- a/setup/db/db/schema-410to420.sql
+++ b/setup/db/db/schema-410to420.sql
@@ -2202,3 +2202,21 @@ CREATE VIEW `cloud`.`account_vmstats_view` AS
     where
         vm_type = 'User'
     group by account_id , state;
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Network', 'DEFAULT', 'management-server', 'network.loadbalancer.haproxy.max.conn', '4096', 'Load Balancer(haproxy) maximum number of concurrent connections(global max)');
+
+
+DROP TABLE IF EXISTS `cloud_usage`.`usage_vmsnapshot`;
+CREATE TABLE `cloud_usage`.`usage_vmsnapshot` (
+  `id` bigint(20) unsigned NOT NULL,
+  `zone_id` bigint(20) unsigned NOT NULL,
+  `account_id` bigint(20) unsigned NOT NULL,
+  `domain_id` bigint(20) unsigned NOT NULL,
+  `vm_id` bigint(20) unsigned NOT NULL,
+  `disk_offering_id` bigint(20) unsigned,
+  `size` bigint(20),
+  `created` datetime NOT NULL,
+  `processed` datetime,
+  INDEX `i_usage_vmsnapshot` (`account_id`,`id`,`vm_id`,`created`)
+) ENGINE=InnoDB CHARSET=utf8;
+
+ALTER TABLE volumes ADD COLUMN vm_snapshot_chain_size bigint(20) unsigned;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/51e4f597/usage/src/com/cloud/usage/UsageManagerImpl.java
----------------------------------------------------------------------
diff --git a/usage/src/com/cloud/usage/UsageManagerImpl.java b/usage/src/com/cloud/usage/UsageManagerImpl.java
index 8dc62c3..80f1f55 100644
--- a/usage/src/com/cloud/usage/UsageManagerImpl.java
+++ b/usage/src/com/cloud/usage/UsageManagerImpl.java
@@ -53,6 +53,7 @@ import com.cloud.usage.dao.UsagePortForwardingRuleDao;
 import com.cloud.usage.dao.UsageSecurityGroupDao;
 import com.cloud.usage.dao.UsageStorageDao;
 import com.cloud.usage.dao.UsageVMInstanceDao;
+import com.cloud.usage.dao.UsageVMSnapshotDao;
 import com.cloud.usage.dao.UsageVPNUserDao;
 import com.cloud.usage.dao.UsageVmDiskDao;
 import com.cloud.usage.dao.UsageVolumeDao;
@@ -64,6 +65,7 @@ import com.cloud.usage.parser.PortForwardingUsageParser;
 import com.cloud.usage.parser.SecurityGroupUsageParser;
 import com.cloud.usage.parser.StorageUsageParser;
 import com.cloud.usage.parser.VMInstanceUsageParser;
+import com.cloud.usage.parser.VMSnapshotUsageParser;
 import com.cloud.usage.parser.VPNUserUsageParser;
 import com.cloud.usage.parser.VmDiskUsageParser;
 import com.cloud.usage.parser.VolumeUsageParser;
@@ -116,7 +118,8 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
     @Inject protected AlertManager _alertMgr;
     @Inject protected UsageEventDao _usageEventDao;
     @Inject ConfigurationDao _configDao;
-
+    @Inject private UsageVMSnapshotDao m_usageVMSnapshotDao;
+    
     private String m_version = null;
     private final Calendar m_jobExecTime = Calendar.getInstance();
     private int m_aggregationDuration = 0;
@@ -236,7 +239,7 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
         }
 
         // use the configured exec time and aggregation duration for scheduling the job
-        m_scheduledFuture = m_executor.scheduleAtFixedRate(this, m_jobExecTime.getTimeInMillis() - System.currentTimeMillis(), m_aggregationDuration * 60 * 1000, TimeUnit.MILLISECONDS);
+        m_scheduledFuture = m_executor.scheduleAtFixedRate(this, m_jobExecTime.getTimeInMillis()  - System.currentTimeMillis(), m_aggregationDuration * 60 * 1000, TimeUnit.MILLISECONDS);
 
         m_heartbeat = m_heartbeatExecutor.scheduleAtFixedRate(new Heartbeat(), /* start in 15 seconds...*/15*1000, /* check database every minute*/60*1000, TimeUnit.MILLISECONDS);
         
@@ -857,6 +860,12 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
                 s_logger.debug("VPN user usage successfully parsed? " + parsed + " (for account: " + account.getAccountName() + ", id: " + account.getId() + ")");
             }
         }
+        parsed = VMSnapshotUsageParser.parse(account, currentStartDate, currentEndDate);
+        if (s_logger.isDebugEnabled()) {
+            if (!parsed) {
+                s_logger.debug("VM Snapshot usage successfully parsed? " + parsed + " (for account: " + account.getAccountName() + ", id: " + account.getId() + ")");
+            }
+        }
         return parsed;
     }
 
@@ -884,6 +893,8 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
             createVPNUserEvent(event);
         } else if (isSecurityGroupEvent(eventType)) {
             createSecurityGroupEvent(event);
+        } else if (isVmSnapshotEvent(eventType)){
+            createVMSnapshotEvent(event);
         }
     }
 
@@ -951,7 +962,12 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
         return (eventType.equals(EventTypes.EVENT_SECURITY_GROUP_ASSIGN) ||
                 eventType.equals(EventTypes.EVENT_SECURITY_GROUP_REMOVE));
     }
-    
+
+    private boolean isVmSnapshotEvent(String eventType){
+        if (eventType == null) return false;
+        return (eventType.equals(EventTypes.EVENT_VM_SNAPSHOT_CREATE) ||
+                eventType.equals(EventTypes.EVENT_VM_SNAPSHOT_DELETE));
+    }
     private void createVMHelperEvent(UsageEventVO event) {
 
         // One record for handling VM.START and VM.STOP
@@ -1618,7 +1634,22 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
             }
         }
     }
-    
+
+    private void createVMSnapshotEvent(UsageEventVO event){
+        Long vmId = event.getResourceId();
+        Long volumeId = event.getTemplateId();
+        Long offeringId = event.getOfferingId();
+        Long zoneId = event.getZoneId();
+        Long accountId = event.getAccountId();
+        long size = event.getSize();
+        Date created = event.getCreateDate();
+        Account acct = m_accountDao.findByIdIncludingRemoved(event.getAccountId());
+        Long domainId = acct.getDomainId();
+        UsageVMSnapshotVO vsVO  = new UsageVMSnapshotVO(volumeId,zoneId,accountId,
+                domainId,vmId,offeringId, size, created, null);
+        m_usageVMSnapshotDao.persist(vsVO);
+    }
+
     private class Heartbeat implements Runnable {
         public void run() {
             Transaction usageTxn = Transaction.open(Transaction.USAGE_DB);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/51e4f597/usage/src/com/cloud/usage/parser/VMSnapshotUsageParser.java
----------------------------------------------------------------------
diff --git a/usage/src/com/cloud/usage/parser/VMSnapshotUsageParser.java b/usage/src/com/cloud/usage/parser/VMSnapshotUsageParser.java
new file mode 100644
index 0000000..16a6b98
--- /dev/null
+++ b/usage/src/com/cloud/usage/parser/VMSnapshotUsageParser.java
@@ -0,0 +1,153 @@
+// 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
+// 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.usage.parser;
+
+import java.text.DecimalFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+
+import org.apache.cloudstack.usage.UsageTypes;
+import org.apache.log4j.Logger;
+import org.springframework.stereotype.Component;
+
+import com.cloud.usage.UsageVMSnapshotVO;
+import com.cloud.usage.UsageVO;
+import com.cloud.usage.dao.UsageDao;
+import com.cloud.usage.dao.UsageVMSnapshotDao;
+import com.cloud.user.AccountVO;
+
+@Component
+public class VMSnapshotUsageParser {
+    public static final Logger s_logger = Logger.getLogger(VMSnapshotUsageParser.class.getName());
+    
+    private static UsageDao m_usageDao;
+    private static UsageVMSnapshotDao m_usageVMSnapshotDao;
+
+    @Inject private UsageDao _usageDao;
+    @Inject private UsageVMSnapshotDao _usageVMSnapshotDao;
+    
+    @PostConstruct
+    void init() {
+        m_usageDao = _usageDao;
+        m_usageVMSnapshotDao = _usageVMSnapshotDao;
+    }
+    
+    public static boolean parse(AccountVO account, Date startDate, Date endDate) {
+        if (s_logger.isDebugEnabled()) {
+            s_logger.debug("Parsing all VmSnapshot volume usage events for account: " + account.getId());
+        }
+        if ((endDate == null) || endDate.after(new Date())) {
+            endDate = new Date();
+        }
+
+        List<UsageVMSnapshotVO> usageUsageVMSnapshots = 
+                m_usageVMSnapshotDao.getUsageRecords(account.getId(), account.getDomainId(), startDate, endDate);
+        
+        if(usageUsageVMSnapshots.isEmpty()){
+            s_logger.debug("No VM snapshot usage events for this period");
+            return true;
+        }
+
+        Map<String, UsageVMSnapshotVO> unprocessedUsage = new HashMap<String, UsageVMSnapshotVO>();
+        for (UsageVMSnapshotVO usageRec : usageUsageVMSnapshots) {
+            long zoneId = usageRec.getZoneId();
+            Long volId = usageRec.getId();
+            long vmId = usageRec.getVmId();
+            String key = vmId+":"+volId;
+            if(usageRec.getCreated().before(startDate)){
+                unprocessedUsage.put(key, usageRec);
+                continue;
+            }
+            UsageVMSnapshotVO previousEvent = m_usageVMSnapshotDao.
+                    getPreviousUsageRecord(usageRec);
+            if(previousEvent == null || previousEvent.getSize() == 0){
+                unprocessedUsage.put(key, usageRec);
+                continue;
+            }
+           
+            Date previousCreated = previousEvent.getCreated();
+            if (previousCreated.before(startDate)) {
+                previousCreated = startDate;
+            }
+
+            Date createDate = usageRec.getCreated();
+            long duration = (createDate.getTime() - previousCreated.getTime()) + 1;
+            
+            createUsageRecord(UsageTypes.VM_SNAPSHOT, duration, previousCreated, createDate, 
+                    account, volId, zoneId, previousEvent.getDiskOfferingId(), 
+                    vmId, previousEvent.getSize());
+            previousEvent.setProcessed(new Date());
+            m_usageVMSnapshotDao.update(previousEvent);
+            
+            if(usageRec.getSize() == 0){
+                usageRec.setProcessed(new Date());
+                m_usageVMSnapshotDao.update(usageRec);
+            }
+            else
+                unprocessedUsage.put(key, usageRec);
+        }
+
+        for (String key : unprocessedUsage.keySet()){
+            UsageVMSnapshotVO usageRec = unprocessedUsage.get(key);
+            Date created = usageRec.getCreated();
+            if (created.before(startDate)) {
+                created = startDate;
+            }
+            long duration = (endDate.getTime() - created.getTime()) + 1;
+            createUsageRecord(UsageTypes.VM_SNAPSHOT, duration, created, endDate, 
+                    account, usageRec.getId(), usageRec.getZoneId(), usageRec.getDiskOfferingId(), 
+                    usageRec.getVmId(), usageRec.getSize());
+        }
+
+        return true;
+    }
+
+    private static void createUsageRecord(int type, long runningTime, Date startDate, Date endDate, AccountVO account, long volId, long zoneId, Long doId, Long vmId, long size) {
+        // Our smallest increment is hourly for now
+        if (s_logger.isDebugEnabled()) {
+            s_logger.debug("Total running time " + runningTime + "ms");
+        }
+
+        float usage = runningTime / 1000f / 60f / 60f;
+
+        DecimalFormat dFormat = new DecimalFormat("#.######");
+        String usageDisplay = dFormat.format(usage);
+
+        if (s_logger.isDebugEnabled()) {
+            s_logger.debug("Creating VMSnapshot Volume usage record for vol: " + volId + ", usage: " + usageDisplay + ", startDate: " + startDate + ", endDate: " + endDate + ", for account: " + account.getId());
+        }
+
+        // Create the usage record
+        String usageDesc = "VMSnapshot Usage: " + "VM Id: " + vmId + " Volume Id: "+volId+" ";
+
+        if(doId != null){
+            usageDesc += " DiskOffering: " +doId ;
+        } 
+        
+        usageDesc += " Size: " + size;
+        
+        UsageVO usageRecord = new UsageVO(zoneId, account.getId(), account.getDomainId(), usageDesc, usageDisplay + " Hrs", type,
+                new Double(usage), vmId, null, doId, null, volId, size, startDate, endDate);
+        m_usageDao.persist(usageRecord);
+    }
+
+}


Mime
View raw message