cloudstack-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ahu...@apache.org
Subject [02/11] git commit: updated refs/heads/master to 5b616ea
Date Tue, 13 Aug 2013 04:09:34 GMT
Added missing file


Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/ae8ed582
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/ae8ed582
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/ae8ed582

Branch: refs/heads/master
Commit: ae8ed582280b37e0c24a8f0f54702f02819a10b7
Parents: 0e19e82
Author: Alex Huang <alex.huang@citrix.com>
Authored: Mon Aug 12 17:37:28 2013 -0700
Committer: Alex Huang <alex.huang@citrix.com>
Committed: Mon Aug 12 19:05:23 2013 -0700

----------------------------------------------------------------------
 .../com/cloud/storage/VolumeApiServiceImpl.java | 1730 ++++++++++++++++++
 1 file changed, 1730 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/ae8ed582/server/src/com/cloud/storage/VolumeApiServiceImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/com/cloud/storage/VolumeApiServiceImpl.java
new file mode 100644
index 0000000..f7beb9a
--- /dev/null
+++ b/server/src/com/cloud/storage/VolumeApiServiceImpl.java
@@ -0,0 +1,1730 @@
+// 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.storage;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.ExecutionException;
+
+import javax.inject.Inject;
+
+import org.apache.log4j.Logger;
+
+import org.apache.cloudstack.api.BaseCmd;
+import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd;
+import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd;
+import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd;
+import org.apache.cloudstack.api.command.user.volume.ExtractVolumeCmd;
+import org.apache.cloudstack.api.command.user.volume.MigrateVolumeCmd;
+import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd;
+import org.apache.cloudstack.api.command.user.volume.UpdateVolumeCmd;
+import org.apache.cloudstack.api.command.user.volume.UploadVolumeCmd;
+import org.apache.cloudstack.context.CallContext;
+import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
+import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
+import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
+import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProviderManager;
+import org.apache.cloudstack.engine.subsystem.api.storage.HostScope;
+import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreInfo;
+import org.apache.cloudstack.engine.subsystem.api.storage.Scope;
+import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotDataFactory;
+import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
+import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator;
+import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory;
+import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
+import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
+import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService;
+import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService.VolumeApiResult;
+import org.apache.cloudstack.framework.async.AsyncCallFuture;
+import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
+import org.apache.cloudstack.framework.jobs.AsyncJob;
+import org.apache.cloudstack.framework.jobs.AsyncJobExecutionContext;
+import org.apache.cloudstack.framework.jobs.AsyncJobManager;
+import org.apache.cloudstack.storage.command.AttachAnswer;
+import org.apache.cloudstack.storage.command.AttachCommand;
+import org.apache.cloudstack.storage.command.DettachCommand;
+import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
+import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
+import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreDao;
+import org.apache.cloudstack.storage.datastore.db.VolumeDataStoreVO;
+import org.apache.cloudstack.storage.image.datastore.ImageStoreEntity;
+
+import com.cloud.agent.AgentManager;
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.to.DataTO;
+import com.cloud.agent.api.to.DiskTO;
+import com.cloud.alert.AlertManager;
+import com.cloud.api.ApiDBUtils;
+import com.cloud.capacity.CapacityManager;
+import com.cloud.capacity.dao.CapacityDao;
+import com.cloud.configuration.Config;
+import com.cloud.configuration.ConfigurationManager;
+import com.cloud.configuration.Resource.ResourceType;
+import com.cloud.consoleproxy.ConsoleProxyManager;
+import com.cloud.dc.ClusterVO;
+import com.cloud.dc.DataCenter;
+import com.cloud.dc.DataCenterVO;
+import com.cloud.dc.Pod;
+import com.cloud.dc.dao.ClusterDao;
+import com.cloud.dc.dao.DataCenterDao;
+import com.cloud.dc.dao.HostPodDao;
+import com.cloud.domain.Domain;
+import com.cloud.domain.dao.DomainDao;
+import com.cloud.event.ActionEvent;
+import com.cloud.event.EventTypes;
+import com.cloud.event.UsageEventUtils;
+import com.cloud.event.dao.EventDao;
+import com.cloud.exception.ConcurrentOperationException;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.exception.PermissionDeniedException;
+import com.cloud.exception.ResourceAllocationException;
+import com.cloud.exception.StorageUnavailableException;
+import com.cloud.host.HostVO;
+import com.cloud.host.dao.HostDao;
+import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.hypervisor.HypervisorCapabilitiesVO;
+import com.cloud.hypervisor.HypervisorGuruManager;
+import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao;
+import com.cloud.network.NetworkModel;
+import com.cloud.org.Grouping;
+import com.cloud.resource.ResourceManager;
+import com.cloud.server.ManagementServer;
+import com.cloud.service.dao.ServiceOfferingDao;
+import com.cloud.storage.Storage.ImageFormat;
+import com.cloud.storage.dao.DiskOfferingDao;
+import com.cloud.storage.dao.SnapshotDao;
+import com.cloud.storage.dao.SnapshotPolicyDao;
+import com.cloud.storage.dao.StoragePoolHostDao;
+import com.cloud.storage.dao.StoragePoolWorkDao;
+import com.cloud.storage.dao.UploadDao;
+import com.cloud.storage.dao.VMTemplateDao;
+import com.cloud.storage.dao.VMTemplatePoolDao;
+import com.cloud.storage.dao.VolumeDao;
+import com.cloud.storage.dao.VolumeDetailsDao;
+import com.cloud.storage.download.DownloadMonitor;
+import com.cloud.storage.secondary.SecondaryStorageVmManager;
+import com.cloud.storage.snapshot.SnapshotApiService;
+import com.cloud.storage.snapshot.SnapshotManager;
+import com.cloud.storage.snapshot.SnapshotScheduler;
+import com.cloud.storage.upload.UploadMonitor;
+import com.cloud.tags.dao.ResourceTagDao;
+import com.cloud.template.TemplateManager;
+import com.cloud.user.Account;
+import com.cloud.user.AccountManager;
+import com.cloud.user.ResourceLimitService;
+import com.cloud.user.VmDiskStatisticsVO;
+import com.cloud.user.dao.AccountDao;
+import com.cloud.user.dao.UserDao;
+import com.cloud.user.dao.VmDiskStatisticsDao;
+import com.cloud.utils.EnumUtils;
+import com.cloud.utils.NumbersUtil;
+import com.cloud.utils.Pair;
+import com.cloud.utils.UriUtils;
+import com.cloud.utils.component.ManagerBase;
+import com.cloud.utils.db.DB;
+import com.cloud.utils.db.EntityManager;
+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.DiskProfile;
+import com.cloud.vm.UserVmManager;
+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.dao.ConsoleProxyDao;
+import com.cloud.vm.dao.DomainRouterDao;
+import com.cloud.vm.dao.SecondaryStorageVmDao;
+import com.cloud.vm.dao.UserVmDao;
+import com.cloud.vm.dao.VMInstanceDao;
+import com.cloud.vm.snapshot.VMSnapshotVO;
+import com.cloud.vm.snapshot.dao.VMSnapshotDao;
+
+public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiService {
+    private final static Logger s_logger = Logger.getLogger(VolumeApiServiceImpl.class);
+    @Inject
+    VolumeManager _volumeMgr;
+
+    @Inject
+    EntityManager _entityMgr;
+    @Inject
+    protected UserVmManager _userVmMgr;
+    @Inject
+    protected AgentManager _agentMgr;
+    @Inject
+    protected TemplateManager _tmpltMgr;
+    @Inject
+    protected AsyncJobManager _asyncMgr;
+    @Inject
+    protected SnapshotManager _snapshotMgr;
+    @Inject
+    protected SnapshotScheduler _snapshotScheduler;
+    @Inject
+    protected AccountManager _accountMgr;
+    @Inject
+    protected ConfigurationManager _configMgr;
+    @Inject
+    protected ConsoleProxyManager _consoleProxyMgr;
+    @Inject
+    protected SecondaryStorageVmManager _secStorageMgr;
+    @Inject
+    protected NetworkModel _networkMgr;
+    @Inject
+    protected ServiceOfferingDao _serviceOfferingDao;
+    @Inject
+    protected VolumeDao _volsDao;
+    @Inject
+    protected HostDao _hostDao;
+    @Inject
+    protected ConsoleProxyDao _consoleProxyDao;
+    @Inject
+    protected SnapshotDao _snapshotDao;
+    @Inject
+    protected SnapshotManager _snapMgr;
+    @Inject
+    protected SnapshotPolicyDao _snapshotPolicyDao;
+    @Inject
+    protected StoragePoolHostDao _storagePoolHostDao;
+    @Inject
+    StoragePoolDetailsDao storagePoolDetailsDao;
+    @Inject
+    protected AlertManager _alertMgr;
+    @Inject
+    protected TemplateDataStoreDao _vmTemplateStoreDao = null;
+    @Inject
+    protected VMTemplatePoolDao _vmTemplatePoolDao = null;
+    @Inject
+    protected VMTemplateDao _vmTemplateDao = null;
+    @Inject
+    protected StoragePoolHostDao _poolHostDao = null;
+    @Inject
+    protected UserVmDao _userVmDao;
+    @Inject
+    VolumeDataStoreDao _volumeStoreDao;
+    @Inject
+    protected VMInstanceDao _vmInstanceDao;
+    @Inject
+    protected PrimaryDataStoreDao _storagePoolDao = null;
+    @Inject
+    protected CapacityDao _capacityDao;
+    @Inject
+    protected CapacityManager _capacityMgr;
+    @Inject
+    protected DiskOfferingDao _diskOfferingDao;
+    @Inject
+    protected AccountDao _accountDao;
+    @Inject
+    protected EventDao _eventDao = null;
+    @Inject
+    protected DataCenterDao _dcDao = null;
+    @Inject
+    protected HostPodDao _podDao = null;
+    @Inject
+    protected VMTemplateDao _templateDao;
+    @Inject
+    protected ServiceOfferingDao _offeringDao;
+    @Inject
+    protected DomainDao _domainDao;
+    @Inject
+    protected UserDao _userDao;
+    @Inject
+    protected ClusterDao _clusterDao;
+    @Inject
+    protected VirtualMachineManager _vmMgr;
+    @Inject
+    protected DomainRouterDao _domrDao;
+    @Inject
+    protected SecondaryStorageVmDao _secStrgDao;
+    @Inject
+    protected StoragePoolWorkDao _storagePoolWorkDao;
+    @Inject
+    protected HypervisorGuruManager _hvGuruMgr;
+    @Inject
+    protected VolumeDao _volumeDao;
+    @Inject
+    protected OCFS2Manager _ocfs2Mgr;
+    @Inject
+    protected ResourceLimitService _resourceLimitMgr;
+    @Inject
+    protected SecondaryStorageVmManager _ssvmMgr;
+    @Inject
+    protected ResourceManager _resourceMgr;
+    @Inject
+    protected DownloadMonitor _downloadMonitor;
+    @Inject
+    protected ResourceTagDao _resourceTagDao;
+    @Inject
+    protected VmDiskStatisticsDao _vmDiskStatsDao;
+    @Inject
+    protected VMSnapshotDao _vmSnapshotDao;
+    @Inject
+    protected List<StoragePoolAllocator> _storagePoolAllocators;
+    @Inject
+    ConfigurationDao _configDao;
+    @Inject
+    VolumeDetailsDao _volDetailDao;
+    @Inject
+    ManagementServer _msServer;
+    @Inject
+    DataStoreManager dataStoreMgr;
+    @Inject
+    DataStoreProviderManager dataStoreProviderMgr;
+    @Inject
+    VolumeService volService;
+    @Inject
+    VolumeDataFactory volFactory;
+    @Inject
+    TemplateDataFactory tmplFactory;
+    @Inject
+    SnapshotDataFactory snapshotFactory;
+    @Inject
+    SnapshotApiService snapshotMgr;
+    @Inject
+    UploadMonitor _uploadMonitor;
+    @Inject
+    UploadDao _uploadDao;
+
+    @Inject
+    protected HypervisorCapabilitiesDao _hypervisorCapabilitiesDao;
+    @Inject
+    StorageManager storageMgr;
+    private int _customDiskOfferingMinSize = 1;
+    private final int _customDiskOfferingMaxSize = 1024;
+    private long _maxVolumeSizeInGb;
+    private final StateMachine2<Volume.State, Volume.Event, Volume> _volStateMachine;
+
+    protected VolumeApiServiceImpl() {
+        _volStateMachine = Volume.State.getStateMachine();
+    }
+
+    /*
+     * Upload the volume to secondary storage.
+     */
+    @Override
+    @DB
+    @ActionEvent(eventType = EventTypes.EVENT_VOLUME_UPLOAD, eventDescription = "uploading volume", async = true)
+    public VolumeVO uploadVolume(UploadVolumeCmd cmd) throws ResourceAllocationException {
+        Account caller = CallContext.current().getCallingAccount();
+        long ownerId = cmd.getEntityOwnerId();
+        Account owner = _entityMgr.findById(Account.class, ownerId);
+        Long zoneId = cmd.getZoneId();
+        String volumeName = cmd.getVolumeName();
+        String url = cmd.getUrl();
+        String format = cmd.getFormat();
+        String imageStoreUuid = cmd.getImageStoreUuid();
+        DataStore store = _tmpltMgr.getImageStore(imageStoreUuid, zoneId);
+
+        validateVolume(caller, ownerId, zoneId, volumeName, url, format);
+
+        VolumeVO volume = persistVolume(owner, zoneId, volumeName, url, cmd.getFormat());
+
+        VolumeInfo vol = volFactory.getVolume(volume.getId());
+
+        RegisterVolumePayload payload = new RegisterVolumePayload(cmd.getUrl(), cmd.getChecksum(), cmd.getFormat());
+        vol.addPayload(payload);
+
+        volService.registerVolume(vol, store);
+        return volume;
+    }
+
+    private boolean validateVolume(Account caller, long ownerId, Long zoneId, String volumeName, String url, String format) throws ResourceAllocationException {
+
+        // permission check
+        _accountMgr.checkAccess(caller, null, true, _accountMgr.getActiveAccountById(ownerId));
+
+        // Check that the resource limit for volumes won't be exceeded
+        _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(ownerId), ResourceType.volume);
+
+        // Verify that zone exists
+        DataCenterVO zone = _dcDao.findById(zoneId);
+        if (zone == null) {
+            throw new InvalidParameterValueException("Unable to find zone by id " + zoneId);
+        }
+
+        // Check if zone is disabled
+        if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getType())) {
+            throw new PermissionDeniedException("Cannot perform this operation, Zone is currently disabled: " + zoneId);
+        }
+
+        if (url.toLowerCase().contains("file://")) {
+            throw new InvalidParameterValueException("File:// type urls are currently unsupported");
+        }
+
+        ImageFormat imgfmt = ImageFormat.valueOf(format.toUpperCase());
+        if (imgfmt == null) {
+            throw new IllegalArgumentException("Image format is incorrect " + format + ". Supported formats are " + EnumUtils.listValues(ImageFormat.values()));
+        }
+
+        String userSpecifiedName = volumeName;
+        if (userSpecifiedName == null) {
+            userSpecifiedName = getRandomVolumeName();
+        }
+        if ((!url.toLowerCase().endsWith("vhd")) && (!url.toLowerCase().endsWith("vhd.zip")) && (!url.toLowerCase().endsWith("vhd.bz2")) &&
+            (!url.toLowerCase().endsWith("vhd.gz")) && (!url.toLowerCase().endsWith("qcow2")) && (!url.toLowerCase().endsWith("qcow2.zip")) &&
+            (!url.toLowerCase().endsWith("qcow2.bz2")) && (!url.toLowerCase().endsWith("qcow2.gz")) && (!url.toLowerCase().endsWith("ova")) &&
+            (!url.toLowerCase().endsWith("ova.zip")) && (!url.toLowerCase().endsWith("ova.bz2")) && (!url.toLowerCase().endsWith("ova.gz")) &&
+            (!url.toLowerCase().endsWith("img")) && (!url.toLowerCase().endsWith("raw"))) {
+            throw new InvalidParameterValueException("Please specify a valid " + format.toLowerCase());
+        }
+
+        if ((format.equalsIgnoreCase("vhd") && (!url.toLowerCase().endsWith(".vhd") && !url.toLowerCase().endsWith("vhd.zip") && !url.toLowerCase().endsWith("vhd.bz2") && !url
+                .toLowerCase().endsWith("vhd.gz"))) ||
+            (format.equalsIgnoreCase("qcow2") && (!url.toLowerCase().endsWith(".qcow2") && !url.toLowerCase().endsWith("qcow2.zip") && !url.toLowerCase().endsWith("qcow2.bz2") && !url
+                    .toLowerCase().endsWith("qcow2.gz"))) ||
+            (format.equalsIgnoreCase("ova") && (!url.toLowerCase().endsWith(".ova") && !url.toLowerCase().endsWith("ova.zip") && !url.toLowerCase().endsWith("ova.bz2") && !url
+                    .toLowerCase().endsWith("ova.gz"))) || (format.equalsIgnoreCase("raw") && (!url.toLowerCase().endsWith(".img") && !url.toLowerCase().endsWith("raw")))) {
+            throw new InvalidParameterValueException("Please specify a valid URL. URL:" + url + " is an invalid for the format " + format.toLowerCase());
+        }
+        UriUtils.validateUrl(url);
+
+        // Check that the resource limit for secondary storage won't be exceeded
+        _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(ownerId), ResourceType.secondary_storage, UriUtils.getRemoteSize(url));
+
+        return false;
+    }
+
+    public String getRandomVolumeName() {
+        return UUID.randomUUID().toString();
+    }
+
+    @DB
+    protected VolumeVO persistVolume(Account owner, Long zoneId, String volumeName, String url, String format) {
+
+        Transaction txn = Transaction.currentTxn();
+        txn.start();
+
+        VolumeVO volume = new VolumeVO(volumeName, zoneId, -1, -1, -1, new Long(-1), null, null, 0, Volume.Type.DATADISK);
+        volume.setPoolId(null);
+        volume.setDataCenterId(zoneId);
+        volume.setPodId(null);
+        volume.setAccountId(owner.getAccountId());
+        volume.setDomainId(owner.getDomainId());
+        long diskOfferingId = _diskOfferingDao.findByUniqueName("Cloud.com-Custom").getId();
+        volume.setDiskOfferingId(diskOfferingId);
+        // volume.setSize(size);
+        volume.setInstanceId(null);
+        volume.setUpdated(new Date());
+        volume.setDomainId((owner == null) ? Domain.ROOT_DOMAIN : owner.getDomainId());
+        volume.setFormat(ImageFormat.valueOf(format));
+        volume = _volsDao.persist(volume);
+        CallContext.current().setEventDetails("Volume Id: " + volume.getId());
+
+        // Increment resource count during allocation; if actual creation fails,
+        // decrement it
+        _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.volume);
+        _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.secondary_storage, UriUtils.getRemoteSize(url));
+
+        txn.commit();
+        return volume;
+    }
+
+    /*
+     * Just allocate a volume in the database, don't send the createvolume cmd
+     * to hypervisor. The volume will be finally created only when it's attached
+     * to a VM.
+     */
+    @Override
+    @DB
+    @ActionEvent(eventType = EventTypes.EVENT_VOLUME_CREATE, eventDescription = "creating volume", create = true)
+    public VolumeVO allocVolume(CreateVolumeCmd cmd) throws ResourceAllocationException {
+        // FIXME: some of the scheduled event stuff might be missing here...
+        Account caller = CallContext.current().getCallingAccount();
+
+        long ownerId = cmd.getEntityOwnerId();
+        Boolean displayVolumeEnabled = cmd.getDisplayVolume();
+
+        // permission check
+        _accountMgr.checkAccess(caller, null, true, _accountMgr.getActiveAccountById(ownerId));
+
+        // Check that the resource limit for volumes won't be exceeded
+        _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(ownerId), ResourceType.volume);
+
+        Long zoneId = cmd.getZoneId();
+        Long diskOfferingId = null;
+        DiskOfferingVO diskOffering = null;
+        Long size = null;
+        Long minIops = null;
+        Long maxIops = null;
+        // Volume VO used for extracting the source template id
+        VolumeVO parentVolume = null;
+
+        // validate input parameters before creating the volume
+        if ((cmd.getSnapshotId() == null && cmd.getDiskOfferingId() == null) || (cmd.getSnapshotId() != null && cmd.getDiskOfferingId() != null)) {
+            throw new InvalidParameterValueException("Either disk Offering Id or snapshot Id must be passed whilst creating volume");
+        }
+
+        if (cmd.getSnapshotId() == null) {// create a new volume
+
+            diskOfferingId = cmd.getDiskOfferingId();
+            size = cmd.getSize();
+            Long sizeInGB = size;
+            if (size != null) {
+                if (size > 0) {
+                    size = size * 1024 * 1024 * 1024; // user specify size in GB
+                } else {
+                    throw new InvalidParameterValueException("Disk size must be larger than 0");
+                }
+            }
+
+            // Check that the the disk offering is specified
+            diskOffering = _diskOfferingDao.findById(diskOfferingId);
+            if ((diskOffering == null) || diskOffering.getRemoved() != null || !DiskOfferingVO.Type.Disk.equals(diskOffering.getType())) {
+                throw new InvalidParameterValueException("Please specify a valid disk offering.");
+            }
+
+            if (diskOffering.isCustomized()) {
+                if (size == null) {
+                    throw new InvalidParameterValueException("This disk offering requires a custom size specified");
+                }
+                if ((sizeInGB < _customDiskOfferingMinSize) || (sizeInGB > _customDiskOfferingMaxSize)) {
+                    throw new InvalidParameterValueException("Volume size: " + sizeInGB + "GB is out of allowed range. Max: " + _customDiskOfferingMaxSize + " Min:" +
+                                                             _customDiskOfferingMinSize);
+                }
+            }
+
+            if (!diskOffering.isCustomized() && size != null) {
+                throw new InvalidParameterValueException("This disk offering does not allow custom size");
+            }
+
+            if (diskOffering.getDomainId() == null) {
+                // do nothing as offering is public
+            } else {
+                _configMgr.checkDiskOfferingAccess(caller, diskOffering);
+            }
+
+            if (diskOffering.getDiskSize() > 0) {
+                size = diskOffering.getDiskSize();
+            }
+
+            Boolean isCustomizedIops = diskOffering.isCustomizedIops();
+
+            if (isCustomizedIops != null) {
+                if (isCustomizedIops) {
+                    minIops = cmd.getMinIops();
+                    maxIops = cmd.getMaxIops();
+
+                    if (minIops == null && maxIops == null) {
+                        minIops = 0L;
+                        maxIops = 0L;
+                    } else {
+                        if (minIops == null || minIops <= 0) {
+                            throw new InvalidParameterValueException("The min IOPS must be greater than 0.");
+                        }
+
+                        if (maxIops == null) {
+                            maxIops = 0L;
+                        }
+
+                        if (minIops > maxIops) {
+                            throw new InvalidParameterValueException("The min IOPS must be less than or equal to the max IOPS.");
+                        }
+                    }
+                } else {
+                    minIops = diskOffering.getMinIops();
+                    maxIops = diskOffering.getMaxIops();
+                }
+            }
+
+            if (!validateVolumeSizeRange(size)) {// convert size from mb to gb
+                // for validation
+                throw new InvalidParameterValueException("Invalid size for custom volume creation: " + size + " ,max volume size is:" + _maxVolumeSizeInGb);
+            }
+        } else { // create volume from snapshot
+            Long snapshotId = cmd.getSnapshotId();
+            SnapshotVO snapshotCheck = _snapshotDao.findById(snapshotId);
+            if (snapshotCheck == null) {
+                throw new InvalidParameterValueException("unable to find a snapshot with id " + snapshotId);
+            }
+
+            if (snapshotCheck.getState() != Snapshot.State.BackedUp) {
+                throw new InvalidParameterValueException("Snapshot id=" + snapshotId + " is not in " + Snapshot.State.BackedUp + " state yet and can't be used for volume creation");
+            }
+            parentVolume = _volsDao.findByIdIncludingRemoved(snapshotCheck.getVolumeId());
+
+            diskOfferingId = snapshotCheck.getDiskOfferingId();
+            diskOffering = _diskOfferingDao.findById(diskOfferingId);
+            zoneId = snapshotCheck.getDataCenterId();
+            size = snapshotCheck.getSize(); // ; disk offering is used for tags
+            // purposes
+
+            // check snapshot permissions
+            _accountMgr.checkAccess(caller, null, true, snapshotCheck);
+        }
+
+        if (displayVolumeEnabled == null) {
+            displayVolumeEnabled = true;
+        } else {
+            if (!_accountMgr.isRootAdmin(caller.getType())) {
+                throw new PermissionDeniedException("Cannot update parameter displayvolume, only admin permitted ");
+            }
+        }
+
+        // Check that the resource limit for primary storage won't be exceeded
+        _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(ownerId), ResourceType.primary_storage, new Long(size));
+
+        // Verify that zone exists
+        DataCenterVO zone = _dcDao.findById(zoneId);
+        if (zone == null) {
+            throw new InvalidParameterValueException("Unable to find zone by id " + zoneId);
+        }
+
+        // Check if zone is disabled
+        if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getType())) {
+            throw new PermissionDeniedException("Cannot perform this operation, Zone is currently disabled: " + zoneId);
+        }
+
+        // If local storage is disabled then creation of volume with local disk
+        // offering not allowed
+        if (!zone.isLocalStorageEnabled() && diskOffering.getUseLocalStorage()) {
+            throw new InvalidParameterValueException("Zone is not configured to use local storage but volume's disk offering " + diskOffering.getName() + " uses it");
+        }
+
+        String userSpecifiedName = cmd.getVolumeName();
+        if (userSpecifiedName == null) {
+            userSpecifiedName = getRandomVolumeName();
+        }
+
+        Transaction txn = Transaction.currentTxn();
+        txn.start();
+
+        VolumeVO volume = new VolumeVO(userSpecifiedName, -1, -1, -1, -1, new Long(-1), null, null, 0, Volume.Type.DATADISK);
+        volume.setPoolId(null);
+        volume.setDataCenterId(zoneId);
+        volume.setPodId(null);
+        volume.setAccountId(ownerId);
+        volume.setDomainId(((caller == null) ? Domain.ROOT_DOMAIN : caller.getDomainId()));
+        volume.setDiskOfferingId(diskOfferingId);
+        volume.setSize(size);
+        volume.setMinIops(minIops);
+        volume.setMaxIops(maxIops);
+        volume.setInstanceId(null);
+        volume.setUpdated(new Date());
+        volume.setDomainId((caller == null) ? Domain.ROOT_DOMAIN : caller.getDomainId());
+        volume.setDisplayVolume(displayVolumeEnabled);
+        if (parentVolume != null) {
+            volume.setTemplateId(parentVolume.getTemplateId());
+            volume.setFormat(parentVolume.getFormat());
+        } else {
+            volume.setTemplateId(null);
+        }
+
+        volume = _volsDao.persist(volume);
+        if (cmd.getSnapshotId() == null) {
+            // for volume created from snapshot, create usage event after volume creation
+            UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), diskOfferingId,
+                    null, size, Volume.class.getName(), volume.getUuid());
+        }
+
+        CallContext.current().setEventDetails("Volume Id: " + volume.getId());
+
+        // Increment resource count during allocation; if actual creation fails,
+        // decrement it
+        _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.volume);
+        _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.primary_storage, new Long(volume.getSize()));
+
+        txn.commit();
+
+        return volume;
+    }
+
+    public boolean validateVolumeSizeRange(long size) {
+        if (size < 0 || (size > 0 && size < (1024 * 1024 * 1024))) {
+            throw new InvalidParameterValueException("Please specify a size of at least 1 Gb.");
+        } else if (size > (_maxVolumeSizeInGb * 1024 * 1024 * 1024)) {
+            throw new InvalidParameterValueException("volume size " + size + ", but the maximum size allowed is " + _maxVolumeSizeInGb + " Gb.");
+        }
+
+        return true;
+    }
+
+    @Override
+    @DB
+    @ActionEvent(eventType = EventTypes.EVENT_VOLUME_CREATE, eventDescription = "creating volume", async = true)
+    public VolumeVO createVolume(CreateVolumeCmd cmd) {
+        VolumeVO volume = _volsDao.findById(cmd.getEntityId());
+        boolean created = true;
+
+        try {
+            if (cmd.getSnapshotId() != null) {
+                volume = createVolumeFromSnapshot(volume, cmd.getSnapshotId());
+                if (volume.getState() != Volume.State.Ready) {
+                    created = false;
+                }
+            }
+            return volume;
+        } catch (Exception e) {
+            created = false;
+            s_logger.debug("Failed to create volume: " + volume.getId(), e);
+            return null;
+        } finally {
+            if (!created) {
+                s_logger.trace("Decrementing volume resource count for account id=" + volume.getAccountId() + " as volume failed to create on the backend");
+                _resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.volume);
+                _resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.primary_storage, new Long(volume.getSize()));
+            }
+        }
+    }
+
+    protected VolumeVO createVolumeFromSnapshot(VolumeVO volume, long snapshotId) throws StorageUnavailableException {
+        VolumeInfo createdVolume = null;
+        SnapshotVO snapshot = _snapshotDao.findById(snapshotId);
+        createdVolume = createVolumeFromSnapshot(volume, snapshot);
+
+        UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, createdVolume.getAccountId(), createdVolume.getDataCenterId(), createdVolume.getId(),
+                createdVolume.getName(), createdVolume.getDiskOfferingId(), null, createdVolume.getSize(), Volume.class.getName(), createdVolume.getUuid());
+
+        return _volsDao.findById(createdVolume.getId());
+    }
+
+    @DB
+    protected VolumeInfo createVolumeFromSnapshot(VolumeVO volume, SnapshotVO snapshot) throws StorageUnavailableException {
+        Account account = _accountDao.findById(volume.getAccountId());
+
+        final HashSet<StoragePool> poolsToAvoid = new HashSet<StoragePool>();
+        StoragePool pool = null;
+
+        Set<Long> podsToAvoid = new HashSet<Long>();
+        Pair<Pod, Long> pod = null;
+
+        DiskOfferingVO diskOffering = _diskOfferingDao.findByIdIncludingRemoved(volume.getDiskOfferingId());
+        DataCenterVO dc = _dcDao.findById(volume.getDataCenterId());
+        DiskProfile dskCh = new DiskProfile(volume, diskOffering, snapshot.getHypervisorType());
+
+        // Determine what pod to store the volume in
+        while ((pod = _resourceMgr.findPod(null, null, dc, account.getId(), podsToAvoid)) != null) {
+            podsToAvoid.add(pod.first().getId());
+            // Determine what storage pool to store the volume in
+            while ((pool = storageMgr.findStoragePool(dskCh, dc, pod.first(), null, null, null, poolsToAvoid)) != null) {
+                break;
+            }
+        }
+
+        if (pool == null) {
+            String msg = "There are no available storage pools to store the volume in";
+            s_logger.info(msg);
+            throw new StorageUnavailableException(msg, -1);
+        }
+
+        VolumeInfo vol = volFactory.getVolume(volume.getId());
+        DataStore store = dataStoreMgr.getDataStore(pool.getId(), DataStoreRole.Primary);
+        SnapshotInfo snapInfo = snapshotFactory.getSnapshot(snapshot.getId(), DataStoreRole.Image);
+        AsyncCallFuture<VolumeApiResult> future = volService.createVolumeFromSnapshot(vol, store, snapInfo);
+        try {
+            VolumeApiResult result = future.get();
+            if (result.isFailed()) {
+                s_logger.debug("Failed to create volume from snapshot:" + result.getResult());
+                throw new CloudRuntimeException("Failed to create volume from snapshot:" + result.getResult());
+            }
+            return result.getVolume();
+        } catch (InterruptedException e) {
+            s_logger.debug("Failed to create volume from snapshot", e);
+            throw new CloudRuntimeException("Failed to create volume from snapshot", e);
+        } catch (ExecutionException e) {
+            s_logger.debug("Failed to create volume from snapshot", e);
+            throw new CloudRuntimeException("Failed to create volume from snapshot", e);
+        }
+
+    }
+
+    @Override
+    @DB
+    @ActionEvent(eventType = EventTypes.EVENT_VOLUME_RESIZE, eventDescription = "resizing volume", async = true)
+    public VolumeVO resizeVolume(ResizeVolumeCmd cmd) throws ResourceAllocationException {
+        Long newSize = null;
+        boolean shrinkOk = cmd.getShrinkOk();
+
+        VolumeVO volume = _volsDao.findById(cmd.getEntityId());
+        if (volume == null) {
+            throw new InvalidParameterValueException("No such volume");
+        }
+
+        DiskOfferingVO diskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId());
+        DiskOfferingVO newDiskOffering = null;
+
+        newDiskOffering = _diskOfferingDao.findById(cmd.getNewDiskOfferingId());
+
+        /*
+         * Volumes with no hypervisor have never been assigned, and can be
+         * resized by recreating. perhaps in the future we can just update the
+         * db entry for the volume
+         */
+        if (_volsDao.getHypervisorType(volume.getId()) == HypervisorType.None) {
+            throw new InvalidParameterValueException("Can't resize a volume that has never been attached, not sure which hypervisor type. Recreate volume to resize.");
+        }
+
+        /* Only works for KVM/Xen for now */
+        if (_volsDao.getHypervisorType(volume.getId()) != HypervisorType.KVM && _volsDao.getHypervisorType(volume.getId()) != HypervisorType.XenServer &&
+            _volsDao.getHypervisorType(volume.getId()) != HypervisorType.VMware) {
+            throw new InvalidParameterValueException("Cloudstack currently only supports volumes marked as KVM or XenServer hypervisor for resize");
+        }
+
+        if (volume.getState() != Volume.State.Ready) {
+            throw new InvalidParameterValueException("Volume should be in ready state before attempting a resize");
+        }
+
+        if (!volume.getVolumeType().equals(Volume.Type.DATADISK)) {
+            throw new InvalidParameterValueException("Can only resize DATA volumes");
+        }
+
+        /*
+         * figure out whether or not a new disk offering or size parameter is
+         * required, get the correct size value
+         */
+        if (newDiskOffering == null) {
+            if (diskOffering.isCustomized()) {
+                newSize = cmd.getSize();
+
+                if (newSize == null) {
+                    throw new InvalidParameterValueException("new offering is of custom size, need to specify a size");
+                }
+
+                newSize = (newSize << 30);
+            } else {
+                throw new InvalidParameterValueException("current offering" + volume.getDiskOfferingId() + " cannot be resized, need to specify a disk offering");
+            }
+        } else {
+
+            if (newDiskOffering.getRemoved() != null || !DiskOfferingVO.Type.Disk.equals(newDiskOffering.getType())) {
+                throw new InvalidParameterValueException("Disk offering ID is missing or invalid");
+            }
+
+            if (diskOffering.getTags() != null) {
+                if (!newDiskOffering.getTags().equals(diskOffering.getTags())) {
+                    throw new InvalidParameterValueException("Tags on new and old disk offerings must match");
+                }
+            } else if (newDiskOffering.getTags() != null) {
+                throw new InvalidParameterValueException("There are no tags on current disk offering, new disk offering needs to have no tags");
+            }
+
+            if (newDiskOffering.getDomainId() == null) {
+                // do nothing as offering is public
+            } else {
+                _configMgr.checkDiskOfferingAccess(CallContext.current().getCallingAccount(), newDiskOffering);
+            }
+
+            if (newDiskOffering.isCustomized()) {
+                newSize = cmd.getSize();
+
+                if (newSize == null) {
+                    throw new InvalidParameterValueException("new offering is of custom size, need to specify a size");
+                }
+
+                newSize = (newSize << 30);
+            } else {
+                newSize = newDiskOffering.getDiskSize();
+            }
+        }
+
+        if (newSize == null) {
+            throw new InvalidParameterValueException("could not detect a size parameter or fetch one from the diskofferingid parameter");
+        }
+
+        if (!validateVolumeSizeRange(newSize)) {
+            throw new InvalidParameterValueException("Requested size out of range");
+        }
+
+        /* does the caller have the authority to act on this volume? */
+        _accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, true, volume);
+
+        UserVmVO userVm = _userVmDao.findById(volume.getInstanceId());
+
+        long currentSize = volume.getSize();
+
+        /*
+         * lets make certain they (think they) know what they're doing if they
+         * want to shrink, by forcing them to provide the shrinkok parameter.
+         * This will be checked again at the hypervisor level where we can see
+         * the actual disk size
+         */
+        if (currentSize > newSize && !shrinkOk) {
+            throw new InvalidParameterValueException("Going from existing size of " + currentSize + " to size of " + newSize +
+                                                     " would shrink the volume, need to sign off by supplying the shrinkok parameter with value of true");
+        }
+
+        if (!shrinkOk) {
+            /* Check resource limit for this account on primary storage resource */
+            _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(volume.getAccountId()), ResourceType.primary_storage, new Long(newSize - currentSize));
+        }
+
+        /*
+         * get a list of hosts to send the commands to, try the system the
+         * associated vm is running on first, then the last known place it ran.
+         * If not attached to a userVm, we pass 'none' and resizevolume.sh is ok
+         * with that since it only needs the vm name to live resize
+         */
+        long[] hosts = null;
+        String instanceName = "none";
+        if (userVm != null) {
+            instanceName = userVm.getInstanceName();
+            if (userVm.getHostId() != null) {
+                hosts = new long[] {userVm.getHostId()};
+            } else if (userVm.getLastHostId() != null) {
+                hosts = new long[] {userVm.getLastHostId()};
+            }
+
+            /* Xen only works offline, SR does not support VDI.resizeOnline */
+            if (_volsDao.getHypervisorType(volume.getId()) == HypervisorType.XenServer && !userVm.getState().equals(State.Stopped)) {
+                throw new InvalidParameterValueException("VM must be stopped or disk detached in order to resize with the Xen HV");
+            }
+        }
+
+        ResizeVolumePayload payload = new ResizeVolumePayload(newSize, shrinkOk, instanceName, hosts);
+
+        try {
+            VolumeInfo vol = volFactory.getVolume(volume.getId());
+            vol.addPayload(payload);
+
+            AsyncCallFuture<VolumeApiResult> future = volService.resize(vol);
+            VolumeApiResult result = future.get();
+            if (result.isFailed()) {
+                s_logger.warn("Failed to resize the volume " + volume);
+                return null;
+            }
+
+            volume = _volsDao.findById(volume.getId());
+
+            if (newDiskOffering != null) {
+                volume.setDiskOfferingId(cmd.getNewDiskOfferingId());
+            }
+            _volsDao.update(volume.getId(), volume);
+            // Log usage event for volumes belonging user VM's only
+            UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_RESIZE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(),
+                    volume.getDiskOfferingId(), volume.getTemplateId(), volume.getSize(), Volume.class.getName(), volume.getUuid());
+
+            /* Update resource count for the account on primary storage resource */
+            if (!shrinkOk) {
+                _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.primary_storage, new Long(newSize - currentSize));
+            } else {
+                _resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.primary_storage, new Long(currentSize - newSize));
+            }
+            return volume;
+        } catch (InterruptedException e) {
+            s_logger.warn("failed get resize volume result", e);
+        } catch (ExecutionException e) {
+            s_logger.warn("failed get resize volume result", e);
+        } catch (Exception e) {
+            s_logger.warn("failed get resize volume result", e);
+        }
+
+        return null;
+    }
+
+    @Override
+    @DB
+    @ActionEvent(eventType = EventTypes.EVENT_VOLUME_DELETE, eventDescription = "deleting volume")
+    public boolean deleteVolume(long volumeId, Account caller) throws ConcurrentOperationException {
+
+        VolumeVO volume = _volsDao.findById(volumeId);
+        if (volume == null) {
+            throw new InvalidParameterValueException("Unable to aquire volume with ID: " + volumeId);
+        }
+
+        if (!_snapshotMgr.canOperateOnVolume(volume)) {
+            throw new InvalidParameterValueException("There are snapshot creating on it, Unable to delete the volume");
+        }
+
+        _accountMgr.checkAccess(caller, null, true, volume);
+
+        if (volume.getInstanceId() != null) {
+            throw new InvalidParameterValueException("Please specify a volume that is not attached to any VM.");
+        }
+
+        if (volume.getState() == Volume.State.UploadOp) {
+            VolumeDataStoreVO volumeStore = _volumeStoreDao.findByVolume(volume.getId());
+            if (volumeStore.getDownloadState() == VMTemplateStorageResourceAssoc.Status.DOWNLOAD_IN_PROGRESS) {
+                throw new InvalidParameterValueException("Please specify a volume that is not uploading");
+            }
+        }
+
+        try {
+            if (volume.getState() != Volume.State.Destroy && volume.getState() != Volume.State.Expunging && volume.getState() != Volume.State.Expunging) {
+                Long instanceId = volume.getInstanceId();
+                if (!volService.destroyVolume(volume.getId())) {
+                    return false;
+                }
+
+                VMInstanceVO vmInstance = _vmInstanceDao.findById(instanceId);
+                if (instanceId == null || (vmInstance.getType().equals(VirtualMachine.Type.User))) {
+                    // Decrement the resource count for volumes and primary storage belonging user VM's only
+                    _resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.volume);
+                    /* If volume is in primary storage, decrement primary storage count else decrement secondary
+                     storage count (in case of upload volume). */
+                    if (volume.getFolder() != null || volume.getPath() != null || volume.getState() == Volume.State.Allocated) {
+                        _resourceLimitMgr.decrementResourceCount(volume.getAccountId(), ResourceType.primary_storage, new Long(volume.getSize()));
+                    } else {
+                        _resourceLimitMgr.recalculateResourceCount(volume.getAccountId(), volume.getDomainId(), ResourceType.secondary_storage.getOrdinal());
+                    }
+
+                    // Log usage event for volumes belonging user VM's only
+                    UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_DELETE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(),
+                            Volume.class.getName(), volume.getUuid());
+                }
+            }
+            // Mark volume as removed if volume has not been created on primary or secondary
+            if (volume.getState() == Volume.State.Allocated) {
+                _volsDao.remove(volumeId);
+                stateTransitTo(volume, Volume.Event.DestroyRequested);
+                return true;
+            }
+            // expunge volume from primary if volume is on primary
+            VolumeInfo volOnPrimary = volFactory.getVolume(volume.getId(), DataStoreRole.Primary);
+            if (volOnPrimary != null) {
+                s_logger.info("Expunging volume " + volume.getId() + " from primary data store");
+                AsyncCallFuture<VolumeApiResult> future = volService.expungeVolumeAsync(volOnPrimary);
+                future.get();
+            }
+            // expunge volume from secondary if volume is on image store
+            VolumeInfo volOnSecondary = volFactory.getVolume(volume.getId(), DataStoreRole.Image);
+            if (volOnSecondary != null) {
+                s_logger.info("Expunging volume " + volume.getId() + " from secondary data store");
+                AsyncCallFuture<VolumeApiResult> future2 = volService.expungeVolumeAsync(volOnSecondary);
+                future2.get();
+            }
+        } catch (Exception e) {
+            s_logger.warn("Failed to expunge volume:", e);
+            return false;
+        }
+
+        return true;
+    }
+
+    private boolean stateTransitTo(Volume vol, Volume.Event event) throws NoTransitionException {
+        return _volStateMachine.transitTo(vol, event, null, _volsDao);
+    }
+
+    @Override
+    @ActionEvent(eventType = EventTypes.EVENT_VOLUME_ATTACH, eventDescription = "attaching volume", async = true)
+    public Volume attachVolumeToVM(AttachVolumeCmd command) {
+        Long vmId = command.getVirtualMachineId();
+        Long volumeId = command.getId();
+        Long deviceId = command.getDeviceId();
+        Account caller = CallContext.current().getCallingAccount();
+
+        // Check that the volume ID is valid
+        VolumeInfo volume = volFactory.getVolume(volumeId);
+        // Check that the volume is a data volume
+        if (volume == null || volume.getVolumeType() != Volume.Type.DATADISK) {
+            throw new InvalidParameterValueException("Please specify a valid data volume.");
+        }
+
+        // Check that the volume is not currently attached to any VM
+        if (volume.getInstanceId() != null) {
+            throw new InvalidParameterValueException("Please specify a volume that is not attached to any VM.");
+        }
+
+        // Check that the volume is not destroyed
+        if (volume.getState() == Volume.State.Destroy) {
+            throw new InvalidParameterValueException("Please specify a volume that is not destroyed.");
+        }
+
+        // Check that the virtual machine ID is valid and it's a user vm
+        UserVmVO vm = _userVmDao.findById(vmId);
+        if (vm == null || vm.getType() != VirtualMachine.Type.User) {
+            throw new InvalidParameterValueException("Please specify a valid User VM.");
+        }
+
+        // Check that the VM is in the correct state
+        if (vm.getState() != State.Running && vm.getState() != State.Stopped) {
+            throw new InvalidParameterValueException("Please specify a VM that is either running or stopped.");
+        }
+
+        // Check that the device ID is valid
+        if (deviceId != null) {
+            if (deviceId.longValue() == 0) {
+                throw new InvalidParameterValueException("deviceId can't be 0, which is used by Root device");
+            }
+        }
+
+        // Check that the number of data volumes attached to VM is less than
+        // that supported by hypervisor
+        List<VolumeVO> existingDataVolumes = _volsDao.findByInstanceAndType(vmId, Volume.Type.DATADISK);
+        int maxDataVolumesSupported = getMaxDataVolumesSupported(vm);
+        if (existingDataVolumes.size() >= maxDataVolumesSupported) {
+            throw new InvalidParameterValueException("The specified VM already has the maximum number of data disks (" + maxDataVolumesSupported + "). Please specify another VM.");
+        }
+
+        // Check that the VM and the volume are in the same zone
+        if (vm.getDataCenterId() != volume.getDataCenterId()) {
+            throw new InvalidParameterValueException("Please specify a VM that is in the same zone as the volume.");
+        }
+
+        // If local storage is disabled then attaching a volume with local disk
+        // offering not allowed
+        DataCenterVO dataCenter = _dcDao.findById(volume.getDataCenterId());
+        if (!dataCenter.isLocalStorageEnabled()) {
+            DiskOfferingVO diskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId());
+            if (diskOffering.getUseLocalStorage()) {
+                throw new InvalidParameterValueException("Zone is not configured to use local storage but volume's disk offering " + diskOffering.getName() + " uses it");
+            }
+        }
+
+        // if target VM has associated VM snapshots
+        List<VMSnapshotVO> vmSnapshots = _vmSnapshotDao.findByVm(vmId);
+        if (vmSnapshots.size() > 0) {
+            throw new InvalidParameterValueException("Unable to attach volume, please specify a VM that does not have VM snapshots");
+        }
+
+        // permission check
+        _accountMgr.checkAccess(caller, null, true, volume, vm);
+
+        if (!(Volume.State.Allocated.equals(volume.getState()) || Volume.State.Ready.equals(volume.getState()) || Volume.State.Uploaded.equals(volume.getState()))) {
+            throw new InvalidParameterValueException("Volume state must be in Allocated, Ready or in Uploaded state");
+        }
+
+        VolumeVO rootVolumeOfVm = null;
+        List<VolumeVO> rootVolumesOfVm = _volsDao.findByInstanceAndType(vmId, Volume.Type.ROOT);
+        if (rootVolumesOfVm.size() != 1) {
+            throw new CloudRuntimeException("The VM " + vm.getHostName() + " has more than one ROOT volume and is in an invalid state.");
+        } else {
+            rootVolumeOfVm = rootVolumesOfVm.get(0);
+        }
+
+        HypervisorType rootDiskHyperType = vm.getHypervisorType();
+
+        HypervisorType dataDiskHyperType = _volsDao.getHypervisorType(volume.getId());
+        if (dataDiskHyperType != HypervisorType.None && rootDiskHyperType != dataDiskHyperType) {
+            throw new InvalidParameterValueException("Can't attach a volume created by: " + dataDiskHyperType + " to a " + rootDiskHyperType + " vm");
+        }
+
+        deviceId = getDeviceId(vmId, deviceId);
+        VolumeInfo volumeOnPrimaryStorage = volume;
+        if (volume.getState().equals(Volume.State.Allocated) || volume.getState() == Volume.State.Uploaded) {
+            try {
+                volumeOnPrimaryStorage = _volumeMgr.createVolumeOnPrimaryStorage(vm, rootVolumeOfVm, volume, rootDiskHyperType);
+            } catch (NoTransitionException e) {
+                s_logger.debug("Failed to create volume on primary storage", e);
+                throw new CloudRuntimeException("Failed to create volume on primary storage", e);
+            }
+        }
+
+        // reload the volume from db
+        volumeOnPrimaryStorage = volFactory.getVolume(volumeOnPrimaryStorage.getId());
+        boolean moveVolumeNeeded = needMoveVolume(rootVolumeOfVm, volumeOnPrimaryStorage);
+
+        if (moveVolumeNeeded) {
+            PrimaryDataStoreInfo primaryStore = (PrimaryDataStoreInfo)volumeOnPrimaryStorage.getDataStore();
+            if (primaryStore.isLocal()) {
+                throw new CloudRuntimeException("Failed to attach local data volume " + volume.getName() + " to VM " + vm.getDisplayName() +
+                                                " as migration of local data volume is not allowed");
+            }
+            StoragePoolVO vmRootVolumePool = _storagePoolDao.findById(rootVolumeOfVm.getPoolId());
+
+            try {
+                volumeOnPrimaryStorage = _volumeMgr.moveVolume(volumeOnPrimaryStorage, vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(),
+                        vmRootVolumePool.getClusterId(), dataDiskHyperType);
+            } catch (ConcurrentOperationException e) {
+                s_logger.debug("move volume failed", e);
+                throw new CloudRuntimeException("move volume failed", e);
+            }
+        }
+
+        AsyncJobExecutionContext asyncExecutionContext = AsyncJobExecutionContext.getCurrentExecutionContext();
+
+        if (asyncExecutionContext != null) {
+            AsyncJob job = asyncExecutionContext.getJob();
+
+            if (s_logger.isInfoEnabled()) {
+                s_logger.info("Trying to attaching volume " + volumeId + " to vm instance:" + vm.getId() + ", update async job-" + job.getId() + " progress status");
+            }
+
+            _asyncMgr.updateAsyncJobAttachment(job.getId(), "volume", volumeId);
+            _asyncMgr.updateAsyncJobStatus(job.getId(), BaseCmd.PROGRESS_INSTANCE_CREATED, Long.toString(volumeId));
+        }
+
+        VolumeVO newVol = _volumeDao.findById(volumeOnPrimaryStorage.getId());
+        newVol = sendAttachVolumeCommand(vm, newVol, deviceId);
+        return newVol;
+    }
+
+    @Override
+    public Volume updateVolume(UpdateVolumeCmd cmd) {
+        Long volumeId = cmd.getId();
+        String path = cmd.getPath();
+
+        if (path == null) {
+            throw new InvalidParameterValueException("Failed to update the volume as path was null");
+        }
+
+        VolumeVO volume = ApiDBUtils.findVolumeById(volumeId);
+        volume.setPath(path);
+        _volumeDao.update(volumeId, volume);
+
+        return volume;
+    }
+
+    @Override
+    @ActionEvent(eventType = EventTypes.EVENT_VOLUME_DETACH, eventDescription = "detaching volume", async = true)
+    public Volume detachVolumeFromVM(DetachVolumeCmd cmmd) {
+        Account caller = CallContext.current().getCallingAccount();
+        if ((cmmd.getId() == null && cmmd.getDeviceId() == null && cmmd.getVirtualMachineId() == null) ||
+            (cmmd.getId() != null && (cmmd.getDeviceId() != null || cmmd.getVirtualMachineId() != null)) ||
+            (cmmd.getId() == null && (cmmd.getDeviceId() == null || cmmd.getVirtualMachineId() == null))) {
+            throw new InvalidParameterValueException("Please provide either a volume id, or a tuple(device id, instance id)");
+        }
+
+        Long volumeId = cmmd.getId();
+        VolumeVO volume = null;
+
+        if (volumeId != null) {
+            volume = _volsDao.findById(volumeId);
+        } else {
+            volume = _volsDao.findByInstanceAndDeviceId(cmmd.getVirtualMachineId(), cmmd.getDeviceId()).get(0);
+        }
+
+        Long vmId = null;
+
+        if (cmmd.getVirtualMachineId() == null) {
+            vmId = volume.getInstanceId();
+        } else {
+            vmId = cmmd.getVirtualMachineId();
+        }
+
+        // Check that the volume ID is valid
+        if (volume == null) {
+            throw new InvalidParameterValueException("Unable to find volume with ID: " + volumeId);
+        }
+
+        // Permissions check
+        _accountMgr.checkAccess(caller, null, true, volume);
+
+        // Check that the volume is a data volume
+        if (volume.getVolumeType() != Volume.Type.DATADISK) {
+            throw new InvalidParameterValueException("Please specify a data volume.");
+        }
+
+        // Check that the volume is currently attached to a VM
+        if (vmId == null) {
+            throw new InvalidParameterValueException("The specified volume is not attached to a VM.");
+        }
+
+        // Check that the VM is in the correct state
+        UserVmVO vm = _userVmDao.findById(vmId);
+        if (vm.getState() != State.Running && vm.getState() != State.Stopped && vm.getState() != State.Destroyed) {
+            throw new InvalidParameterValueException("Please specify a VM that is either running or stopped.");
+        }
+
+        AsyncJobExecutionContext asyncExecutionContext = AsyncJobExecutionContext.getCurrentExecutionContext();
+        if (asyncExecutionContext != null) {
+            AsyncJob job = asyncExecutionContext.getJob();
+
+            if (s_logger.isInfoEnabled()) {
+                s_logger.info("Trying to attaching volume " + volumeId + "to vm instance:" + vm.getId() + ", update async job-" + job.getId() + " progress status");
+            }
+
+            _asyncMgr.updateAsyncJobAttachment(job.getId(), "volume", volumeId);
+            _asyncMgr.updateAsyncJobStatus(job.getId(), BaseCmd.PROGRESS_INSTANCE_CREATED, volumeId.toString());
+        }
+
+        String errorMsg = "Failed to detach volume: " + volume.getName() + " from VM: " + vm.getHostName();
+        boolean sendCommand = (vm.getState() == State.Running);
+        Answer answer = null;
+
+        if (sendCommand) {
+            StoragePoolVO volumePool = _storagePoolDao.findById(volume.getPoolId());
+
+            DataTO volTO = volFactory.getVolume(volume.getId()).getTO();
+            DiskTO disk = new DiskTO(volTO, volume.getDeviceId(), null, volume.getVolumeType());
+
+            DettachCommand cmd = new DettachCommand(disk, vm.getInstanceName());
+
+            cmd.setManaged(volumePool.isManaged());
+
+            cmd.setStorageHost(volumePool.getHostAddress());
+            cmd.setStoragePort(volumePool.getPort());
+
+            cmd.set_iScsiName(volume.get_iScsiName());
+
+            try {
+                answer = _agentMgr.send(vm.getHostId(), cmd);
+            } catch (Exception e) {
+                throw new CloudRuntimeException(errorMsg + " due to: " + e.getMessage());
+            }
+        }
+
+        if (!sendCommand || (answer != null && answer.getResult())) {
+            // Mark the volume as detached
+            _volsDao.detachVolume(volume.getId());
+
+            return _volsDao.findById(volumeId);
+        } else {
+
+            if (answer != null) {
+                String details = answer.getDetails();
+                if (details != null && !details.isEmpty()) {
+                    errorMsg += "; " + details;
+                }
+            }
+
+            throw new CloudRuntimeException(errorMsg);
+        }
+    }
+
+    @DB
+    @Override
+    public Volume migrateVolume(MigrateVolumeCmd cmd) {
+        Long volumeId = cmd.getVolumeId();
+        Long storagePoolId = cmd.getStoragePoolId();
+
+        VolumeVO vol = _volsDao.findById(volumeId);
+        if (vol == null) {
+            throw new InvalidParameterValueException("Failed to find the volume id: " + volumeId);
+        }
+
+        if (vol.getState() != Volume.State.Ready) {
+            throw new InvalidParameterValueException("Volume must be in ready state");
+        }
+
+        boolean liveMigrateVolume = false;
+        Long instanceId = vol.getInstanceId();
+        VMInstanceVO vm = null;
+        if (instanceId != null) {
+            vm = _vmInstanceDao.findById(instanceId);
+        }
+
+        if (vm != null && vm.getState() == State.Running) {
+            // Check if the underlying hypervisor supports storage motion.
+            Long hostId = vm.getHostId();
+            if (hostId != null) {
+                HostVO host = _hostDao.findById(hostId);
+                HypervisorCapabilitiesVO capabilities = null;
+                if (host != null) {
+                    capabilities = _hypervisorCapabilitiesDao.findByHypervisorTypeAndVersion(host.getHypervisorType(), host.getHypervisorVersion());
+                }
+
+                if (capabilities != null) {
+                    liveMigrateVolume = capabilities.isStorageMotionSupported();
+                }
+            }
+        }
+
+        // If the disk is not attached to any VM then it can be moved. Otherwise, it needs to be attached to a vm
+        // running on a hypervisor that supports storage motion so that it be be migrated.
+        if (instanceId != null && !liveMigrateVolume) {
+            throw new InvalidParameterValueException("Volume needs to be detached from VM");
+        }
+
+        if (liveMigrateVolume && !cmd.isLiveMigrate()) {
+            throw new InvalidParameterValueException("The volume " + vol + "is attached to a vm and for migrating it " + "the parameter livemigrate should be specified");
+        }
+
+        StoragePool destPool = (StoragePool)dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary);
+        if (destPool == null) {
+            throw new InvalidParameterValueException("Failed to find the destination storage pool: " + storagePoolId);
+        }
+
+        if (!_volumeMgr.volumeOnSharedStoragePool(vol)) {
+            throw new InvalidParameterValueException("Migration of volume from local storage pool is not supported");
+        }
+
+        Volume newVol = null;
+        if (liveMigrateVolume) {
+            newVol = liveMigrateVolume(vol, destPool);
+        } else {
+            newVol = _volumeMgr.migrateVolume(vol, destPool);
+        }
+        return newVol;
+    }
+
+    @DB
+    protected Volume liveMigrateVolume(Volume volume, StoragePool destPool) {
+        VolumeInfo vol = volFactory.getVolume(volume.getId());
+        AsyncCallFuture<VolumeApiResult> future = volService.migrateVolume(vol, (DataStore)destPool);
+        try {
+            VolumeApiResult result = future.get();
+            if (result.isFailed()) {
+                s_logger.debug("migrate volume failed:" + result.getResult());
+                return null;
+            }
+            return result.getVolume();
+        } catch (InterruptedException e) {
+            s_logger.debug("migrate volume failed", e);
+            return null;
+        } catch (ExecutionException e) {
+            s_logger.debug("migrate volume failed", e);
+            return null;
+        }
+    }
+
+    @Override
+    public Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account account) throws ResourceAllocationException {
+        VolumeInfo volume = volFactory.getVolume(volumeId);
+        if (volume == null) {
+            throw new InvalidParameterValueException("Creating snapshot failed due to volume:" + volumeId + " doesn't exist");
+        }
+
+        if (volume.getState() != Volume.State.Ready) {
+            throw new InvalidParameterValueException("VolumeId: " + volumeId + " is not in " + Volume.State.Ready + " state but " + volume.getState() + ". Cannot take snapshot.");
+        }
+
+        CreateSnapshotPayload payload = new CreateSnapshotPayload();
+        payload.setSnapshotId(snapshotId);
+        payload.setSnapshotPolicyId(policyId);
+        payload.setAccount(account);
+        volume.addPayload(payload);
+        return volService.takeSnapshot(volume);
+    }
+
+    @Override
+    public Snapshot allocSnapshot(Long volumeId, Long policyId) throws ResourceAllocationException {
+        Account caller = CallContext.current().getCallingAccount();
+
+        VolumeInfo volume = volFactory.getVolume(volumeId);
+        if (volume == null) {
+            throw new InvalidParameterValueException("Creating snapshot failed due to volume:" + volumeId + " doesn't exist");
+        }
+        DataCenter zone = _dcDao.findById(volume.getDataCenterId());
+        if (zone == null) {
+            throw new InvalidParameterValueException("Can't find zone by id " + volume.getDataCenterId());
+        }
+
+        if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getType())) {
+            throw new PermissionDeniedException("Cannot perform this operation, Zone is currently disabled: " + zone.getName());
+        }
+
+        if (volume.getState() != Volume.State.Ready) {
+            throw new InvalidParameterValueException("VolumeId: " + volumeId + " is not in " + Volume.State.Ready + " state but " + volume.getState() + ". Cannot take snapshot.");
+        }
+
+        if (volume.getTemplateId() != null) {
+            VMTemplateVO template = _templateDao.findById(volume.getTemplateId());
+            if (template != null && template.getTemplateType() == Storage.TemplateType.SYSTEM) {
+                throw new InvalidParameterValueException("VolumeId: " + volumeId + " is for System VM , Creating snapshot against System VM volumes is not supported");
+            }
+        }
+
+        StoragePool storagePool = (StoragePool)volume.getDataStore();
+        if (storagePool == null) {
+            throw new InvalidParameterValueException("VolumeId: " + volumeId + " please attach this volume to a VM before create snapshot for it");
+        }
+
+        return snapshotMgr.allocSnapshot(volumeId, policyId);
+    }
+
+    @Override
+    @ActionEvent(eventType = EventTypes.EVENT_VOLUME_EXTRACT, eventDescription = "extracting volume", async = true)
+    public String extractVolume(ExtractVolumeCmd cmd) {
+        Long volumeId = cmd.getId();
+        Long zoneId = cmd.getZoneId();
+        String mode = cmd.getMode();
+        Account account = CallContext.current().getCallingAccount();
+
+        if (!_accountMgr.isRootAdmin(account.getType()) && ApiDBUtils.isExtractionDisabled()) {
+            throw new PermissionDeniedException("Extraction has been disabled by admin");
+        }
+
+        VolumeVO volume = _volumeDao.findById(volumeId);
+        if (volume == null) {
+            InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find volume with specified volumeId");
+            ex.addProxyObject(volumeId.toString(), "volumeId");
+            throw ex;
+        }
+
+        // perform permission check
+        _accountMgr.checkAccess(account, null, true, volume);
+
+        if (_dcDao.findById(zoneId) == null) {
+            throw new InvalidParameterValueException("Please specify a valid zone.");
+        }
+        if (volume.getPoolId() == null) {
+            throw new InvalidParameterValueException("The volume doesnt belong to a storage pool so cant extract it");
+        }
+        // Extract activity only for detached volumes or for volumes whose
+        // instance is stopped
+        if (volume.getInstanceId() != null && ApiDBUtils.findVMInstanceById(volume.getInstanceId()).getState() != State.Stopped) {
+            s_logger.debug("Invalid state of the volume with ID: " + volumeId + ". It should be either detached or the VM should be in stopped state.");
+            PermissionDeniedException ex = new PermissionDeniedException(
+                    "Invalid state of the volume with specified ID. It should be either detached or the VM should be in stopped state.");
+            ex.addProxyObject(volume.getUuid(), "volumeId");
+            throw ex;
+        }
+
+        if (volume.getVolumeType() != Volume.Type.DATADISK) {
+            // Datadisk dont have any template dependence.
+
+            VMTemplateVO template = ApiDBUtils.findTemplateById(volume.getTemplateId());
+            if (template != null) { // For ISO based volumes template = null and
+                // we allow extraction of all ISO based
+                // volumes
+                boolean isExtractable = template.isExtractable() && template.getTemplateType() != Storage.TemplateType.SYSTEM;
+                if (!isExtractable && account != null && account.getType() != Account.ACCOUNT_TYPE_ADMIN) {
+                    // Global admins are always allowed to extract
+                    PermissionDeniedException ex = new PermissionDeniedException("The volume with specified volumeId is not allowed to be extracted");
+                    ex.addProxyObject(volume.getUuid(), "volumeId");
+                    throw ex;
+                }
+            }
+        }
+
+        Upload.Mode extractMode;
+        if (mode == null || (!mode.equals(Upload.Mode.FTP_UPLOAD.toString()) && !mode.equals(Upload.Mode.HTTP_DOWNLOAD.toString()))) {
+            throw new InvalidParameterValueException("Please specify a valid extract Mode ");
+        } else {
+            extractMode = mode.equals(Upload.Mode.FTP_UPLOAD.toString()) ? Upload.Mode.FTP_UPLOAD : Upload.Mode.HTTP_DOWNLOAD;
+        }
+
+        // Check if the url already exists
+        VolumeDataStoreVO volumeStoreRef = _volumeStoreDao.findByVolume(volumeId);
+        if (volumeStoreRef != null && volumeStoreRef.getExtractUrl() != null) {
+            return volumeStoreRef.getExtractUrl();
+        }
+
+        // Clean up code to remove all those previous uploadVO and uploadMonitor code. Previous code is trying to fake an async operation purely in
+        // db table with uploadVO and async_job entry, but internal implementation is actually synchronous.
+        StoragePool srcPool = (StoragePool)dataStoreMgr.getPrimaryDataStore(volume.getPoolId());
+        ImageStoreEntity secStore = (ImageStoreEntity)dataStoreMgr.getImageStore(zoneId);
+        String secondaryStorageURL = secStore.getUri();
+
+        String value = _configDao.getValue(Config.CopyVolumeWait.toString());
+        int copyvolumewait = NumbersUtil.parseInt(value, Integer.parseInt(Config.CopyVolumeWait.getDefaultValue()));
+        // Copy volume from primary to secondary storage
+        VolumeInfo srcVol = volFactory.getVolume(volume.getId());
+        AsyncCallFuture<VolumeApiResult> cvAnswer = volService.copyVolume(srcVol, secStore);
+        // Check if you got a valid answer.
+        VolumeApiResult cvResult = null;
+        try {
+            cvResult = cvAnswer.get();
+        } catch (InterruptedException e1) {
+            s_logger.debug("failed copy volume", e1);
+            throw new CloudRuntimeException("Failed to copy volume", e1);
+        } catch (ExecutionException e1) {
+            s_logger.debug("failed copy volume", e1);
+            throw new CloudRuntimeException("Failed to copy volume", e1);
+        }
+        if (cvResult == null || cvResult.isFailed()) {
+            String errorString = "Failed to copy the volume from the source primary storage pool to secondary storage.";
+            throw new CloudRuntimeException(errorString);
+        }
+
+        VolumeInfo vol = cvResult.getVolume();
+
+        String extractUrl = secStore.createEntityExtractUrl(vol.getPath(), vol.getFormat(), vol);
+        volumeStoreRef = _volumeStoreDao.findByVolume(volumeId);
+        volumeStoreRef.setExtractUrl(extractUrl);
+        _volumeStoreDao.update(volumeStoreRef.getId(), volumeStoreRef);
+
+        return extractUrl;
+    }
+
+    private String getFormatForPool(StoragePool pool) {
+        ClusterVO cluster = ApiDBUtils.findClusterById(pool.getClusterId());
+
+        if (cluster.getHypervisorType() == HypervisorType.XenServer) {
+            return "vhd";
+        } else if (cluster.getHypervisorType() == HypervisorType.KVM) {
+            return "qcow2";
+        } else if (cluster.getHypervisorType() == HypervisorType.VMware) {
+            return "ova";
+        } else if (cluster.getHypervisorType() == HypervisorType.Ovm) {
+            return "raw";
+        } else {
+            return null;
+        }
+    }
+
+    private boolean needMoveVolume(VolumeVO rootVolumeOfVm, VolumeInfo volume) {
+        if (rootVolumeOfVm.getPoolId() == null || volume.getPoolId() == null) {
+            return false;
+        }
+
+        DataStore storeForRootVol = dataStoreMgr.getPrimaryDataStore(rootVolumeOfVm.getPoolId());
+        DataStore storeForDataVol = dataStoreMgr.getPrimaryDataStore(volume.getPoolId());
+
+        Scope storeForRootStoreScope = storeForRootVol.getScope();
+        if (storeForRootStoreScope == null) {
+            throw new CloudRuntimeException("Can't get scope of data store: " + storeForRootVol.getId());
+        }
+
+        Scope storeForDataStoreScope = storeForDataVol.getScope();
+        if (storeForDataStoreScope == null) {
+            throw new CloudRuntimeException("Can't get scope of data store: " + storeForDataVol.getId());
+        }
+
+        if (storeForDataStoreScope.getScopeType() == ScopeType.ZONE) {
+            return false;
+        }
+
+        if (storeForRootStoreScope.getScopeType() != storeForDataStoreScope.getScopeType()) {
+            if (storeForDataStoreScope.getScopeType() == ScopeType.CLUSTER && storeForRootStoreScope.getScopeType() == ScopeType.HOST) {
+                HostScope hs = (HostScope)storeForRootStoreScope;
+                if (storeForDataStoreScope.getScopeId().equals(hs.getClusterId())) {
+                    return false;
+                }
+            }
+            if (storeForRootStoreScope.getScopeType() == ScopeType.CLUSTER && storeForDataStoreScope.getScopeType() == ScopeType.HOST) {
+                HostScope hs = (HostScope)storeForDataStoreScope;
+                if (storeForRootStoreScope.getScopeId().equals(hs.getClusterId())) {
+                    return false;
+                }
+            }
+            throw new CloudRuntimeException("Can't move volume between scope: " + storeForDataStoreScope.getScopeType() + " and " + storeForRootStoreScope.getScopeType());
+        }
+
+        return !storeForRootStoreScope.isSameScope(storeForDataStoreScope);
+    }
+
+    private VolumeVO sendAttachVolumeCommand(UserVmVO vm, VolumeVO volumeToAttach, Long deviceId) {
+        String errorMsg = "Failed to attach volume: " + volumeToAttach.getName() + " to VM: " + vm.getHostName();
+        boolean sendCommand = (vm.getState() == State.Running);
+        AttachAnswer answer = null;
+        Long hostId = vm.getHostId();
+        if (hostId == null) {
+            hostId = vm.getLastHostId();
+            HostVO host = _hostDao.findById(hostId);
+            if (host != null && host.getHypervisorType() == HypervisorType.VMware) {
+                sendCommand = true;
+            }
+        }
+
+        StoragePoolVO volumeToAttachStoragePool = null;
+
+        if (sendCommand) {
+            volumeToAttachStoragePool = _storagePoolDao.findById(volumeToAttach.getPoolId());
+            long storagePoolId = volumeToAttachStoragePool.getId();
+
+            DataTO volTO = volFactory.getVolume(volumeToAttach.getId()).getTO();
+            DiskTO disk = new DiskTO(volTO, deviceId, null, volumeToAttach.getVolumeType());
+
+            AttachCommand cmd = new AttachCommand(disk, vm.getInstanceName());
+
+            cmd.setManaged(volumeToAttachStoragePool.isManaged());
+
+            cmd.setStorageHost(volumeToAttachStoragePool.getHostAddress());
+            cmd.setStoragePort(volumeToAttachStoragePool.getPort());
+
+            cmd.set_iScsiName(volumeToAttach.get_iScsiName());
+
+            VolumeInfo volumeInfo = volFactory.getVolume(volumeToAttach.getId());
+            DataStore dataStore = dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary);
+            ChapInfo chapInfo = volService.getChapInfo(volumeInfo, dataStore);
+
+            if (chapInfo != null) {
+                cmd.setChapInitiatorUsername(chapInfo.getInitiatorUsername());
+                cmd.setChapInitiatorPassword(chapInfo.getInitiatorSecret());
+                cmd.setChapTargetUsername(chapInfo.getTargetUsername());
+                cmd.setChapTargetPassword(chapInfo.getTargetSecret());
+            }
+
+            try {
+                answer = (AttachAnswer)_agentMgr.send(hostId, cmd);
+            } catch (Exception e) {
+                throw new CloudRuntimeException(errorMsg + " due to: " + e.getMessage());
+            }
+        }
+
+        if (!sendCommand || (answer != null && answer.getResult())) {
+            // Mark the volume as attached
+            if (sendCommand) {
+                DiskTO disk = answer.getDisk();
+                _volsDao.attachVolume(volumeToAttach.getId(), vm.getId(), disk.getDiskSeq());
+
+                volumeToAttach = _volsDao.findById(volumeToAttach.getId());
+
+                if (volumeToAttachStoragePool.isManaged() && volumeToAttach.getPath() == null) {
+                    volumeToAttach.setPath(answer.getDisk().getVdiUuid());
+
+                    _volsDao.update(volumeToAttach.getId(), volumeToAttach);
+                }
+            } else {
+                _volsDao.attachVolume(volumeToAttach.getId(), vm.getId(), deviceId);
+            }
+
+            // insert record for disk I/O statistics
+            VmDiskStatisticsVO diskstats = _vmDiskStatsDao.findBy(vm.getAccountId(), vm.getDataCenterId(), vm.getId(), volumeToAttach.getId());
+            if (diskstats == null) {
+                diskstats = new VmDiskStatisticsVO(vm.getAccountId(), vm.getDataCenterId(), vm.getId(), volumeToAttach.getId());
+                _vmDiskStatsDao.persist(diskstats);
+            }
+
+            return _volsDao.findById(volumeToAttach.getId());
+        } else {
+            if (answer != null) {
+                String details = answer.getDetails();
+                if (details != null && !details.isEmpty()) {
+                    errorMsg += "; " + details;
+                }
+            }
+            throw new CloudRuntimeException(errorMsg);
+        }
+    }
+
+    private int getMaxDataVolumesSupported(UserVmVO vm) {
+        Long hostId = vm.getHostId();
+        if (hostId == null) {
+            hostId = vm.getLastHostId();
+        }
+        HostVO host = _hostDao.findById(hostId);
+        Integer maxDataVolumesSupported = null;
+        if (host != null) {
+            _hostDao.loadDetails(host);
+            maxDataVolumesSupported = _hypervisorCapabilitiesDao.getMaxDataVolumesLimit(host.getHypervisorType(), host.getDetail("product_version"));
+        }
+        if (maxDataVolumesSupported == null) {
+            maxDataVolumesSupported = 6; // 6 data disks by default if nothing
+            // is specified in
+            // 'hypervisor_capabilities' table
+        }
+
+        return maxDataVolumesSupported.intValue();
+    }
+
+
+    private Long getDeviceId(long vmId, Long deviceId) {
+        // allocate deviceId
+        List<VolumeVO> vols = _volsDao.findByInstance(vmId);
+        if (deviceId != null) {
+            if (deviceId.longValue() > 15 || deviceId.longValue() == 0 || deviceId.longValue() == 3) {
+                throw new RuntimeException("deviceId should be 1,2,4-15");
+            }
+            for (VolumeVO vol : vols) {
+                if (vol.getDeviceId().equals(deviceId)) {
+                    throw new RuntimeException("deviceId " + deviceId + " is used by vm" + vmId);
+                }
+            }
+        } else {
+            // allocate deviceId here
+            List<String> devIds = new ArrayList<String>();
+            for (int i = 1; i < 15; i++) {
+                devIds.add(String.valueOf(i));
+            }
+            devIds.remove("3");
+            for (VolumeVO vol : vols) {
+                devIds.remove(vol.getDeviceId().toString().trim());
+            }
+            deviceId = Long.parseLong(devIds.iterator().next());
+        }
+
+        return deviceId;
+    }
+
+    @Override
+    public boolean configure(String name, Map<String, Object> params) {
+        String _customDiskOfferingMinSizeStr = _configDao.getValue(Config.CustomDiskOfferingMinSize.toString());
+        _customDiskOfferingMinSize = NumbersUtil.parseInt(_customDiskOfferingMinSizeStr, Integer.parseInt(Config.CustomDiskOfferingMinSize.getDefaultValue()));
+
+        return true;
+    }
+
+}


Mime
View raw message