You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by mc...@apache.org on 2012/12/26 02:21:31 UTC

git commit: Create DB view for Volume to speed up ListVolumesCmd.

Updated Branches:
  refs/heads/api_refactoring 78d70d349 -> 21c1623a0


Create DB view for Volume to speed up ListVolumesCmd.

Signed-off-by: Min Chen <mi...@citrix.com>


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

Branch: refs/heads/api_refactoring
Commit: 21c1623a0321adb1082ec4677252cdd14f5f96ac
Parents: 78d70d3
Author: Min Chen <mi...@citrix.com>
Authored: Tue Dec 25 17:08:50 2012 -0800
Committer: Min Chen <mi...@citrix.com>
Committed: Tue Dec 25 17:08:50 2012 -0800

----------------------------------------------------------------------
 api/src/com/cloud/storage/StorageService.java      |    1 -
 .../api/command/user/volume/ListVolumesCmd.java    |   12 +-
 .../cloudstack/api/response/VolumeResponse.java    |   16 +-
 .../org/apache/cloudstack/query/QueryService.java  |    7 +-
 server/src/com/cloud/api/ApiDBUtils.java           |   20 +
 server/src/com/cloud/api/ApiResponseHelper.java    |  137 +--
 server/src/com/cloud/api/ApiServer.java            |    4 +-
 .../src/com/cloud/api/query/QueryManagerImpl.java  |  158 +++-
 .../com/cloud/api/query/ViewResponseHelper.java    |   19 +
 .../src/com/cloud/api/query/dao/VolumeJoinDao.java |   37 +
 .../com/cloud/api/query/dao/VolumeJoinDaoImpl.java |  230 ++++
 .../src/com/cloud/api/query/vo/VolumeJoinVO.java   | 1032 +++++++++++++++
 .../configuration/DefaultComponentLibrary.java     |    3 +
 .../src/com/cloud/storage/StorageManagerImpl.java  |  117 --
 server/test/com/cloud/api/ListPerfTest.java        |   14 +
 setup/db/create-schema.sql                         |   82 ++
 setup/db/db/schema-40to410.sql                     |   85 ++
 17 files changed, 1703 insertions(+), 271 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/21c1623a/api/src/com/cloud/storage/StorageService.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/storage/StorageService.java b/api/src/com/cloud/storage/StorageService.java
index 80f64d8..1967cba 100644
--- a/api/src/com/cloud/storage/StorageService.java
+++ b/api/src/com/cloud/storage/StorageService.java
@@ -115,7 +115,6 @@ public interface StorageService{
 
     Volume migrateVolume(Long volumeId, Long storagePoolId) throws ConcurrentOperationException;
 
-    Pair<List<? extends Volume>, Integer> searchForVolumes(ListVolumesCmd cmd);
 
     /**
      * Uploads the volume to secondary storage

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/21c1623a/api/src/org/apache/cloudstack/api/command/user/volume/ListVolumesCmd.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/command/user/volume/ListVolumesCmd.java b/api/src/org/apache/cloudstack/api/command/user/volume/ListVolumesCmd.java
index c47e73d..bbee677 100644
--- a/api/src/org/apache/cloudstack/api/command/user/volume/ListVolumesCmd.java
+++ b/api/src/org/apache/cloudstack/api/command/user/volume/ListVolumesCmd.java
@@ -116,17 +116,7 @@ public class ListVolumesCmd extends BaseListTaggedResourcesCmd {
 
     @Override
     public void execute(){
-        Pair<List<? extends Volume>, Integer> volumes = _storageService.searchForVolumes(this);
-
-        ListResponse<VolumeResponse> response = new ListResponse<VolumeResponse>();
-        List<VolumeResponse> volResponses = new ArrayList<VolumeResponse>();
-        for (Volume volume : volumes.first()) {
-            VolumeResponse volResponse = _responseGenerator.createVolumeResponse(volume);
-            volResponse.setObjectName("volume");
-            volResponses.add(volResponse);
-        }
-
-        response.setResponses(volResponses, volumes.second());
+        ListResponse<VolumeResponse> response = _queryService.searchForVolumes(this);
         response.setResponseName(getCommandName());
         this.setResponseObject(response);
     }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/21c1623a/api/src/org/apache/cloudstack/api/response/VolumeResponse.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/response/VolumeResponse.java b/api/src/org/apache/cloudstack/api/response/VolumeResponse.java
index 1b1ff14..bc21768 100644
--- a/api/src/org/apache/cloudstack/api/response/VolumeResponse.java
+++ b/api/src/org/apache/cloudstack/api/response/VolumeResponse.java
@@ -17,7 +17,9 @@
 package org.apache.cloudstack.api.response;
 
 import java.util.Date;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 import com.cloud.storage.Volume;
 import org.apache.cloudstack.api.ApiConstants;
@@ -28,7 +30,7 @@ import org.apache.cloudstack.api.Entity;
 
 @Entity(value=Volume.class)
 @SuppressWarnings("unused")
-public class VolumeResponse extends BaseResponse implements ControlledEntityResponse{
+public class VolumeResponse extends BaseResponse implements ControlledViewEntityResponse{
     @SerializedName(ApiConstants.ID)
     @Param(description = "ID of the disk volume")
     private String id;
@@ -156,9 +158,11 @@ public class VolumeResponse extends BaseResponse implements ControlledEntityResp
     private String status;
 
     @SerializedName(ApiConstants.TAGS)  @Param(description="the list of resource tags associated with volume", responseObject = ResourceTagResponse.class)
-    private List<ResourceTagResponse> tags;
-
+    private Set<ResourceTagResponse> tags;
 
+    public VolumeResponse(){
+        tags = new HashSet<ResourceTagResponse>();
+    }
 
     @Override
     public String getObjectId() {
@@ -304,7 +308,11 @@ public class VolumeResponse extends BaseResponse implements ControlledEntityResp
         this.projectName = projectName;
     }
 
-    public void setTags(List<ResourceTagResponse> tags) {
+    public void setTags(Set<ResourceTagResponse> tags) {
         this.tags = tags;
     }
+
+    public void addTag(ResourceTagResponse tag){
+        this.tags.add(tag);
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/21c1623a/api/src/org/apache/cloudstack/query/QueryService.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/query/QueryService.java b/api/src/org/apache/cloudstack/query/QueryService.java
index 1847ede..ef3c52f 100644
--- a/api/src/org/apache/cloudstack/query/QueryService.java
+++ b/api/src/org/apache/cloudstack/query/QueryService.java
@@ -27,6 +27,7 @@ import org.apache.cloudstack.api.command.user.securitygroup.ListSecurityGroupsCm
 import org.apache.cloudstack.api.command.user.tag.ListTagsCmd;
 import org.apache.cloudstack.api.command.user.vm.ListVMsCmd;
 import org.apache.cloudstack.api.command.user.vmgroup.ListVMGroupsCmd;
+import org.apache.cloudstack.api.command.user.volume.ListVolumesCmd;
 import org.apache.cloudstack.api.response.DomainRouterResponse;
 import org.apache.cloudstack.api.response.EventResponse;
 import org.apache.cloudstack.api.response.HostResponse;
@@ -39,11 +40,11 @@ import org.apache.cloudstack.api.response.ResourceTagResponse;
 import org.apache.cloudstack.api.response.SecurityGroupResponse;
 import org.apache.cloudstack.api.response.UserResponse;
 import org.apache.cloudstack.api.response.UserVmResponse;
+import org.apache.cloudstack.api.response.VolumeResponse;
+
 
 
-import com.cloud.async.AsyncJob;
 import com.cloud.exception.PermissionDeniedException;
-import com.cloud.storage.Snapshot;
 
 /**
  * Service used for list api query.
@@ -73,4 +74,6 @@ public interface QueryService {
     public ListResponse<ProjectAccountResponse> listProjectAccounts(ListProjectAccountsCmd cmd);
 
     public ListResponse<HostResponse> searchForServers(ListHostsCmd cmd);
+
+    public ListResponse<VolumeResponse> searchForVolumes(ListVolumesCmd cmd);
 }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/21c1623a/server/src/com/cloud/api/ApiDBUtils.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiDBUtils.java b/server/src/com/cloud/api/ApiDBUtils.java
index 5fa9ef7..7465873 100755
--- a/server/src/com/cloud/api/ApiDBUtils.java
+++ b/server/src/com/cloud/api/ApiDBUtils.java
@@ -35,6 +35,7 @@ import org.apache.cloudstack.api.response.ResourceTagResponse;
 import org.apache.cloudstack.api.response.SecurityGroupResponse;
 import org.apache.cloudstack.api.response.UserResponse;
 import org.apache.cloudstack.api.response.UserVmResponse;
+import org.apache.cloudstack.api.response.VolumeResponse;
 
 import com.cloud.api.query.dao.DomainRouterJoinDao;
 import com.cloud.api.query.dao.HostJoinDao;
@@ -45,6 +46,7 @@ import com.cloud.api.query.dao.ProjectJoinDao;
 import com.cloud.api.query.dao.ResourceTagJoinDao;
 import com.cloud.api.query.dao.SecurityGroupJoinDao;
 import com.cloud.api.query.dao.UserVmJoinDao;
+import com.cloud.api.query.dao.VolumeJoinDao;
 import com.cloud.api.query.vo.DomainRouterJoinVO;
 import com.cloud.api.query.vo.EventJoinVO;
 import com.cloud.api.query.vo.HostJoinVO;
@@ -56,6 +58,7 @@ import com.cloud.api.query.vo.ResourceTagJoinVO;
 import com.cloud.api.query.vo.SecurityGroupJoinVO;
 import com.cloud.api.query.vo.UserAccountJoinVO;
 import com.cloud.api.query.vo.UserVmJoinVO;
+import com.cloud.api.query.vo.VolumeJoinVO;
 import com.cloud.async.AsyncJob;
 import com.cloud.async.AsyncJobManager;
 import com.cloud.async.AsyncJobVO;
@@ -180,6 +183,7 @@ import com.cloud.storage.VMTemplateHostVO;
 import com.cloud.storage.VMTemplateS3VO;
 import com.cloud.storage.VMTemplateSwiftVO;
 import com.cloud.storage.VMTemplateVO;
+import com.cloud.storage.Volume;
 import com.cloud.storage.Volume.Type;
 import com.cloud.storage.VolumeHostVO;
 import com.cloud.storage.VolumeVO;
@@ -313,6 +317,7 @@ public class ApiDBUtils {
     private static ProjectAccountJoinDao _projectAccountJoinDao;
     private static ProjectInvitationJoinDao _projectInvitationJoinDao;
     private static HostJoinDao _hostJoinDao;
+    private static VolumeJoinDao _volJoinDao;
 
     private static PhysicalNetworkTrafficTypeDao _physicalNetworkTrafficTypeDao;
     private static PhysicalNetworkServiceProviderDao _physicalNetworkServiceProviderDao;
@@ -400,6 +405,7 @@ public class ApiDBUtils {
         _projectAccountJoinDao = locator.getDao(ProjectAccountJoinDao.class);
         _projectInvitationJoinDao = locator.getDao(ProjectInvitationJoinDao.class);
         _hostJoinDao = locator.getDao(HostJoinDao.class);
+        _volJoinDao = locator.getDao(VolumeJoinDao.class);
 
         _physicalNetworkTrafficTypeDao = locator.getDao(PhysicalNetworkTrafficTypeDao.class);
         _physicalNetworkServiceProviderDao = locator.getDao(PhysicalNetworkServiceProviderDao.class);
@@ -1324,4 +1330,18 @@ public class ApiDBUtils {
     public static List<HostJoinVO> newHostView(Host vr){
         return _hostJoinDao.newHostView(vr);
     }
+
+    public static VolumeResponse newVolumeResponse(VolumeJoinVO vr) {
+        return _volJoinDao.newVolumeResponse(vr);
+    }
+
+
+    public static VolumeResponse fillVolumeDetails(VolumeResponse vrData, VolumeJoinVO vr){
+        return _volJoinDao.setVolumeResponse(vrData, vr);
+   }
+
+   public static List<VolumeJoinVO> newVolumeView(Volume vr){
+       return _volJoinDao.newVolumeView(vr);
+   }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/21c1623a/server/src/com/cloud/api/ApiResponseHelper.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java
index b2bfee8..22e89dd 100755
--- a/server/src/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/com/cloud/api/ApiResponseHelper.java
@@ -57,6 +57,7 @@ import com.cloud.api.query.vo.ResourceTagJoinVO;
 import com.cloud.api.query.vo.SecurityGroupJoinVO;
 import com.cloud.api.query.vo.UserAccountJoinVO;
 import com.cloud.api.query.vo.UserVmJoinVO;
+import com.cloud.api.query.vo.VolumeJoinVO;
 import com.cloud.api.response.ApiResponseSerializer;
 import org.apache.cloudstack.api.response.AsyncJobResponse;
 import org.apache.cloudstack.api.response.AutoScalePolicyResponse;
@@ -1019,138 +1020,10 @@ public class ApiResponseHelper implements ResponseGenerator {
 
     @Override
     public VolumeResponse createVolumeResponse(Volume volume) {
-        VolumeResponse volResponse = new VolumeResponse();
-        volResponse.setId(volume.getUuid());
-
-        if (volume.getName() != null) {
-            volResponse.setName(volume.getName());
-        } else {
-            volResponse.setName("");
-        }
-
-        DataCenter zone = ApiDBUtils.findZoneById(volume.getDataCenterId());
-        if (zone != null) {
-            volResponse.setZoneId(zone.getUuid());
-            volResponse.setZoneName(zone.getName());
-        }
-
-        volResponse.setVolumeType(volume.getVolumeType().toString());
-        volResponse.setDeviceId(volume.getDeviceId());
-
-        Long instanceId = volume.getInstanceId();
-        if (instanceId != null && volume.getState() != Volume.State.Destroy) {
-            VMInstanceVO vm = ApiDBUtils.findVMInstanceById(instanceId);
-            if (vm != null) {
-                volResponse.setVirtualMachineId(vm.getUuid());
-                volResponse.setVirtualMachineName(vm.getHostName());
-                volResponse.setVirtualMachineState(vm.getState().toString());
-                UserVm userVm = ApiDBUtils.findUserVmById(vm.getId());
-                if (userVm != null) {
-                    if (userVm.getDisplayName() != null) {
-                        volResponse.setVirtualMachineDisplayName(userVm.getDisplayName());
-                    }
-                } else {
-                        s_logger.error("User Vm with Id: " + instanceId + " does not exist for volume " + volume.getId());
-                }
-            } else {
-                    s_logger.error("Vm with Id: " + instanceId + " does not exist for volume " + volume.getId());
-            }
-        }
-
-        // Show the virtual size of the volume
-        volResponse.setSize(volume.getSize());
-
-        volResponse.setCreated(volume.getCreated());
-        volResponse.setState(volume.getState().toString());
-        if(volume.getState() == Volume.State.UploadOp){
-            com.cloud.storage.VolumeHostVO volumeHostRef = ApiDBUtils.findVolumeHostRef(volume.getId(), volume.getDataCenterId());
-            volResponse.setSize(volumeHostRef.getSize());
-            volResponse.setCreated(volumeHostRef.getCreated());
-            Account caller = UserContext.current().getCaller();
-            if (caller.getType() == Account.ACCOUNT_TYPE_ADMIN || caller.getType() == Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN)
-                volResponse.setHypervisor(ApiDBUtils.getHypervisorTypeFromFormat(volumeHostRef.getFormat()).toString());
-            if (volumeHostRef.getDownloadState() != Status.DOWNLOADED) {
-                String volumeStatus = "Processing";
-                if (volumeHostRef.getDownloadState() == VMTemplateHostVO.Status.DOWNLOAD_IN_PROGRESS) {
-                    if (volumeHostRef.getDownloadPercent() == 100) {
-                        volumeStatus = "Checking Volume";
-                    } else {
-                        volumeStatus = volumeHostRef.getDownloadPercent() + "% Uploaded";
-                    }
-                    volResponse.setState("Uploading");
-                } else {
-                    volumeStatus = volumeHostRef.getErrorString();
-                    if(volumeHostRef.getDownloadState() == VMTemplateHostVO.Status.NOT_DOWNLOADED){
-                        volResponse.setState("UploadNotStarted");
-                    }else {
-                        volResponse.setState("UploadError");
-                    }
-                }
-                volResponse.setStatus(volumeStatus);
-            } else if (volumeHostRef.getDownloadState() == VMTemplateHostVO.Status.DOWNLOADED) {
-                volResponse.setStatus("Upload Complete");
-                volResponse.setState("Uploaded");
-            } else {
-                volResponse.setStatus("Successfully Installed");
-            }
-        }
-
-        populateOwner(volResponse, volume);
-
-        DiskOfferingVO diskOffering = ApiDBUtils.findDiskOfferingById(volume.getDiskOfferingId());
-        if (diskOffering != null) {
-            if (volume.getVolumeType().equals(Volume.Type.ROOT)) {
-                volResponse.setServiceOfferingId(diskOffering.getUuid());
-            } else {
-                volResponse.setDiskOfferingId(diskOffering.getUuid());
-            }
-
-            if (volume.getVolumeType().equals(Volume.Type.ROOT)) {
-                volResponse.setServiceOfferingName(diskOffering.getName());
-                volResponse.setServiceOfferingDisplayText(diskOffering.getDisplayText());
-            } else {
-                volResponse.setDiskOfferingName(diskOffering.getName());
-                volResponse.setDiskOfferingDisplayText(diskOffering.getDisplayText());
-            }
-            volResponse.setStorageType(diskOffering.getUseLocalStorage() ? ServiceOffering.StorageType.local.toString()
-                    : ServiceOffering.StorageType.shared.toString());
-        }
-        Long poolId = volume.getPoolId();
-        String poolName = (poolId == null) ? "none" : ApiDBUtils.findStoragePoolById(poolId).getName();
-        volResponse.setStoragePoolName(poolName);
-        // volResponse.setSourceId(volume.getSourceId());
-        // if (volume.getSourceType() != null) {
-        // volResponse.setSourceType(volume.getSourceType().toString());
-        // }
-
-        // return hypervisor for ROOT and Resource domain only
-        Account caller = UserContext.current().getCaller();
-        if ((caller.getType() == Account.ACCOUNT_TYPE_ADMIN || caller.getType() == Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN) && volume.getState() != Volume.State.UploadOp) {
-            volResponse.setHypervisor(ApiDBUtils.getVolumeHyperType(volume.getId()).toString());
-        }
-
-        volResponse.setAttached(volume.getAttached());
-        volResponse.setDestroyed(volume.getState() == Volume.State.Destroy);
-            boolean isExtractable = true;
-            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
-                    isExtractable = template.isExtractable() && template.getTemplateType() != Storage.TemplateType.SYSTEM;
-                }
-            }
-
-            //set tag information
-            List<? extends ResourceTag> tags = ApiDBUtils.listByResourceTypeAndId(TaggedResourceType.Volume, volume.getId());
-            List<ResourceTagResponse> tagResponses = new ArrayList<ResourceTagResponse>();
-            for (ResourceTag tag : tags) {
-                ResourceTagResponse tagResponse = createResourceTagResponse(tag, true);
-                tagResponses.add(tagResponse);
-            }
-            volResponse.setTags(tagResponses);
-
-        volResponse.setExtractable(isExtractable);
-        volResponse.setObjectName("volume");
-        return volResponse;
+        List<VolumeJoinVO> viewVrs = ApiDBUtils.newVolumeView(volume);
+        List<VolumeResponse> listVrs = ViewResponseHelper.createVolumeResponse(viewVrs.toArray(new VolumeJoinVO[viewVrs.size()]));
+        assert listVrs != null && listVrs.size() == 1 : "There should be one volume returned";
+        return listVrs.get(0);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/21c1623a/server/src/com/cloud/api/ApiServer.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/ApiServer.java b/server/src/com/cloud/api/ApiServer.java
index 1551e44..3f027ed 100755
--- a/server/src/com/cloud/api/ApiServer.java
+++ b/server/src/com/cloud/api/ApiServer.java
@@ -55,6 +55,7 @@ import org.apache.cloudstack.api.command.user.account.ListProjectAccountsCmd;
 import org.apache.cloudstack.api.command.user.event.ListEventsCmd;
 import org.apache.cloudstack.api.command.user.vm.ListVMsCmd;
 import org.apache.cloudstack.api.command.user.vmgroup.ListVMGroupsCmd;
+import org.apache.cloudstack.api.command.user.volume.ListVolumesCmd;
 import org.apache.commons.codec.binary.Base64;
 import org.apache.http.client.utils.URLEncodedUtils;
 import org.apache.http.ConnectionClosedException;
@@ -464,7 +465,8 @@ public class ApiServer implements HttpRequestHandler {
                     && !(cmdObj instanceof ListProjectsCmd)
                     && !(cmdObj instanceof ListProjectAccountsCmd)
                     && !(cmdObj instanceof ListProjectInvitationsCmd)
-                    && !(cmdObj instanceof ListHostsCmd)) {
+                    && !(cmdObj instanceof ListHostsCmd)
+                    && !(cmdObj instanceof ListVolumesCmd)) {
                 buildAsyncListResponse((BaseListCmd) cmdObj, caller);
             }
 

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/21c1623a/server/src/com/cloud/api/query/QueryManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/QueryManagerImpl.java b/server/src/com/cloud/api/query/QueryManagerImpl.java
index 61af161..19577d2 100644
--- a/server/src/com/cloud/api/query/QueryManagerImpl.java
+++ b/server/src/com/cloud/api/query/QueryManagerImpl.java
@@ -39,6 +39,7 @@ import org.apache.cloudstack.api.command.user.securitygroup.ListSecurityGroupsCm
 import org.apache.cloudstack.api.command.user.tag.ListTagsCmd;
 import org.apache.cloudstack.api.command.user.vm.ListVMsCmd;
 import org.apache.cloudstack.api.command.user.vmgroup.ListVMGroupsCmd;
+import org.apache.cloudstack.api.command.user.volume.ListVolumesCmd;
 import org.apache.cloudstack.api.response.DomainRouterResponse;
 import org.apache.cloudstack.api.response.EventResponse;
 import org.apache.cloudstack.api.response.HostResponse;
@@ -51,6 +52,7 @@ import org.apache.cloudstack.api.response.ResourceTagResponse;
 import org.apache.cloudstack.api.response.SecurityGroupResponse;
 import org.apache.cloudstack.api.response.UserResponse;
 import org.apache.cloudstack.api.response.UserVmResponse;
+import org.apache.cloudstack.api.response.VolumeResponse;
 import org.apache.cloudstack.query.QueryService;
 import org.apache.log4j.Logger;
 
@@ -65,6 +67,7 @@ import com.cloud.api.query.dao.ProjectJoinDao;
 import com.cloud.api.query.dao.ResourceTagJoinDao;
 import com.cloud.api.query.dao.SecurityGroupJoinDao;
 import com.cloud.api.query.dao.UserVmJoinDao;
+import com.cloud.api.query.dao.VolumeJoinDao;
 import com.cloud.api.query.vo.DomainRouterJoinVO;
 import com.cloud.api.query.vo.EventJoinVO;
 import com.cloud.api.query.vo.HostJoinVO;
@@ -76,6 +79,7 @@ import com.cloud.api.query.vo.ResourceTagJoinVO;
 import com.cloud.api.query.vo.SecurityGroupJoinVO;
 import com.cloud.api.query.vo.UserAccountJoinVO;
 import com.cloud.api.query.vo.UserVmJoinVO;
+import com.cloud.api.query.vo.VolumeJoinVO;
 import com.cloud.async.AsyncJob;
 import com.cloud.domain.Domain;
 import com.cloud.domain.DomainVO;
@@ -98,7 +102,12 @@ import com.cloud.projects.ProjectService;
 import com.cloud.projects.dao.ProjectAccountDao;
 import com.cloud.projects.dao.ProjectDao;
 import com.cloud.server.Criteria;
+import com.cloud.server.ResourceTag.TaggedResourceType;
+import com.cloud.storage.DiskOfferingVO;
 import com.cloud.storage.Snapshot;
+import com.cloud.storage.Volume;
+import com.cloud.storage.VolumeVO;
+import com.cloud.tags.ResourceTagVO;
 import com.cloud.user.Account;
 import com.cloud.user.AccountManager;
 import com.cloud.user.AccountManagerImpl;
@@ -119,6 +128,8 @@ import com.cloud.utils.db.SearchCriteria;
 import com.cloud.utils.db.SearchCriteria.Func;
 import com.cloud.utils.db.SearchCriteria.Op;
 import com.cloud.vm.UserVmVO;
+import com.cloud.vm.VMInstanceVO;
+import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.dao.UserVmDao;
 
 /**
@@ -189,6 +200,9 @@ public class QueryManagerImpl implements QueryService, Manager {
     private HostJoinDao _hostJoinDao;
 
     @Inject
+    private VolumeJoinDao _volumeJoinDao;
+
+    @Inject
     private HighAvailabilityManager _haMgr;
 
     @Override
@@ -1371,9 +1385,9 @@ public class QueryManagerImpl implements QueryService, Manager {
             if ((Boolean) haHosts) {
                 sb.and("tag", sb.entity().getTag(), SearchCriteria.Op.EQ);
             } else {
-                sb.and("tag", sb.entity().getTag(), SearchCriteria.Op.NEQ);
-                //FIXME: should we have another condition say tag = null?
-                //hostTagSearch.or("tagNull", hostTagSearch.entity().getTag(), SearchCriteria.Op.NULL);
+                sb.and().op("tag", sb.entity().getTag(), SearchCriteria.Op.NEQ);
+                sb.or("tagNull", sb.entity().getTag(), SearchCriteria.Op.NULL);
+                sb.cp();
             }
 
         }
@@ -1439,4 +1453,142 @@ public class QueryManagerImpl implements QueryService, Manager {
 
     }
 
+    @Override
+    public ListResponse<VolumeResponse> searchForVolumes(ListVolumesCmd cmd) {
+        Pair<List<VolumeJoinVO>, Integer> result = searchForVolumesInternal(cmd);
+        ListResponse<VolumeResponse> response = new ListResponse<VolumeResponse>();
+
+        List<VolumeResponse> routerResponses = ViewResponseHelper.createVolumeResponse(result.first().toArray(new VolumeJoinVO[result.first().size()]));
+        response.setResponses(routerResponses, result.second());
+        return response;
+    }
+
+
+    private Pair<List<VolumeJoinVO>, Integer> searchForVolumesInternal(ListVolumesCmd cmd) {
+
+        Account caller = UserContext.current().getCaller();
+        List<Long> permittedAccounts = new ArrayList<Long>();
+
+        Long id = cmd.getId();
+        Long vmInstanceId = cmd.getVirtualMachineId();
+        String name = cmd.getVolumeName();
+        String keyword = cmd.getKeyword();
+        String type = cmd.getType();
+        Map<String, String> tags = cmd.getTags();
+
+        Long zoneId = cmd.getZoneId();
+        Long podId = null;
+        if (_accountMgr.isAdmin(caller.getType())) {
+            podId = cmd.getPodId();
+        }
+
+        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, cmd.listAll(), false);
+        Long domainId = domainIdRecursiveListProject.first();
+        Boolean isRecursive = domainIdRecursiveListProject.second();
+        ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
+        Filter searchFilter = new Filter(VolumeJoinVO.class, "created", false, cmd.getStartIndex(), cmd.getPageSizeVal());
+
+        // hack for now, this should be done better but due to needing a join I opted to
+        // do this quickly and worry about making it pretty later
+        SearchBuilder<VolumeJoinVO> sb = _volumeJoinDao.createSearchBuilder();
+        sb.select(null, Func.DISTINCT, sb.entity().getId()); // select distinct
+        // ids to get
+        // number of
+        // records with
+        // pagination
+        _accountMgr.buildACLViewSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
+
+        sb.and("name", sb.entity().getName(), SearchCriteria.Op.LIKE);
+        sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ);
+        sb.and("volumeType", sb.entity().getVolumeType(), SearchCriteria.Op.LIKE);
+        sb.and("instanceId", sb.entity().getVmId(), SearchCriteria.Op.EQ);
+        sb.and("dataCenterId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ);
+        sb.and("podId", sb.entity().getPodId(), SearchCriteria.Op.EQ);
+        // Only return volumes that are not destroyed
+        sb.and("state", sb.entity().getState(), SearchCriteria.Op.NEQ);
+        sb.and("systemUse", sb.entity().isSystemUse(), SearchCriteria.Op.NEQ);
+        // display UserVM volumes only
+        sb.and().op("type", sb.entity().getVmType(), SearchCriteria.Op.NIN);
+        sb.or("nulltype", sb.entity().getVmType(), SearchCriteria.Op.NULL);
+        sb.cp();
+
+        if (tags != null && !tags.isEmpty()) {
+            for (int count=0; count < tags.size(); count++) {
+                sb.or().op("key" + String.valueOf(count), sb.entity().getTagKey(), SearchCriteria.Op.EQ);
+                sb.and("value" + String.valueOf(count), sb.entity().getTagValue(), SearchCriteria.Op.EQ);
+                sb.cp();
+            }
+        }
+
+
+
+        // now set the SC criteria...
+        SearchCriteria<VolumeJoinVO> sc = sb.create();
+        _accountMgr.buildACLViewSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
+
+        if (keyword != null) {
+            SearchCriteria<VolumeJoinVO> ssc = _volumeJoinDao.createSearchCriteria();
+            ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%");
+            ssc.addOr("volumeType", SearchCriteria.Op.LIKE, "%" + keyword + "%");
+
+            sc.addAnd("name", SearchCriteria.Op.SC, ssc);
+        }
+
+        if (name != null) {
+            sc.setParameters("name", "%" + name + "%");
+        }
+
+        sc.setParameters("systemUse", 1);
+
+        if (tags != null && !tags.isEmpty()) {
+            int count = 0;
+             for (String key : tags.keySet()) {
+                sc.setParameters("key" + String.valueOf(count), key);
+                sc.setParameters("value" + String.valueOf(count), tags.get(key));
+                count++;
+            }
+        }
+
+        if (id != null) {
+            sc.setParameters("id", id);
+        }
+
+        if (type != null) {
+            sc.setParameters("volumeType", "%" + type + "%");
+        }
+        if (vmInstanceId != null) {
+            sc.setParameters("instanceId", vmInstanceId);
+        }
+        if (zoneId != null) {
+            sc.setParameters("dataCenterId", zoneId);
+        }
+        if (podId != null) {
+            sc.setParameters("podId", podId);
+        }
+
+        // Don't return DomR and ConsoleProxy volumes
+        sc.setParameters("type", VirtualMachine.Type.ConsoleProxy, VirtualMachine.Type.SecondaryStorageVm, VirtualMachine.Type.DomainRouter);
+
+        // Only return volumes that are not destroyed
+        sc.setParameters("state", Volume.State.Destroy);
+
+        // search Volume details by ids
+        Pair<List<VolumeJoinVO>, Integer> uniqueVolPair = _volumeJoinDao.searchAndCount(sc, searchFilter);
+        Integer count = uniqueVolPair.second();
+        if (count.intValue() == 0) {
+            // empty result
+            return uniqueVolPair;
+        }
+        List<VolumeJoinVO> uniqueVols = uniqueVolPair.first();
+        Long[] vrIds = new Long[uniqueVols.size()];
+        int i = 0;
+        for (VolumeJoinVO v : uniqueVols) {
+            vrIds[i++] = v.getId();
+        }
+        List<VolumeJoinVO> vrs = _volumeJoinDao.searchByIds(vrIds);
+        return new Pair<List<VolumeJoinVO>, Integer>(vrs, count);
+    }
+
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/21c1623a/server/src/com/cloud/api/query/ViewResponseHelper.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/ViewResponseHelper.java b/server/src/com/cloud/api/query/ViewResponseHelper.java
index bb6c66b..b1a5399 100644
--- a/server/src/com/cloud/api/query/ViewResponseHelper.java
+++ b/server/src/com/cloud/api/query/ViewResponseHelper.java
@@ -34,6 +34,7 @@ import org.apache.cloudstack.api.response.ResourceTagResponse;
 import org.apache.cloudstack.api.response.SecurityGroupResponse;
 import org.apache.cloudstack.api.response.UserResponse;
 import org.apache.cloudstack.api.response.UserVmResponse;
+import org.apache.cloudstack.api.response.VolumeResponse;
 import org.apache.log4j.Logger;
 
 import com.cloud.api.ApiDBUtils;
@@ -48,6 +49,7 @@ import com.cloud.api.query.vo.ResourceTagJoinVO;
 import com.cloud.api.query.vo.SecurityGroupJoinVO;
 import com.cloud.api.query.vo.UserAccountJoinVO;
 import com.cloud.api.query.vo.UserVmJoinVO;
+import com.cloud.api.query.vo.VolumeJoinVO;
 import com.cloud.user.Account;
 import com.cloud.user.UserContext;
 
@@ -214,4 +216,21 @@ public class ViewResponseHelper {
         }
         return new ArrayList<HostResponse>(vrDataList.values());
     }
+
+    public static List<VolumeResponse> createVolumeResponse(VolumeJoinVO... volumes) {
+        Hashtable<Long, VolumeResponse> vrDataList = new Hashtable<Long, VolumeResponse>();
+        for (VolumeJoinVO vr : volumes) {
+            VolumeResponse vrData = vrDataList.get(vr.getId());
+            if ( vrData == null ){
+                // first time encountering this volume
+                vrData = ApiDBUtils.newVolumeResponse(vr);
+            }
+            else{
+                // update tags
+                vrData = ApiDBUtils.fillVolumeDetails(vrData, vr);
+            }
+            vrDataList.put(vr.getId(), vrData);
+        }
+        return new ArrayList<VolumeResponse>(vrDataList.values());
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/21c1623a/server/src/com/cloud/api/query/dao/VolumeJoinDao.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/dao/VolumeJoinDao.java b/server/src/com/cloud/api/query/dao/VolumeJoinDao.java
new file mode 100644
index 0000000..67509cf
--- /dev/null
+++ b/server/src/com/cloud/api/query/dao/VolumeJoinDao.java
@@ -0,0 +1,37 @@
+// 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.api.query.dao;
+
+import java.util.List;
+
+import org.apache.cloudstack.api.response.VolumeResponse;
+
+
+import com.cloud.api.query.vo.VolumeJoinVO;
+import com.cloud.storage.Volume;
+import com.cloud.utils.db.GenericDao;
+
+public interface VolumeJoinDao extends GenericDao<VolumeJoinVO, Long> {
+
+    VolumeResponse newVolumeResponse(VolumeJoinVO vol);
+
+    VolumeResponse setVolumeResponse(VolumeResponse volData, VolumeJoinVO vol);
+
+    List<VolumeJoinVO> newVolumeView(Volume vol);
+
+    List<VolumeJoinVO> searchByIds(Long... ids);
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/21c1623a/server/src/com/cloud/api/query/dao/VolumeJoinDaoImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/dao/VolumeJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/VolumeJoinDaoImpl.java
new file mode 100644
index 0000000..f4691ce
--- /dev/null
+++ b/server/src/com/cloud/api/query/dao/VolumeJoinDaoImpl.java
@@ -0,0 +1,230 @@
+// 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.api.query.dao;
+
+import java.util.List;
+
+import javax.ejb.Local;
+
+import org.apache.log4j.Logger;
+
+import com.cloud.api.ApiDBUtils;
+import com.cloud.api.ApiResponseHelper;
+import com.cloud.api.query.vo.ResourceTagJoinVO;
+import com.cloud.api.query.vo.VolumeJoinVO;
+import org.apache.cloudstack.api.response.VolumeResponse;
+
+import com.cloud.offering.ServiceOffering;
+import com.cloud.storage.Storage;
+import com.cloud.storage.VMTemplateHostVO;
+import com.cloud.storage.Volume;
+import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
+import com.cloud.user.Account;
+import com.cloud.user.UserContext;
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+
+
+@Local(value={VolumeJoinDao.class})
+public class VolumeJoinDaoImpl extends GenericDaoBase<VolumeJoinVO, Long> implements VolumeJoinDao {
+    public static final Logger s_logger = Logger.getLogger(VolumeJoinDaoImpl.class);
+
+    private SearchBuilder<VolumeJoinVO> volSearch;
+
+    private SearchBuilder<VolumeJoinVO> volIdSearch;
+
+    protected VolumeJoinDaoImpl() {
+
+        volSearch = createSearchBuilder();
+        volSearch.and("idIN", volSearch.entity().getId(), SearchCriteria.Op.IN);
+        volSearch.done();
+
+        volIdSearch = createSearchBuilder();
+        volIdSearch.and("id", volIdSearch.entity().getId(), SearchCriteria.Op.EQ);
+        volIdSearch.done();
+
+        this._count = "select count(distinct id) from volume_view WHERE ";
+    }
+
+
+
+
+    @Override
+    public VolumeResponse newVolumeResponse(VolumeJoinVO volume) {
+        Account caller = UserContext.current().getCaller();
+
+        VolumeResponse volResponse = new VolumeResponse();
+        volResponse.setId(volume.getUuid());
+
+        if (volume.getName() != null) {
+            volResponse.setName(volume.getName());
+        } else {
+            volResponse.setName("");
+        }
+
+        volResponse.setZoneId(volume.getDataCenterUuid());
+        volResponse.setZoneName(volume.getDataCenterName());
+
+        volResponse.setVolumeType(volume.getVolumeType().toString());
+        volResponse.setDeviceId(volume.getDeviceId());
+
+        long instanceId = volume.getVmId();
+        if (instanceId > 0 && volume.getState() != Volume.State.Destroy) {
+            volResponse.setVirtualMachineId(volume.getVmUuid());
+            volResponse.setVirtualMachineName(volume.getVmName());
+            volResponse.setVirtualMachineState(volume.getVmState().toString());
+            volResponse.setVirtualMachineDisplayName(volume.getVmDisplayName());
+        }
+
+        // Show the virtual size of the volume
+        volResponse.setSize(volume.getSize());
+
+        volResponse.setCreated(volume.getCreated());
+        volResponse.setState(volume.getState().toString());
+        if (volume.getState() == Volume.State.UploadOp) {
+            // com.cloud.storage.VolumeHostVO volumeHostRef =
+            // ApiDBUtils.findVolumeHostRef(volume.getId(),
+            // volume.getDataCenterId());
+            volResponse.setSize(volume.getVolumeHostSize());
+            volResponse.setCreated(volume.getVolumeHostCreated());
+
+            if (caller.getType() == Account.ACCOUNT_TYPE_ADMIN || caller.getType() == Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN)
+                volResponse.setHypervisor(ApiDBUtils.getHypervisorTypeFromFormat(volume.getFormat()).toString());
+            if (volume.getDownloadState() != Status.DOWNLOADED) {
+                String volumeStatus = "Processing";
+                if (volume.getDownloadState() == VMTemplateHostVO.Status.DOWNLOAD_IN_PROGRESS) {
+                    if (volume.getDownloadPercent() == 100) {
+                        volumeStatus = "Checking Volume";
+                    } else {
+                        volumeStatus = volume.getDownloadPercent() + "% Uploaded";
+                    }
+                    volResponse.setState("Uploading");
+                } else {
+                    volumeStatus = volume.getErrorString();
+                    if (volume.getDownloadState() == VMTemplateHostVO.Status.NOT_DOWNLOADED) {
+                        volResponse.setState("UploadNotStarted");
+                    } else {
+                        volResponse.setState("UploadError");
+                    }
+                }
+                volResponse.setStatus(volumeStatus);
+            } else if (volume.getDownloadState() == VMTemplateHostVO.Status.DOWNLOADED) {
+                volResponse.setStatus("Upload Complete");
+                volResponse.setState("Uploaded");
+            } else {
+                volResponse.setStatus("Successfully Installed");
+            }
+        }
+
+        // populate owner.
+        ApiResponseHelper.populateOwner(volResponse, volume);
+
+        // DiskOfferingVO diskOffering =
+        // ApiDBUtils.findDiskOfferingById(volume.getDiskOfferingId());
+        if (volume.getDiskOfferingId() > 0) {
+            if (volume.getVolumeType().equals(Volume.Type.ROOT)) {
+                volResponse.setServiceOfferingId(volume.getDiskOfferingUuid());
+            } else {
+                volResponse.setDiskOfferingId(volume.getDiskOfferingUuid());
+            }
+
+            if (volume.getVolumeType().equals(Volume.Type.ROOT)) {
+                volResponse.setServiceOfferingName(volume.getDiskOfferingName());
+                volResponse.setServiceOfferingDisplayText(volume.getDiskOfferingDisplayText());
+            } else {
+                volResponse.setDiskOfferingName(volume.getDiskOfferingName());
+                volResponse.setDiskOfferingDisplayText(volume.getDiskOfferingDisplayText());
+            }
+            volResponse.setStorageType(volume.isUseLocalStorage() ? ServiceOffering.StorageType.local.toString() : ServiceOffering.StorageType.shared
+                    .toString());
+        }
+        Long poolId = volume.getPoolId();
+        String poolName = (poolId == null) ? "none" : volume.getPoolName();
+        volResponse.setStoragePoolName(poolName);
+
+        // return hypervisor for ROOT and Resource domain only
+        if ((caller.getType() == Account.ACCOUNT_TYPE_ADMIN || caller.getType() == Account.ACCOUNT_TYPE_RESOURCE_DOMAIN_ADMIN)
+                && volume.getState() != Volume.State.UploadOp && volume.getHypervisorType() != null) {
+            volResponse.setHypervisor(volume.getHypervisorType().toString());
+        }
+
+        volResponse.setAttached(volume.getAttached());
+        volResponse.setDestroyed(volume.getState() == Volume.State.Destroy);
+        boolean isExtractable = true;
+        if (volume.getVolumeType() != Volume.Type.DATADISK) { // Datadisk dont
+                                                              // have any
+                                                              // template
+                                                              // dependence.
+            if (volume.getTemplateId() > 0) { // For ISO based volumes template
+                                              // = null and we allow extraction
+                                              // of all ISO based volumes
+                isExtractable = volume.isExtractable() && volume.getTemplateType() != Storage.TemplateType.SYSTEM;
+            }
+        }
+
+        // update tag information
+        long tag_id = volume.getTagId();
+        if (tag_id > 0) {
+            ResourceTagJoinVO vtag = ApiDBUtils.findResourceTagViewById(tag_id);
+            if (vtag != null) {
+                volResponse.addTag(ApiDBUtils.newResourceTagResponse(vtag, false));
+            }
+        }
+
+        volResponse.setExtractable(isExtractable);
+        volResponse.setObjectName("volume");
+        return volResponse;
+    }
+
+
+
+    @Override
+    public VolumeResponse setVolumeResponse(VolumeResponse volData, VolumeJoinVO vol) {
+        long tag_id = vol.getTagId();
+        if (tag_id > 0) {
+            ResourceTagJoinVO vtag = ApiDBUtils.findResourceTagViewById(tag_id);
+            if ( vtag != null ){
+                volData.addTag(ApiDBUtils.newResourceTagResponse(vtag, false));
+            }
+        }
+        return volData;
+    }
+
+
+
+
+    @Override
+    public List<VolumeJoinVO> newVolumeView(Volume vol) {
+        SearchCriteria<VolumeJoinVO> sc = volIdSearch.create();
+        sc.setParameters("id", vol.getId());
+        return searchIncludingRemoved(sc, null, null, false);
+    }
+
+
+
+
+    @Override
+    public List<VolumeJoinVO> searchByIds(Long... ids) {
+        SearchCriteria<VolumeJoinVO> sc = volSearch.create();
+        sc.setParameters("idIN", ids);
+        return searchIncludingRemoved(sc, null, null, false);
+    }
+
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/21c1623a/server/src/com/cloud/api/query/vo/VolumeJoinVO.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/vo/VolumeJoinVO.java b/server/src/com/cloud/api/query/vo/VolumeJoinVO.java
new file mode 100644
index 0000000..96c2615
--- /dev/null
+++ b/server/src/com/cloud/api/query/vo/VolumeJoinVO.java
@@ -0,0 +1,1032 @@
+// 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.api.query.vo;
+
+import java.util.Date;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.server.ResourceTag.TaggedResourceType;
+import com.cloud.storage.Storage;
+import com.cloud.storage.Volume;
+import com.cloud.storage.VMTemplateStorageResourceAssoc.Status;
+import com.cloud.utils.db.GenericDao;
+import com.cloud.vm.VirtualMachine;
+import com.cloud.vm.VirtualMachine.Type;
+
+@Entity
+@Table(name="volume_view")
+public class VolumeJoinVO extends BaseViewVO implements ControlledViewEntity {
+
+    @Column(name="id")
+    private long id;
+
+    @Column(name="uuid")
+    private String uuid;
+
+
+    @Column(name="name")
+    private String name;
+
+    @Column(name = "device_id")
+    Long deviceId = null;
+
+    @Column(name = "volume_type")
+    @Enumerated(EnumType.STRING)
+    Volume.Type volumeType;
+
+    @Column(name = "size")
+    long size;
+
+    @Column(name = "state")
+    @Enumerated(value = EnumType.STRING)
+    private Volume.State state;
+
+    @Column(name=GenericDao.CREATED_COLUMN)
+    private Date created;
+
+    @Column(name = "attached")
+    @Temporal(value = TemporalType.TIMESTAMP)
+    Date attached;
+
+    @Column(name=GenericDao.REMOVED_COLUMN)
+    private Date removed;
+
+
+    @Column(name="account_id")
+    private long accountId;
+
+    @Column(name="account_uuid")
+    private String accountUuid;
+
+    @Column(name="account_name")
+    private String accountName = null;
+
+    @Column(name="account_type")
+    private short accountType;
+
+    @Column(name="domain_id")
+    private long domainId;
+
+    @Column(name="domain_uuid")
+    private String domainUuid;
+
+    @Column(name="domain_name")
+    private String domainName = null;
+
+    @Column(name="domain_path")
+    private String domainPath = null;
+
+    @Column(name="project_id")
+    private long projectId;
+
+    @Column(name="project_uuid")
+    private String projectUuid;
+
+    @Column(name="project_name")
+    private String projectName;
+
+    @Column(name="pod_id")
+    private long podId;
+
+    @Column(name="data_center_id")
+    private long dataCenterId;
+
+    @Column(name="data_center_uuid")
+    private String dataCenterUuid;
+
+    @Column(name="data_center_name")
+    private String dataCenterName;
+
+    @Column(name="vm_id")
+    private long vmId;
+
+    @Column(name="vm_uuid")
+    private String vmUuid;
+
+    @Column(name="vm_name")
+    private String vmName;
+
+    @Column(name="vm_display_name")
+    private String vmDisplayName;
+
+    @Column(name="vm_state")
+    @Enumerated(value=EnumType.STRING)
+    protected VirtualMachine.State vmState = null;
+
+    @Column(name="vm_type")
+    @Enumerated(value=EnumType.STRING)
+    protected VirtualMachine.Type vmType;
+
+    @Column (name="volume_host_size")
+    private long volumeHostSize;
+
+    @Column(name="volume_host_created")
+    private Date volumeHostCreated;
+
+    @Column(name="format")
+    private Storage.ImageFormat format;
+
+    @Column (name="download_pct")
+    private int downloadPercent;
+
+    @Column (name="download_state")
+    @Enumerated(EnumType.STRING)
+    private Status downloadState;
+
+    @Column (name="error_str")
+    private String errorString;
+
+    @Column(name="hypervisor_type")
+    @Enumerated(value=EnumType.STRING)
+    private HypervisorType hypervisorType;
+
+
+    @Column(name="disk_offering_id")
+    private long diskOfferingId;
+
+    @Column(name="disk_offering_uuid")
+    private String diskOfferingUuid;
+
+    @Column(name="disk_offering_name")
+    private String diskOfferingName;
+
+    @Column(name="disk_offering_display_text")
+    private String diskOfferingDisplayText;
+
+    @Column(name="system_use")
+    private boolean systemUse;
+
+
+    @Column(name="use_local_storage")
+    private boolean useLocalStorage;
+
+    @Column(name="pool_id")
+    private long poolId;
+
+    @Column(name="pool_uuid")
+    private String poolUuid;
+
+    @Column(name="pool_name")
+    private String poolName;
+
+    @Column(name="template_id")
+    private long templateId;
+
+    @Column(name="template_uuid")
+    private String templateUuid;
+
+    @Column(name="extractable")
+    private boolean extractable;
+
+    @Column(name="template_type")
+    private Storage.TemplateType templateType;
+
+    @Column(name="job_id")
+    private long jobId;
+
+    @Column(name="job_uuid")
+    private String jobUuid;
+
+    @Column(name="job_status")
+    private int jobStatus;
+
+    @Column(name="tag_id")
+    private long tagId;
+
+    @Column(name="tag_uuid")
+    private String tagUuid;
+
+    @Column(name="tag_key")
+    private String tagKey;
+
+    @Column(name="tag_value")
+    private String tagValue;
+
+    @Column(name="tag_domain_id")
+    private long tagDomainId;
+
+    @Column(name="tag_account_id")
+    private long tagAccountId;
+
+    @Column(name="tag_resource_id")
+    private long tagResourceId;
+
+    @Column(name="tag_resource_uuid")
+    private String tagResourceUuid;
+
+    @Column(name="tag_resource_type")
+    @Enumerated(value=EnumType.STRING)
+    private TaggedResourceType tagResourceType;
+
+    @Column(name="tag_customer")
+    private String tagCustomer;
+
+
+
+    public VolumeJoinVO() {
+    }
+
+
+
+    @Override
+    public long getId() {
+        return id;
+    }
+
+
+
+    @Override
+    public void setId(long id) {
+        this.id = id;
+    }
+
+
+
+    public String getUuid() {
+        return uuid;
+    }
+
+
+
+    public void setUuid(String uuid) {
+        this.uuid = uuid;
+    }
+
+
+
+    public String getName() {
+        return name;
+    }
+
+
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+
+    public Long getDeviceId() {
+        return deviceId;
+    }
+
+
+
+    public void setDeviceId(Long deviceId) {
+        this.deviceId = deviceId;
+    }
+
+
+
+    public Volume.Type getVolumeType() {
+        return volumeType;
+    }
+
+
+
+    public void setVolumeType(Volume.Type volumeType) {
+        this.volumeType = volumeType;
+    }
+
+
+
+    public long getSize() {
+        return size;
+    }
+
+
+
+    public void setSize(long size) {
+        this.size = size;
+    }
+
+
+
+    public Volume.State getState() {
+        return state;
+    }
+
+
+
+    public void setState(Volume.State state) {
+        this.state = state;
+    }
+
+
+
+    public Date getCreated() {
+        return created;
+    }
+
+
+
+    public void setCreated(Date created) {
+        this.created = created;
+    }
+
+
+
+    public Date getAttached() {
+        return attached;
+    }
+
+
+
+    public void setAttached(Date attached) {
+        this.attached = attached;
+    }
+
+
+
+    public Date getRemoved() {
+        return removed;
+    }
+
+
+
+    public void setRemoved(Date removed) {
+        this.removed = removed;
+    }
+
+
+
+    @Override
+    public long getAccountId() {
+        return accountId;
+    }
+
+
+
+    public void setAccountId(long accountId) {
+        this.accountId = accountId;
+    }
+
+
+
+    @Override
+    public String getAccountUuid() {
+        return accountUuid;
+    }
+
+
+
+    public void setAccountUuid(String accountUuid) {
+        this.accountUuid = accountUuid;
+    }
+
+
+
+    @Override
+    public String getAccountName() {
+        return accountName;
+    }
+
+
+
+    public void setAccountName(String accountName) {
+        this.accountName = accountName;
+    }
+
+
+
+    @Override
+    public short getAccountType() {
+        return accountType;
+    }
+
+
+
+    public void setAccountType(short accountType) {
+        this.accountType = accountType;
+    }
+
+
+
+    @Override
+    public long getDomainId() {
+        return domainId;
+    }
+
+
+
+    public void setDomainId(long domainId) {
+        this.domainId = domainId;
+    }
+
+
+
+    @Override
+    public String getDomainUuid() {
+        return domainUuid;
+    }
+
+
+
+    public void setDomainUuid(String domainUuid) {
+        this.domainUuid = domainUuid;
+    }
+
+
+
+    @Override
+    public String getDomainName() {
+        return domainName;
+    }
+
+
+
+    public void setDomainName(String domainName) {
+        this.domainName = domainName;
+    }
+
+
+
+    @Override
+    public String getDomainPath() {
+        return domainPath;
+    }
+
+
+
+    public void setDomainPath(String domainPath) {
+        this.domainPath = domainPath;
+    }
+
+
+
+    public long getProjectId() {
+        return projectId;
+    }
+
+
+
+    public void setProjectId(long projectId) {
+        this.projectId = projectId;
+    }
+
+
+
+    @Override
+    public String getProjectUuid() {
+        return projectUuid;
+    }
+
+
+
+    public void setProjectUuid(String projectUuid) {
+        this.projectUuid = projectUuid;
+    }
+
+
+
+    @Override
+    public String getProjectName() {
+        return projectName;
+    }
+
+
+
+    public void setProjectName(String projectName) {
+        this.projectName = projectName;
+    }
+
+
+
+    public long getVmId() {
+        return vmId;
+    }
+
+
+
+    public void setVmId(long vmId) {
+        this.vmId = vmId;
+    }
+
+
+
+    public String getVmUuid() {
+        return vmUuid;
+    }
+
+
+
+    public void setVmUuid(String vmUuid) {
+        this.vmUuid = vmUuid;
+    }
+
+
+
+    public String getVmName() {
+        return vmName;
+    }
+
+
+
+    public void setVmName(String vmName) {
+        this.vmName = vmName;
+    }
+
+
+
+    public String getVmDisplayName() {
+        return vmDisplayName;
+    }
+
+
+
+    public void setVmDisplayName(String vmDisplayName) {
+        this.vmDisplayName = vmDisplayName;
+    }
+
+
+
+    public VirtualMachine.State getVmState() {
+        return vmState;
+    }
+
+
+
+    public void setVmState(VirtualMachine.State vmState) {
+        this.vmState = vmState;
+    }
+
+
+
+    public VirtualMachine.Type getVmType() {
+        return vmType;
+    }
+
+
+
+    public void setVmType(VirtualMachine.Type vmType) {
+        this.vmType = vmType;
+    }
+
+
+
+    public long getVolumeHostSize() {
+        return volumeHostSize;
+    }
+
+
+
+    public void setVolumeHostSize(long volumeHostSize) {
+        this.volumeHostSize = volumeHostSize;
+    }
+
+
+
+    public Date getVolumeHostCreated() {
+        return volumeHostCreated;
+    }
+
+
+
+    public void setVolumeHostCreated(Date volumeHostCreated) {
+        this.volumeHostCreated = volumeHostCreated;
+    }
+
+
+
+    public Storage.ImageFormat getFormat() {
+        return format;
+    }
+
+
+
+    public void setFormat(Storage.ImageFormat format) {
+        this.format = format;
+    }
+
+
+
+    public int getDownloadPercent() {
+        return downloadPercent;
+    }
+
+
+
+    public void setDownloadPercent(int downloadPercent) {
+        this.downloadPercent = downloadPercent;
+    }
+
+
+
+    public Status getDownloadState() {
+        return downloadState;
+    }
+
+
+
+    public void setDownloadState(Status downloadState) {
+        this.downloadState = downloadState;
+    }
+
+
+
+    public String getErrorString() {
+        return errorString;
+    }
+
+
+
+    public void setErrorString(String errorString) {
+        this.errorString = errorString;
+    }
+
+
+
+    public HypervisorType getHypervisorType() {
+        return hypervisorType;
+    }
+
+
+
+    public void setHypervisorType(HypervisorType hypervisorType) {
+        this.hypervisorType = hypervisorType;
+    }
+
+
+
+    public long getDiskOfferingId() {
+        return diskOfferingId;
+    }
+
+
+
+    public void setDiskOfferingId(long diskOfferingId) {
+        this.diskOfferingId = diskOfferingId;
+    }
+
+
+
+    public String getDiskOfferingUuid() {
+        return diskOfferingUuid;
+    }
+
+
+
+    public void setDiskOfferingUuid(String diskOfferingUuid) {
+        this.diskOfferingUuid = diskOfferingUuid;
+    }
+
+
+
+    public String getDiskOfferingName() {
+        return diskOfferingName;
+    }
+
+
+
+    public void setDiskOfferingName(String diskOfferingName) {
+        this.diskOfferingName = diskOfferingName;
+    }
+
+
+
+    public String getDiskOfferingDisplayText() {
+        return diskOfferingDisplayText;
+    }
+
+
+
+    public void setDiskOfferingDisplayText(String diskOfferingDisplayText) {
+        this.diskOfferingDisplayText = diskOfferingDisplayText;
+    }
+
+
+
+    public boolean isUseLocalStorage() {
+        return useLocalStorage;
+    }
+
+
+
+    public void setUseLocalStorage(boolean useLocalStorage) {
+        this.useLocalStorage = useLocalStorage;
+    }
+
+
+
+    public long getPoolId() {
+        return poolId;
+    }
+
+
+
+    public void setPoolId(long poolId) {
+        this.poolId = poolId;
+    }
+
+
+
+    public String getPoolUuid() {
+        return poolUuid;
+    }
+
+
+
+    public void setPoolUuid(String poolUuid) {
+        this.poolUuid = poolUuid;
+    }
+
+
+
+    public String getPoolName() {
+        return poolName;
+    }
+
+
+
+    public void setPoolName(String poolName) {
+        this.poolName = poolName;
+    }
+
+
+
+    public long getTemplateId() {
+        return templateId;
+    }
+
+
+
+    public void setTemplateId(long templateId) {
+        this.templateId = templateId;
+    }
+
+
+
+    public String getTemplateUuid() {
+        return templateUuid;
+    }
+
+
+
+    public void setTemplateUuid(String templateUuid) {
+        this.templateUuid = templateUuid;
+    }
+
+
+
+    public boolean isExtractable() {
+        return extractable;
+    }
+
+
+
+    public void setExtractable(boolean extractable) {
+        this.extractable = extractable;
+    }
+
+
+
+    public Storage.TemplateType getTemplateType() {
+        return templateType;
+    }
+
+
+
+    public void setTemplateType(Storage.TemplateType templateType) {
+        this.templateType = templateType;
+    }
+
+
+
+    public long getJobId() {
+        return jobId;
+    }
+
+
+
+    public void setJobId(long jobId) {
+        this.jobId = jobId;
+    }
+
+
+
+    public String getJobUuid() {
+        return jobUuid;
+    }
+
+
+
+    public void setJobUuid(String jobUuid) {
+        this.jobUuid = jobUuid;
+    }
+
+
+
+    public int getJobStatus() {
+        return jobStatus;
+    }
+
+
+
+    public void setJobStatus(int jobStatus) {
+        this.jobStatus = jobStatus;
+    }
+
+
+
+    public long getTagId() {
+        return tagId;
+    }
+
+
+
+    public void setTagId(long tagId) {
+        this.tagId = tagId;
+    }
+
+
+
+    public String getTagUuid() {
+        return tagUuid;
+    }
+
+
+
+    public void setTagUuid(String tagUuid) {
+        this.tagUuid = tagUuid;
+    }
+
+
+
+    public String getTagKey() {
+        return tagKey;
+    }
+
+
+
+    public void setTagKey(String tagKey) {
+        this.tagKey = tagKey;
+    }
+
+
+
+    public String getTagValue() {
+        return tagValue;
+    }
+
+
+
+    public void setTagValue(String tagValue) {
+        this.tagValue = tagValue;
+    }
+
+
+
+    public long getTagDomainId() {
+        return tagDomainId;
+    }
+
+
+
+    public void setTagDomainId(long tagDomainId) {
+        this.tagDomainId = tagDomainId;
+    }
+
+
+
+    public long getTagAccountId() {
+        return tagAccountId;
+    }
+
+
+
+    public void setTagAccountId(long tagAccountId) {
+        this.tagAccountId = tagAccountId;
+    }
+
+
+
+    public long getTagResourceId() {
+        return tagResourceId;
+    }
+
+
+
+    public void setTagResourceId(long tagResourceId) {
+        this.tagResourceId = tagResourceId;
+    }
+
+
+
+    public String getTagResourceUuid() {
+        return tagResourceUuid;
+    }
+
+
+
+    public void setTagResourceUuid(String tagResourceUuid) {
+        this.tagResourceUuid = tagResourceUuid;
+    }
+
+
+
+    public TaggedResourceType getTagResourceType() {
+        return tagResourceType;
+    }
+
+
+
+    public void setTagResourceType(TaggedResourceType tagResourceType) {
+        this.tagResourceType = tagResourceType;
+    }
+
+
+
+    public String getTagCustomer() {
+        return tagCustomer;
+    }
+
+
+
+    public void setTagCustomer(String tagCustomer) {
+        this.tagCustomer = tagCustomer;
+    }
+
+
+
+    public long getDataCenterId() {
+        return dataCenterId;
+    }
+
+
+
+    public void setDataCenterId(long dataCenterId) {
+        this.dataCenterId = dataCenterId;
+    }
+
+
+
+    public String getDataCenterUuid() {
+        return dataCenterUuid;
+    }
+
+
+
+    public void setDataCenterUuid(String dataCenterUuid) {
+        this.dataCenterUuid = dataCenterUuid;
+    }
+
+
+
+    public String getDataCenterName() {
+        return dataCenterName;
+    }
+
+
+
+    public void setDataCenterName(String dataCenterName) {
+        this.dataCenterName = dataCenterName;
+    }
+
+
+
+    public long getPodId() {
+        return podId;
+    }
+
+
+
+    public void setPodId(long podId) {
+        this.podId = podId;
+    }
+
+
+
+    public boolean isSystemUse() {
+        return systemUse;
+    }
+
+
+
+    public void setSystemUse(boolean systemUse) {
+        this.systemUse = systemUse;
+    }
+
+
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/21c1623a/server/src/com/cloud/configuration/DefaultComponentLibrary.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/configuration/DefaultComponentLibrary.java b/server/src/com/cloud/configuration/DefaultComponentLibrary.java
index d50ac35..1824f49 100755
--- a/server/src/com/cloud/configuration/DefaultComponentLibrary.java
+++ b/server/src/com/cloud/configuration/DefaultComponentLibrary.java
@@ -34,6 +34,7 @@ import com.cloud.api.query.dao.ResourceTagJoinDaoImpl;
 import com.cloud.api.query.dao.SecurityGroupJoinDaoImpl;
 import com.cloud.api.query.dao.UserVmJoinDaoImpl;
 import com.cloud.api.query.dao.HostJoinDaoImpl;
+import com.cloud.api.query.dao.VolumeJoinDaoImpl;
 import com.cloud.async.AsyncJobExecutorContextImpl;
 import com.cloud.async.AsyncJobManagerImpl;
 import com.cloud.async.SyncQueueManagerImpl;
@@ -222,6 +223,7 @@ import com.cloud.vm.dao.VMInstanceDaoImpl;
 import com.cloud.event.dao.EventJoinDaoImpl;
 
 
+
 public class DefaultComponentLibrary extends ComponentLibraryBase implements ComponentLibrary {
     protected void populateDaos() {
         addDao("StackMaidDao", StackMaidDaoImpl.class);
@@ -241,6 +243,7 @@ public class DefaultComponentLibrary extends ComponentLibraryBase implements Com
         addDao("ProjectAccountJoinDao", ProjectAccountJoinDaoImpl.class);
         addDao("ProjectInvitationJoinDao", ProjectInvitationJoinDaoImpl.class);
         addDao("HostJoinDao", HostJoinDaoImpl.class);
+        addDao("VolumeJoinDao", VolumeJoinDaoImpl.class);
         ComponentInfo<? extends GenericDao<?, ? extends Serializable>> info = addDao("ServiceOfferingDao", ServiceOfferingDaoImpl.class);
         info.addParameter("cache.size", "50");
         info.addParameter("cache.time.to.live", "600");

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/21c1623a/server/src/com/cloud/storage/StorageManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java
index 110e955..21ee972 100755
--- a/server/src/com/cloud/storage/StorageManagerImpl.java
+++ b/server/src/com/cloud/storage/StorageManagerImpl.java
@@ -3859,124 +3859,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag
         return secHost;
     }
 
-    @Override
-    public Pair<List<? extends Volume>, Integer> searchForVolumes(ListVolumesCmd cmd) {
-        Account caller = UserContext.current().getCaller();
-        List<Long> permittedAccounts = new ArrayList<Long>();
-
-        Long id = cmd.getId();
-        Long vmInstanceId = cmd.getVirtualMachineId();
-        String name = cmd.getVolumeName();
-        String keyword = cmd.getKeyword();
-        String type = cmd.getType();
-        Map<String, String> tags = cmd.getTags();
-
-        Long zoneId = cmd.getZoneId();
-        Long podId = null;
-        // Object host = null; TODO
-        if (_accountMgr.isAdmin(caller.getType())) {
-            podId = cmd.getPodId();
-            // host = cmd.getHostId(); TODO
-        }
-
-        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, cmd.listAll(), false);
-        Long domainId = domainIdRecursiveListProject.first();
-        Boolean isRecursive = domainIdRecursiveListProject.second();
-        ListProjectResourcesCriteria listProjectResourcesCriteria = domainIdRecursiveListProject.third();
-        Filter searchFilter = new Filter(VolumeVO.class, "created", false, cmd.getStartIndex(), cmd.getPageSizeVal());
-
-        // hack for now, this should be done better but due to needing a join I opted to
-        // do this quickly and worry about making it pretty later
-        SearchBuilder<VolumeVO> sb = _volumeDao.createSearchBuilder();
-        _accountMgr.buildACLSearchBuilder(sb, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
-
-        sb.and("name", sb.entity().getName(), SearchCriteria.Op.LIKE);
-        sb.and("id", sb.entity().getId(), SearchCriteria.Op.EQ);
-        sb.and("volumeType", sb.entity().getVolumeType(), SearchCriteria.Op.LIKE);
-        sb.and("instanceId", sb.entity().getInstanceId(), SearchCriteria.Op.EQ);
-        sb.and("dataCenterId", sb.entity().getDataCenterId(), SearchCriteria.Op.EQ);
-        sb.and("podId", sb.entity().getPodId(), SearchCriteria.Op.EQ);
-        // Only return volumes that are not destroyed
-        sb.and("state", sb.entity().getState(), SearchCriteria.Op.NEQ);
-
-        SearchBuilder<DiskOfferingVO> diskOfferingSearch = _diskOfferingDao.createSearchBuilder();
-        diskOfferingSearch.and("systemUse", diskOfferingSearch.entity().getSystemUse(), SearchCriteria.Op.NEQ);
-        sb.join("diskOfferingSearch", diskOfferingSearch, sb.entity().getDiskOfferingId(), diskOfferingSearch.entity().getId(), JoinBuilder.JoinType.LEFTOUTER);
-
-        // display UserVM volumes only
-        SearchBuilder<VMInstanceVO> vmSearch = _vmInstanceDao.createSearchBuilder();
-        vmSearch.and("type", vmSearch.entity().getType(), SearchCriteria.Op.NIN);
-        vmSearch.or("nulltype", vmSearch.entity().getType(), SearchCriteria.Op.NULL);
-        sb.join("vmSearch", vmSearch, sb.entity().getInstanceId(), vmSearch.entity().getId(), JoinBuilder.JoinType.LEFTOUTER);
-
-        if (tags != null && !tags.isEmpty()) {
-            SearchBuilder<ResourceTagVO> tagSearch = _resourceTagDao.createSearchBuilder();
-            for (int count=0; count < tags.size(); count++) {
-                tagSearch.or().op("key" + String.valueOf(count), tagSearch.entity().getKey(), SearchCriteria.Op.EQ);
-                tagSearch.and("value" + String.valueOf(count), tagSearch.entity().getValue(), SearchCriteria.Op.EQ);
-                tagSearch.cp();
-            }
-            tagSearch.and("resourceType", tagSearch.entity().getResourceType(), SearchCriteria.Op.EQ);
-            sb.groupBy(sb.entity().getId());
-            sb.join("tagSearch", tagSearch, sb.entity().getId(), tagSearch.entity().getResourceId(), JoinBuilder.JoinType.INNER);
-        }
-
-        // now set the SC criteria...
-        SearchCriteria<VolumeVO> sc = sb.create();
-        _accountMgr.buildACLSearchCriteria(sc, domainId, isRecursive, permittedAccounts, listProjectResourcesCriteria);
-
-        if (keyword != null) {
-            SearchCriteria<VolumeVO> ssc = _volumeDao.createSearchCriteria();
-            ssc.addOr("name", SearchCriteria.Op.LIKE, "%" + keyword + "%");
-            ssc.addOr("volumeType", SearchCriteria.Op.LIKE, "%" + keyword + "%");
-
-            sc.addAnd("name", SearchCriteria.Op.SC, ssc);
-        }
 
-        if (name != null) {
-            sc.setParameters("name", "%" + name + "%");
-        }
-
-        sc.setJoinParameters("diskOfferingSearch", "systemUse", 1);
-
-        if (tags != null && !tags.isEmpty()) {
-            int count = 0;
-            sc.setJoinParameters("tagSearch", "resourceType", TaggedResourceType.Volume.toString());
-            for (String key : tags.keySet()) {
-                sc.setJoinParameters("tagSearch", "key" + String.valueOf(count), key);
-                sc.setJoinParameters("tagSearch", "value" + String.valueOf(count), tags.get(key));
-                count++;
-            }
-        }
-
-        if (id != null) {
-            sc.setParameters("id", id);
-        }
-
-        if (type != null) {
-            sc.setParameters("volumeType", "%" + type + "%");
-        }
-        if (vmInstanceId != null) {
-            sc.setParameters("instanceId", vmInstanceId);
-        }
-        if (zoneId != null) {
-            sc.setParameters("dataCenterId", zoneId);
-        }
-        if (podId != null) {
-            sc.setParameters("podId", podId);
-        }
-
-        // Don't return DomR and ConsoleProxy volumes
-        sc.setJoinParameters("vmSearch", "type", VirtualMachine.Type.ConsoleProxy, VirtualMachine.Type.SecondaryStorageVm, VirtualMachine.Type.DomainRouter);
-
-        // Only return volumes that are not destroyed
-        sc.setParameters("state", Volume.State.Destroy);
-
-        Pair<List<VolumeVO>, Integer> volumes = _volumeDao.searchAndCount(sc, searchFilter);
-
-        return new Pair<List<? extends Volume>, Integer>(volumes.first(), volumes.second());
-    }
 
     @Override
     public String getSupportedImageFormatForCluster(Long clusterId) {

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/21c1623a/server/test/com/cloud/api/ListPerfTest.java
----------------------------------------------------------------------
diff --git a/server/test/com/cloud/api/ListPerfTest.java b/server/test/com/cloud/api/ListPerfTest.java
index 47abcae..9f1b4b5 100644
--- a/server/test/com/cloud/api/ListPerfTest.java
+++ b/server/test/com/cloud/api/ListPerfTest.java
@@ -106,4 +106,18 @@ public class ListPerfTest extends APITest {
         System.out.println("Time taken to list Hosts: " + (after - before) + " ms");
 
     }
+
+    @Test
+    public void testListVolumes(){
+        // issue list Hosts calls
+        HashMap<String, String> params = new HashMap<String, String>();
+        params.put("response", "json");
+        params.put("listAll", "true");
+        params.put("sessionkey", sessionKey);
+        long before = System.currentTimeMillis();
+        String result = this.sendRequest("listVolumes", params);
+        long after = System.currentTimeMillis();
+        System.out.println("Time taken to list Volumes: " + (after - before) + " ms");
+
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/21c1623a/setup/db/create-schema.sql
----------------------------------------------------------------------
diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql
index a95f190..4068cc6 100755
--- a/setup/db/create-schema.sql
+++ b/setup/db/create-schema.sql
@@ -3062,4 +3062,86 @@ left join op_host_capacity mem_caps on host.id = mem_caps.host_id and mem_caps.c
 left join op_host_capacity cpu_caps on host.id = cpu_caps.host_id and cpu_caps.capacity_type = 1
 left join async_job on async_job.instance_id = host.id and async_job.instance_type = "Host" and async_job.job_status = 0;
 
+DROP VIEW IF EXISTS `cloud`.`volume_view`;
+CREATE VIEW volume_view AS
+select 
+volumes.id,
+volumes.uuid,
+volumes.name,
+volumes.device_id,
+volumes.volume_type,
+volumes.size,
+volumes.created,
+volumes.state,
+volumes.attached,
+volumes.removed,
+volumes.pod_id,
+account.id account_id, 
+account.uuid account_uuid,
+account.account_name account_name,
+account.type account_type,
+domain.id domain_id, 
+domain.uuid domain_uuid,
+domain.name domain_name, 
+domain.path domain_path,
+projects.id project_id,
+projects.uuid project_uuid,
+projects.name project_name,
+data_center.id data_center_id, 
+data_center.uuid data_center_uuid,
+data_center.name data_center_name, 
+vm_instance.id vm_id,
+vm_instance.uuid vm_uuid,
+vm_instance.name vm_name,
+vm_instance.state vm_state,
+vm_instance.vm_type,
+user_vm.display_name vm_display_name,
+volume_host_ref.size volume_host_size,
+volume_host_ref.created volume_host_created,
+volume_host_ref.format,
+volume_host_ref.download_pct,
+volume_host_ref.download_state,
+volume_host_ref.error_str,
+disk_offering.id disk_offering_id,
+disk_offering.uuid disk_offering_uuid,
+disk_offering.name disk_offering_name,
+disk_offering.display_text disk_offering_display_text,
+disk_offering.use_local_storage,
+disk_offering.system_use,
+storage_pool.id pool_id,
+storage_pool.uuid pool_uuid,
+storage_pool.name pool_name,
+cluster.hypervisor_type,
+vm_template.id template_id,
+vm_template.uuid template_uuid,
+vm_template.extractable,
+vm_template.type template_type,
+resource_tags.id tag_id, 
+resource_tags.uuid tag_uuid,
+resource_tags.key tag_key,
+resource_tags.value tag_value,
+resource_tags.domain_id tag_domain_id, 
+resource_tags.account_id tag_account_id, 
+resource_tags.resource_id tag_resource_id, 
+resource_tags.resource_uuid tag_resource_uuid, 
+resource_tags.resource_type tag_resource_type, 
+resource_tags.customer tag_customer,
+async_job.id job_id,
+async_job.uuid job_uuid,
+async_job.job_status job_status,
+async_job.account_id job_account_id
+from volumes 
+inner join account on volumes.account_id=account.id 
+inner join domain on volumes.domain_id=domain.id
+left join projects on projects.project_account_id = account.id
+left join data_center on volumes.data_center_id = data_center.id
+left join vm_instance on volumes.instance_id = vm_instance.id
+left join user_vm on user_vm.id = vm_instance.id
+left join volume_host_ref on volumes.id = volume_host_ref.volume_id and volumes.data_center_id = volume_host_ref.zone_id
+left join disk_offering on volumes.disk_offering_id = disk_offering.id
+left join storage_pool on volumes.pool_id = storage_pool.id
+left join cluster on storage_pool.cluster_id = cluster.id
+left join vm_template on volumes.template_id = vm_template.id
+left join resource_tags on resource_tags.resource_id = volumes.id and resource_tags.resource_type = "Volume"
+left join async_job on async_job.instance_id = volumes.id and async_job.instance_type = "Volume" and async_job.job_status = 0;
 

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/21c1623a/setup/db/db/schema-40to410.sql
----------------------------------------------------------------------
diff --git a/setup/db/db/schema-40to410.sql b/setup/db/db/schema-40to410.sql
index 6c58bd9..04df535 100644
--- a/setup/db/db/schema-40to410.sql
+++ b/setup/db/db/schema-40to410.sql
@@ -637,3 +637,88 @@ left join host_tags on host_tags.host_id = host.id
 left join op_host_capacity mem_caps on host.id = mem_caps.host_id and mem_caps.capacity_type = 0
 left join op_host_capacity cpu_caps on host.id = cpu_caps.host_id and cpu_caps.capacity_type = 1
 left join async_job on async_job.instance_id = host.id and async_job.instance_type = "Host" and async_job.job_status = 0;
+
+DROP VIEW IF EXISTS `cloud`.`volume_view`;
+CREATE VIEW volume_view AS
+select 
+volumes.id,
+volumes.uuid,
+volumes.name,
+volumes.device_id,
+volumes.volume_type,
+volumes.size,
+volumes.created,
+volumes.state,
+volumes.attached,
+volumes.removed,
+volumes.pod_id,
+account.id account_id, 
+account.uuid account_uuid,
+account.account_name account_name,
+account.type account_type,
+domain.id domain_id, 
+domain.uuid domain_uuid,
+domain.name domain_name, 
+domain.path domain_path,
+projects.id project_id,
+projects.uuid project_uuid,
+projects.name project_name,
+data_center.id data_center_id, 
+data_center.uuid data_center_uuid,
+data_center.name data_center_name, 
+vm_instance.id vm_id,
+vm_instance.uuid vm_uuid,
+vm_instance.name vm_name,
+vm_instance.state vm_state,
+vm_instance.vm_type,
+user_vm.display_name vm_display_name,
+volume_host_ref.size volume_host_size,
+volume_host_ref.created volume_host_created,
+volume_host_ref.format,
+volume_host_ref.download_pct,
+volume_host_ref.download_state,
+volume_host_ref.error_str,
+disk_offering.id disk_offering_id,
+disk_offering.uuid disk_offering_uuid,
+disk_offering.name disk_offering_name,
+disk_offering.display_text disk_offering_display_text,
+disk_offering.use_local_storage,
+disk_offering.system_use,
+storage_pool.id pool_id,
+storage_pool.uuid pool_uuid,
+storage_pool.name pool_name,
+cluster.hypervisor_type,
+vm_template.id template_id,
+vm_template.uuid template_uuid,
+vm_template.extractable,
+vm_template.type template_type,
+resource_tags.id tag_id, 
+resource_tags.uuid tag_uuid,
+resource_tags.key tag_key,
+resource_tags.value tag_value,
+resource_tags.domain_id tag_domain_id, 
+resource_tags.account_id tag_account_id, 
+resource_tags.resource_id tag_resource_id, 
+resource_tags.resource_uuid tag_resource_uuid, 
+resource_tags.resource_type tag_resource_type, 
+resource_tags.customer tag_customer,
+async_job.id job_id,
+async_job.uuid job_uuid,
+async_job.job_status job_status,
+async_job.account_id job_account_id
+from volumes 
+inner join account on volumes.account_id=account.id 
+inner join domain on volumes.domain_id=domain.id
+left join projects on projects.project_account_id = account.id
+left join data_center on volumes.data_center_id = data_center.id
+left join vm_instance on volumes.instance_id = vm_instance.id
+left join user_vm on user_vm.id = vm_instance.id
+left join volume_host_ref on volumes.id = volume_host_ref.volume_id and volumes.data_center_id = volume_host_ref.zone_id
+left join disk_offering on volumes.disk_offering_id = disk_offering.id
+left join storage_pool on volumes.pool_id = storage_pool.id
+left join cluster on storage_pool.cluster_id = cluster.id
+left join vm_template on volumes.template_id = vm_template.id
+left join resource_tags on resource_tags.resource_id = volumes.id and resource_tags.resource_type = "Volume"
+left join async_job on async_job.instance_id = volumes.id and async_job.instance_type = "Volume" and async_job.job_status = 0;
+
+