You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by we...@apache.org on 2013/06/01 08:17:42 UTC

[1/4] git commit: updated refs/heads/disk_io_stat to 65a886d

Updated Branches:
  refs/heads/disk_io_stat 385ca81e0 -> 65a886de5
  refs/heads/master 8deeb90a6 -> 6dad8adf8


Revert "CLOUDSTACK-1192: add RBD support"

This reverts commit 385ca81e01754f0f65c18d32f0d35b41616498ac.


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

Branch: refs/heads/disk_io_stat
Commit: 65a886de5d45bb6df806ad59de618b586aaba4c5
Parents: 385ca81
Author: Wei Zhou <w....@leaseweb.com>
Authored: Fri May 31 11:18:48 2013 +0200
Committer: Wei Zhou <w....@leaseweb.com>
Committed: Fri May 31 11:18:48 2013 +0200

----------------------------------------------------------------------
 .../kvm/resource/LibvirtComputingResource.java     |   23 ++++++---------
 1 files changed, 9 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/65a886de/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
index d402c61..46fce24 100755
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
@@ -189,7 +189,6 @@ import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.ConsoleDef;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.CpuTuneDef;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DevicesDef;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef;
-import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef.deviceType;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.DiskDef.diskProtocol;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.FeaturesDef;
 import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.FilesystemDef;
@@ -4550,19 +4549,15 @@ ServerResource {
             List<DiskDef> disks = getDisks(conn, vmName);
 
             for (DiskDef disk : disks) {
-                if (disk.getDeviceType().equals(deviceType.DISK)) {
-                    DomainBlockStats blockStats = dm.blockStats(disk.getDiskLabel());
-                    String path = disk.getDiskPath();
-                    String diskPath = null;
-                    if (path != null) {
-                        if (path.startsWith("rbd:"))    // path = rbd:/mnt/pool_uuid/disk_path/
-                            path = path.replace("rbd:", "");
-                        String[] token = path.split("/"); // path = /mnt/pool_uuid/disk_path/
-                        if (token.length > 3) {
-                            diskPath = token[3];
-                            VmDiskStatsEntry stat = new VmDiskStatsEntry(vmName, diskPath, blockStats.wr_req, blockStats.rd_req, blockStats.wr_bytes, blockStats.rd_bytes);
-                            stats.add(stat);
-                        }
+                DomainBlockStats blockStats = dm.blockStats(disk.getDiskLabel());
+                String path = disk.getDiskPath(); // for example, path = /mnt/pool_uuid/disk_path/
+                String diskPath = null;
+                if (path != null) {
+                    String[] token = path.split("/");
+                    if (token.length > 3) {
+                        diskPath = token[3];
+                        VmDiskStatsEntry stat = new VmDiskStatsEntry(vmName, diskPath, blockStats.wr_req, blockStats.rd_req, blockStats.wr_bytes, blockStats.rd_bytes);
+                        stats.add(stat);
                     }
                 }
             }


[4/4] git commit: updated refs/heads/master to 6dad8ad

Posted by we...@apache.org.
CLOUDSTACK-1301: Add VM Disk I/O Throttling


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

Branch: refs/heads/master
Commit: 6dad8adf8fc22417f0242126b8f52ee081f74f49
Parents: b9b0168
Author: Wei Zhou <w....@leaseweb.com>
Authored: Sat Jun 1 08:03:02 2013 +0200
Committer: Wei Zhou <w....@leaseweb.com>
Committed: Sat Jun 1 08:03:02 2013 +0200

----------------------------------------------------------------------
 api/src/com/cloud/agent/api/to/VolumeTO.java       |   18 +++
 api/src/com/cloud/offering/DiskOffering.java       |    8 ++
 api/src/com/cloud/vm/DiskProfile.java              |   18 +++
 .../admin/offering/CreateDiskOfferingCmd.java      |   16 +++-
 .../admin/offering/CreateServiceOfferingCmd.java   |   14 +++
 .../api/response/DiskOfferingResponse.java         |   14 +++
 .../api/response/ServiceOfferingResponse.java      |   14 +++
 .../cloudstack/api/response/VolumeResponse.java    |   14 +++
 .../WEB-INF/classes/resources/messages.properties  |    2 +
 .../com/cloud/agent/api/AttachVolumeCommand.java   |   18 +++
 .../src/com/cloud/storage/DiskOfferingVO.java      |   24 ++++
 .../kvm/resource/LibvirtComputingResource.java     |   15 +++-
 .../kvm/resource/LibvirtDomainXMLParser.java       |   15 +++
 .../hypervisor/kvm/resource/LibvirtVMDef.java      |   34 ++++++
 .../api/query/dao/DiskOfferingJoinDaoImpl.java     |    2 +
 .../api/query/dao/ServiceOfferingJoinDaoImpl.java  |    2 +
 .../com/cloud/api/query/dao/VolumeJoinDaoImpl.java |    2 +
 .../com/cloud/api/query/vo/DiskOfferingJoinVO.java |   21 ++++
 .../cloud/api/query/vo/ServiceOfferingJoinVO.java  |   20 ++++
 .../src/com/cloud/api/query/vo/VolumeJoinVO.java   |   22 ++++
 server/src/com/cloud/configuration/Config.java     |    2 +
 .../cloud/configuration/ConfigurationManager.java  |    4 +-
 .../configuration/ConfigurationManagerImpl.java    |   20 +++-
 server/src/com/cloud/storage/StorageManager.java   |    6 +
 .../src/com/cloud/storage/StorageManagerImpl.java  |   34 ++++++
 .../src/com/cloud/storage/VolumeManagerImpl.java   |   24 ++++-
 server/src/com/cloud/test/DatabaseConfig.java      |    6 +
 .../com/cloud/vm/VirtualMachineManagerImpl.java    |    2 +-
 .../cloud/vpc/MockConfigurationManagerImpl.java    |    4 +-
 setup/db/db/schema-410to420.sql                    |   15 +++-
 ui/dictionary.jsp                                  |    2 +
 ui/scripts/configuration.js                        |   82 +++++++++++++++
 ui/scripts/instances.js                            |    4 +-
 33 files changed, 481 insertions(+), 17 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/api/src/com/cloud/agent/api/to/VolumeTO.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/agent/api/to/VolumeTO.java b/api/src/com/cloud/agent/api/to/VolumeTO.java
index 4cbe82b..8b3a6be 100644
--- a/api/src/com/cloud/agent/api/to/VolumeTO.java
+++ b/api/src/com/cloud/agent/api/to/VolumeTO.java
@@ -37,6 +37,8 @@ public class VolumeTO implements InternalIdentity {
     private long deviceId;
     private String chainInfo;
     private String guestOsType;
+    private long bytesRate;
+    private long iopsRate;
 
     public VolumeTO(long id, Volume.Type type, StoragePoolType poolType, String poolUuid, String name, String mountPoint, String path, long size, String chainInfo) {
         this.id = id;
@@ -133,4 +135,20 @@ public class VolumeTO implements InternalIdentity {
     public String toString() {
         return new StringBuilder("Vol[").append(id).append("|").append(type).append("|").append(path).append("|").append(size).append("]").toString();
     }
+    
+    public void setBytesRate(long bytesRate) {
+        this.bytesRate = bytesRate;
+    }
+    
+    public long getBytesRate() {
+        return bytesRate;
+    }
+    
+    public void setIopsRate(long iopsRate) {
+        this.iopsRate = iopsRate;
+    }
+    
+    public long getIopsRate() {
+        return iopsRate;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/api/src/com/cloud/offering/DiskOffering.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/offering/DiskOffering.java b/api/src/com/cloud/offering/DiskOffering.java
index dd77c70..1618db9 100644
--- a/api/src/com/cloud/offering/DiskOffering.java
+++ b/api/src/com/cloud/offering/DiskOffering.java
@@ -52,4 +52,12 @@ public interface DiskOffering extends InfrastructureEntity, Identity, InternalId
     boolean isCustomized();
 
     void setDiskSize(long diskSize);
+    
+    void setBytesRate(long bytesRate);
+    
+    long getBytesRate();
+    
+    void setIopsRate(long iopsRate);
+    
+    long getIopsRate();
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/api/src/com/cloud/vm/DiskProfile.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/vm/DiskProfile.java b/api/src/com/cloud/vm/DiskProfile.java
index e3a3386..2c77006 100644
--- a/api/src/com/cloud/vm/DiskProfile.java
+++ b/api/src/com/cloud/vm/DiskProfile.java
@@ -35,6 +35,8 @@ public class DiskProfile {
     private Long templateId;
     private long volumeId;
     private String path;
+    private long bytesRate;
+    private long iopsRate;
 
     private HypervisorType hyperType;
 
@@ -154,4 +156,20 @@ public class DiskProfile {
     public void setSize(long size) {
     	this.size = size;
     }
+    
+    public void setBytesRate(long bytesRate) {
+        this.bytesRate = bytesRate;
+    }
+    
+    public long getBytesRate() {
+        return bytesRate;
+    }
+    
+    public void setIopsRate(long iopsRate) {
+        this.iopsRate = iopsRate;
+    }
+    
+    public long getIopsRate() {
+        return iopsRate;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/api/src/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java b/api/src/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java
index aa11599..848d158 100644
--- a/api/src/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java
+++ b/api/src/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java
@@ -61,6 +61,12 @@ public class CreateDiskOfferingCmd extends BaseCmd {
 
     @Parameter(name=ApiConstants.STORAGE_TYPE, type=CommandType.STRING, description="the storage type of the disk offering. Values are local and shared.")
     private String storageType = ServiceOffering.StorageType.shared.toString();
+    
+    @Parameter(name="bytesRate", type=CommandType.LONG, required=false, description="bytes rate of the disk offering")
+    private Long bytesRate;
+
+    @Parameter(name="iopsRate", type=CommandType.LONG, required=false, description="io requests rate of the disk offering")
+    private Long iopsRate;
 
     @Parameter(name=ApiConstants.DISPLAY_OFFERING, type=CommandType.BOOLEAN, description="an optional field, whether to display the offering to the end user or not.")
     private Boolean displayOffering;
@@ -92,6 +98,14 @@ public class CreateDiskOfferingCmd extends BaseCmd {
     public Long getDomainId(){
         return domainId;
     }
+    
+    public Long getBytesRate() {
+        return bytesRate;
+    }
+    
+    public Long getIopsRate() {
+        return iopsRate;
+    }
 
     public String getStorageType() {
         return storageType;
@@ -114,7 +128,7 @@ public class CreateDiskOfferingCmd extends BaseCmd {
     public long getEntityOwnerId() {
         return Account.ACCOUNT_ID_SYSTEM;
     }
-
+    
     @Override
     public void execute(){
         DiskOffering offering = _configService.createDiskOffering(this);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java b/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java
index 4c54a4e..28f1a68 100644
--- a/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java
+++ b/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java
@@ -93,6 +93,12 @@ public class CreateServiceOfferingCmd extends BaseCmd {
     @Parameter(name = ApiConstants.SERVICE_OFFERING_DETAILS, type = CommandType.MAP, description = "details for planner, used to store specific parameters")
     private Map<String, String> details;
 
+    @Parameter(name="bytesRate", type=CommandType.LONG, required=false, description="bytes rate of the disk offering")
+    private Long bytesRate;
+
+    @Parameter(name="iopsRate", type=CommandType.LONG, required=false, description="io requests rate of the disk offering")
+    private Long iopsRate;
+
     /////////////////////////////////////////////////////
     /////////////////// Accessors ///////////////////////
     /////////////////////////////////////////////////////
@@ -171,6 +177,14 @@ public class CreateServiceOfferingCmd extends BaseCmd {
         return params;
     }
 
+    public long getBytesRate() {
+       return bytesRate;
+    }
+
+    public long getIopsRate() {
+        return iopsRate;
+    }
+
     /////////////////////////////////////////////////////
     /////////////// API Implementation///////////////////
     /////////////////////////////////////////////////////

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/api/src/org/apache/cloudstack/api/response/DiskOfferingResponse.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/response/DiskOfferingResponse.java b/api/src/org/apache/cloudstack/api/response/DiskOfferingResponse.java
index 377e66e..8d4bec3 100644
--- a/api/src/org/apache/cloudstack/api/response/DiskOfferingResponse.java
+++ b/api/src/org/apache/cloudstack/api/response/DiskOfferingResponse.java
@@ -57,6 +57,12 @@ public class DiskOfferingResponse extends BaseResponse {
 
     @SerializedName("storagetype") @Param(description="the storage type for this disk offering")
     private String storageType;
+    
+    @SerializedName("diskBytesRate") @Param(description="bytes rate of the disk offering")
+    private Long bytesRate;
+
+    @SerializedName("diskIORate") @Param(description="io requests rate of the disk offering")
+    private Long iopsRate;
 
     @SerializedName("displayoffering") @Param(description="whether to display the offering to the end user or not.")
     private Boolean displayOffering;
@@ -149,4 +155,12 @@ public class DiskOfferingResponse extends BaseResponse {
     public void setStorageType(String storageType) {
         this.storageType = storageType;
     }
+    
+    public void setBytesRate(long bytesRate) {
+        this.bytesRate = bytesRate;
+    }
+    
+    public void setIopsRate(long iopsRate) {
+        this.iopsRate = iopsRate;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/api/src/org/apache/cloudstack/api/response/ServiceOfferingResponse.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/response/ServiceOfferingResponse.java b/api/src/org/apache/cloudstack/api/response/ServiceOfferingResponse.java
index 31533f8..b98fd4d 100644
--- a/api/src/org/apache/cloudstack/api/response/ServiceOfferingResponse.java
+++ b/api/src/org/apache/cloudstack/api/response/ServiceOfferingResponse.java
@@ -86,6 +86,12 @@ public class ServiceOfferingResponse extends BaseResponse {
 
     @SerializedName(ApiConstants.NETWORKRATE) @Param(description="data transfer rate in megabits per second allowed.")
     private Integer networkRate;
+    
+    @SerializedName("diskBytesRate") @Param(description="bytes rate of the service offering")
+    private Long bytesRate;
+
+    @SerializedName("diskIORate") @Param(description="io requests rate of the service offering")
+    private Long iopsRate;
 
     @SerializedName(ApiConstants.DEPLOYMENT_PLANNER) @Param(description="deployment strategy used to deploy VM.")
     private String deploymentPlanner;
@@ -248,4 +254,12 @@ public class ServiceOfferingResponse extends BaseResponse {
     public void setVolatileVm(boolean isVolatile) {
         this.isVolatile = isVolatile;
     }
+    
+    public void setBytesRate(long bytesRate) {
+        this.bytesRate = bytesRate;
+    }
+    
+    public void setIopsRate(long iopsRate) {
+        this.iopsRate = iopsRate;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/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 21d7d1a..8e38d85 100644
--- a/api/src/org/apache/cloudstack/api/response/VolumeResponse.java
+++ b/api/src/org/apache/cloudstack/api/response/VolumeResponse.java
@@ -110,6 +110,12 @@ public class VolumeResponse extends BaseResponse implements ControlledViewEntity
     @Param(description = "shared or local storage")
     private String storageType;
 
+    @SerializedName("diskBytesRate") @Param(description="bytes rate of the disk offering")
+    private Long bytesRate;
+
+    @SerializedName("diskIORate") @Param(description="io requests rate of the disk offering")
+    private Long iopsRate;
+
     @SerializedName(ApiConstants.HYPERVISOR)
     @Param(description = "Hypervisor the volume belongs to")
     private String hypervisor;
@@ -258,6 +264,14 @@ public class VolumeResponse extends BaseResponse implements ControlledViewEntity
         this.storageType = storageType;
     }
 
+    public void setBytesRate(long bytesRate) {
+        this.bytesRate = bytesRate;
+    }
+
+    public void setIopsRate(long iopsRate) {
+        this.iopsRate = iopsRate;
+    }
+
     public void setHypervisor(String hypervisor) {
         this.hypervisor = hypervisor;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/client/WEB-INF/classes/resources/messages.properties
----------------------------------------------------------------------
diff --git a/client/WEB-INF/classes/resources/messages.properties b/client/WEB-INF/classes/resources/messages.properties
index ce20fa4..fc7715d 100644
--- a/client/WEB-INF/classes/resources/messages.properties
+++ b/client/WEB-INF/classes/resources/messages.properties
@@ -463,6 +463,8 @@ label.disable.vpn=Disable VPN
 label.disabled=Disabled
 label.disabling.vpn.access=Disabling VPN Access
 label.disk.allocated=Disk Allocated
+label.disk.bytes.rate=Disk Rate (BPS)
+label.disk.iops.rate=Disk Rate (IOPS)
 label.disk.offering=Disk Offering
 label.disk.read.bytes=Disk Read (Bytes)
 label.disk.read.io=Disk Read (IO)

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/core/src/com/cloud/agent/api/AttachVolumeCommand.java
----------------------------------------------------------------------
diff --git a/core/src/com/cloud/agent/api/AttachVolumeCommand.java b/core/src/com/cloud/agent/api/AttachVolumeCommand.java
index 302b8f8..101c254 100644
--- a/core/src/com/cloud/agent/api/AttachVolumeCommand.java
+++ b/core/src/com/cloud/agent/api/AttachVolumeCommand.java
@@ -29,6 +29,8 @@ public class AttachVolumeCommand extends Command {
 	String volumeName;
 	Long deviceId;
 	String chainInfo;
+    long bytesRate;
+    long iopsRate;
 
 	protected AttachVolumeCommand() {
 	}
@@ -96,4 +98,20 @@ public class AttachVolumeCommand extends Command {
     public String getChainInfo() {
     	return chainInfo;
     }
+    
+    public void setBytesRate(long bytesRate) {
+        this.bytesRate = bytesRate;
+    }
+    
+    public long getBytesRate() {
+        return bytesRate;
+    }
+    
+    public void setIopsRate(long iopsRate) {
+        this.iopsRate = iopsRate;
+    }
+    
+    public long getIopsRate() {
+        return iopsRate;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/engine/schema/src/com/cloud/storage/DiskOfferingVO.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/storage/DiskOfferingVO.java b/engine/schema/src/com/cloud/storage/DiskOfferingVO.java
index 909d7fe..239ddb0 100755
--- a/engine/schema/src/com/cloud/storage/DiskOfferingVO.java
+++ b/engine/schema/src/com/cloud/storage/DiskOfferingVO.java
@@ -99,6 +99,13 @@ public class DiskOfferingVO implements DiskOffering {
 
     @Column(name="sort_key")
     int sortKey;
+    
+    @Column(name="bytes_rate")
+    long bytesRate;
+
+    @Column(name="iops_rate")
+    long iopsRate;
+
 
     @Column(name="display_offering")
     boolean displayOffering;
@@ -327,4 +334,21 @@ public class DiskOfferingVO implements DiskOffering {
     public void setDisplayOffering(boolean displayOffering) {
         this.displayOffering = displayOffering;
     }
+
+    public void setBytesRate(long bytesRate) {
+       this.bytesRate = bytesRate;
+    }
+
+    public long getBytesRate() {
+       return bytesRate;
+    }
+
+    public void setIopsRate(long iopsRate) {
+        this.iopsRate = iopsRate;
+    }
+
+    public long getIopsRate() {
+        return iopsRate;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
index 46fce24..9b26ef2 100755
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
@@ -1393,6 +1393,8 @@ ServerResource {
             VolumeTO volume = new VolumeTO(cmd.getVolumeId(), dskch.getType(),
                     pool.getType(), pool.getUuid(), pool.getPath(),
                     vol.getName(), vol.getName(), disksize, null);
+            volume.setBytesRate(dskch.getBytesRate());
+            volume.setIopsRate(dskch.getIopsRate());
             return new CreateAnswer(cmd, volume);
         } catch (CloudRuntimeException e) {
             s_logger.debug("Failed to create volume: " + e.toString());
@@ -2608,7 +2610,7 @@ ServerResource {
                     cmd.getPoolUuid());
             KVMPhysicalDisk disk = primary.getPhysicalDisk(cmd.getVolumePath());
             attachOrDetachDisk(conn, cmd.getAttach(), cmd.getVmName(), disk,
-                    cmd.getDeviceId().intValue());
+                    cmd.getDeviceId().intValue(), cmd.getBytesRate(), cmd.getIopsRate());
         } catch (LibvirtException e) {
             return new AttachVolumeAnswer(cmd, e.toString());
         } catch (InternalErrorException e) {
@@ -3476,6 +3478,11 @@ ServerResource {
                 }
 
             }
+            
+            if (volume.getBytesRate() > 0)
+                disk.setBytesRate(volume.getBytesRate());
+            if (volume.getIopsRate() > 0)
+                disk.setIopsRate(volume.getIopsRate());
 
             vm.getDevices().addDevice(disk);
         }
@@ -3610,7 +3617,7 @@ ServerResource {
 
     protected synchronized String attachOrDetachDisk(Connect conn,
             boolean attach, String vmName, KVMPhysicalDisk attachingDisk,
-            int devId) throws LibvirtException, InternalErrorException {
+            int devId, long bytesRate, long iopsRate) throws LibvirtException, InternalErrorException {
         List<DiskDef> disks = null;
         Domain dm = null;
         DiskDef diskdef = null;
@@ -3650,6 +3657,10 @@ ServerResource {
                     diskdef.defBlockBasedDisk(attachingDisk.getPath(), devId,
                             DiskDef.diskBus.VIRTIO);
                 }
+                if (bytesRate > 0)
+                    diskdef.setBytesRate(bytesRate);
+                if (iopsRate > 0)
+                    diskdef.setIopsRate(iopsRate);
             }
 
             String xml = diskdef.toString();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java
index b8645e1..cccdc39 100644
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java
@@ -105,6 +105,21 @@ public class LibvirtDomainXMLParser {
                                 DiskDef.diskBus.valueOf(bus.toUpperCase()));
                     }
                 }
+                
+                NodeList iotune = disk.getElementsByTagName("iotune");
+                if ((iotune != null) && (iotune.getLength() !=0)) {
+                    String bytesRateStr = getTagValue("total_bytes_sec", (Element)iotune.item(0));
+                    if (bytesRateStr != null) {
+                        Long bytesRate = Long.parseLong(bytesRateStr);
+                        def.setBytesRate(bytesRate);
+                    }
+                    String iopsRateStr = getTagValue("total_iops_sec", (Element)iotune.item(0));
+                    if (iopsRateStr != null) {
+                        Long iopsRate = Long.parseLong(iopsRateStr);
+                        def.setIopsRate(iopsRate);
+                    }
+                }
+
                 diskDefs.add(def);
             }
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
index 9cddb2e..2959821 100644
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
@@ -22,6 +22,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
+import com.cloud.utils.script.Script;
+
 public class LibvirtVMDef {
     private String _hvsType;
     private String _domName;
@@ -439,6 +441,8 @@ public class LibvirtVMDef {
         private boolean _readonly = false;
         private boolean _shareable = false;
         private boolean _deferAttach = false;
+        private Long _bytesRate;
+        private Long _iopsRate;
 
         public void setDeviceType(deviceType deviceType) {
             _deviceType = deviceType;
@@ -583,6 +587,14 @@ public class LibvirtVMDef {
             char suffix = this._diskLabel.charAt(this._diskLabel.length() - 1);
             return suffix - 'a';
         }
+        
+        public void setBytesRate(Long bytesRate) {
+            _bytesRate = bytesRate;
+        }
+        
+        public void setIopsRate(Long iopsRate) {
+            _iopsRate = iopsRate;
+        }
 
         @Override
         public String toString() {
@@ -627,6 +639,28 @@ public class LibvirtVMDef {
                 diskBuilder.append(" bus='" + _bus + "'");
             }
             diskBuilder.append("/>\n");
+            
+            String libvirtVersion = Script.runSimpleBashScript("virsh version |grep API | awk '{print $4}'");
+            String qemuVersion = Script.runSimpleBashScript("virsh version |grep hypervisor | awk '{print $4}'");
+            if ((_deviceType != deviceType.CDROM) && (libvirtVersion != null) && (qemuVersion != null) 
+                    && (((_bytesRate != null) && (_bytesRate > 0)) || ((_iopsRate != null) && (_iopsRate > 0)))) { // not CDROM, from libvirt 0.9.8 and QEMU 1.1.0
+                String[] libvirtVersions = libvirtVersion.split("\\.");
+                String[] qemuVersions = qemuVersion.split("\\.");
+                if (((libvirtVersions != null) && (libvirtVersions.length == 3) && ((Integer.valueOf(libvirtVersions[0]) > 0)
+                        || ((Integer.valueOf(libvirtVersions[0]) == 0) && (Integer.valueOf(libvirtVersions[1]) > 9))
+                        || ((Integer.valueOf(libvirtVersions[0]) == 0) && (Integer.valueOf(libvirtVersions[1]) == 9) && (Integer.valueOf(libvirtVersions[1]) >= 8))))
+                        && ((qemuVersions != null) && (qemuVersions.length == 3) && ((Integer.valueOf(qemuVersions[0]) > 1)
+                        || ((Integer.valueOf(qemuVersions[0]) == 1) && (Integer.valueOf(qemuVersions[1]) > 1))
+                        || ((Integer.valueOf(qemuVersions[0]) == 1) && (Integer.valueOf(qemuVersions[1]) == 1) && (Integer.valueOf(qemuVersions[1]) >= 0))))) {
+                    diskBuilder.append("<iotune>\n");
+                    if ((_bytesRate != null) && (_bytesRate > 0))
+                        diskBuilder.append("<total_bytes_sec>" + _bytesRate + "</total_bytes_sec>\n");
+                    if ((_iopsRate != null) && (_iopsRate > 0))
+                        diskBuilder.append("<total_iops_sec>" + _iopsRate + "</total_iops_sec>\n");
+                    diskBuilder.append("</iotune>\n");
+                }
+            }
+            
             diskBuilder.append("</disk>\n");
             return diskBuilder.toString();
         }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/server/src/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java
index 283181f..bfae58b 100644
--- a/server/src/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java
+++ b/server/src/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java
@@ -76,6 +76,8 @@ public class DiskOfferingJoinDaoImpl extends GenericDaoBase<DiskOfferingJoinVO,
         diskOfferingResponse.setCustomized(offering.isCustomized());
         diskOfferingResponse.setStorageType(offering.isUseLocalStorage() ? ServiceOffering.StorageType.local.toString() : ServiceOffering.StorageType.shared.toString());
         diskOfferingResponse.setObjectName("diskoffering");
+        diskOfferingResponse.setBytesRate(offering.getBytesRate());
+        diskOfferingResponse.setIopsRate(offering.getIopsRate());
         return diskOfferingResponse;
     }
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/server/src/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java
index 56e4d0a..ec67175 100644
--- a/server/src/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java
+++ b/server/src/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java
@@ -76,6 +76,8 @@ public class ServiceOfferingJoinDaoImpl extends GenericDaoBase<ServiceOfferingJo
         offeringResponse.setHostTag(offering.getHostTag());
         offeringResponse.setDeploymentPlanner(offering.getDeploymentPlanner());
         offeringResponse.setObjectName("serviceoffering");
+        offeringResponse.setBytesRate(offering.getBytesRate());
+        offeringResponse.setIopsRate(offering.getIopsRate());
 
         return offeringResponse;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/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
index e27e2d9..e65f78a 100644
--- a/server/src/com/cloud/api/query/dao/VolumeJoinDaoImpl.java
+++ b/server/src/com/cloud/api/query/dao/VolumeJoinDaoImpl.java
@@ -160,6 +160,8 @@ public class VolumeJoinDaoImpl extends GenericDaoBase<VolumeJoinVO, Long> implem
             }
             volResponse.setStorageType(volume.isUseLocalStorage() ? ServiceOffering.StorageType.local.toString() : ServiceOffering.StorageType.shared
                     .toString());
+            volResponse.setBytesRate(volume.getBytesRate());
+            volResponse.setIopsRate(volume.getIopsRate());
         }
         Long poolId = volume.getPoolId();
         String poolName = (poolId == null) ? "none" : volume.getPoolName();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/server/src/com/cloud/api/query/vo/DiskOfferingJoinVO.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/vo/DiskOfferingJoinVO.java b/server/src/com/cloud/api/query/vo/DiskOfferingJoinVO.java
index 6d3cdcb..c44c171 100644
--- a/server/src/com/cloud/api/query/vo/DiskOfferingJoinVO.java
+++ b/server/src/com/cloud/api/query/vo/DiskOfferingJoinVO.java
@@ -64,6 +64,12 @@ public class DiskOfferingJoinVO extends BaseViewVO implements InternalIdentity,
     @Column(name="sort_key")
     int sortKey;
 
+    @Column(name="bytes_rate")
+    long bytesRate;
+
+    @Column(name="iops_rate")
+    long iopsRate;
+
     @Column(name="type")
     Type type;
 
@@ -239,6 +245,21 @@ public class DiskOfferingJoinVO extends BaseViewVO implements InternalIdentity,
         this.type = type;
     }
 
+    public void setBytesRate(long bytesRate) {
+       this.bytesRate = bytesRate;
+    }
+
+    public long getBytesRate() {
+       return bytesRate;
+    }
+
+    public void setIopsRate(long iopsRate) {
+        this.iopsRate = iopsRate;
+    }
+
+    public long getIopsRate() {
+        return iopsRate;
+    }
 
 
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/server/src/com/cloud/api/query/vo/ServiceOfferingJoinVO.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/vo/ServiceOfferingJoinVO.java b/server/src/com/cloud/api/query/vo/ServiceOfferingJoinVO.java
index e87a101..89c665e 100644
--- a/server/src/com/cloud/api/query/vo/ServiceOfferingJoinVO.java
+++ b/server/src/com/cloud/api/query/vo/ServiceOfferingJoinVO.java
@@ -90,6 +90,11 @@ public class ServiceOfferingJoinVO extends BaseViewVO implements InternalIdentit
     @Column(name="sort_key")
     int sortKey;
 
+    @Column(name="bytes_rate")
+    long bytesRate;
+
+    @Column(name="iops_rate")
+    long iopsRate;
 
     @Column(name=GenericDao.CREATED_COLUMN)
     private Date created;
@@ -329,5 +334,20 @@ public class ServiceOfferingJoinVO extends BaseViewVO implements InternalIdentit
         this.volatileVm = volatileVm;
     }
 
+    public void setBytesRate(long bytesRate) {
+        this.bytesRate = bytesRate;
+    }
+
+    public long getBytesRate() {
+        return bytesRate;
+    }
+
+    public void setIopsRate(long iopsRate) {
+        this.iopsRate = iopsRate;
+    }
+
+    public long getIopsRate() {
+        return iopsRate;
+    }
 
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/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
index 6ef8c91..5831659 100644
--- a/server/src/com/cloud/api/query/vo/VolumeJoinVO.java
+++ b/server/src/com/cloud/api/query/vo/VolumeJoinVO.java
@@ -184,6 +184,12 @@ public class VolumeJoinVO extends BaseViewVO implements ControlledViewEntity {
     @Column(name="use_local_storage")
     private boolean useLocalStorage;
 
+    @Column(name="bytes_rate")
+    long bytesRate;
+
+    @Column(name="iops_rate")
+    long iopsRate;
+
     @Column(name="pool_id")
     private long poolId;
 
@@ -742,6 +748,22 @@ public class VolumeJoinVO extends BaseViewVO implements ControlledViewEntity {
     }
 
 
+    public void setBytesRate(long bytesRate) {
+       this.bytesRate = bytesRate;
+    }
+
+    public long getBytesRate() {
+       return bytesRate;
+    }
+
+    public void setIopsRate(long iopsRate) {
+        this.iopsRate = iopsRate;
+    }
+
+    public long getIopsRate() {
+        return iopsRate;
+    }
+
 
     public long getPoolId() {
         return poolId;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/server/src/com/cloud/configuration/Config.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java
index 5ee0fad..d822ae2 100755
--- a/server/src/com/cloud/configuration/Config.java
+++ b/server/src/com/cloud/configuration/Config.java
@@ -232,6 +232,8 @@ public enum Config {
 	VmDiskStatsInterval("Advanced", ManagementServer.class, Integer.class, "vm.disk.stats.interval", "0", "Interval (in seconds) to report vm disk statistics.", null),
 	VmTransitionWaitInterval("Advanced", ManagementServer.class, Integer.class, "vm.tranisition.wait.interval", "3600", "Time (in seconds) to wait before taking over a VM in transition state", null),
 	VmDestroyForcestop("Advanced", ManagementServer.class, Boolean.class, "vm.destroy.forcestop", "false", "On destroy, force-stop takes this value ", null),
+        VmDiskThrottlingIORate("Advanced", ManagementServer.class, Integer.class, "vm.disk.throttling.iops_rate", "0", "Default disk I/O rate in requests per second allowed in User vm's disk.", null),
+        VmDiskThrottlingBytesRate("Advanced", ManagementServer.class, Integer.class, "vm.disk.throttling.bytes_rate", "0", "Default disk I/O rate in bytes per second allowed in User vm's disk.", null),
 
 	ControlCidr("Advanced", ManagementServer.class, String.class, "control.cidr", "169.254.0.0/16", "Changes the cidr for the control network traffic.  Defaults to using link local.  Must be unique within pods", null),
 	ControlGateway("Advanced", ManagementServer.class, String.class, "control.gateway", "169.254.0.1", "gateway for the control network traffic", null),

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/server/src/com/cloud/configuration/ConfigurationManager.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/configuration/ConfigurationManager.java b/server/src/com/cloud/configuration/ConfigurationManager.java
index 8db037b..975fad1 100755
--- a/server/src/com/cloud/configuration/ConfigurationManager.java
+++ b/server/src/com/cloud/configuration/ConfigurationManager.java
@@ -84,7 +84,7 @@ public interface ConfigurationManager extends ConfigurationService, Manager {
      * @return ID
      */
     ServiceOfferingVO createServiceOffering(long userId, boolean isSystem, VirtualMachine.Type vm_typeType, String name, int cpu, int ramSize, int speed, String displayText, boolean localStorageRequired,
-            boolean offerHA, boolean limitResourceUse, boolean volatileVm, String tags, Long domainId, String hostTag, Integer networkRate, String deploymentPlanner, Map<String, String> details);
+            boolean offerHA, boolean limitResourceUse, boolean volatileVm, String tags, Long domainId, String hostTag, Integer networkRate, String deploymentPlanner, Map<String, String> details, Long bytesRate, Long iopsRate);
 
     /**
      * Creates a new disk offering
@@ -99,7 +99,7 @@ public interface ConfigurationManager extends ConfigurationService, Manager {
      * @param isDisplayOfferingEnabled
      * @return newly created disk offering
      */
-    DiskOfferingVO createDiskOffering(Long domainId, String name, String description, Long numGibibytes, String tags, boolean isCustomized, boolean localStorageRequired, boolean isDisplayOfferingEnabled);
+    DiskOfferingVO createDiskOffering(Long domainId, String name, String description, Long numGibibytes, String tags, boolean isCustomized, boolean localStorageRequired, boolean isDisplayOfferingEnabled, Long bytesRate, Long iopsRate);
 
     /**
      * Creates a new pod

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
index 59e70cf..0bfd286 100755
--- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java
@@ -2031,7 +2031,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
         return createServiceOffering(userId, cmd.getIsSystem(), vmType, cmd.getServiceOfferingName(),
                 cpuNumber.intValue(), memory.intValue(), cpuSpeed.intValue(), cmd.getDisplayText(),
                 localStorageRequired, offerHA, limitCpuUse, volatileVm, cmd.getTags(), cmd.getDomainId(),
-                cmd.getHostTag(), cmd.getNetworkRate(), cmd.getDeploymentPlanner(), cmd.getDetails());
+                cmd.getHostTag(), cmd.getNetworkRate(), cmd.getDeploymentPlanner(), cmd.getDetails(),
+                cmd.getBytesRate(), cmd.getIopsRate());
     }
 
     @Override
@@ -2039,11 +2040,16 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
     public ServiceOfferingVO createServiceOffering(long userId, boolean isSystem, VirtualMachine.Type vm_type,
             String name, int cpu, int ramSize, int speed, String displayText, boolean localStorageRequired,
             boolean offerHA, boolean limitResourceUse, boolean volatileVm,  String tags, Long domainId, String hostTag,
-            Integer networkRate, String deploymentPlanner, Map<String, String> details) {
+            Integer networkRate, String deploymentPlanner, Map<String, String> details, Long bytesRate, Long iopsRate) {
         tags = cleanupTags(tags);
         ServiceOfferingVO offering = new ServiceOfferingVO(name, cpu, ramSize, speed, networkRate, null, offerHA, limitResourceUse, volatileVm, displayText, localStorageRequired, false, tags, isSystem, vm_type,
                 domainId, hostTag, deploymentPlanner);
 
+        if ((bytesRate != null) && (bytesRate > 0))
+            offering.setBytesRate(bytesRate);
+        if ((iopsRate != null) && (iopsRate > 0))
+            offering.setIopsRate(iopsRate);
+
         if ((offering = _serviceOfferingDao.persist(offering)) != null) {
             if (details != null) {
                 _serviceOfferingDetailsDao.persist(offering.getId(), details);
@@ -2129,7 +2135,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
 
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_DISK_OFFERING_CREATE, eventDescription = "creating disk offering")
-    public DiskOfferingVO createDiskOffering(Long domainId, String name, String description, Long numGibibytes, String tags, boolean isCustomized, boolean localStorageRequired, boolean isDisplayOfferingEnabled) {
+    public DiskOfferingVO createDiskOffering(Long domainId, String name, String description, Long numGibibytes, String tags, boolean isCustomized, boolean localStorageRequired, boolean isDisplayOfferingEnabled, Long bytesRate, Long iopsRate) {
         long diskSize = 0;// special case for custom disk offerings
         if (numGibibytes != null && (numGibibytes <= 0)) {
             throw new InvalidParameterValueException("Please specify a disk size of at least 1 Gb.");
@@ -2149,6 +2155,10 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
         DiskOfferingVO newDiskOffering = new DiskOfferingVO(domainId, name, description, diskSize, tags, isCustomized);
         newDiskOffering.setUseLocalStorage(localStorageRequired);
         newDiskOffering.setDisplayOffering(isDisplayOfferingEnabled);
+        if (bytesRate != null && (bytesRate > 0))
+            newDiskOffering.setBytesRate(bytesRate);
+        if (iopsRate != null && (iopsRate > 0))
+            newDiskOffering.setIopsRate(iopsRate);
         UserContext.current().setEventDetails("Disk offering id=" + newDiskOffering.getId());
         DiskOfferingVO offering = _diskOfferingDao.persist(newDiskOffering);
         if (offering != null) {
@@ -2189,7 +2199,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
             }
         }
 
-        return createDiskOffering(domainId, name, description, numGibibytes, tags, isCustomized, localStorageRequired, isDisplayOfferingEnabled);
+        Long bytesRate = cmd.getBytesRate();
+        Long iopsRate = cmd.getIopsRate();
+        return createDiskOffering(domainId, name, description, numGibibytes, tags, isCustomized, localStorageRequired, isDisplayOfferingEnabled, bytesRate, iopsRate);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/server/src/com/cloud/storage/StorageManager.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/StorageManager.java b/server/src/com/cloud/storage/StorageManager.java
index d49a7f8..7f5b3ff 100755
--- a/server/src/com/cloud/storage/StorageManager.java
+++ b/server/src/com/cloud/storage/StorageManager.java
@@ -35,6 +35,8 @@ import com.cloud.exception.ConnectionException;
 import com.cloud.exception.StorageUnavailableException;
 import com.cloud.host.Host;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.service.ServiceOfferingVO;
+import com.cloud.storage.DiskOfferingVO;
 import com.cloud.storage.Storage.ImageFormat;
 import com.cloud.utils.Pair;
 import com.cloud.vm.DiskProfile;
@@ -122,4 +124,8 @@ public interface StorageManager extends StorageService {
     DataStore createLocalStorage(Host host, StoragePoolInfo poolInfo) throws ConnectionException;
 
     BigDecimal getStorageOverProvisioningFactor(Long dcId);
+
+    Long getDiskBytesRate(ServiceOfferingVO offering, DiskOfferingVO diskOffering);
+
+    Long getDiskIORate(ServiceOfferingVO offering, DiskOfferingVO diskOffering);
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/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 956aa87..f0cb1ff 100755
--- a/server/src/com/cloud/storage/StorageManagerImpl.java
+++ b/server/src/com/cloud/storage/StorageManagerImpl.java
@@ -127,6 +127,7 @@ import com.cloud.resource.ResourceState;
 import com.cloud.server.ManagementServer;
 import com.cloud.server.StatsCollector;
 import com.cloud.service.dao.ServiceOfferingDao;
+import com.cloud.service.ServiceOfferingVO;
 import com.cloud.storage.Storage.ImageFormat;
 import com.cloud.storage.Storage.StoragePoolType;
 import com.cloud.storage.Volume.Type;
@@ -142,6 +143,7 @@ import com.cloud.storage.dao.VMTemplateS3Dao;
 import com.cloud.storage.dao.VMTemplateSwiftDao;
 import com.cloud.storage.dao.VolumeDao;
 import com.cloud.storage.dao.VolumeHostDao;
+import com.cloud.storage.DiskOfferingVO;
 import com.cloud.storage.download.DownloadMonitor;
 import com.cloud.storage.listener.StoragePoolMonitor;
 import com.cloud.storage.listener.VolumeStateListener;
@@ -1883,4 +1885,36 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
         return null;
     }
 
+    // get bytesRate from disk_offering and vm.disk.throttling.bytes_rate
+    @Override
+    public Long getDiskBytesRate(ServiceOfferingVO offering, DiskOfferingVO diskOffering) {
+        if ((offering != null) && (offering.getBytesRate() > 0)) {
+            return offering.getBytesRate();
+        } else if ((diskOffering != null) && (diskOffering.getBytesRate() > 0)) {
+            return diskOffering.getBytesRate();
+        } else {
+            Long bytesRate = Long.parseLong(_configDao.getValue(Config.VmDiskThrottlingBytesRate.key()));
+            if (bytesRate > 0)  {
+                return bytesRate;
+            }
+        }
+        return 0L;
+    }
+
+    // get iopsRate from disk_offering and vm.disk.throttling.io_rate
+    @Override
+    public Long getDiskIORate(ServiceOfferingVO offering, DiskOfferingVO diskOffering) {
+        if ((offering != null) && (offering.getIopsRate() > 0)) {
+            return offering.getIopsRate();
+        } else if ((diskOffering != null) && (diskOffering.getIopsRate() > 0)) {
+            return diskOffering.getIopsRate();
+        } else {
+            Long iopsRate = Long.parseLong(_configDao.getValue(Config.VmDiskThrottlingIORate.key()));
+            if (iopsRate > 0)  {
+                return iopsRate;
+            }
+        }
+        return 0L;
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/server/src/com/cloud/storage/VolumeManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/VolumeManagerImpl.java b/server/src/com/cloud/storage/VolumeManagerImpl.java
index 43f3681..63f3254 100644
--- a/server/src/com/cloud/storage/VolumeManagerImpl.java
+++ b/server/src/com/cloud/storage/VolumeManagerImpl.java
@@ -115,6 +115,7 @@ import com.cloud.server.ManagementServer;
 import com.cloud.service.ServiceOfferingVO;
 import com.cloud.service.dao.ServiceOfferingDao;
 import com.cloud.storage.Storage.ImageFormat;
+import com.cloud.storage.StorageManager;
 import com.cloud.storage.Storage.StoragePoolType;
 import com.cloud.storage.Volume.Event;
 import com.cloud.storage.Volume.Type;
@@ -336,6 +337,9 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
                 diskOffering.getUseLocalStorage(),
                 diskOffering.isRecreatable(), null);
         dskCh.setHyperType(dataDiskHyperType);
+        dskCh.setBytesRate(storageMgr.getDiskBytesRate(null, diskOffering));
+        dskCh.setIopsRate(storageMgr.getDiskIORate(null, diskOffering));
+
         DataCenterVO destPoolDataCenter = _dcDao.findById(destPoolDcId);
         HostPodVO destPoolPod = _podDao.findById(destPoolPodId);
         
@@ -511,6 +515,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
         DataCenterVO dc = _dcDao.findById(volume.getDataCenterId());
         DiskProfile dskCh = new DiskProfile(volume, diskOffering,
                 snapshot.getHypervisorType());
+        dskCh.setBytesRate(storageMgr.getDiskBytesRate(null, diskOffering));
+        dskCh.setIopsRate(storageMgr.getDiskIORate(null, diskOffering));
 
         // Determine what pod to store the volume in
         while ((pod = _resourceMgr.findPod(null, null, dc, account.getId(),
@@ -611,6 +617,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
         DiskProfile dskCh = createDiskCharacteristics(volume, template, dc,
                 diskOffering);
         dskCh.setHyperType(vm.getHypervisorType());
+        dskCh.setBytesRate(storageMgr.getDiskBytesRate(offering, diskOffering));
+        dskCh.setIopsRate(storageMgr.getDiskIORate(offering, diskOffering));
         // Find a suitable storage to create volume on
         StoragePool destPool = storageMgr.findStoragePool(dskCh, dc, pod,
                 clusterId, null, vm, avoidPools);
@@ -655,6 +663,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
         }
 
         dskCh.setHyperType(hyperType);
+        dskCh.setBytesRate(storageMgr.getDiskBytesRate(offering, diskOffering));
+        dskCh.setIopsRate(storageMgr.getDiskIORate(offering, diskOffering));
         
         final HashSet<StoragePool> avoidPools = new HashSet<StoragePool>(
                 avoids);
@@ -2232,7 +2242,12 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
 
         for (VolumeVO vol : vols) {
             PrimaryDataStoreInfo pool = (PrimaryDataStoreInfo)this.dataStoreMgr.getDataStore(vol.getPoolId(), DataStoreRole.Primary);
-            vm.addDisk(new VolumeTO(vol, pool));
+            ServiceOfferingVO offering = _offeringDao.findById(vm.getServiceOfferingId());
+            DiskOfferingVO diskOffering = _diskOfferingDao.findById(vol.getDiskOfferingId());
+            VolumeTO newVolume = new VolumeTO(vol, pool);
+            newVolume.setBytesRate(storageMgr.getDiskBytesRate(offering, diskOffering));
+            newVolume.setIopsRate(storageMgr.getDiskIORate(offering, diskOffering));
+            vm.addDisk(newVolume);
         }
 
         if (vm.getType() == VirtualMachine.Type.User) {
@@ -2462,7 +2477,12 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
                 pool = (StoragePool)dataStoreMgr.getDataStore(result.second().getId(), DataStoreRole.Primary);
                 vol = result.first();
             }
-            vm.addDisk(new VolumeTO(vol, pool));
+            ServiceOfferingVO offering = _offeringDao.findById(vm.getServiceOfferingId());
+            DiskOfferingVO diskOffering = _diskOfferingDao.findById(vol.getDiskOfferingId());
+            VolumeTO newVolume = new VolumeTO(vol, pool);
+            newVolume.setBytesRate(storageMgr.getDiskBytesRate(offering, diskOffering));
+            newVolume.setIopsRate(storageMgr.getDiskIORate(offering, diskOffering));
+            vm.addDisk(newVolume);
         }
     }
     

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/server/src/com/cloud/test/DatabaseConfig.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/test/DatabaseConfig.java b/server/src/com/cloud/test/DatabaseConfig.java
index 70c8178..16829f8 100755
--- a/server/src/com/cloud/test/DatabaseConfig.java
+++ b/server/src/com/cloud/test/DatabaseConfig.java
@@ -967,6 +967,12 @@ public class DatabaseConfig {
         }
         DiskOfferingVO diskOffering = new DiskOfferingVO(domainId, name, displayText, diskSpace , tags, false);
         diskOffering.setUseLocalStorage(local);
+        Long bytesRate = Long.parseLong(_currentObjectParams.get("bytesRate"));
+        if (bytesRate != null && (bytesRate > 0))
+            diskOffering.setBytesRate(bytesRate);
+        Long iopsRate = Long.parseLong(_currentObjectParams.get("iopsRate"));
+        if (iopsRate != null && (iopsRate > 0))
+            diskOffering.setIopsRate(iopsRate);
         DiskOfferingDaoImpl offering = ComponentContext.inject(DiskOfferingDaoImpl.class);
         try {
             offering.persist(diskOffering);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/server/src/com/cloud/vm/VirtualMachineManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java
index 568fe55..971de56 100755
--- a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java
+++ b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java
@@ -1328,7 +1328,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
             throw new CloudRuntimeException("Unable to migrate vm: " + e.toString());
         }
 
-        VirtualMachineProfile<VMInstanceVO> profile = new VirtualMachineProfileImpl<VMInstanceVO>(vm);
+        VirtualMachineProfile<T> profile = new VirtualMachineProfileImpl<T>(vm);
         boolean migrationResult = false;
         try {
             migrationResult = this.volumeMgr.storageMigration(profile, destPool);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java
index 21b3590..686040b 100755
--- a/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java
+++ b/server/test/com/cloud/vpc/MockConfigurationManagerImpl.java
@@ -489,7 +489,7 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu
      */
     @Override
     public ServiceOfferingVO createServiceOffering(long userId, boolean isSystem, Type vm_typeType, String name, int cpu, int ramSize, int speed, String displayText, boolean localStorageRequired, boolean offerHA,
-            boolean limitResourceUse, boolean volatileVm, String tags, Long domainId, String hostTag, Integer networkRate, String deploymentPlanner, Map<String, String> details) {
+            boolean limitResourceUse, boolean volatileVm, String tags, Long domainId, String hostTag, Integer networkRate, String deploymentPlanner, Map<String, String> details, Long bytesRate, Long iopsRate) {
         // TODO Auto-generated method stub
         return null;
     }
@@ -654,7 +654,7 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu
      * @see com.cloud.configuration.ConfigurationManager#createDiskOffering(java.lang.Long, java.lang.String, java.lang.String, java.lang.Long, java.lang.String, boolean, boolean, boolean)
      */
     @Override
-    public DiskOfferingVO createDiskOffering(Long domainId, String name, String description, Long numGibibytes, String tags, boolean isCustomized, boolean localStorageRequired, boolean isDisplayOfferingEnabled) {
+    public DiskOfferingVO createDiskOffering(Long domainId, String name, String description, Long numGibibytes, String tags, boolean isCustomized, boolean localStorageRequired, boolean isDisplayOfferingEnabled, Long bytesRate, Long iopsRate) {
         // TODO Auto-generated method stub
         return null;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/setup/db/db/schema-410to420.sql
----------------------------------------------------------------------
diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql
index bcfbcc9..a59cf1c 100644
--- a/setup/db/db/schema-410to420.sql
+++ b/setup/db/db/schema-410to420.sql
@@ -290,6 +290,10 @@ ALTER TABLE `cloud`.`nics` ADD COLUMN `display_nic` tinyint(1) NOT NULL DEFAULT
 
 ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `display_offering` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Should disk offering be displayed to the end user';
 
+ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `bytes_rate` bigint(20) unsigned DEFAULT 0;
+
+ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `iops_rate` bigint(20) unsigned DEFAULT 0;
+
 CREATE TABLE `cloud`.`volume_details` (
   `id` bigint unsigned NOT NULL auto_increment,
   `volume_id` bigint unsigned NOT NULL COMMENT 'volume id',
@@ -762,6 +766,8 @@ CREATE VIEW `cloud`.`volume_view` AS
         disk_offering.display_text disk_offering_display_text,
         disk_offering.use_local_storage,
         disk_offering.system_use,
+        disk_offering.bytes_rate,
+        disk_offering.iops_rate,
         storage_pool.id pool_id,
         storage_pool.uuid pool_uuid,
         storage_pool.name pool_name,
@@ -1070,6 +1076,8 @@ CREATE VIEW `cloud`.`service_offering_view` AS
         disk_offering.removed,
         disk_offering.use_local_storage,
         disk_offering.system_use,
+        disk_offering.bytes_rate,
+        disk_offering.iops_rate,
         service_offering.cpu,
         service_offering.speed,
         service_offering.ram_size,
@@ -1376,6 +1384,8 @@ CREATE VIEW `cloud`.`disk_offering_view` AS
         disk_offering.removed,
         disk_offering.use_local_storage,
         disk_offering.system_use,
+        disk_offering.bytes_rate,
+        disk_offering.iops_rate,
         disk_offering.sort_key,
         disk_offering.type,
 	disk_offering.display_offering,
@@ -1616,6 +1626,8 @@ CREATE VIEW `cloud`.`volume_view` AS
         disk_offering.display_text disk_offering_display_text,
         disk_offering.use_local_storage,
         disk_offering.system_use,
+        disk_offering.bytes_rate,
+        disk_offering.iops_rate,
         storage_pool.id pool_id,
         storage_pool.uuid pool_uuid,
         storage_pool.name pool_name,
@@ -1846,7 +1858,8 @@ CREATE TABLE `cloud_usage`.`usage_vm_disk` (
 ) ENGINE=InnoDB CHARSET=utf8;
 
 INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'vm.disk.stats.interval', 0, 'Interval (in seconds) to report vm disk statistics.');
-
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'vm.disk.throttling.iops_rate', 0, 'Default disk I/O rate in requests per second allowed in User vm\'s disk. ');
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'vm.disk.throttling.bytes_rate', 0, 'Default disk I/O rate in bytes per second allowed in User vm\'s disk. ');
 
 -- Re-enable foreign key checking, at the end of the upgrade path
 SET foreign_key_checks = 1;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/ui/dictionary.jsp
----------------------------------------------------------------------
diff --git a/ui/dictionary.jsp b/ui/dictionary.jsp
index d7f7dd5..13206c3 100644
--- a/ui/dictionary.jsp
+++ b/ui/dictionary.jsp
@@ -469,6 +469,8 @@ dictionary = {
 'label.disable.vpn': '<fmt:message key="label.disable.vpn" />',
 'label.disabling.vpn.access': '<fmt:message key="label.disabling.vpn.access" />',
 'label.disk.allocated': '<fmt:message key="label.disk.allocated" />',
+'label.disk.bytes.rate': '<fmt:message key="label.disk.bytes.rate" />',
+'label.disk.iops.rate': '<fmt:message key="label.disk.iops.rate" />',
 'label.disk.read.bytes': '<fmt:message key="label.disk.read.bytes" />',
 'label.disk.read.io': '<fmt:message key="label.disk.read.io" />',
 'label.disk.offering': '<fmt:message key="label.disk.offering" />',

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/ui/scripts/configuration.js
----------------------------------------------------------------------
diff --git a/ui/scripts/configuration.js b/ui/scripts/configuration.js
index 8a4aa7d..daed97a 100644
--- a/ui/scripts/configuration.js
+++ b/ui/scripts/configuration.js
@@ -109,6 +109,20 @@
                       number: true
                     }
                   },
+                  diskBytesRate: {
+                	  label: 'label.disk.bytes.rate',
+                      validation: {
+                        required: false, //optional
+                        number: true
+                      }
+                  },
+                  diskIORate: {
+                	  label: 'label.disk.iops.rate',
+                      validation: {
+                        required: false, //optional
+                        number: true
+                      }
+                  },
                   offerHA: {
                     label: 'label.offer.ha',
                     docID: 'helpComputeOfferingHA',
@@ -227,6 +241,16 @@
 									  networkrate: args.data.networkRate
 									});
 								}
+                if(args.data.diskBytesRate != null && args.data.diskBytesRate.length > 0) {
+                                                                  $.extend(data, {
+                                                                          bytesRate: args.data.diskBytesRate
+                                                                        });
+                                                                }
+                if(args.data.diskIORate != null && args.data.diskIORate.length > 0) {
+                                                                  $.extend(data, {
+                                                                          iopsRate: args.data.diskIORate
+                                                                        });
+                                                                }
 
                 $.extend(data, {
                   offerha: (args.data.offerHA == "on")
@@ -396,6 +420,8 @@
                       }
                     },
                     networkrate: { label: 'label.network.rate' },
+                    diskBytesRate: { label: 'label.disk.bytes.rate' },
+                    diskIORate: { label: 'label.disk.iops.rate' },
                     offerha: {
                       label: 'label.offer.ha',
                       converter: cloudStack.converters.toBooleanText
@@ -534,6 +560,20 @@
                       number: true
                     }
                   },
+                  diskBytesRate: {
+                	  label: 'label.disk.bytes.rate',
+                      validation: {
+                        required: false, //optional
+                        number: true
+                      }
+                  },
+                  diskIORate: {
+                	  label: 'label.disk.iops.rate',
+                      validation: {
+                        required: false, //optional
+                        number: true
+                      }
+                  },
                   offerHA: {
                     label: 'label.offer.ha',
                     docID: 'helpSystemOfferingHA',
@@ -602,6 +642,16 @@
 									  networkrate: args.data.networkRate
 									});								
 								}
+                if(args.data.diskBytesRate != null && args.data.diskBytesRate.length > 0) {
+                                                                  $.extend(data, {
+                                                                          bytesRate: args.data.diskBytesRate
+                                                                        });
+                                                                }
+                if(args.data.diskIORate != null && args.data.diskIORate.length > 0) {
+                                                                  $.extend(data, {
+                                                                          iopsRate: args.data.diskIORate
+                                                                        });
+                                                                }
 
 								$.extend(data, {
 								  offerha: (args.data.offerHA == "on")
@@ -781,6 +831,8 @@
                       }
                     },
                     networkrate: { label: 'label.network.rate' },
+                    diskBytesRate: { label: 'label.disk.bytes.rate' },
+                    diskIORate: { label: 'label.disk.iops.rate' },
                     offerha: {
                       label: 'label.offer.ha',
                       converter: cloudStack.converters.toBooleanText
@@ -840,6 +892,20 @@
                 else
                   return "N/A";
               }
+            },
+            diskBytesRate: {
+          	  label: 'label.disk.bytes.rate',
+                validation: {
+                  required: false, //optional
+                  number: true
+                }
+            },
+            diskIORate: {
+          	  label: 'label.disk.iops.rate',
+                validation: {
+                  required: false, //optional
+                  number: true
+                }
             }
           },
 
@@ -911,6 +977,20 @@
                     dependsOn: 'isCustomized',
                     validation: { required: true, number: true }
                   },
+                  diskBytesRate: {
+                	  label: 'label.disk.bytes.rate',
+                      validation: {
+                        required: false, //optional
+                        number: true
+                      }
+                  },
+                  diskIORate: {
+                	  label: 'label.disk.iops.rate',
+                      validation: {
+                        required: false, //optional
+                        number: true
+                      }
+                  },
                   tags: {
                     label: 'label.storage.tags',
                     docID: 'helpDiskOfferingStorageTags'
@@ -1084,6 +1164,8 @@
                           return "N/A";
                       }
                     },
+                    diskBytesRate: { label: 'label.disk.bytes.rate' },
+                    diskIORate: { label: 'label.disk.iops.rate' },
                     tags: { label: 'label.storage.tags' },
                     domain: { label: 'label.domain' },
                     storagetype: { label: 'label.storage.type' }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/6dad8adf/ui/scripts/instances.js
----------------------------------------------------------------------
diff --git a/ui/scripts/instances.js b/ui/scripts/instances.js
index 6a589ba..99b1a0f 100644
--- a/ui/scripts/instances.js
+++ b/ui/scripts/instances.js
@@ -1669,8 +1669,8 @@
                   networkkbswrite: (jsonObj.networkkbswrite == null)? "N/A": cloudStack.converters.convertBytes(jsonObj.networkkbswrite * 1024),
                   diskkbsread: (jsonObj.diskkbsread == null)? "N/A": cloudStack.converters.convertBytes(jsonObj.diskkbsread * 1024),
                   diskkbswrite: (jsonObj.diskkbswrite == null)? "N/A": cloudStack.converters.convertBytes(jsonObj.diskkbswrite * 1024),
-                  diskioread: (jsonObj.diskioread == null)? "N/A": cloudStack.converters.convertBytes(jsonObj.diskioread * 1024),
-                  diskiowrite: (jsonObj.diskiowrite == null)? "N/A": cloudStack.converters.convertBytes(jsonObj.diskiowrite * 1024)
+                  diskioread: (jsonObj.diskioread == null)? "N/A": jsonObj.diskioread,
+                  diskiowrite: (jsonObj.diskiowrite == null)? "N/A": jsonObj.diskiowrite
                   }
                 });
               }


[2/4] CLOUDSTACK-1192: Add Disk I/O Statistics

Posted by we...@apache.org.
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/server/src/com/cloud/server/StatsCollector.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/server/StatsCollector.java b/server/src/com/cloud/server/StatsCollector.java
index 05be0e2..39b7439 100755
--- a/server/src/com/cloud/server/StatsCollector.java
+++ b/server/src/com/cloud/server/StatsCollector.java
@@ -17,11 +17,14 @@
 package com.cloud.server;
 
 import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.TimeZone;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
@@ -45,8 +48,11 @@ import com.cloud.agent.api.Answer;
 import com.cloud.agent.api.GetFileStatsCommand;
 import com.cloud.agent.api.GetStorageStatsCommand;
 import com.cloud.agent.api.HostStatsEntry;
+import com.cloud.agent.api.VmDiskStatsEntry;
 import com.cloud.agent.api.VmStatsEntry;
 import com.cloud.agent.manager.Commands;
+import com.cloud.cluster.ManagementServerHostVO;
+import com.cloud.cluster.dao.ManagementServerHostDao;
 import com.cloud.exception.AgentUnavailableException;
 import com.cloud.exception.StorageUnavailableException;
 import com.cloud.host.Host;
@@ -59,19 +65,31 @@ import com.cloud.resource.ResourceState;
 import com.cloud.storage.StorageManager;
 import com.cloud.storage.StoragePoolHostVO;
 import com.cloud.storage.StorageStats;
+import com.cloud.storage.Volume;
 import com.cloud.storage.VolumeStats;
 import com.cloud.storage.VolumeVO;
 import com.cloud.storage.dao.StoragePoolHostDao;
 import com.cloud.storage.dao.VolumeDao;
 import com.cloud.storage.secondary.SecondaryStorageVmManager;
+import com.cloud.user.UserStatisticsVO;
+import com.cloud.user.UserStatsLogVO;
+import com.cloud.user.VmDiskStatisticsVO;
+import com.cloud.user.dao.VmDiskStatisticsDao;
 import com.cloud.utils.NumbersUtil;
 import com.cloud.utils.component.ComponentMethodInterceptable;
 import com.cloud.utils.component.ManagerBase;
 import com.cloud.utils.concurrency.NamedThreadFactory;
+import com.cloud.utils.db.Filter;
+import com.cloud.utils.db.GlobalLock;
 import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.Transaction;
+import com.cloud.utils.net.MacAddress;
+import com.cloud.vm.NicVO;
 import com.cloud.vm.UserVmManager;
 import com.cloud.vm.UserVmVO;
 import com.cloud.vm.VmStats;
+import com.cloud.vm.VirtualMachine;
+import com.cloud.vm.VirtualMachine.State;
 import com.cloud.vm.dao.UserVmDao;
 
 /**
@@ -96,6 +114,8 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc
 	@Inject private SecondaryStorageVmManager _ssvmMgr;
 	@Inject private ResourceManager _resourceMgr;
     @Inject private ConfigurationDao _configDao;
+    @Inject private VmDiskStatisticsDao _vmDiskStatsDao;
+    @Inject private ManagementServerHostDao _msHostDao;
 
 	private ConcurrentHashMap<Long, HostStats> _hostStats = new ConcurrentHashMap<Long, HostStats>();
 	private final ConcurrentHashMap<Long, VmStats> _VmStats = new ConcurrentHashMap<Long, VmStats>();
@@ -107,6 +127,15 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc
 	long hostAndVmStatsInterval = -1L;
 	long storageStatsInterval = -1L;
 	long volumeStatsInterval = -1L;
+	int vmDiskStatsInterval = 0;
+	
+	private ScheduledExecutorService _diskStatsUpdateExecutor;
+    private int _usageAggregationRange = 1440;
+    private String _usageTimeZone = "GMT";
+    private final long mgmtSrvrId = MacAddress.getMacAddress().toLong();
+    private static final int ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION = 5;    // 5 seconds
+    private static final int USAGE_AGGREGATION_RANGE_MIN = 10; // 10 minutes, same to com.cloud.usage.UsageManagerImpl.USAGE_AGGREGATION_RANGE_MIN
+    private boolean _dailyOrHourly = false;
 
 	//private final GlobalLock m_capacityCheckLock = GlobalLock.getInternLock("capacity.check");
 
@@ -136,6 +165,7 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc
 		 hostAndVmStatsInterval = NumbersUtil.parseLong(configs.get("vm.stats.interval"), 60000L);
 		 storageStatsInterval = NumbersUtil.parseLong(configs.get("storage.stats.interval"), 60000L);
 		 volumeStatsInterval = NumbersUtil.parseLong(configs.get("volume.stats.interval"), -1L);
+		 vmDiskStatsInterval = NumbersUtil.parseInt(configs.get("vm.disk.stats.interval"), 0);
 
 		 if (hostStatsInterval > 0) {
 		     _executor.scheduleWithFixedDelay(new HostCollector(), 15000L, hostStatsInterval, TimeUnit.MILLISECONDS);
@@ -148,6 +178,12 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc
 		 if (storageStatsInterval > 0) {
 		     _executor.scheduleWithFixedDelay(new StorageCollector(), 15000L, storageStatsInterval, TimeUnit.MILLISECONDS);
 		 }
+		 
+		 if (vmDiskStatsInterval > 0) {
+                     if (vmDiskStatsInterval < 300)
+                         vmDiskStatsInterval = 300;
+             _executor.scheduleAtFixedRate(new VmDiskStatsTask(), vmDiskStatsInterval, vmDiskStatsInterval, TimeUnit.SECONDS);
+         }
 		
 		// -1 means we don't even start this thread to pick up any data.
 		if (volumeStatsInterval > 0) {
@@ -155,6 +191,49 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc
 		} else {
 			s_logger.info("Disabling volume stats collector");
 		}
+		
+        //Schedule disk stats update task
+        _diskStatsUpdateExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DiskStatsUpdater"));
+        String aggregationRange = configs.get("usage.stats.job.aggregation.range");
+        _usageAggregationRange  = NumbersUtil.parseInt(aggregationRange, 1440);
+        _usageTimeZone = configs.get("usage.aggregation.timezone");
+        if(_usageTimeZone == null){
+            _usageTimeZone = "GMT";
+        }
+        TimeZone usageTimezone = TimeZone.getTimeZone(_usageTimeZone);
+        Calendar cal = Calendar.getInstance(usageTimezone);
+        cal.setTime(new Date());
+        long endDate = 0;
+        int HOURLY_TIME = 60;
+        final int DAILY_TIME = 60 * 24;
+        if (_usageAggregationRange == DAILY_TIME) {
+            cal.set(Calendar.HOUR_OF_DAY, 0);
+            cal.set(Calendar.MINUTE, 0);
+            cal.set(Calendar.SECOND, 0);
+            cal.set(Calendar.MILLISECOND, 0);
+            cal.roll(Calendar.DAY_OF_YEAR, true);
+            cal.add(Calendar.MILLISECOND, -1);
+            endDate = cal.getTime().getTime();
+            _dailyOrHourly = true;
+        } else if (_usageAggregationRange == HOURLY_TIME) {
+            cal.set(Calendar.MINUTE, 0);
+            cal.set(Calendar.SECOND, 0);
+            cal.set(Calendar.MILLISECOND, 0);
+            cal.roll(Calendar.HOUR_OF_DAY, true);
+            cal.add(Calendar.MILLISECOND, -1);
+            endDate = cal.getTime().getTime();
+            _dailyOrHourly = true;
+        } else {
+            endDate = cal.getTime().getTime();
+            _dailyOrHourly = false;
+        }
+        if (_usageAggregationRange < USAGE_AGGREGATION_RANGE_MIN) {
+            s_logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + USAGE_AGGREGATION_RANGE_MIN);
+            _usageAggregationRange = USAGE_AGGREGATION_RANGE_MIN;
+        }
+        _diskStatsUpdateExecutor.scheduleAtFixedRate(new VmDiskStatsUpdaterTask(), (endDate - System.currentTimeMillis()),
+                (_usageAggregationRange * 60 * 1000), TimeUnit.MILLISECONDS);
+        
 	}
 
 	class HostCollector implements Runnable {
@@ -249,6 +328,10 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc
 										statsInMemory.setNumCPUs(statsForCurrentIteration.getNumCPUs());
 										statsInMemory.setNetworkReadKBs(statsInMemory.getNetworkReadKBs() + statsForCurrentIteration.getNetworkReadKBs());
 										statsInMemory.setNetworkWriteKBs(statsInMemory.getNetworkWriteKBs() + statsForCurrentIteration.getNetworkWriteKBs());
+										statsInMemory.setDiskWriteKBs(statsInMemory.getDiskWriteKBs() + statsForCurrentIteration.getDiskWriteKBs());
+                                                                                statsInMemory.setDiskReadIOs(statsInMemory.getDiskReadIOs() + statsForCurrentIteration.getDiskReadIOs());
+                                                                                statsInMemory.setDiskWriteIOs(statsInMemory.getDiskWriteIOs() + statsForCurrentIteration.getDiskWriteIOs());
+                                                                                statsInMemory.setDiskReadKBs(statsInMemory.getDiskReadKBs() + statsForCurrentIteration.getDiskReadKBs());
 										
 										_VmStats.put(vmId, statsInMemory);
 									}
@@ -270,6 +353,175 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc
 	public VmStats getVmStats(long id) {
 		return _VmStats.get(id);
 	}
+	
+    class VmDiskStatsUpdaterTask implements Runnable {
+        @Override
+        public void run() {
+            GlobalLock scanLock = GlobalLock.getInternLock("vm.disk.stats");
+            try {
+                if(scanLock.lock(ACQUIRE_GLOBAL_LOCK_TIMEOUT_FOR_COOPERATION)) {
+                    //Check for ownership
+                    //msHost in UP state with min id should run the job
+                    ManagementServerHostVO msHost = _msHostDao.findOneInUpState(new Filter(ManagementServerHostVO.class, "id", true, 0L, 1L));
+                    if(msHost == null || (msHost.getMsid() != mgmtSrvrId)){
+                        s_logger.debug("Skipping aggregate disk stats update");
+                        scanLock.unlock();
+                        return;
+                    }
+                    Transaction txn = Transaction.open(Transaction.CLOUD_DB);
+                    try {
+                        txn.start();
+                        //get all stats with delta > 0
+                        List<VmDiskStatisticsVO> updatedVmNetStats = _vmDiskStatsDao.listUpdatedStats();
+                        for(VmDiskStatisticsVO stat : updatedVmNetStats){
+                            if (_dailyOrHourly) {
+                                //update agg bytes                    
+                                stat.setAggBytesRead(stat.getCurrentBytesRead() + stat.getNetBytesRead());
+                                stat.setAggBytesWrite(stat.getCurrentBytesWrite() + stat.getNetBytesWrite());
+                                stat.setAggIORead(stat.getCurrentIORead() + stat.getNetIORead());
+                                stat.setAggIOWrite(stat.getCurrentIOWrite() + stat.getNetIOWrite());
+                                _vmDiskStatsDao.update(stat.getId(), stat);
+                            }
+                        }
+                        s_logger.debug("Successfully updated aggregate vm disk stats");
+                        txn.commit();
+                    } catch (Exception e){
+                        txn.rollback();
+                        s_logger.debug("Failed to update aggregate disk stats", e);
+                    } finally {
+                        scanLock.unlock();
+                        txn.close();
+                    }
+                }
+            } catch (Exception e){
+                s_logger.debug("Exception while trying to acquire disk stats lock", e);
+            }  finally {
+                scanLock.releaseRef();
+            }
+        }
+    }
+    
+    class VmDiskStatsTask implements Runnable {
+        @Override
+        public void run() {
+            // collect the vm disk statistics(total) from hypervisor. added by weizhou, 2013.03.
+            Transaction txn = Transaction.open(Transaction.CLOUD_DB);
+            try {
+                txn.start();
+                SearchCriteria<HostVO> sc = _hostDao.createSearchCriteria();
+                sc.addAnd("status", SearchCriteria.Op.EQ, Status.Up.toString());
+                sc.addAnd("resourceState", SearchCriteria.Op.NIN, ResourceState.Maintenance, ResourceState.PrepareForMaintenance, ResourceState.ErrorInMaintenance);
+                sc.addAnd("type", SearchCriteria.Op.EQ, Host.Type.Routing.toString());
+                List<HostVO> hosts = _hostDao.search(sc, null);
+                
+                for (HostVO host : hosts) {
+                    List<UserVmVO> vms = _userVmDao.listRunningByHostId(host.getId());
+                    List<Long> vmIds = new ArrayList<Long>();
+                    
+                    for (UserVmVO vm : vms) {
+                        if (vm.getType() == VirtualMachine.Type.User) // user vm
+                            vmIds.add(vm.getId());
+                    }
+                    
+                    HashMap<Long, List<VmDiskStatsEntry>> vmDiskStatsById = _userVmMgr.getVmDiskStatistics(host.getId(), host.getName(), vmIds);
+                    if (vmDiskStatsById == null)
+                        continue;
+                
+                    Set<Long> vmIdSet = vmDiskStatsById.keySet();
+                    for(Long vmId : vmIdSet)
+                    {
+                        List<VmDiskStatsEntry> vmDiskStats = vmDiskStatsById.get(vmId);
+                        if (vmDiskStats == null)
+                                continue;
+                        UserVmVO userVm = _userVmDao.findById(vmId);
+                        for (VmDiskStatsEntry vmDiskStat:vmDiskStats) {
+                            SearchCriteria<VolumeVO> sc_volume = _volsDao.createSearchCriteria();
+                            sc_volume.addAnd("path", SearchCriteria.Op.EQ, vmDiskStat.getPath());
+                            VolumeVO volume = _volsDao.search(sc_volume, null).get(0);
+                            VmDiskStatisticsVO previousVmDiskStats = _vmDiskStatsDao.findBy(userVm.getAccountId(), userVm.getDataCenterId(), vmId, volume.getId());
+                            VmDiskStatisticsVO vmDiskStat_lock = _vmDiskStatsDao.lock(userVm.getAccountId(), userVm.getDataCenterId(), vmId, volume.getId());
+                            
+                            if ((vmDiskStat.getBytesRead() == 0) && (vmDiskStat.getBytesWrite() == 0)
+                                    && (vmDiskStat.getIORead() == 0) && (vmDiskStat.getIOWrite() == 0)) {
+                                s_logger.debug("IO/bytes read and write are all 0. Not updating vm_disk_statistics");
+                                continue;
+                            }
+                            
+                            if (vmDiskStat_lock == null) {
+                                s_logger.warn("unable to find vm disk stats from host for account: " + userVm.getAccountId() + " with vmId: " + userVm.getId()+ " and volumeId:" + volume.getId());
+                                continue;
+                            }
+
+                            if (previousVmDiskStats != null
+                                    && ((previousVmDiskStats.getCurrentBytesRead() != vmDiskStat_lock.getCurrentBytesRead())
+                                    || (previousVmDiskStats.getCurrentBytesWrite() != vmDiskStat_lock.getCurrentBytesWrite())
+                                    || (previousVmDiskStats.getCurrentIORead() != vmDiskStat_lock.getCurrentIORead())
+                                    || (previousVmDiskStats.getCurrentIOWrite() != vmDiskStat_lock.getCurrentIOWrite()))) {
+                                s_logger.debug("vm disk stats changed from the time GetVmDiskStatsCommand was sent. " +
+                                        "Ignoring current answer. Host: " + host.getName()  + " . VM: " + vmDiskStat.getVmName() + 
+                                        " Read(Bytes): " + vmDiskStat.getBytesRead() + " write(Bytes): " + vmDiskStat.getBytesWrite() +
+                                        " Read(IO): " + vmDiskStat.getIORead() + " write(IO): " + vmDiskStat.getIOWrite());
+                                continue;
+                            }
+
+                            if (vmDiskStat_lock.getCurrentBytesRead() > vmDiskStat.getBytesRead()) {
+                                if (s_logger.isDebugEnabled()) {
+                                    s_logger.debug("Read # of bytes that's less than the last one.  " +
+                                            "Assuming something went wrong and persisting it. Host: " + host.getName() + " . VM: " + vmDiskStat.getVmName() +
+                                            " Reported: " + vmDiskStat.getBytesRead() + " Stored: " + vmDiskStat_lock.getCurrentBytesRead());
+                                }
+                                vmDiskStat_lock.setNetBytesRead(vmDiskStat_lock.getNetBytesRead() + vmDiskStat_lock.getCurrentBytesRead());
+                            }
+                            vmDiskStat_lock.setCurrentBytesRead(vmDiskStat.getBytesRead());
+                            if (vmDiskStat_lock.getCurrentBytesWrite() > vmDiskStat.getBytesWrite()) {
+                                if (s_logger.isDebugEnabled()) {
+                                    s_logger.debug("Write # of bytes that's less than the last one.  " +
+                                            "Assuming something went wrong and persisting it. Host: " + host.getName() + " . VM: " + vmDiskStat.getVmName() +
+                                            " Reported: " + vmDiskStat.getBytesWrite() + " Stored: " + vmDiskStat_lock.getCurrentBytesWrite());
+                                }
+                                vmDiskStat_lock.setNetBytesWrite(vmDiskStat_lock.getNetBytesWrite() + vmDiskStat_lock.getCurrentBytesWrite());
+                            }
+                            vmDiskStat_lock.setCurrentBytesWrite(vmDiskStat.getBytesWrite());
+                            if (vmDiskStat_lock.getCurrentIORead() > vmDiskStat.getIORead()) {
+                                if (s_logger.isDebugEnabled()) {
+                                    s_logger.debug("Read # of IO that's less than the last one.  " +
+                                            "Assuming something went wrong and persisting it. Host: " + host.getName() + " . VM: " + vmDiskStat.getVmName() +
+                                            " Reported: " + vmDiskStat.getIORead() + " Stored: " + vmDiskStat_lock.getCurrentIORead());
+                                }
+                                vmDiskStat_lock.setNetIORead(vmDiskStat_lock.getNetIORead() + vmDiskStat_lock.getCurrentIORead());
+                            }
+                            vmDiskStat_lock.setCurrentIORead(vmDiskStat.getIORead());
+                            if (vmDiskStat_lock.getCurrentIOWrite() > vmDiskStat.getIOWrite()) {
+                                if (s_logger.isDebugEnabled()) {
+                                    s_logger.debug("Write # of IO that's less than the last one.  " +
+                                            "Assuming something went wrong and persisting it. Host: " + host.getName() + " . VM: " + vmDiskStat.getVmName() +
+                                            " Reported: " + vmDiskStat.getIOWrite() + " Stored: " + vmDiskStat_lock.getCurrentIOWrite());
+                                }
+                                vmDiskStat_lock.setNetIOWrite(vmDiskStat_lock.getNetIOWrite() + vmDiskStat_lock.getCurrentIOWrite());
+                            }
+                            vmDiskStat_lock.setCurrentIOWrite(vmDiskStat.getIOWrite());
+                            
+                            if (! _dailyOrHourly) {
+                                //update agg bytes 
+                                vmDiskStat_lock.setAggBytesWrite(vmDiskStat_lock.getNetBytesWrite() + vmDiskStat_lock.getCurrentBytesWrite());
+                                vmDiskStat_lock.setAggBytesRead(vmDiskStat_lock.getNetBytesRead() + vmDiskStat_lock.getCurrentBytesRead());
+                                vmDiskStat_lock.setAggIOWrite(vmDiskStat_lock.getNetIOWrite() + vmDiskStat_lock.getCurrentIOWrite());
+                                vmDiskStat_lock.setAggIORead(vmDiskStat_lock.getNetIORead() + vmDiskStat_lock.getCurrentIORead());
+                            }
+
+                            _vmDiskStatsDao.update(vmDiskStat_lock.getId(), vmDiskStat_lock);
+                        }
+                    }
+                }
+                txn.commit();
+            } catch (Exception e) {
+                s_logger.warn("Error while collecting vm disk stats from hosts", e);
+            } finally {
+                txn.close();
+            }
+            
+        }
+    }
 
 	class StorageCollector implements Runnable {
 		@Override

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/server/src/com/cloud/storage/VolumeManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/VolumeManagerImpl.java b/server/src/com/cloud/storage/VolumeManagerImpl.java
index d064f3b..43f3681 100644
--- a/server/src/com/cloud/storage/VolumeManagerImpl.java
+++ b/server/src/com/cloud/storage/VolumeManagerImpl.java
@@ -128,9 +128,11 @@ 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.UserContext;
 import com.cloud.user.dao.AccountDao;
 import com.cloud.user.dao.UserDao;
+import com.cloud.user.dao.VmDiskStatisticsDao;
 import com.cloud.uservm.UserVm;
 import com.cloud.utils.EnumUtils;
 import com.cloud.utils.NumbersUtil;
@@ -280,6 +282,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
     @Inject
     protected ResourceTagDao _resourceTagDao;
     @Inject
+    protected VmDiskStatisticsDao _vmDiskStatsDao;
+    @Inject
     protected VMSnapshotDao _vmSnapshotDao;
     @Inject
     protected List<StoragePoolAllocator> _storagePoolAllocators;
@@ -1558,6 +1562,13 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
             } else {
                 _volsDao.attachVolume(volume.getId(), vm.getId(), deviceId);
             }
+            // insert record for disk I/O statistics
+            VmDiskStatisticsVO diskstats = _vmDiskStatsDao.findBy(vm.getAccountId(), vm.getDataCenterId(),vm.getId(), volume.getId());
+            if (diskstats == null) {
+               diskstats = new VmDiskStatisticsVO(vm.getAccountId(), vm.getDataCenterId(),vm.getId(), volume.getId());
+               _vmDiskStatsDao.persist(diskstats);
+            }
+
             return _volsDao.findById(volume.getId());
         } else {
             if (answer != null) {
@@ -1895,6 +1906,9 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
                     .getPoolId());
             cmd.setPoolUuid(volumePool.getUuid());
 
+            // Collect vm disk statistics from host before stopping Vm
+            _userVmMgr.collectVmDiskStatistics(vm);
+
             try {
                 answer = _agentMgr.send(vm.getHostId(), cmd);
             } catch (Exception e) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/server/src/com/cloud/vm/UserVmManager.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/vm/UserVmManager.java b/server/src/com/cloud/vm/UserVmManager.java
index 4dcfb73..348017a 100755
--- a/server/src/com/cloud/vm/UserVmManager.java
+++ b/server/src/com/cloud/vm/UserVmManager.java
@@ -20,6 +20,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import com.cloud.agent.api.VmDiskStatsEntry;
 import com.cloud.agent.api.VmStatsEntry;
 import com.cloud.api.query.vo.UserVmJoinVO;
 import com.cloud.exception.*;
@@ -65,6 +66,8 @@ public interface UserVmManager extends VirtualMachineGuru<UserVmVO>, UserVmServi
      */
     HashMap<Long, VmStatsEntry> getVirtualMachineStatistics(long hostId, String hostName, List<Long> vmIds);
     
+    HashMap<Long, List<VmDiskStatsEntry>> getVmDiskStatistics(long hostId, String hostName, List<Long> vmIds);
+    
     boolean deleteVmGroup(long groupId);
 
     boolean addInstanceToGroup(long userVmId, String group);
@@ -95,4 +98,6 @@ public interface UserVmManager extends VirtualMachineGuru<UserVmVO>, UserVmServi
     boolean upgradeVirtualMachine(Long id, Long serviceOfferingId) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException;
 
     boolean setupVmForPvlan(boolean add, Long hostId, NicProfile nic);
+
+    void collectVmDiskStatistics (UserVmVO userVm);
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/server/src/com/cloud/vm/UserVmManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java
index 86bdb14..8cf05aa 100755
--- a/server/src/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/com/cloud/vm/UserVmManagerImpl.java
@@ -66,6 +66,8 @@ import org.apache.log4j.Logger;
 import com.cloud.agent.AgentManager;
 import com.cloud.agent.AgentManager.OnError;
 import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.GetVmDiskStatsAnswer;
+import com.cloud.agent.api.GetVmDiskStatsCommand;
 import com.cloud.agent.api.GetVmStatsAnswer;
 import com.cloud.agent.api.GetVmStatsCommand;
 import com.cloud.agent.api.PlugNicAnswer;
@@ -75,6 +77,7 @@ import com.cloud.agent.api.StartAnswer;
 import com.cloud.agent.api.StopAnswer;
 import com.cloud.agent.api.UnPlugNicAnswer;
 import com.cloud.agent.api.UnPlugNicCommand;
+import com.cloud.agent.api.VmDiskStatsEntry;
 import com.cloud.agent.api.VmStatsEntry;
 import com.cloud.agent.api.to.NicTO;
 import com.cloud.agent.api.to.VirtualMachineTO;
@@ -213,9 +216,11 @@ import com.cloud.user.SSHKeyPairVO;
 import com.cloud.user.User;
 import com.cloud.user.UserContext;
 import com.cloud.user.UserVO;
+import com.cloud.user.VmDiskStatisticsVO;
 import com.cloud.user.dao.AccountDao;
 import com.cloud.user.dao.SSHKeyPairDao;
 import com.cloud.user.dao.UserDao;
+import com.cloud.user.dao.VmDiskStatisticsDao;
 import com.cloud.uservm.UserVm;
 import com.cloud.utils.Journal;
 import com.cloud.utils.NumbersUtil;
@@ -239,6 +244,7 @@ import com.cloud.vm.VirtualMachine.State;
 import com.cloud.vm.dao.InstanceGroupDao;
 import com.cloud.vm.dao.InstanceGroupVMMapDao;
 import com.cloud.vm.dao.NicDao;
+import com.cloud.vm.dao.SecondaryStorageVmDao;
 import com.cloud.vm.dao.UserVmCloneSettingDao;
 import com.cloud.vm.dao.UserVmDao;
 import com.cloud.vm.dao.UserVmDetailsDao;
@@ -394,6 +400,12 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
     protected GuestOSCategoryDao _guestOSCategoryDao;
     @Inject
     UsageEventDao _usageEventDao;
+
+    @Inject
+    SecondaryStorageVmDao _secondaryDao;
+    @Inject
+    VmDiskStatisticsDao _vmDiskStatsDao;
+
     @Inject
     protected VMSnapshotDao _vmSnapshotDao;
     @Inject
@@ -411,6 +423,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
     protected ScheduledExecutorService _executor = null;
     protected int _expungeInterval;
     protected int _expungeDelay;
+    protected boolean _dailyOrHourly = false;
 
     protected String _name;
     protected String _instance;
@@ -1098,6 +1111,41 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
         }
 
     }
+    
+    @Override
+    public HashMap<Long, List<VmDiskStatsEntry>> getVmDiskStatistics(long hostId, String hostName, List<Long> vmIds) throws CloudRuntimeException {
+        HashMap<Long, List<VmDiskStatsEntry>> vmDiskStatsById = new HashMap<Long, List<VmDiskStatsEntry>>();
+
+        if (vmIds.isEmpty()) {
+            return vmDiskStatsById;
+        }
+
+        List<String> vmNames = new ArrayList<String>();
+
+        for (Long vmId : vmIds) {
+            UserVmVO vm = _vmDao.findById(vmId);
+            vmNames.add(vm.getInstanceName());
+        }
+
+        Answer answer = _agentMgr.easySend(hostId, new GetVmDiskStatsCommand(vmNames, _hostDao.findById(hostId).getGuid(), hostName));
+        if (answer == null || !answer.getResult()) {
+            s_logger.warn("Unable to obtain VM disk statistics.");
+            return null;
+        } else {
+            HashMap<String, List<VmDiskStatsEntry>> vmDiskStatsByName = ((GetVmDiskStatsAnswer)answer).getVmDiskStatsMap();
+
+            if (vmDiskStatsByName == null) {
+                s_logger.warn("Unable to obtain VM disk statistics.");
+                return null;
+            }
+
+            for (String vmName : vmDiskStatsByName.keySet()) {
+                vmDiskStatsById.put(vmIds.get(vmNames.indexOf(vmName)), vmDiskStatsByName.get(vmName));
+            }
+        }
+
+        return vmDiskStatsById;
+    }
 
     @Override
     public boolean upgradeVirtualMachine(Long vmId, Long newServiceOfferingId) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException{
@@ -1397,6 +1445,18 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
 
         _executor = Executors.newScheduledThreadPool(wrks, new NamedThreadFactory("UserVm-Scavenger"));
 
+        String aggregationRange = configs.get("usage.stats.job.aggregation.range");
+        int _usageAggregationRange  = NumbersUtil.parseInt(aggregationRange, 1440);
+        int HOURLY_TIME = 60;
+        final int DAILY_TIME = 60 * 24;
+        if (_usageAggregationRange == DAILY_TIME) {
+            _dailyOrHourly = true;
+        } else if (_usageAggregationRange == HOURLY_TIME) {
+            _dailyOrHourly = true;
+        } else {
+            _dailyOrHourly = false;
+        }
+
         _itMgr.registerGuru(VirtualMachine.Type.User, this);
 
         VirtualMachine.State.getStateMachine().registerListener(
@@ -2929,6 +2989,17 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
                 userVm.setPrivateMacAddress(nic.getMacAddress());
             }
         }
+
+        List<VolumeVO> volumes = _volsDao.findByInstance(userVm.getId());
+        VmDiskStatisticsVO diskstats = null;
+        for (VolumeVO volume : volumes) {
+               diskstats = _vmDiskStatsDao.findBy(userVm.getAccountId(), userVm.getDataCenterId(),userVm.getId(), volume.getId());
+            if (diskstats == null) {
+               diskstats = new VmDiskStatisticsVO(userVm.getAccountId(), userVm.getDataCenterId(),userVm.getId(), volume.getId());
+               _vmDiskStatsDao.persist(diskstats);
+            }
+        }
+
         return true;
     }
 
@@ -3308,6 +3379,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
         boolean status;
         State vmState = vm.getState();
 
+        // Collect vm disk statistics from host before stopping Vm
+         collectVmDiskStatistics(vm);
+
         try {
             VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid());
             status = vmEntity.destroy(new Long(userId).toString());
@@ -3344,6 +3418,122 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
             ex.addProxyObject(vm.getUuid(), "vmId");
             throw ex;
         }
+
+    }
+
+    @Override
+    public void collectVmDiskStatistics (UserVmVO userVm) {
+    	// Collect vm disk statistics from host before stopping Vm
+    	long hostId = userVm.getHostId();
+    	List<String> vmNames = new ArrayList<String>();
+    	vmNames.add(userVm.getInstanceName());
+    	HostVO host = _hostDao.findById(hostId);
+    	
+    	GetVmDiskStatsAnswer diskStatsAnswer = null;
+    	try {
+    		diskStatsAnswer = (GetVmDiskStatsAnswer) _agentMgr.easySend(hostId, new GetVmDiskStatsCommand(vmNames, host.getGuid(), host.getName()));
+    	} catch (Exception e) {
+            s_logger.warn("Error while collecting disk stats for vm: " + userVm.getHostName() + " from host: " + host.getName(), e);
+            return;
+        }
+        if (diskStatsAnswer != null) {
+            if (!diskStatsAnswer.getResult()) {
+                s_logger.warn("Error while collecting disk stats vm: " + userVm.getHostName() + " from host: " + host.getName() + "; details: " + diskStatsAnswer.getDetails());
+                return;
+            }
+            Transaction txn = Transaction.open(Transaction.CLOUD_DB);
+            try {
+                txn.start();
+                HashMap<String, List<VmDiskStatsEntry>> vmDiskStatsByName = diskStatsAnswer.getVmDiskStatsMap();
+                List<VmDiskStatsEntry> vmDiskStats = vmDiskStatsByName.get(userVm.getInstanceName());
+                
+                if (vmDiskStats == null)
+		    return;
+	        	
+	        for (VmDiskStatsEntry vmDiskStat:vmDiskStats) {
+                    SearchCriteria<VolumeVO> sc_volume = _volsDao.createSearchCriteria();
+                    sc_volume.addAnd("path", SearchCriteria.Op.EQ, vmDiskStat.getPath());
+                    VolumeVO volume = _volsDao.search(sc_volume, null).get(0);
+	            VmDiskStatisticsVO previousVmDiskStats = _vmDiskStatsDao.findBy(userVm.getAccountId(), userVm.getDataCenterId(), userVm.getId(), volume.getId());
+	            VmDiskStatisticsVO vmDiskStat_lock = _vmDiskStatsDao.lock(userVm.getAccountId(), userVm.getDataCenterId(), userVm.getId(), volume.getId());
+	                
+	                if ((vmDiskStat.getIORead() == 0) && (vmDiskStat.getIOWrite() == 0) && (vmDiskStat.getBytesRead() == 0) && (vmDiskStat.getBytesWrite() == 0)) {
+	                    s_logger.debug("Read/Write of IO and Bytes are both 0. Not updating vm_disk_statistics");
+	                    continue;
+	                }
+	                
+	                if (vmDiskStat_lock == null) {
+	                    s_logger.warn("unable to find vm disk stats from host for account: " + userVm.getAccountId() + " with vmId: " + userVm.getId()+ " and volumeId:" + volume.getId());
+	                    continue;
+	                }
+	
+	                if (previousVmDiskStats != null
+	                        && ((previousVmDiskStats.getCurrentIORead() != vmDiskStat_lock.getCurrentIORead())
+	                        || ((previousVmDiskStats.getCurrentIOWrite() != vmDiskStat_lock.getCurrentIOWrite())
+	                        || (previousVmDiskStats.getCurrentBytesRead() != vmDiskStat_lock.getCurrentBytesRead())
+	    	                || (previousVmDiskStats.getCurrentBytesWrite() != vmDiskStat_lock.getCurrentBytesWrite())))) {
+	                    s_logger.debug("vm disk stats changed from the time GetVmDiskStatsCommand was sent. " +
+	                            "Ignoring current answer. Host: " + host.getName() + " . VM: " + vmDiskStat.getVmName() + 
+	                            " IO Read: " + vmDiskStat.getIORead() + " IO Write: " + vmDiskStat.getIOWrite() +
+	                            " Bytes Read: " + vmDiskStat.getBytesRead() + " Bytes Write: " + vmDiskStat.getBytesWrite());
+	                    continue;
+	                }
+	
+	                if (vmDiskStat_lock.getCurrentIORead() > vmDiskStat.getIORead()) {
+	                    if (s_logger.isDebugEnabled()) {
+	                        s_logger.debug("Read # of IO that's less than the last one.  " +
+	                                "Assuming something went wrong and persisting it. Host: " + host.getName() + " . VM: " + vmDiskStat.getVmName() +
+	                                " Reported: " + vmDiskStat.getIORead() + " Stored: " + vmDiskStat_lock.getCurrentIORead());
+	                    }
+	                    vmDiskStat_lock.setNetIORead(vmDiskStat_lock.getNetIORead() + vmDiskStat_lock.getCurrentIORead());
+	                }
+	                vmDiskStat_lock.setCurrentIORead(vmDiskStat.getIORead());
+	                if (vmDiskStat_lock.getCurrentIOWrite() > vmDiskStat.getIOWrite()) {
+	                    if (s_logger.isDebugEnabled()) {
+	                        s_logger.debug("Write # of IO that's less than the last one.  " +
+	                                "Assuming something went wrong and persisting it. Host: " + host.getName() + " . VM: " + vmDiskStat.getVmName() +
+	                                " Reported: " + vmDiskStat.getIOWrite() + " Stored: " + vmDiskStat_lock.getCurrentIOWrite());
+	                    }
+	                    vmDiskStat_lock.setNetIOWrite(vmDiskStat_lock.getNetIOWrite() + vmDiskStat_lock.getCurrentIOWrite());
+	                }
+	                vmDiskStat_lock.setCurrentIOWrite(vmDiskStat.getIOWrite());
+	                if (vmDiskStat_lock.getCurrentBytesRead() > vmDiskStat.getBytesRead()) {
+	                    if (s_logger.isDebugEnabled()) {
+	                        s_logger.debug("Read # of Bytes that's less than the last one.  " +
+	                                "Assuming something went wrong and persisting it. Host: " + host.getName() + " . VM: " + vmDiskStat.getVmName() +
+	                                " Reported: " + vmDiskStat.getBytesRead() + " Stored: " + vmDiskStat_lock.getCurrentBytesRead());
+	                    }
+	                    vmDiskStat_lock.setNetBytesRead(vmDiskStat_lock.getNetBytesRead() + vmDiskStat_lock.getCurrentBytesRead());
+	                }
+	                vmDiskStat_lock.setCurrentBytesRead(vmDiskStat.getBytesRead());
+	                if (vmDiskStat_lock.getCurrentBytesWrite() > vmDiskStat.getBytesWrite()) {
+	                    if (s_logger.isDebugEnabled()) {
+	                        s_logger.debug("Write # of Bytes that's less than the last one.  " +
+	                                "Assuming something went wrong and persisting it. Host: " + host.getName() + " . VM: " + vmDiskStat.getVmName() +
+	                                " Reported: " + vmDiskStat.getBytesWrite() + " Stored: " + vmDiskStat_lock.getCurrentBytesWrite());
+	                    }
+	                    vmDiskStat_lock.setNetBytesWrite(vmDiskStat_lock.getNetBytesWrite() + vmDiskStat_lock.getCurrentBytesWrite());
+	                }
+	                vmDiskStat_lock.setCurrentBytesWrite(vmDiskStat.getBytesWrite());
+	                
+	                if (! _dailyOrHourly) {
+	                    //update agg bytes 
+	                	vmDiskStat_lock.setAggIORead(vmDiskStat_lock.getNetIORead() + vmDiskStat_lock.getCurrentIORead());
+	                	vmDiskStat_lock.setAggIOWrite(vmDiskStat_lock.getNetIOWrite() + vmDiskStat_lock.getCurrentIOWrite());
+	                	vmDiskStat_lock.setAggBytesRead(vmDiskStat_lock.getNetBytesRead() + vmDiskStat_lock.getCurrentBytesRead());
+	                	vmDiskStat_lock.setAggBytesWrite(vmDiskStat_lock.getNetBytesWrite() + vmDiskStat_lock.getCurrentBytesWrite());
+	                }
+	
+	                _vmDiskStatsDao.update(vmDiskStat_lock.getId(), vmDiskStat_lock);
+	        	}
+	        	txn.commit();
+            } catch (Exception e) {
+                txn.rollback();
+                s_logger.warn("Unable to update vm disk statistics for vm: " + userVm.getId() + " from host: " + hostId, e);
+            } finally {
+                txn.close();
+            }
+        }
     }
 
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/server/test/async-job-component.xml
----------------------------------------------------------------------
diff --git a/server/test/async-job-component.xml b/server/test/async-job-component.xml
index 4698252..55f47cc 100644
--- a/server/test/async-job-component.xml
+++ b/server/test/async-job-component.xml
@@ -74,6 +74,7 @@ under the License.
             <param name="cache.time.to.live">300</param>
         </dao>
         <dao name="UserStats" class="com.cloud.user.dao.UserStatisticsDaoImpl"/>
+        <dao name="VmDiskStats" class="com.cloud.user.dao.VmDiskStatisticsDaoImpl"/>
         <dao name="Disk Template" class="com.cloud.storage.dao.DiskTemplateDaoImpl">
             <param name="cache.size">50</param>
             <param name="cache.time.to.live">-1</param>

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/server/test/com/cloud/vm/MockUserVmManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/test/com/cloud/vm/MockUserVmManagerImpl.java b/server/test/com/cloud/vm/MockUserVmManagerImpl.java
index 448a5dd..40c49d4 100644
--- a/server/test/com/cloud/vm/MockUserVmManagerImpl.java
+++ b/server/test/com/cloud/vm/MockUserVmManagerImpl.java
@@ -47,6 +47,7 @@ import org.apache.cloudstack.api.command.user.vmgroup.DeleteVMGroupCmd;
 import org.springframework.stereotype.Component;
 
 import com.cloud.agent.api.StopAnswer;
+import com.cloud.agent.api.VmDiskStatsEntry;
 import com.cloud.agent.api.VmStatsEntry;
 import com.cloud.agent.api.to.NicTO;
 import com.cloud.agent.api.to.VirtualMachineTO;
@@ -169,6 +170,12 @@ public class MockUserVmManagerImpl extends ManagerBase implements UserVmManager,
     }
 
     @Override
+    public HashMap<Long, List<VmDiskStatsEntry>> getVmDiskStatistics(long hostId, String hostName, List<Long> vmIds) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
     public boolean deleteVmGroup(long groupId) {
         // TODO Auto-generated method stub
         return false;
@@ -461,4 +468,9 @@ public class MockUserVmManagerImpl extends ManagerBase implements UserVmManager,
 		// TODO Auto-generated method stub
 		return false;
 	}
+
+    @Override
+    public void collectVmDiskStatistics (UserVmVO userVm) {
+        // TODO Auto-generated method stub
+    }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/setup/db/db/schema-410to420.sql
----------------------------------------------------------------------
diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql
index 03e14a9..bcfbcc9 100644
--- a/setup/db/db/schema-410to420.sql
+++ b/setup/db/db/schema-410to420.sql
@@ -1771,6 +1771,82 @@ INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'manag
 
 UPDATE `cloud`.`snapshots` set swift_id=null where swift_id=0;
 
+DROP TABLE IF EXISTS `cloud`.`vm_disk_statistics`;
+CREATE TABLE `cloud`.`vm_disk_statistics` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+  `data_center_id` bigint(20) unsigned NOT NULL,
+  `account_id` bigint(20) unsigned NOT NULL,
+  `vm_id` bigint(20) unsigned NOT NULL,
+  `volume_id` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `net_io_read` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `net_io_write` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `current_io_read` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `current_io_write` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `agg_io_read` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `agg_io_write` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `net_bytes_read` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `net_bytes_write` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `current_bytes_read` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `current_bytes_write` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `agg_bytes_read` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `agg_bytes_write` bigint(20) unsigned NOT NULL DEFAULT '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `id` (`id`),
+  UNIQUE KEY `account_id` (`account_id`,`data_center_id`,`vm_id`,`volume_id`),
+  KEY `i_vm_disk_statistics__account_id` (`account_id`),
+  KEY `i_vm_disk_statistics__account_id_data_center_id` (`account_id`,`data_center_id`),
+  CONSTRAINT `fk_vm_disk_statistics__account_id` FOREIGN KEY (`account_id`) REFERENCES `account` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=utf8;
+
+insert into `cloud`.`vm_disk_statistics`(data_center_id,account_id,vm_id,volume_id) 
+select volumes.data_center_id, volumes.account_id, vm_instance.id, volumes.id from volumes,vm_instance where vm_instance.vm_type="User" and vm_instance.state<>"Expunging" and volumes.instance_id=vm_instance.id order by vm_instance.id;
+
+DROP TABLE IF EXISTS `cloud_usage`.`vm_disk_statistics`;
+CREATE TABLE `cloud_usage`.`vm_disk_statistics` (
+  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
+  `data_center_id` bigint(20) unsigned NOT NULL,
+  `account_id` bigint(20) unsigned NOT NULL,
+  `vm_id` bigint(20) unsigned NOT NULL,
+  `volume_id` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `net_io_read` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `net_io_write` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `current_io_read` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `current_io_write` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `agg_io_read` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `agg_io_write` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `net_bytes_read` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `net_bytes_write` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `current_bytes_read` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `current_bytes_write` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `agg_bytes_read` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `agg_bytes_write` bigint(20) unsigned NOT NULL DEFAULT '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `id` (`id`),
+  UNIQUE KEY `account_id` (`account_id`,`data_center_id`,`vm_id`,`volume_id`)
+) ENGINE=InnoDB CHARSET=utf8;
+
+insert into `cloud_usage`.`vm_disk_statistics` select * from `cloud`.`vm_disk_statistics`;
+
+DROP TABLE IF EXISTS `cloud_usage`.`usage_vm_disk`;
+CREATE TABLE `cloud_usage`.`usage_vm_disk` (
+  `account_id` bigint(20) unsigned NOT NULL,
+  `zone_id` bigint(20) unsigned NOT NULL,
+  `vm_id` bigint(20) unsigned NOT NULL,
+  `volume_id` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `io_read` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `io_write` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `agg_io_read` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `agg_io_write` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `bytes_read` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `bytes_write` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `agg_bytes_read` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `agg_bytes_write` bigint(20) unsigned NOT NULL DEFAULT '0',
+  `event_time_millis` bigint(20) unsigned NOT NULL DEFAULT '0',
+  PRIMARY KEY (`account_id`,`zone_id`,`vm_id`,`volume_id`,`event_time_millis`)
+) ENGINE=InnoDB CHARSET=utf8;
+
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Advanced', 'DEFAULT', 'management-server', 'vm.disk.stats.interval', 0, 'Interval (in seconds) to report vm disk statistics.');
+
 
 -- Re-enable foreign key checking, at the end of the upgrade path
 SET foreign_key_checks = 1;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/ui/dictionary.jsp
----------------------------------------------------------------------
diff --git a/ui/dictionary.jsp b/ui/dictionary.jsp
index ded9ea0..d7f7dd5 100644
--- a/ui/dictionary.jsp
+++ b/ui/dictionary.jsp
@@ -469,11 +469,15 @@ dictionary = {
 'label.disable.vpn': '<fmt:message key="label.disable.vpn" />',
 'label.disabling.vpn.access': '<fmt:message key="label.disabling.vpn.access" />',
 'label.disk.allocated': '<fmt:message key="label.disk.allocated" />',
+'label.disk.read.bytes': '<fmt:message key="label.disk.read.bytes" />',
+'label.disk.read.io': '<fmt:message key="label.disk.read.io" />',
 'label.disk.offering': '<fmt:message key="label.disk.offering" />',
 'label.disk.size': '<fmt:message key="label.disk.size" />',
 'label.disk.size.gb': '<fmt:message key="label.disk.size.gb" />',
 'label.disk.total': '<fmt:message key="label.disk.total" />',
 'label.disk.volume': '<fmt:message key="label.disk.volume" />',
+'label.disk.write.bytes': '<fmt:message key="label.disk.write.bytes" />',
+'label.disk.write.io': '<fmt:message key="label.disk.write.io" />',
 'label.display.name': '<fmt:message key="label.display.name" />',
 'label.display.text': '<fmt:message key="label.display.text" />',
 'label.dns.1': '<fmt:message key="label.dns.1" />',

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/ui/scripts/instances.js
----------------------------------------------------------------------
diff --git a/ui/scripts/instances.js b/ui/scripts/instances.js
index 31237a8..6a589ba 100644
--- a/ui/scripts/instances.js
+++ b/ui/scripts/instances.js
@@ -1648,7 +1648,11 @@
               totalCPU: { label: 'label.total.cpu' },
               cpuused: { label: 'label.cpu.utilized' },
               networkkbsread: { label: 'label.network.read' },
-              networkkbswrite: { label: 'label.network.write' }
+              networkkbswrite: { label: 'label.network.write' },
+              diskkbsread: { label: 'label.disk.read.bytes' },
+              diskkbswrite: { label: 'label.disk.write.bytes' },
+              diskioread: { label: 'label.disk.read.io' },
+              diskiowrite: { label: 'label.disk.write.io' }
             },
             dataProvider: function(args) {
               $.ajax({
@@ -1662,7 +1666,11 @@
                   totalCPU: jsonObj.cpunumber + " x " + cloudStack.converters.convertHz(jsonObj.cpuspeed),
                   cpuused: jsonObj.cpuused,
                   networkkbsread: (jsonObj.networkkbsread == null)? "N/A": cloudStack.converters.convertBytes(jsonObj.networkkbsread * 1024),
-                  networkkbswrite: (jsonObj.networkkbswrite == null)? "N/A": cloudStack.converters.convertBytes(jsonObj.networkkbswrite * 1024)
+                  networkkbswrite: (jsonObj.networkkbswrite == null)? "N/A": cloudStack.converters.convertBytes(jsonObj.networkkbswrite * 1024),
+                  diskkbsread: (jsonObj.diskkbsread == null)? "N/A": cloudStack.converters.convertBytes(jsonObj.diskkbsread * 1024),
+                  diskkbswrite: (jsonObj.diskkbswrite == null)? "N/A": cloudStack.converters.convertBytes(jsonObj.diskkbswrite * 1024),
+                  diskioread: (jsonObj.diskioread == null)? "N/A": cloudStack.converters.convertBytes(jsonObj.diskioread * 1024),
+                  diskiowrite: (jsonObj.diskiowrite == null)? "N/A": cloudStack.converters.convertBytes(jsonObj.diskiowrite * 1024)
                   }
                 });
               }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/usage/src/com/cloud/usage/UsageManagerImpl.java
----------------------------------------------------------------------
diff --git a/usage/src/com/cloud/usage/UsageManagerImpl.java b/usage/src/com/cloud/usage/UsageManagerImpl.java
index 0c2ad6e..65f354c 100644
--- a/usage/src/com/cloud/usage/UsageManagerImpl.java
+++ b/usage/src/com/cloud/usage/UsageManagerImpl.java
@@ -54,6 +54,7 @@ import com.cloud.usage.dao.UsageSecurityGroupDao;
 import com.cloud.usage.dao.UsageStorageDao;
 import com.cloud.usage.dao.UsageVMInstanceDao;
 import com.cloud.usage.dao.UsageVPNUserDao;
+import com.cloud.usage.dao.UsageVmDiskDao;
 import com.cloud.usage.dao.UsageVolumeDao;
 import com.cloud.usage.parser.IPAddressUsageParser;
 import com.cloud.usage.parser.LoadBalancerUsageParser;
@@ -64,13 +65,15 @@ import com.cloud.usage.parser.SecurityGroupUsageParser;
 import com.cloud.usage.parser.StorageUsageParser;
 import com.cloud.usage.parser.VMInstanceUsageParser;
 import com.cloud.usage.parser.VPNUserUsageParser;
+import com.cloud.usage.parser.VmDiskUsageParser;
 import com.cloud.usage.parser.VolumeUsageParser;
 import com.cloud.user.Account;
 import com.cloud.user.AccountVO;
 import com.cloud.user.UserStatisticsVO;
+import com.cloud.user.VmDiskStatisticsVO;
 import com.cloud.user.dao.AccountDao;
 import com.cloud.user.dao.UserStatisticsDao;
-
+import com.cloud.user.dao.VmDiskStatisticsDao;
 
 import com.cloud.utils.component.ManagerBase;
 import com.cloud.utils.concurrency.NamedThreadFactory;
@@ -108,6 +111,8 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
     @Inject private UsageVPNUserDao m_usageVPNUserDao;
     @Inject private UsageSecurityGroupDao m_usageSecurityGroupDao;
     @Inject private UsageJobDao m_usageJobDao;
+    @Inject private VmDiskStatisticsDao m_vmDiskStatsDao;
+    @Inject private UsageVmDiskDao m_usageVmDiskDao;
     @Inject protected AlertManager _alertMgr;
     @Inject protected UsageEventDao _usageEventDao;
     @Inject ConfigurationDao _configDao;
@@ -121,6 +126,7 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
     TimeZone m_usageTimezone = TimeZone.getTimeZone("GMT");;
     private final GlobalLock m_heartbeatLock = GlobalLock.getInternLock("usage.job.heartbeat.check");
     private List<UsageNetworkVO> usageNetworks = new ArrayList<UsageNetworkVO>();
+    private List<UsageVmDiskVO> usageVmDisks = new ArrayList<UsageVmDiskVO>();
 
     private final ScheduledExecutorService m_executor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("Usage-Job"));
     private final ScheduledExecutorService m_heartbeatExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreadFactory("Usage-HB"));
@@ -389,6 +395,8 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
             List<AccountVO> accounts = null;
             List<UserStatisticsVO> userStats = null;
             Map<String, UsageNetworkVO> networkStats = null;
+            List<VmDiskStatisticsVO> vmDiskStats = null;
+            Map<String, UsageVmDiskVO> vmDiskUsages = null;
             Transaction userTxn = Transaction.open(Transaction.CLOUD_DB);
             try {
                 Long limit = Long.valueOf(500);
@@ -479,6 +487,46 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
                     }
                     offset = new Long(offset.longValue() + limit.longValue());
                 } while ((userStats != null) && !userStats.isEmpty());
+
+                // reset offset
+                offset = Long.valueOf(0);
+
+                // get all the vm network stats to create usage_vm_network records for the vm network usage
+                Long lastVmDiskStatsId = m_usageDao.getLastVmDiskStatsId();
+                if (lastVmDiskStatsId == null) {
+                       lastVmDiskStatsId = Long.valueOf(0);
+                }
+                SearchCriteria<VmDiskStatisticsVO> sc4 = m_vmDiskStatsDao.createSearchCriteria();
+                sc4.addAnd("id", SearchCriteria.Op.LTEQ, lastVmDiskStatsId);
+                do {
+                    Filter filter = new Filter(VmDiskStatisticsVO.class, "id", true, offset, limit);
+
+                    vmDiskStats = m_vmDiskStatsDao.search(sc4, filter);
+
+                    if ((vmDiskStats != null) && !vmDiskStats.isEmpty()) {
+                        // now copy the accounts to cloud_usage db
+                        m_usageDao.updateVmDiskStats(vmDiskStats);
+                    }
+                    offset = new Long(offset.longValue() + limit.longValue());
+                } while ((vmDiskStats != null) && !vmDiskStats.isEmpty());
+
+                // reset offset
+                offset = Long.valueOf(0);
+
+                sc4 = m_vmDiskStatsDao.createSearchCriteria();
+                sc4.addAnd("id", SearchCriteria.Op.GT, lastVmDiskStatsId);
+                do {
+                    Filter filter = new Filter(VmDiskStatisticsVO.class, "id", true, offset, limit);
+
+                    vmDiskStats = m_vmDiskStatsDao.search(sc4, filter);
+
+                    if ((vmDiskStats != null) && !vmDiskStats.isEmpty()) {
+                        // now copy the accounts to cloud_usage db
+                        m_usageDao.saveVmDiskStats(vmDiskStats);
+                    }
+                    offset = new Long(offset.longValue() + limit.longValue());
+                } while ((vmDiskStats != null) && !vmDiskStats.isEmpty());
+
             } finally {
                 userTxn.close();
             }
@@ -565,6 +613,53 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
                     s_logger.debug("created network stats helper entries for " + numAcctsProcessed + " accts");
                 }
 
+                // get vm disk stats in order to compute vm disk usage
+                vmDiskUsages = m_usageVmDiskDao.getRecentVmDiskStats();
+
+                // Keep track of user stats for an account, across all of its public IPs
+                Map<String, VmDiskStatisticsVO> aggregatedDiskStats = new HashMap<String, VmDiskStatisticsVO>();
+                startIndex = 0;
+                do {
+                       vmDiskStats = m_vmDiskStatsDao.listActiveAndRecentlyDeleted(recentlyDeletedDate, startIndex, 500);
+
+                    if (vmDiskUsages != null) {
+                        for (VmDiskStatisticsVO vmDiskStat : vmDiskStats) {
+                            if(vmDiskStat.getVmId() != null){
+                                String hostKey = vmDiskStat.getDataCenterId() + "-" + vmDiskStat.getAccountId()+"-Vm-" + vmDiskStat.getVmId()+"-Disk-" + vmDiskStat.getVolumeId();
+                                VmDiskStatisticsVO hostAggregatedStat = aggregatedDiskStats.get(hostKey);
+                                if (hostAggregatedStat == null) {
+                                    hostAggregatedStat = new VmDiskStatisticsVO(vmDiskStat.getAccountId(), vmDiskStat.getDataCenterId(), vmDiskStat.getVmId(),vmDiskStat.getVolumeId());
+                                }
+
+                                hostAggregatedStat.setAggIORead(hostAggregatedStat.getAggIORead() + vmDiskStat.getAggIORead());
+                                hostAggregatedStat.setAggIOWrite(hostAggregatedStat.getAggIOWrite() + vmDiskStat.getAggIOWrite());
+                                hostAggregatedStat.setAggBytesRead(hostAggregatedStat.getAggBytesRead() + vmDiskStat.getAggBytesRead());
+                                hostAggregatedStat.setAggBytesWrite(hostAggregatedStat.getAggBytesWrite() + vmDiskStat.getAggBytesWrite());
+                                aggregatedDiskStats.put(hostKey, hostAggregatedStat);
+                            }
+                        }
+                    }
+                    startIndex += 500;
+                } while ((userStats != null) && !userStats.isEmpty());
+
+                // loop over the user stats, create delta entries in the usage_disk helper table
+                numAcctsProcessed = 0;
+                usageVmDisks.clear();
+                for (String key : aggregatedDiskStats.keySet()) {
+                       UsageVmDiskVO currentVmDiskStats = null;
+                    if (vmDiskStats != null) {
+                        currentVmDiskStats = vmDiskUsages.get(key);
+                    }
+
+                    createVmDiskHelperEntry(aggregatedDiskStats.get(key), currentVmDiskStats, endDateMillis);
+                    numAcctsProcessed++;
+                }
+                m_usageVmDiskDao.saveUsageVmDisks(usageVmDisks);
+
+                if (s_logger.isDebugEnabled()) {
+                    s_logger.debug("created vm disk stats helper entries for " + numAcctsProcessed + " accts");
+                }
+
                 // commit the helper records, then start a new transaction
                 usageTxn.commit();
                 usageTxn.start();
@@ -701,6 +796,13 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
             }
         }
 
+        parsed = VmDiskUsageParser.parse(account, currentStartDate, currentEndDate);
+        if (s_logger.isDebugEnabled()) {
+            if (!parsed) {
+                s_logger.debug("vm disk usage successfully parsed? " + parsed + " (for account: " + account.getAccountName() + ", id: " + account.getId() + ")");
+            }
+        }
+
         parsed = VolumeUsageParser.parse(account, currentStartDate, currentEndDate);
         if (s_logger.isDebugEnabled()) {
             if (!parsed) {
@@ -1006,6 +1108,59 @@ public class UsageManagerImpl extends ManagerBase implements UsageManager, Runna
         usageNetworks.add(usageNetworkVO);
     }
 
+    private void createVmDiskHelperEntry(VmDiskStatisticsVO vmDiskStat, UsageVmDiskVO usageVmDiskStat, long timestamp) {
+        long currentAccountedIORead = 0L;
+        long currentAccountedIOWrite = 0L;
+        long currentAccountedBytesRead = 0L;
+        long currentAccountedBytesWrite = 0L;
+        if (usageVmDiskStat != null) {
+            if (s_logger.isDebugEnabled()) {
+                s_logger.debug("getting current accounted bytes for... accountId: " + usageVmDiskStat.getAccountId() + " in zone: " + vmDiskStat.getDataCenterId() + "; aiw: " + vmDiskStat.getAggIOWrite() +
+                        "; air: " + usageVmDiskStat.getAggIORead() + "; abw: " + vmDiskStat.getAggBytesWrite() + "; abr: " + usageVmDiskStat.getAggBytesRead());
+            }
+            currentAccountedIORead = usageVmDiskStat.getAggIORead();
+            currentAccountedIOWrite = usageVmDiskStat.getAggIOWrite();
+            currentAccountedBytesRead = usageVmDiskStat.getAggBytesRead();
+            currentAccountedBytesWrite = usageVmDiskStat.getAggBytesWrite();
+        }
+        long ioRead = vmDiskStat.getAggIORead()  - currentAccountedIORead;
+        long ioWrite = vmDiskStat.getAggIOWrite() - currentAccountedIOWrite;
+        long bytesRead = vmDiskStat.getAggBytesRead()  - currentAccountedBytesRead;
+        long bytesWrite = vmDiskStat.getAggBytesWrite() - currentAccountedBytesWrite;
+
+        if (ioRead < 0) {
+            s_logger.warn("Calculated negative value for io read: " + ioRead + ", vm disk stats say: " + vmDiskStat.getAggIORead() + ", previous vm disk usage was: " + currentAccountedIORead);
+            ioRead = 0;
+        }
+        if (ioWrite < 0) {
+            s_logger.warn("Calculated negative value for io write: " + ioWrite + ", vm disk stats say: " + vmDiskStat.getAggIOWrite() + ", previous vm disk usage was: " + currentAccountedIOWrite);
+            ioWrite = 0;
+        }
+        if (bytesRead < 0) {
+            s_logger.warn("Calculated negative value for bytes read: " + bytesRead + ", vm disk stats say: " + vmDiskStat.getAggBytesRead() + ", previous vm disk usage was: " + currentAccountedBytesRead);
+            bytesRead = 0;
+        }
+        if (bytesWrite < 0) {
+            s_logger.warn("Calculated negative value for bytes write: " + bytesWrite + ", vm disk stats say: " + vmDiskStat.getAggBytesWrite() + ", previous vm disk usage was: " + currentAccountedBytesWrite);
+            bytesWrite = 0;
+        }
+
+        long vmId = 0;
+
+        if(vmDiskStat.getVmId() != null){
+               vmId = vmDiskStat.getVmId();
+        }
+
+        UsageVmDiskVO usageVmDiskVO = new UsageVmDiskVO(vmDiskStat.getAccountId(), vmDiskStat.getDataCenterId(), vmId, vmDiskStat.getVolumeId(), ioRead, ioWrite,
+                       vmDiskStat.getAggIORead(), vmDiskStat.getAggIOWrite(), bytesRead, bytesWrite, vmDiskStat.getAggBytesRead(), vmDiskStat.getAggBytesWrite(), timestamp);
+        if (s_logger.isDebugEnabled()) {
+            s_logger.debug("creating vmDiskHelperEntry... accountId: " + vmDiskStat.getAccountId() + " in zone: " + vmDiskStat.getDataCenterId() + "; aiw: " + vmDiskStat.getAggIOWrite() + "; air: " + vmDiskStat.getAggIORead() +
+                    "; curAIR: " + currentAccountedIORead + "; curAIW: " + currentAccountedIOWrite + "; uir: " + ioRead + "; uiw: " + ioWrite + "; abw: " + vmDiskStat.getAggBytesWrite() + "; abr: " + vmDiskStat.getAggBytesRead() +
+                    "; curABR: " + currentAccountedBytesRead + "; curABW: " + currentAccountedBytesWrite + "; ubr: " + bytesRead + "; ubw: " + bytesWrite);
+        }
+        usageVmDisks.add(usageVmDiskVO);
+    }
+
     private void createIPHelperEvent(UsageEventVO event) {
 
         String ipAddress = event.getResourceName();

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/usage/src/com/cloud/usage/parser/VmDiskUsageParser.java
----------------------------------------------------------------------
diff --git a/usage/src/com/cloud/usage/parser/VmDiskUsageParser.java b/usage/src/com/cloud/usage/parser/VmDiskUsageParser.java
new file mode 100644
index 0000000..b8a5f98
--- /dev/null
+++ b/usage/src/com/cloud/usage/parser/VmDiskUsageParser.java
@@ -0,0 +1,208 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// the License.  You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.usage.parser;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.PostConstruct;
+import javax.inject.Inject;
+
+import org.apache.log4j.Logger;
+import org.apache.cloudstack.usage.UsageTypes;
+
+import com.cloud.usage.UsageVO;
+import com.cloud.usage.UsageVmDiskVO;
+import com.cloud.usage.dao.UsageDao;
+import com.cloud.usage.dao.UsageVmDiskDao;
+import com.cloud.user.AccountVO;
+
+import com.cloud.utils.db.SearchCriteria;
+import org.springframework.stereotype.Component;
+
+@Component
+public class VmDiskUsageParser {
+public static final Logger s_logger = Logger.getLogger(VmDiskUsageParser.class.getName());
+
+    private static UsageDao m_usageDao;
+    private static UsageVmDiskDao m_usageVmDiskDao;
+
+    @Inject private UsageDao _usageDao;
+    @Inject private UsageVmDiskDao _usageVmDiskDao;
+
+    @PostConstruct
+    void init() {
+        m_usageDao = _usageDao;
+        m_usageVmDiskDao = _usageVmDiskDao;
+    }
+
+    public static boolean parse(AccountVO account, Date startDate, Date endDate) {
+        if (s_logger.isDebugEnabled()) {
+            s_logger.debug("Parsing all Vm Disk usage events for account: " + account.getId());
+        }
+
+        if ((endDate == null) || endDate.after(new Date())) {
+            endDate = new Date();
+        }
+
+        // - query usage_disk table for all entries for userId with
+        // event_date in the given range
+        SearchCriteria<UsageVmDiskVO> sc = m_usageVmDiskDao.createSearchCriteria();
+        sc.addAnd("accountId", SearchCriteria.Op.EQ, account.getId());
+        sc.addAnd("eventTimeMillis", SearchCriteria.Op.BETWEEN, startDate.getTime(), endDate.getTime());
+        List<UsageVmDiskVO> usageVmDiskVOs = m_usageVmDiskDao.search(sc, null);
+
+        Map<String, VmDiskInfo> vmDiskUsageByZone = new HashMap<String, VmDiskInfo>();
+
+        // Calculate the bytes since last parsing
+        for (UsageVmDiskVO usageVmDisk : usageVmDiskVOs) {
+            long zoneId = usageVmDisk.getZoneId();
+            String key = ""+zoneId;
+            if(usageVmDisk.getVmId() != 0){
+                key += "-Vm-" + usageVmDisk.getVmId()+"-Disk-" + usageVmDisk.getVolumeId();
+            }
+            VmDiskInfo vmDiskInfo = vmDiskUsageByZone.get(key);
+
+            long ioRead = usageVmDisk.getIORead();
+            long ioWrite = usageVmDisk.getIOWrite();
+            long bytesRead = usageVmDisk.getBytesRead();
+            long bytesWrite = usageVmDisk.getBytesWrite();
+            if (vmDiskInfo != null) {
+                ioRead += vmDiskInfo.getIORead();
+                ioWrite += vmDiskInfo.getIOWrite();
+                bytesRead += vmDiskInfo.getBytesRead();
+                bytesWrite += vmDiskInfo.getBytesWrite();
+            }
+
+            vmDiskUsageByZone.put(key, new VmDiskInfo(zoneId, usageVmDisk.getVmId(), usageVmDisk.getVolumeId(), ioRead, ioWrite, bytesRead, bytesWrite));
+        }
+
+        for (String key : vmDiskUsageByZone.keySet()) {
+            VmDiskInfo vmDiskInfo = vmDiskUsageByZone.get(key);
+            long ioRead = vmDiskInfo.getIORead();
+            long ioWrite = vmDiskInfo.getIOWrite();
+            long bytesRead = vmDiskInfo.getBytesRead();
+            long bytesWrite = vmDiskInfo.getBytesWrite();
+
+            if ((ioRead > 0L) || (ioWrite > 0L) || (bytesRead > 0L) || (bytesWrite > 0L)) {
+                if (s_logger.isDebugEnabled()) {
+                    s_logger.debug("Creating vm disk usage record, io read:" + ioRead + ", io write: " + ioWrite + "bytes read:" + bytesRead + ", bytes write: " + bytesWrite + "for account: "
+                            + account.getId() + " in availability zone " + vmDiskInfo.getZoneId() + ", start: " + startDate + ", end: " + endDate);
+                }
+
+                Long vmId = null;
+                
+                // Create the usage record for bytes read
+                String usageDesc = "disk bytes read";
+                if(vmDiskInfo.getVmId() != 0){
+                    vmId = vmDiskInfo.getVmId();
+                    usageDesc += " for Vm: "+vmDiskInfo.getVmId()+" and Volume: "+ vmDiskInfo.getVolumeId(); 
+                }
+                UsageVO usageRecord = new UsageVO(vmDiskInfo.getZoneId(), account.getId(), account.getDomainId(), usageDesc, ioRead + " io read",
+                        UsageTypes.VM_DISK_IO_READ, new Double(ioRead), vmId, "VirtualMachine", vmDiskInfo.getVolumeId(), startDate, endDate);
+                m_usageDao.persist(usageRecord);
+
+                // Create the usage record for bytes write
+                usageDesc = "disk bytes write";
+                if(vmDiskInfo.getVmId() != 0){
+                    usageDesc += " for Vm: "+vmDiskInfo.getVmId()+" and Volume: "+ vmDiskInfo.getVolumeId(); 
+                }
+                usageRecord = new UsageVO(vmDiskInfo.getZoneId(), account.getId(), account.getDomainId(), usageDesc, ioWrite + " io write",
+                        UsageTypes.VM_DISK_BYTES_WRITE, new Double(ioWrite), vmId, "VirtualMachine", vmDiskInfo.getVolumeId(), startDate, endDate);
+                m_usageDao.persist(usageRecord);
+                
+                // Create the usage record for bytes read
+                usageDesc = "disk bytes read";
+                if(vmDiskInfo.getVmId() != 0){
+                    vmId = vmDiskInfo.getVmId();
+                    usageDesc += " for Vm: "+vmDiskInfo.getVmId()+" and Volume: "+ vmDiskInfo.getVolumeId(); 
+                }
+                usageRecord = new UsageVO(vmDiskInfo.getZoneId(), account.getId(), account.getDomainId(), usageDesc, bytesRead + " bytes read",
+                        UsageTypes.VM_DISK_BYTES_READ, new Double(bytesRead), vmId, "VirtualMachine", vmDiskInfo.getVolumeId(), startDate, endDate);
+                m_usageDao.persist(usageRecord);
+
+                // Create the usage record for bytes write
+                usageDesc = "disk bytes write";
+                if(vmDiskInfo.getVmId() != 0){
+                    usageDesc += " for Vm: "+vmDiskInfo.getVmId()+" and Volume: "+ vmDiskInfo.getVolumeId(); 
+                }
+                usageRecord = new UsageVO(vmDiskInfo.getZoneId(), account.getId(), account.getDomainId(), usageDesc, bytesWrite + " bytes write",
+                        UsageTypes.VM_DISK_BYTES_WRITE, new Double(bytesWrite), vmId, "VirtualMachine", vmDiskInfo.getVolumeId(), startDate, endDate);
+                m_usageDao.persist(usageRecord);
+                
+            } else {
+                // Don't charge anything if there were zero bytes processed
+                if (s_logger.isDebugEnabled()) {
+                    s_logger.debug("No vm disk usage record (0 bytes used) generated for account: " + account.getId());
+                }
+            }
+        }
+
+        return true;
+    }
+    
+    private static class VmDiskInfo {
+        private long zoneId;
+        private long vmId;
+        private Long volumeId;
+        private long ioRead;
+        private long ioWrite;
+        private long bytesRead;
+        private long bytesWrite;
+
+        public VmDiskInfo(long zoneId, long vmId, Long volumeId, long ioRead, long ioWrite, long bytesRead, long bytesWrite) {
+            this.zoneId = zoneId;
+            this.vmId = vmId;
+            this.volumeId = volumeId;
+            this.ioRead = ioRead;
+            this.ioWrite = ioWrite;
+            this.bytesRead = bytesRead;
+            this.bytesWrite = bytesWrite;
+        }
+
+        public long getZoneId() {
+            return zoneId;
+        }
+
+        public long getVmId() {
+            return vmId;
+        }
+        
+        public Long getVolumeId() {
+            return volumeId;
+        }
+
+        public long getIORead() {
+            return ioRead;
+        }
+
+        public long getIOWrite() {
+            return ioWrite;
+        }
+        
+        public long getBytesRead() {
+            return bytesRead;
+        }
+
+        public long getBytesWrite() {
+            return bytesWrite;
+        }
+    
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/usage/test/com/cloud/usage/UsageManagerTest.java
----------------------------------------------------------------------
diff --git a/usage/test/com/cloud/usage/UsageManagerTest.java b/usage/test/com/cloud/usage/UsageManagerTest.java
index eac3fcb..520ab26 100644
--- a/usage/test/com/cloud/usage/UsageManagerTest.java
+++ b/usage/test/com/cloud/usage/UsageManagerTest.java
@@ -46,6 +46,8 @@ public class UsageManagerTest extends TestCase {
     @Inject
     NetworkUsageParser netParser = null;
     @Inject
+    VmDiskUsageParser vmdiskParser = null;
+    @Inject
     PortForwardingUsageParser pfParser = null;
     @Inject
     SecurityGroupUsageParser sgParser = null;
@@ -87,6 +89,7 @@ public class UsageManagerTest extends TestCase {
         lbParser.parse(account, startDate, endDate);
         noParser.parse(account, startDate, endDate);
         netParser.parse(account, startDate, endDate);
+        vmdiskParser.parse(account, startDate, endDate);
         pfParser.parse(account, startDate, endDate);
         sgParser.parse(account, startDate, endDate);
         stParser.parse(account, startDate, endDate);

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/usage/test/com/cloud/usage/UsageManagerTestConfiguration.java
----------------------------------------------------------------------
diff --git a/usage/test/com/cloud/usage/UsageManagerTestConfiguration.java b/usage/test/com/cloud/usage/UsageManagerTestConfiguration.java
index 1d3ed7b..1a342b5 100644
--- a/usage/test/com/cloud/usage/UsageManagerTestConfiguration.java
+++ b/usage/test/com/cloud/usage/UsageManagerTestConfiguration.java
@@ -53,6 +53,7 @@ import java.io.IOException;
         UsagePortForwardingRuleDaoImpl.class,
         UsageNetworkOfferingDaoImpl.class,
         UsageVPNUserDaoImpl.class,
+        UsageVmDiskDaoImpl.class,
         UsageSecurityGroupDaoImpl.class,
         ConfigurationDaoImpl.class,
         UsageManagerImpl.class,
@@ -64,6 +65,7 @@ import java.io.IOException;
         PortForwardingUsageParser.class,
         SecurityGroupUsageParser.class,
         StorageUsageParser.class,
+        VmDiskUsageParser.class,
         VolumeUsageParser.class,
         VPNUserUsageParser.class,
         UserStatisticsDaoImpl.class},


[3/4] git commit: updated refs/heads/master to 6dad8ad

Posted by we...@apache.org.
CLOUDSTACK-1192: Add Disk I/O Statistics


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

Branch: refs/heads/master
Commit: b9b0168da4c7b8c2d36a226b2747db5dab60fa98
Parents: 8deeb90
Author: Wei Zhou <w....@leaseweb.com>
Authored: Sat Jun 1 08:00:48 2013 +0200
Committer: Wei Zhou <w....@leaseweb.com>
Committed: Sat Jun 1 08:00:48 2013 +0200

----------------------------------------------------------------------
 api/src/com/cloud/vm/VmDiskStats.java              |   30 ++
 api/src/com/cloud/vm/VmStats.java                  |    8 +
 .../cloudstack/api/response/UserVmResponse.java    |   28 ++
 .../org/apache/cloudstack/usage/UsageTypes.java    |    8 +
 .../WEB-INF/classes/resources/messages.properties  |    4 +
 client/tomcatconf/applicationContext.xml.in        |    2 +
 .../com/cloud/agent/api/GetVmDiskStatsAnswer.java  |   47 +++
 .../com/cloud/agent/api/GetVmDiskStatsCommand.java |   54 +++
 core/src/com/cloud/agent/api/VmDiskStatsEntry.java |   90 +++++
 core/src/com/cloud/agent/api/VmStatsEntry.java     |   48 +++
 .../schema/src/com/cloud/usage/UsageVmDiskVO.java  |  180 ++++++++++
 .../schema/src/com/cloud/usage/dao/UsageDao.java   |    4 +
 .../src/com/cloud/usage/dao/UsageDaoImpl.java      |  105 ++++++
 .../src/com/cloud/usage/dao/UsageVmDiskDao.java    |   29 ++
 .../com/cloud/usage/dao/UsageVmDiskDaoImpl.java    |  139 ++++++++
 .../src/com/cloud/user/VmDiskStatisticsVO.java     |  216 ++++++++++++
 .../com/cloud/user/dao/VmDiskStatisticsDao.java    |   35 ++
 .../cloud/user/dao/VmDiskStatisticsDaoImpl.java    |  134 ++++++++
 engine/schema/src/com/cloud/vm/dao/UserVmData.java |   36 ++
 .../kvm/resource/LibvirtComputingResource.java     |   96 ++++++
 .../xen/resource/CitrixResourceBase.java           |  103 ++++++
 server/src/com/cloud/api/ApiResponseHelper.java    |   11 +
 .../com/cloud/api/query/dao/UserVmJoinDaoImpl.java |   12 +
 server/src/com/cloud/configuration/Config.java     |    1 +
 server/src/com/cloud/server/StatsCollector.java    |  252 +++++++++++++++
 .../src/com/cloud/storage/VolumeManagerImpl.java   |   14 +
 server/src/com/cloud/vm/UserVmManager.java         |    5 +
 server/src/com/cloud/vm/UserVmManagerImpl.java     |  190 +++++++++++
 server/test/async-job-component.xml                |    1 +
 .../test/com/cloud/vm/MockUserVmManagerImpl.java   |   12 +
 setup/db/db/schema-410to420.sql                    |   76 +++++
 ui/dictionary.jsp                                  |    4 +
 ui/scripts/instances.js                            |   12 +-
 usage/src/com/cloud/usage/UsageManagerImpl.java    |  157 +++++++++-
 .../com/cloud/usage/parser/VmDiskUsageParser.java  |  208 ++++++++++++
 usage/test/com/cloud/usage/UsageManagerTest.java   |    3 +
 .../cloud/usage/UsageManagerTestConfiguration.java |    2 +
 37 files changed, 2353 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/api/src/com/cloud/vm/VmDiskStats.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/vm/VmDiskStats.java b/api/src/com/cloud/vm/VmDiskStats.java
new file mode 100644
index 0000000..0cf82d0
--- /dev/null
+++ b/api/src/com/cloud/vm/VmDiskStats.java
@@ -0,0 +1,30 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package com.cloud.vm;
+
+public interface VmDiskStats {
+    // vm related disk stats
+
+    public Long getIORead();
+
+    public Long getIOWrite();
+    
+    public Long getBytesRead();
+
+    public Long getBytesWrite();
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/api/src/com/cloud/vm/VmStats.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/vm/VmStats.java b/api/src/com/cloud/vm/VmStats.java
index 7d0bd61..d284db0 100644
--- a/api/src/com/cloud/vm/VmStats.java
+++ b/api/src/com/cloud/vm/VmStats.java
@@ -23,5 +23,13 @@ public interface VmStats {
     public double getNetworkReadKBs();
 
     public double getNetworkWriteKBs();
+    
+    public double getDiskReadIOs();
+
+    public double getDiskWriteIOs();
+    
+    public double getDiskReadKBs();
+
+    public double getDiskWriteKBs();
 
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/api/src/org/apache/cloudstack/api/response/UserVmResponse.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/response/UserVmResponse.java b/api/src/org/apache/cloudstack/api/response/UserVmResponse.java
index c3bbf8d..1f9eb1a 100644
--- a/api/src/org/apache/cloudstack/api/response/UserVmResponse.java
+++ b/api/src/org/apache/cloudstack/api/response/UserVmResponse.java
@@ -137,6 +137,18 @@ public class UserVmResponse extends BaseResponse implements ControlledEntityResp
     @SerializedName("networkkbswrite") @Param(description="the outgoing network traffic on the host")
     private Long networkKbsWrite;
 
+    @SerializedName("diskkbsread") @Param(description="the read (bytes) of disk on the vm")
+    private Long diskKbsRead;
+    
+    @SerializedName("diskkbswrite") @Param(description="the write (bytes) of disk on the vm")
+    private Long diskKbsWrite;
+    
+    @SerializedName("diskioread") @Param(description="the read (io) of disk on the vm")
+    private Long diskIORead;
+    
+    @SerializedName("diskiowrite") @Param(description="the write (io) of disk on the vm")
+    private Long diskIOWrite;
+    
     @SerializedName("guestosid") @Param(description="Os type ID of the virtual machine")
     private String guestOsId;
 
@@ -300,6 +312,22 @@ public class UserVmResponse extends BaseResponse implements ControlledEntityResp
     public void setIsoDisplayText(String isoDisplayText) {
         this.isoDisplayText = isoDisplayText;
     }
+    
+    public void setDiskKbsRead(Long diskKbsRead) {
+        this.diskKbsRead = diskKbsRead;
+    }
+
+    public void setDiskKbsWrite(Long diskKbsWrite) {
+        this.diskKbsWrite = diskKbsWrite;
+    }
+    
+    public void setDiskIORead(Long diskIORead) {
+        this.diskIORead = diskIORead;
+    }
+
+    public void setDiskIOWrite(Long diskIOWrite) {
+        this.diskIOWrite = diskIOWrite;
+    }
 
     public void setServiceOfferingId(String serviceOfferingId) {
         this.serviceOfferingId = serviceOfferingId;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/api/src/org/apache/cloudstack/usage/UsageTypes.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/usage/UsageTypes.java b/api/src/org/apache/cloudstack/usage/UsageTypes.java
index 2baa1d2..ddf1097 100644
--- a/api/src/org/apache/cloudstack/usage/UsageTypes.java
+++ b/api/src/org/apache/cloudstack/usage/UsageTypes.java
@@ -36,6 +36,10 @@ public class UsageTypes {
     public static final int PORT_FORWARDING_RULE = 12;
     public static final int NETWORK_OFFERING = 13;
     public static final int VPN_USERS = 14;
+    public static final int VM_DISK_IO_READ = 21;
+    public static final int VM_DISK_IO_WRITE = 22;
+    public static final int VM_DISK_BYTES_READ = 23;
+    public static final int VM_DISK_BYTES_WRITE = 24;
     
     public static List<UsageTypeResponse> listUsageTypes(){
     	List<UsageTypeResponse> responseList = new ArrayList<UsageTypeResponse>();
@@ -53,6 +57,10 @@ public class UsageTypes {
     	responseList.add(new UsageTypeResponse(PORT_FORWARDING_RULE, "Port Forwarding Usage"));
     	responseList.add(new UsageTypeResponse(NETWORK_OFFERING, "Network Offering Usage"));
     	responseList.add(new UsageTypeResponse(VPN_USERS, "VPN users usage"));
+        responseList.add(new UsageTypeResponse(VM_DISK_IO_READ, "VM Disk usage(I/O Read)"));
+        responseList.add(new UsageTypeResponse(VM_DISK_IO_WRITE, "VM Disk usage(I/O Write)"));
+        responseList.add(new UsageTypeResponse(VM_DISK_BYTES_READ, "VM Disk usage(Bytes Read)"));
+        responseList.add(new UsageTypeResponse(VM_DISK_BYTES_WRITE, "VM Disk usage(Bytes Write)"));
     	return responseList;
     }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/client/WEB-INF/classes/resources/messages.properties
----------------------------------------------------------------------
diff --git a/client/WEB-INF/classes/resources/messages.properties b/client/WEB-INF/classes/resources/messages.properties
index 1638be1..ce20fa4 100644
--- a/client/WEB-INF/classes/resources/messages.properties
+++ b/client/WEB-INF/classes/resources/messages.properties
@@ -464,10 +464,14 @@ label.disabled=Disabled
 label.disabling.vpn.access=Disabling VPN Access
 label.disk.allocated=Disk Allocated
 label.disk.offering=Disk Offering
+label.disk.read.bytes=Disk Read (Bytes)
+label.disk.read.io=Disk Read (IO)
 label.disk.size.gb=Disk Size (in GB)
 label.disk.size=Disk Size
 label.disk.total=Disk Total
 label.disk.volume=Disk Volume
+label.disk.write.bytes=Disk Write (Bytes)
+label.disk.write.io=Disk Write (IO)
 label.display.name=Display name
 label.display.text=Display Text
 label.dns.1=DNS 1

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/client/tomcatconf/applicationContext.xml.in
----------------------------------------------------------------------
diff --git a/client/tomcatconf/applicationContext.xml.in b/client/tomcatconf/applicationContext.xml.in
index 2fe5141..11ed42b 100644
--- a/client/tomcatconf/applicationContext.xml.in
+++ b/client/tomcatconf/applicationContext.xml.in
@@ -341,11 +341,13 @@
   <bean id="usageVMInstanceDaoImpl" class="com.cloud.usage.dao.UsageVMInstanceDaoImpl" />
   <bean id="usageVPNUserDaoImpl" class="com.cloud.usage.dao.UsageVPNUserDaoImpl" />
   <bean id="usageVolumeDaoImpl" class="com.cloud.usage.dao.UsageVolumeDaoImpl" />
+  <bean id="usageVmDiskDaoImpl" class="com.cloud.usage.dao.UsageVmDiskDaoImpl" />
   <bean id="userAccountDaoImpl" class="com.cloud.user.dao.UserAccountDaoImpl" />
   <bean id="userAccountJoinDaoImpl" class="com.cloud.api.query.dao.UserAccountJoinDaoImpl" />
   <bean id="userIpv6AddressDaoImpl" class="com.cloud.network.dao.UserIpv6AddressDaoImpl" />
   <bean id="userStatisticsDaoImpl" class="com.cloud.user.dao.UserStatisticsDaoImpl" />
   <bean id="userStatsLogDaoImpl" class="com.cloud.user.dao.UserStatsLogDaoImpl" />
+  <bean id="userVmDiskStatsDaoImpl" class="com.cloud.user.dao.VmDiskStatisticsDaoImpl" />
   <bean id="userVmCloneSettingDaoImpl" class="com.cloud.vm.dao.UserVmCloneSettingDaoImpl" />
   <bean id="userVmDaoImpl" class="com.cloud.vm.dao.UserVmDaoImpl" />
   <bean id="userVmDetailsDaoImpl" class="com.cloud.vm.dao.UserVmDetailsDaoImpl" />

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/core/src/com/cloud/agent/api/GetVmDiskStatsAnswer.java
----------------------------------------------------------------------
diff --git a/core/src/com/cloud/agent/api/GetVmDiskStatsAnswer.java b/core/src/com/cloud/agent/api/GetVmDiskStatsAnswer.java
new file mode 100644
index 0000000..18cb794
--- /dev/null
+++ b/core/src/com/cloud/agent/api/GetVmDiskStatsAnswer.java
@@ -0,0 +1,47 @@
+// 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.agent.api;
+
+import java.util.HashMap;
+import java.util.List;
+
+import com.cloud.agent.api.LogLevel.Log4jLevel;
+
+@LogLevel(Log4jLevel.Trace)
+public class GetVmDiskStatsAnswer extends Answer {
+
+	String hostName;
+    HashMap<String, List<VmDiskStatsEntry>> vmDiskStatsMap;
+
+    public GetVmDiskStatsAnswer(GetVmDiskStatsCommand cmd, String details, String hostName, HashMap<String, List<VmDiskStatsEntry>> vmDiskStatsMap) {
+    	super(cmd, true, details);
+        this.hostName = hostName;
+        this.vmDiskStatsMap = vmDiskStatsMap;
+    }
+
+    public String getHostName() {
+        return hostName;
+    }
+
+    public HashMap<String, List<VmDiskStatsEntry>> getVmDiskStatsMap() {
+        return vmDiskStatsMap;
+    }
+
+    protected GetVmDiskStatsAnswer() {
+        //no-args constructor for json serialization-deserialization
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/core/src/com/cloud/agent/api/GetVmDiskStatsCommand.java
----------------------------------------------------------------------
diff --git a/core/src/com/cloud/agent/api/GetVmDiskStatsCommand.java b/core/src/com/cloud/agent/api/GetVmDiskStatsCommand.java
new file mode 100644
index 0000000..2b69002
--- /dev/null
+++ b/core/src/com/cloud/agent/api/GetVmDiskStatsCommand.java
@@ -0,0 +1,54 @@
+// 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.agent.api;
+
+import java.util.List;
+
+import com.cloud.agent.api.LogLevel.Log4jLevel;
+
+@LogLevel(Log4jLevel.Trace)
+public class GetVmDiskStatsCommand extends Command {
+    List<String> vmNames;
+    String hostGuid;
+    String hostName;
+
+    protected GetVmDiskStatsCommand() {
+    }
+
+    public GetVmDiskStatsCommand(List<String> vmNames, String hostGuid, String hostName) {
+        this.vmNames = vmNames;
+        this.hostGuid = hostGuid;
+        this.hostName = hostName;
+    }
+
+    public List<String> getVmNames() {
+        return vmNames;
+    }
+
+    public String getHostGuid(){
+        return this.hostGuid;
+    }
+
+    public String getHostName(){
+        return this.hostName;
+    }
+
+    @Override
+    public boolean executeInSequence() {
+        return false;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/core/src/com/cloud/agent/api/VmDiskStatsEntry.java
----------------------------------------------------------------------
diff --git a/core/src/com/cloud/agent/api/VmDiskStatsEntry.java b/core/src/com/cloud/agent/api/VmDiskStatsEntry.java
new file mode 100644
index 0000000..9bec031
--- /dev/null
+++ b/core/src/com/cloud/agent/api/VmDiskStatsEntry.java
@@ -0,0 +1,90 @@
+// 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.agent.api;
+
+import com.cloud.vm.VmDiskStats;
+
+public class VmDiskStatsEntry implements VmDiskStats {
+	
+	String vmName;
+	String path;
+	Long ioRead = 0L;
+	Long ioWrite = 0L;
+	Long bytesWrite = 0L;
+	Long bytesRead = 0L;
+	    
+    public VmDiskStatsEntry() {
+    }
+    
+    public VmDiskStatsEntry(String vmName, String path, Long ioWrite, Long ioRead, Long bytesWrite, Long bytesRead) {
+        this.ioRead = ioRead;
+        this.ioWrite = ioWrite;
+        this.bytesRead = bytesRead;
+        this.bytesWrite = bytesWrite;
+        this.vmName = vmName;
+        this.path = path;
+    }
+
+    public void setVmName(String vmName) {
+        this.vmName = vmName;
+    }
+
+    public String getVmName() {
+        return vmName;
+    }
+    
+    public void setPath(String path) {
+        this.path = path;
+    }
+
+    public String getPath() {
+        return path;
+    }
+    
+    public void setBytesRead(Long bytesRead) {
+    	this.bytesRead = bytesRead;
+    }
+
+    public Long getBytesRead() {
+    	return bytesRead;
+    }
+    
+    public void setBytesWrite(Long bytesWrite) {
+    	this.bytesWrite = bytesWrite;
+    }
+    
+    public Long getBytesWrite() {
+    	return bytesWrite;
+    }
+    
+    public void setIORead(Long ioRead) {
+        this.ioRead = ioRead;
+    }
+
+    public Long getIORead() {
+        return ioRead;
+    }
+    
+    public void setIOWrite(Long ioWrite) {
+        this.ioWrite = ioWrite;
+    }
+    
+    public Long getIOWrite() {
+        return ioWrite;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/core/src/com/cloud/agent/api/VmStatsEntry.java
----------------------------------------------------------------------
diff --git a/core/src/com/cloud/agent/api/VmStatsEntry.java b/core/src/com/cloud/agent/api/VmStatsEntry.java
index 8828e91..9c6df1a 100755
--- a/core/src/com/cloud/agent/api/VmStatsEntry.java
+++ b/core/src/com/cloud/agent/api/VmStatsEntry.java
@@ -23,6 +23,10 @@ public class VmStatsEntry implements VmStats {
 	double cpuUtilization;
 	double networkReadKBs;
 	double networkWriteKBs;
+    double diskReadIOs;
+    double diskWriteIOs;
+    double diskReadKBs;
+    double diskWriteKBs;
 	int numCPUs;
 	String entityType;
 
@@ -37,6 +41,18 @@ public class VmStatsEntry implements VmStats {
         this.numCPUs = numCPUs;
         this.entityType = entityType;
     }
+    
+    public VmStatsEntry(double cpuUtilization, double networkReadKBs, double networkWriteKBs, 
+            double diskReadKBs, double diskWriteKBs, int numCPUs, String entityType) 
+    {
+        this.cpuUtilization = cpuUtilization;
+        this.networkReadKBs = networkReadKBs;
+        this.networkWriteKBs = networkWriteKBs;
+        this.diskReadKBs = diskReadKBs;
+        this.diskWriteKBs = diskWriteKBs;        
+        this.numCPUs = numCPUs;
+        this.entityType = entityType;
+    }
 
     public double getCPUUtilization() {
     	return cpuUtilization;
@@ -62,6 +78,38 @@ public class VmStatsEntry implements VmStats {
     	this.networkWriteKBs = networkWriteKBs;
     }
 
+    public double getDiskReadIOs() {
+        return diskReadIOs;
+    }
+
+    public void setDiskReadIOs(double diskReadIOs) {
+        this.diskReadIOs = diskReadIOs;
+    }
+
+    public double getDiskWriteIOs() {
+        return diskWriteIOs;
+    }
+
+    public void setDiskWriteIOs(double diskWriteIOs) {
+        this.diskWriteIOs = diskWriteIOs;
+    }
+
+    public double getDiskReadKBs() {
+        return diskReadKBs;
+    }
+
+    public void setDiskReadKBs(double diskReadKBs) {
+        this.diskReadKBs = diskReadKBs;
+    }
+
+    public double getDiskWriteKBs() {
+        return diskWriteKBs;
+    }
+
+    public void setDiskWriteKBs(double diskWriteKBs) {
+        this.diskWriteKBs = diskWriteKBs;
+    }
+
     public int getNumCPUs() {
     	return numCPUs;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/engine/schema/src/com/cloud/usage/UsageVmDiskVO.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/usage/UsageVmDiskVO.java b/engine/schema/src/com/cloud/usage/UsageVmDiskVO.java
new file mode 100644
index 0000000..6c3ca69
--- /dev/null
+++ b/engine/schema/src/com/cloud/usage/UsageVmDiskVO.java
@@ -0,0 +1,180 @@
+// 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.usage;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+@Entity
+@Table(name="usage_vm_disk")
+public class UsageVmDiskVO {
+	@Id
+    @Column(name="account_id")
+    private long accountId;
+
+	@Column(name="zone_id")
+	private long zoneId;
+	
+	@Column(name="vm_id")
+	private Long vmId;
+	
+	@Column(name="volume_id")
+	private Long volumeId;
+
+	@Column(name="io_read")
+	private long ioRead;
+
+    @Column(name="io_write")
+    private long ioWrite;
+
+    @Column(name="agg_io_write")
+    private long aggIOWrite;
+    
+    @Column(name="agg_io_read")
+    private long aggIORead;
+    
+	@Column(name="bytes_read")
+	private long bytesRead;
+
+    @Column(name="bytes_write")
+    private long bytesWrite;
+
+    @Column(name="agg_bytes_write")
+    private long aggBytesWrite;
+    
+    @Column(name="agg_bytes_read")
+    private long aggBytesRead;
+    
+	@Column(name="event_time_millis")
+	private long eventTimeMillis = 0;
+	
+	protected UsageVmDiskVO() {
+	}
+
+	public UsageVmDiskVO(Long accountId, long zoneId, Long vmId, Long volumeId, long ioRead, long ioWrite, long aggIORead, long aggIOWrite,
+			long bytesRead, long bytesWrite, long aggBytesRead, long aggBytesWrite, long eventTimeMillis) {
+		this.accountId = accountId;
+		this.zoneId = zoneId;
+		this.vmId = vmId;
+		this.volumeId = volumeId;
+		this.ioRead = ioRead;
+        this.ioWrite = ioWrite;
+		this.aggIOWrite = aggIOWrite;
+		this.aggIORead = aggIORead;
+		this.bytesRead = bytesRead;
+        this.bytesWrite = bytesWrite;
+		this.aggBytesWrite = aggBytesWrite;
+		this.aggBytesRead = aggBytesRead;
+		this.eventTimeMillis = eventTimeMillis;
+	}
+
+	public long getAccountId() {
+		return accountId;
+	}
+
+	public void setAccountId(long accountId) {
+		this.accountId = accountId;
+	}
+
+	public long getZoneId() {
+	    return zoneId;
+	}
+	public void setZoneId(long zoneId) {
+	    this.zoneId = zoneId;
+	}
+
+	public Long getIORead() {
+		return ioRead;
+	}
+	
+	public void setIORead(Long ioRead) {
+	    this.ioRead = ioRead;
+	}
+
+    public Long getIOWrite() {
+        return ioWrite;
+    }
+    
+    public void setIOWrite(Long ioWrite) {
+        this.ioWrite = ioWrite;
+    }
+    
+	public Long getBytesRead() {
+		return bytesRead;
+	}
+	
+	public void setBytesRead(Long bytesRead) {
+	    this.bytesRead = bytesRead;
+	}
+
+    public Long getBytesWrite() {
+        return bytesWrite;
+    }
+    
+    public void setBytesWrite(Long bytesWrite) {
+        this.bytesWrite = bytesWrite;
+    }
+
+    public long getEventTimeMillis() {
+	    return eventTimeMillis;
+	}
+	public void setEventTimeMillis(long eventTimeMillis) {
+	    this.eventTimeMillis = eventTimeMillis;
+	}
+
+    public Long getVmId() {
+        return vmId;
+    }
+    
+    public Long getVolumeId() {
+        return volumeId;
+    }
+
+	public long getAggIOWrite() {
+		return aggIOWrite;
+	}
+
+	public void setAggIOWrite(long aggIOWrite) {
+		this.aggIOWrite = aggIOWrite;
+	}
+
+	public long getAggIORead() {
+		return aggIORead;
+	}
+
+	public void setAggIORead(long aggIORead) {
+		this.aggIORead = aggIORead;
+	}
+	
+	public long getAggBytesWrite() {
+		return aggBytesWrite;
+	}
+
+	public void setAggBytesWrite(long aggBytesWrite) {
+		this.aggBytesWrite = aggBytesWrite;
+	}
+
+	public long getAggBytesRead() {
+		return aggBytesRead;
+	}
+
+	public void setAggBytesRead(long aggBytesRead) {
+		this.aggBytesRead = aggBytesRead;
+	}
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/engine/schema/src/com/cloud/usage/dao/UsageDao.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/usage/dao/UsageDao.java b/engine/schema/src/com/cloud/usage/dao/UsageDao.java
index 6d0c162..8a80655 100644
--- a/engine/schema/src/com/cloud/usage/dao/UsageDao.java
+++ b/engine/schema/src/com/cloud/usage/dao/UsageDao.java
@@ -21,6 +21,7 @@ import java.util.List;
 import com.cloud.usage.UsageVO;
 import com.cloud.user.AccountVO;
 import com.cloud.user.UserStatisticsVO;
+import com.cloud.user.VmDiskStatisticsVO;
 import com.cloud.utils.db.Filter;
 import com.cloud.utils.db.GenericDao;
 import com.cloud.utils.db.SearchCriteria;
@@ -36,4 +37,7 @@ public interface UsageDao extends GenericDao<UsageVO, Long> {
     Long getLastAccountId();
     Long getLastUserStatsId();
     List<Long> listPublicTemplatesByAccount(long accountId);
+    Long getLastVmDiskStatsId();
+    void updateVmDiskStats(List<VmDiskStatisticsVO> vmNetStats);
+    void saveVmDiskStats(List<VmDiskStatisticsVO> vmNetStats);
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/engine/schema/src/com/cloud/usage/dao/UsageDaoImpl.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/usage/dao/UsageDaoImpl.java b/engine/schema/src/com/cloud/usage/dao/UsageDaoImpl.java
index a5867f0..f7d5069 100644
--- a/engine/schema/src/com/cloud/usage/dao/UsageDaoImpl.java
+++ b/engine/schema/src/com/cloud/usage/dao/UsageDaoImpl.java
@@ -32,6 +32,7 @@ import org.springframework.stereotype.Component;
 import com.cloud.usage.UsageVO;
 import com.cloud.user.AccountVO;
 import com.cloud.user.UserStatisticsVO;
+import com.cloud.user.VmDiskStatisticsVO;
 import com.cloud.utils.DateUtil;
 import com.cloud.utils.db.Filter;
 import com.cloud.utils.db.GenericDaoBase;
@@ -56,6 +57,13 @@ public class UsageDaoImpl extends GenericDaoBase<UsageVO, Long> implements Usage
     private static final String GET_LAST_USER_STATS = "SELECT id FROM cloud_usage.user_statistics ORDER BY id DESC LIMIT 1";
     private static final String GET_PUBLIC_TEMPLATES_BY_ACCOUNTID = "SELECT id FROM cloud.vm_template WHERE account_id = ? AND public = '1' AND removed IS NULL";
 
+    private static final String GET_LAST_VM_DISK_STATS = "SELECT id FROM cloud_usage.vm_disk_statistics ORDER BY id DESC LIMIT 1";
+    private static final String INSERT_VM_DISK_STATS = "INSERT INTO cloud_usage.vm_disk_statistics (id, data_center_id, account_id, vm_id, volume_id, net_io_read, net_io_write, current_io_read, " +
+               "current_io_write, agg_io_read, agg_io_write, net_bytes_read, net_bytes_write, current_bytes_read, current_bytes_write, agg_bytes_read, agg_bytes_write) " +
+                       " VALUES (?,?,?,?,?,?,?,?,?,?, ?, ?, ?, ?,?, ?, ?)";
+    private static final String UPDATE_VM_DISK_STATS = "UPDATE cloud_usage.vm_disk_statistics SET net_io_read=?, net_io_write=?, current_io_read=?, current_io_write=?, agg_io_read=?, agg_io_write=?, " +
+               "net_bytes_read=?, net_bytes_write=?, current_bytes_read=?, current_bytes_write=?, agg_bytes_read=?, agg_bytes_write=?  WHERE id=?";
+
     protected final static TimeZone s_gmtTimeZone = TimeZone.getTimeZone("GMT");
 
     public UsageDaoImpl () {}
@@ -270,4 +278,101 @@ public class UsageDaoImpl extends GenericDaoBase<UsageVO, Long> implements Usage
         }
         return templateList;
     }
+
+       @Override
+       public Long getLastVmDiskStatsId() {
+               Transaction txn = Transaction.currentTxn();
+        PreparedStatement pstmt = null;
+        String sql = GET_LAST_VM_DISK_STATS;
+        try {
+            pstmt = txn.prepareAutoCloseStatement(sql);
+            ResultSet rs = pstmt.executeQuery();
+            if (rs.next()) {
+                return Long.valueOf(rs.getLong(1));
+            }
+        } catch (Exception ex) {
+            s_logger.error("error getting last vm disk stats id", ex);
+        }
+        return null;
+       }
+
+       @Override
+       public void updateVmDiskStats(List<VmDiskStatisticsVO> vmDiskStats) {
+               Transaction txn = Transaction.currentTxn();
+        try {
+            txn.start();
+            String sql = UPDATE_VM_DISK_STATS;
+            PreparedStatement pstmt = null;
+            pstmt = txn.prepareAutoCloseStatement(sql);  // in reality I just want CLOUD_USAGE dataSource connection
+            for (VmDiskStatisticsVO vmDiskStat : vmDiskStats) {
+                pstmt.setLong(1, vmDiskStat.getNetIORead());
+                pstmt.setLong(2, vmDiskStat.getNetIOWrite());
+                pstmt.setLong(3, vmDiskStat.getCurrentIORead());
+                pstmt.setLong(4, vmDiskStat.getCurrentIOWrite());
+                pstmt.setLong(5, vmDiskStat.getAggIORead());
+                pstmt.setLong(6, vmDiskStat.getAggIOWrite());
+                pstmt.setLong(7, vmDiskStat.getNetBytesRead());
+                pstmt.setLong(8, vmDiskStat.getNetBytesWrite());
+                pstmt.setLong(9, vmDiskStat.getCurrentBytesRead());
+                pstmt.setLong(10, vmDiskStat.getCurrentBytesWrite());
+                pstmt.setLong(11, vmDiskStat.getAggBytesRead());
+                pstmt.setLong(12, vmDiskStat.getAggBytesWrite());
+                pstmt.setLong(13, vmDiskStat.getId());
+                pstmt.addBatch();
+            }
+            pstmt.executeBatch();
+            txn.commit();
+        } catch (Exception ex) {
+            txn.rollback();
+            s_logger.error("error saving vm disk stats to cloud_usage db", ex);
+            throw new CloudRuntimeException(ex.getMessage());
+        }
+
+       }
+
+       @Override
+       public void saveVmDiskStats(List<VmDiskStatisticsVO> vmDiskStats) {
+               Transaction txn = Transaction.currentTxn();
+        try {
+            txn.start();
+            String sql = INSERT_VM_DISK_STATS;
+            PreparedStatement pstmt = null;
+            pstmt = txn.prepareAutoCloseStatement(sql); // in reality I just want CLOUD_USAGE dataSource connection
+            for (VmDiskStatisticsVO vmDiskStat : vmDiskStats) {
+                pstmt.setLong(1, vmDiskStat.getId());
+                pstmt.setLong(2, vmDiskStat.getDataCenterId());
+                pstmt.setLong(3, vmDiskStat.getAccountId());
+                if(vmDiskStat.getVmId() != null){
+                    pstmt.setLong(4, vmDiskStat.getVmId());
+                } else {
+                    pstmt.setNull(4, Types.BIGINT);
+                }
+                if(vmDiskStat.getVolumeId() != null){
+                    pstmt.setLong(5, vmDiskStat.getVolumeId());
+                } else {
+                    pstmt.setNull(5, Types.BIGINT);
+                }
+                pstmt.setLong(6, vmDiskStat.getNetIORead());
+                pstmt.setLong(7, vmDiskStat.getNetIOWrite());
+                pstmt.setLong(8, vmDiskStat.getCurrentIORead());
+                pstmt.setLong(9, vmDiskStat.getCurrentIOWrite());
+                pstmt.setLong(10, vmDiskStat.getAggIORead());
+                pstmt.setLong(11, vmDiskStat.getAggIOWrite());
+                pstmt.setLong(12, vmDiskStat.getNetBytesRead());
+                pstmt.setLong(13, vmDiskStat.getNetBytesWrite());
+                pstmt.setLong(14, vmDiskStat.getCurrentBytesRead());
+                pstmt.setLong(15, vmDiskStat.getCurrentBytesWrite());
+                pstmt.setLong(16, vmDiskStat.getAggBytesRead());
+                pstmt.setLong(17, vmDiskStat.getAggBytesWrite());
+                pstmt.addBatch();
+            }
+            pstmt.executeBatch();
+            txn.commit();
+        } catch (Exception ex) {
+            txn.rollback();
+            s_logger.error("error saving vm disk stats to cloud_usage db", ex);
+            throw new CloudRuntimeException(ex.getMessage());
+        }
+
+       }
 }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/engine/schema/src/com/cloud/usage/dao/UsageVmDiskDao.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/usage/dao/UsageVmDiskDao.java b/engine/schema/src/com/cloud/usage/dao/UsageVmDiskDao.java
new file mode 100644
index 0000000..b72a8d4
--- /dev/null
+++ b/engine/schema/src/com/cloud/usage/dao/UsageVmDiskDao.java
@@ -0,0 +1,29 @@
+// 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.usage.dao;
+
+import java.util.List;
+import java.util.Map;
+
+import com.cloud.usage.UsageVmDiskVO;
+import com.cloud.utils.db.GenericDao;
+
+public interface UsageVmDiskDao extends GenericDao<UsageVmDiskVO, Long> {
+    Map<String, UsageVmDiskVO> getRecentVmDiskStats();
+    void deleteOldStats(long maxEventTime);
+    void saveUsageVmDisks(List<UsageVmDiskVO> usageVmDisks);
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/engine/schema/src/com/cloud/usage/dao/UsageVmDiskDaoImpl.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/usage/dao/UsageVmDiskDaoImpl.java b/engine/schema/src/com/cloud/usage/dao/UsageVmDiskDaoImpl.java
new file mode 100644
index 0000000..8436c59
--- /dev/null
+++ b/engine/schema/src/com/cloud/usage/dao/UsageVmDiskDaoImpl.java
@@ -0,0 +1,139 @@
+// 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.usage.dao;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.ejb.Local;
+
+import org.apache.log4j.Logger;
+import org.springframework.stereotype.Component;
+
+import com.cloud.usage.UsageVmDiskVO;
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.Transaction;
+import com.cloud.utils.exception.CloudRuntimeException;
+
+@Component
+@Local(value={UsageVmDiskDao.class})
+public class UsageVmDiskDaoImpl extends GenericDaoBase<UsageVmDiskVO, Long> implements UsageVmDiskDao {
+	private static final Logger s_logger = Logger.getLogger(UsageVMInstanceDaoImpl.class.getName());
+	private static final String SELECT_LATEST_STATS = "SELECT uvd.account_id, uvd.zone_id, uvd.vm_id, uvd.volume_id, uvd.io_read, uvd.io_write, uvd.agg_io_read, uvd.agg_io_write, " +
+														"uvd.bytes_read, uvd.bytes_write, uvd.agg_bytes_read, uvd.agg_bytes_write, uvd.event_time_millis " +
+	                                                    "FROM cloud_usage.usage_vm_disk uvd INNER JOIN (SELECT vmdiskusage.account_id as acct_id, vmdiskusage.zone_id as z_id, max(vmdiskusage.event_time_millis) as max_date " +
+	                                                                                                 "FROM cloud_usage.usage_vm_disk vmdiskusage " +
+	                                                                                                 "GROUP BY vmdiskusage.account_id, vmdiskusage.zone_id " +
+	                                                                                                 ") joinnet on uvd.account_id = joinnet.acct_id and uvd.zone_id = joinnet.z_id and uvd.event_time_millis = joinnet.max_date";
+	private static final String DELETE_OLD_STATS = "DELETE FROM cloud_usage.usage_vm_disk WHERE event_time_millis < ?";
+
+	private static final String INSERT_USAGE_VM_DISK = "INSERT INTO cloud_usage.usage_vm_disk (account_id, zone_id, vm_id, volume_id, io_read, io_write, agg_io_read, agg_io_write, bytes_read, bytes_write, agg_bytes_read, agg_bytes_write, event_time_millis) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)";
+
+	public UsageVmDiskDaoImpl() {
+	}
+
+	@Override
+	public Map<String, UsageVmDiskVO> getRecentVmDiskStats() {
+        Transaction txn = Transaction.open(Transaction.USAGE_DB);
+        String sql = SELECT_LATEST_STATS;
+        PreparedStatement pstmt = null;
+        try {
+            pstmt = txn.prepareAutoCloseStatement(sql);
+            ResultSet rs = pstmt.executeQuery();
+            Map<String, UsageVmDiskVO> returnMap = new HashMap<String, UsageVmDiskVO>();
+            while (rs.next()) {
+                long accountId = rs.getLong(1);
+                long zoneId = rs.getLong(2);
+                long vmId = rs.getLong(3);
+                Long volumeId = rs.getLong(4);
+                long ioRead = rs.getLong(5);
+                long ioWrite = rs.getLong(6);
+                long aggIORead = rs.getLong(7);
+                long aggIOWrite = rs.getLong(8);
+                long bytesRead = rs.getLong(9);
+                long bytesWrite = rs.getLong(10);
+                long aggBytesRead = rs.getLong(11);
+                long aggBytesWrite = rs.getLong(12);
+                long eventTimeMillis = rs.getLong(13);
+                if(vmId != 0){
+                    returnMap.put(zoneId + "-" + accountId+ "-Vm-" + vmId+ "-Disk-" + volumeId, new UsageVmDiskVO(accountId, zoneId, vmId, volumeId, ioRead, ioWrite, aggIORead, aggIOWrite, bytesRead, bytesWrite, aggBytesRead, aggBytesWrite, eventTimeMillis));
+                } else {
+                    returnMap.put(zoneId + "-" + accountId, new UsageVmDiskVO(accountId, zoneId, vmId, volumeId, ioRead, ioWrite, aggIORead, aggIOWrite, bytesRead, bytesWrite, aggBytesRead, aggBytesWrite, eventTimeMillis));
+                }
+            }
+            return returnMap;
+        } catch (Exception ex) {
+            s_logger.error("error getting recent usage disk stats", ex);
+        } finally {
+            txn.close();
+        }
+        return null;
+	}
+
+    @Override
+	public void deleteOldStats(long maxEventTime) {
+        Transaction txn = Transaction.currentTxn();
+        String sql = DELETE_OLD_STATS;
+        PreparedStatement pstmt = null;
+        try {
+            txn.start();
+            pstmt = txn.prepareAutoCloseStatement(sql);
+            pstmt.setLong(1, maxEventTime);
+            pstmt.executeUpdate();
+            txn.commit();
+        } catch (Exception ex) {
+            txn.rollback();
+            s_logger.error("error deleting old usage disk stats", ex);
+        }
+	}
+    
+    @Override
+    public void saveUsageVmDisks(List<UsageVmDiskVO> usageVmDisks) {
+        Transaction txn = Transaction.currentTxn();
+        try {
+            txn.start();
+            String sql = INSERT_USAGE_VM_DISK;
+            PreparedStatement pstmt = null;
+            pstmt = txn.prepareAutoCloseStatement(sql); // in reality I just want CLOUD_USAGE dataSource connection
+            for (UsageVmDiskVO usageVmDisk : usageVmDisks) {
+                pstmt.setLong(1, usageVmDisk.getAccountId());
+                pstmt.setLong(2, usageVmDisk.getZoneId());
+                pstmt.setLong(3, usageVmDisk.getVmId());
+                pstmt.setLong(4, usageVmDisk.getVolumeId());
+                pstmt.setLong(5, usageVmDisk.getIORead());
+                pstmt.setLong(6, usageVmDisk.getIOWrite());
+                pstmt.setLong(7, usageVmDisk.getAggIORead());
+                pstmt.setLong(8, usageVmDisk.getAggIOWrite());
+                pstmt.setLong(9, usageVmDisk.getBytesRead());
+                pstmt.setLong(10, usageVmDisk.getBytesWrite());
+                pstmt.setLong(11, usageVmDisk.getAggBytesRead());
+                pstmt.setLong(12, usageVmDisk.getAggBytesWrite());
+                pstmt.setLong(13, usageVmDisk.getEventTimeMillis());
+                pstmt.addBatch();
+            }
+            pstmt.executeBatch();
+            txn.commit();
+        } catch (Exception ex) {
+            txn.rollback();
+            s_logger.error("error saving usage_vm_disk to cloud_usage db", ex);
+            throw new CloudRuntimeException(ex.getMessage());
+        } 
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/engine/schema/src/com/cloud/user/VmDiskStatisticsVO.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/user/VmDiskStatisticsVO.java b/engine/schema/src/com/cloud/user/VmDiskStatisticsVO.java
new file mode 100644
index 0000000..d1842c3
--- /dev/null
+++ b/engine/schema/src/com/cloud/user/VmDiskStatisticsVO.java
@@ -0,0 +1,216 @@
+// 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.user;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+
+@Entity
+@Table(name="vm_disk_statistics")
+public class VmDiskStatisticsVO {
+	@Id
+	@GeneratedValue(strategy=GenerationType.IDENTITY)
+	@Column(name="id")
+	private Long id;
+	
+	@Column(name="data_center_id", updatable=false)
+	private long dataCenterId;
+	
+	@Column(name="account_id", updatable=false)
+	private long accountId;
+	
+	@Column(name="vm_id")
+	private Long vmId;
+	
+	@Column(name="volume_id")
+	private Long volumeId;
+
+	@Column(name="net_io_read")
+	private long netIORead;
+	
+	@Column(name="net_io_write")
+	private long netIOWrite;
+	
+	@Column(name="current_io_read")
+	private long currentIORead;
+	
+	@Column(name="current_io_write")
+	private long currentIOWrite;
+	
+	@Column(name="agg_io_read")
+	private long aggIORead;
+	
+	@Column(name="agg_io_write")
+	private long aggIOWrite;
+	
+	@Column(name="net_bytes_read")
+	private long netBytesRead;
+	
+	@Column(name="net_bytes_write")
+	private long netBytesWrite;
+	
+	@Column(name="current_bytes_read")
+	private long currentBytesRead;
+	
+	@Column(name="current_bytes_write")
+	private long currentBytesWrite;
+	
+	@Column(name="agg_bytes_read")
+	private long aggBytesRead;
+	
+	@Column(name="agg_bytes_write")
+	private long aggBytesWrite;
+	
+	protected VmDiskStatisticsVO() {
+	}
+	
+	public VmDiskStatisticsVO(long accountId, long dcId, Long vmId, Long volumeId) {
+		this.accountId = accountId;
+		this.dataCenterId = dcId;
+		this.vmId = vmId;
+		this.volumeId = volumeId;
+		this.netBytesRead = 0;
+		this.netBytesWrite = 0;
+		this.currentBytesRead = 0;
+		this.currentBytesWrite = 0;
+		this.netBytesRead = 0;
+		this.netBytesWrite = 0;
+		this.currentBytesRead = 0;
+		this.currentBytesWrite = 0;
+	}
+
+	public long getAccountId() {
+		return accountId;
+	}
+
+	public Long getId() {
+        return id;
+    }
+
+    public long getDataCenterId() {
+        return dataCenterId;
+    }
+    
+    public Long getVmId() {
+    	return vmId;
+    }
+    
+    public Long getVolumeId() {
+        return volumeId;
+    }
+    
+    public long getCurrentIORead() {
+        return currentIORead;
+    }
+
+    public void setCurrentIORead(long currentIORead) {
+        this.currentIORead = currentIORead;
+    }
+
+    public long getCurrentIOWrite() {
+        return currentIOWrite;
+    }
+
+    public void setCurrentIOWrite(long currentIOWrite) {
+        this.currentIOWrite = currentIOWrite;
+    }
+
+    public long getNetIORead() {
+		return netIORead;
+	}
+
+	public long getNetIOWrite() {
+		return netIOWrite;
+	}
+
+	public void setNetIORead(long netIORead) {
+		this.netIORead = netIORead;
+	}
+
+	public void setNetIOWrite(long netIOWrite) {
+		this.netIOWrite = netIOWrite;
+	}
+
+	public long getAggIORead() {
+		return aggIORead;
+	}
+
+	public void setAggIORead(long aggIORead) {
+		this.aggIORead = aggIORead;
+	}
+
+	public long getAggIOWrite() {
+		return aggIOWrite;
+	}
+
+	public void setAggIOWrite(long aggIOWrite) {
+		this.aggIOWrite = aggIOWrite;
+	}
+	
+    public long getCurrentBytesRead() {
+        return currentBytesRead;
+    }
+
+    public void setCurrentBytesRead(long currentBytesRead) {
+        this.currentBytesRead = currentBytesRead;
+    }
+
+    public long getCurrentBytesWrite() {
+        return currentBytesWrite;
+    }
+
+    public void setCurrentBytesWrite(long currentBytesWrite) {
+        this.currentBytesWrite = currentBytesWrite;
+    }
+
+    public long getNetBytesRead() {
+		return netBytesRead;
+	}
+
+	public long getNetBytesWrite() {
+		return netBytesWrite;
+	}
+
+	public void setNetBytesRead(long netBytesRead) {
+		this.netBytesRead = netBytesRead;
+	}
+
+	public void setNetBytesWrite(long netBytesWrite) {
+		this.netBytesWrite = netBytesWrite;
+	}
+
+	public long getAggBytesRead() {
+		return aggBytesRead;
+	}
+
+	public void setAggBytesRead(long aggBytesRead) {
+		this.aggBytesRead = aggBytesRead;
+	}
+
+	public long getAggBytesWrite() {
+		return aggBytesWrite;
+	}
+
+	public void setAggBytesWrite(long aggBytesWrite) {
+		this.aggBytesWrite = aggBytesWrite;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/engine/schema/src/com/cloud/user/dao/VmDiskStatisticsDao.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/user/dao/VmDiskStatisticsDao.java b/engine/schema/src/com/cloud/user/dao/VmDiskStatisticsDao.java
new file mode 100644
index 0000000..55206a6
--- /dev/null
+++ b/engine/schema/src/com/cloud/user/dao/VmDiskStatisticsDao.java
@@ -0,0 +1,35 @@
+// 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.user.dao;
+
+import java.util.Date;
+import java.util.List;
+
+import com.cloud.user.VmDiskStatisticsVO;
+import com.cloud.utils.db.GenericDao;
+
+public interface VmDiskStatisticsDao extends GenericDao<VmDiskStatisticsVO, Long> {
+    VmDiskStatisticsVO findBy(long accountId, long dcId, long vmId, long volumeId);
+
+    VmDiskStatisticsVO lock(long accountId, long dcId, long vmId, long volumeId);
+
+    List<VmDiskStatisticsVO> listBy(long accountId);
+
+    List<VmDiskStatisticsVO> listActiveAndRecentlyDeleted(Date minRemovedDate, int startIndex, int limit);
+
+	List<VmDiskStatisticsVO> listUpdatedStats();
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/engine/schema/src/com/cloud/user/dao/VmDiskStatisticsDaoImpl.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/user/dao/VmDiskStatisticsDaoImpl.java b/engine/schema/src/com/cloud/user/dao/VmDiskStatisticsDaoImpl.java
new file mode 100644
index 0000000..02f3406
--- /dev/null
+++ b/engine/schema/src/com/cloud/user/dao/VmDiskStatisticsDaoImpl.java
@@ -0,0 +1,134 @@
+// 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.user.dao;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.TimeZone;
+
+import javax.ejb.Local;
+
+import org.apache.log4j.Logger;
+import org.springframework.stereotype.Component;
+
+import com.cloud.user.VmDiskStatisticsVO;
+import com.cloud.utils.DateUtil;
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+import com.cloud.utils.db.Transaction;
+
+@Component
+@Local(value={VmDiskStatisticsDao.class})
+public class VmDiskStatisticsDaoImpl extends GenericDaoBase<VmDiskStatisticsVO, Long> implements VmDiskStatisticsDao {
+    private static final Logger s_logger = Logger.getLogger(VmDiskStatisticsDaoImpl.class);
+    private static final String ACTIVE_AND_RECENTLY_DELETED_SEARCH = "SELECT vns.id, vns.data_center_id, vns.account_id, vns.vm_id, vns.volume_id, vns.agg_io_read, vns.agg_io_write, vns.agg_bytes_read, vns.agg_bytes_write " +
+                                                                     "FROM vm_disk_statistics vns, account a " +
+                                                                     "WHERE vns.account_id = a.id AND (a.removed IS NULL OR a.removed >= ?) " +
+                                                                     "ORDER BY vns.id";
+    private static final String UPDATED_VM_NETWORK_STATS_SEARCH = "SELECT id, current_io_read, current_io_write, net_io_read, net_io_write, agg_io_read, agg_io_write, " +
+                                                                     "current_bytes_read, current_bytes_write, net_bytes_read, net_bytes_write, agg_bytes_read, agg_bytes_write " +
+                                                                     "from  vm_disk_statistics " +
+                                                                     "where (agg_io_read < net_io_read + current_io_read) OR (agg_io_write < net_io_write + current_io_write) OR " +
+                                                                     "(agg_bytes_read < net_bytes_read + current_bytes_read) OR (agg_bytes_write < net_bytes_write + current_bytes_write)";
+    private final SearchBuilder<VmDiskStatisticsVO> AllFieldsSearch;
+    private final SearchBuilder<VmDiskStatisticsVO> AccountSearch;
+    
+    
+    public VmDiskStatisticsDaoImpl() {
+    	AccountSearch = createSearchBuilder();
+    	AccountSearch.and("account", AccountSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
+    	AccountSearch.done();
+
+    	AllFieldsSearch = createSearchBuilder();
+        AllFieldsSearch.and("account", AllFieldsSearch.entity().getAccountId(), SearchCriteria.Op.EQ);
+        AllFieldsSearch.and("dc", AllFieldsSearch.entity().getDataCenterId(), SearchCriteria.Op.EQ);
+        AllFieldsSearch.and("volume", AllFieldsSearch.entity().getVolumeId(), SearchCriteria.Op.EQ);
+        AllFieldsSearch.and("vm", AllFieldsSearch.entity().getVmId(), SearchCriteria.Op.EQ);
+        AllFieldsSearch.done();
+    }
+    
+    @Override
+    public VmDiskStatisticsVO findBy(long accountId, long dcId, long vmId, long volumeId) {
+        SearchCriteria<VmDiskStatisticsVO> sc = AllFieldsSearch.create();
+        sc.setParameters("account", accountId);
+        sc.setParameters("dc", dcId);
+        sc.setParameters("volume", volumeId);
+        sc.setParameters("vm", vmId);
+        return findOneBy(sc);
+    }
+
+    @Override
+    public VmDiskStatisticsVO lock(long accountId, long dcId, long vmId, long volumeId) {
+        SearchCriteria<VmDiskStatisticsVO> sc = AllFieldsSearch.create();
+        sc.setParameters("account", accountId);
+        sc.setParameters("dc", dcId);
+        sc.setParameters("volume", volumeId);
+        sc.setParameters("vm", vmId);    
+        return lockOneRandomRow(sc, true);
+    }
+
+    @Override
+    public List<VmDiskStatisticsVO> listBy(long accountId) {
+        SearchCriteria<VmDiskStatisticsVO> sc = AccountSearch.create();
+        sc.setParameters("account", accountId);
+        return search(sc, null);
+    }
+
+    @Override
+    public List<VmDiskStatisticsVO> listActiveAndRecentlyDeleted(Date minRemovedDate, int startIndex, int limit) {
+        List<VmDiskStatisticsVO> vmDiskStats = new ArrayList<VmDiskStatisticsVO>();
+        if (minRemovedDate == null) return vmDiskStats;
+
+        Transaction txn = Transaction.currentTxn();
+        try {
+            String sql = ACTIVE_AND_RECENTLY_DELETED_SEARCH + " LIMIT " + startIndex + "," + limit;
+            PreparedStatement pstmt = null;
+            pstmt = txn.prepareAutoCloseStatement(sql);
+            pstmt.setString(1, DateUtil.getDateDisplayString(TimeZone.getTimeZone("GMT"), minRemovedDate));
+            ResultSet rs = pstmt.executeQuery();
+            while (rs.next()) {
+            	vmDiskStats.add(toEntityBean(rs, false));
+            }
+        } catch (Exception ex) {
+            s_logger.error("error saving vm disk stats to cloud_usage db", ex);
+        }
+        return vmDiskStats;
+    }
+
+    @Override
+    public List<VmDiskStatisticsVO> listUpdatedStats() {
+        List<VmDiskStatisticsVO> vmDiskStats = new ArrayList<VmDiskStatisticsVO>();
+
+        Transaction txn = Transaction.currentTxn();
+        try {
+            PreparedStatement pstmt = null;
+            pstmt = txn.prepareAutoCloseStatement(UPDATED_VM_NETWORK_STATS_SEARCH);
+            ResultSet rs = pstmt.executeQuery();
+            while (rs.next()) {
+            	vmDiskStats.add(toEntityBean(rs, false));
+            }
+        } catch (Exception ex) {
+            s_logger.error("error lisitng updated vm disk stats", ex);
+        }
+        return vmDiskStats;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/engine/schema/src/com/cloud/vm/dao/UserVmData.java
----------------------------------------------------------------------
diff --git a/engine/schema/src/com/cloud/vm/dao/UserVmData.java b/engine/schema/src/com/cloud/vm/dao/UserVmData.java
index 674fc00..6622a7d 100644
--- a/engine/schema/src/com/cloud/vm/dao/UserVmData.java
+++ b/engine/schema/src/com/cloud/vm/dao/UserVmData.java
@@ -57,6 +57,10 @@ public class UserVmData {
     private String cpuUsed;
     private Long networkKbsRead;
     private Long networkKbsWrite;
+    private Long diskKbsRead;
+    private Long diskKbsWrite;
+    private Long diskIORead;
+    private Long diskIOWrite;
     private Long guestOsId;
     private Long rootDeviceId;
     private String rootDeviceType;
@@ -364,6 +368,38 @@ public class UserVmData {
         this.networkKbsWrite = networkKbsWrite;
     }
 
+    public Long getDiskKbsRead() {
+        return diskKbsRead;
+    }
+
+    public void setDiskKbsRead(Long diskKbsRead) {
+        this.diskKbsRead = diskKbsRead;
+    }
+    
+    public Long getDiskKbsWrite() {
+        return diskKbsWrite;
+    }
+
+    public void setDiskKbsWrite(Long diskKbsWrite) {
+        this.diskKbsWrite = diskKbsWrite;
+    }
+    
+    public Long getDiskIORead() {
+        return diskIORead;
+    }
+
+    public void setDiskIORead(Long diskIORead) {
+        this.diskIORead = diskIORead;
+    }
+    
+    public Long getDiskIOWrite() {
+        return diskIOWrite;
+    }
+
+    public void setDiskIOWrite(Long diskIOWrite) {
+        this.diskIOWrite = diskIOWrite;
+    }
+    
     public Long getGuestOsId() {
         return guestOsId;
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
index f979cfe..46fce24 100755
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
@@ -63,6 +63,7 @@ import org.apache.cloudstack.utils.qemu.QemuImgFile;
 import org.apache.cloudstack.utils.qemu.QemuImgException;
 import org.libvirt.Connect;
 import org.libvirt.Domain;
+import org.libvirt.DomainBlockStats;
 import org.libvirt.DomainInfo;
 import org.libvirt.DomainInterfaceStats;
 import org.libvirt.DomainSnapshot;
@@ -99,6 +100,8 @@ import com.cloud.agent.api.GetHostStatsAnswer;
 import com.cloud.agent.api.GetHostStatsCommand;
 import com.cloud.agent.api.GetStorageStatsAnswer;
 import com.cloud.agent.api.GetStorageStatsCommand;
+import com.cloud.agent.api.GetVmDiskStatsAnswer;
+import com.cloud.agent.api.GetVmDiskStatsCommand;
 import com.cloud.agent.api.GetVmStatsAnswer;
 import com.cloud.agent.api.GetVmStatsCommand;
 import com.cloud.agent.api.GetVncPortAnswer;
@@ -145,6 +148,7 @@ import com.cloud.agent.api.StopCommand;
 import com.cloud.agent.api.UnPlugNicAnswer;
 import com.cloud.agent.api.UnPlugNicCommand;
 import com.cloud.agent.api.UpgradeSnapshotCommand;
+import com.cloud.agent.api.VmDiskStatsEntry;
 import com.cloud.agent.api.VmStatsEntry;
 import com.cloud.agent.api.check.CheckSshAnswer;
 import com.cloud.agent.api.check.CheckSshCommand;
@@ -1117,6 +1121,8 @@ ServerResource {
                 return execute((StopCommand) cmd);
             } else if (cmd instanceof GetVmStatsCommand) {
                 return execute((GetVmStatsCommand) cmd);
+            } else if (cmd instanceof GetVmDiskStatsCommand) {
+                return execute((GetVmDiskStatsCommand) cmd);
             } else if (cmd instanceof RebootRouterCommand) {
                 return execute((RebootRouterCommand) cmd);
             } else if (cmd instanceof RebootCommand) {
@@ -3007,6 +3013,26 @@ ServerResource {
         }
     }
 
+    protected GetVmDiskStatsAnswer execute(GetVmDiskStatsCommand cmd) {
+        List<String> vmNames = cmd.getVmNames();
+        try {
+            HashMap<String, List<VmDiskStatsEntry>> vmDiskStatsNameMap = new HashMap<String, List<VmDiskStatsEntry>>();
+            Connect conn = LibvirtConnection.getConnection();
+            for (String vmName : vmNames) {
+                List<VmDiskStatsEntry> statEntry = getVmDiskStat(conn, vmName);
+                if (statEntry == null) {
+                    continue;
+                }
+
+                vmDiskStatsNameMap.put(vmName, statEntry);
+            }
+            return new GetVmDiskStatsAnswer(cmd, "", cmd.getHostName(), vmDiskStatsNameMap);
+        } catch (LibvirtException e) {
+            s_logger.debug("Can't get vm disk stats: " + e.toString());
+            return new GetVmDiskStatsAnswer(cmd, null, null, null);
+        }
+    }
+
     protected GetVmStatsAnswer execute(GetVmStatsCommand cmd) {
         List<String> vmNames = cmd.getVmNames();
         try {
@@ -4512,10 +4538,46 @@ ServerResource {
         }
     }
 
+    private List<VmDiskStatsEntry> getVmDiskStat(Connect conn, String vmName)
+            throws LibvirtException {
+        Domain dm = null;
+        try {
+            dm = getDomain(conn, vmName);
+
+            List<VmDiskStatsEntry> stats = new ArrayList<VmDiskStatsEntry>();
+
+            List<DiskDef> disks = getDisks(conn, vmName);
+
+            for (DiskDef disk : disks) {
+                DomainBlockStats blockStats = dm.blockStats(disk.getDiskLabel());
+                String path = disk.getDiskPath(); // for example, path = /mnt/pool_uuid/disk_path/
+                String diskPath = null;
+                if (path != null) {
+                    String[] token = path.split("/");
+                    if (token.length > 3) {
+                        diskPath = token[3];
+                        VmDiskStatsEntry stat = new VmDiskStatsEntry(vmName, diskPath, blockStats.wr_req, blockStats.rd_req, blockStats.wr_bytes, blockStats.rd_bytes);
+                        stats.add(stat);
+                    }
+                }
+            }
+
+            return stats;
+        } finally {
+            if (dm != null) {
+                dm.free();
+            }
+        }
+    }
+
     private class vmStats {
         long _usedTime;
         long _tx;
         long _rx;
+        long _io_rd;
+        long _io_wr;
+        long _bytes_rd;
+        long _bytes_wr;
         Calendar _timestamp;
     }
 
@@ -4572,10 +4634,44 @@ ServerResource {
                     stats.setNetworkWriteKBs(deltatx / 1024);
             }
 
+            /* get disk stats */
+            List<DiskDef> disks = getDisks(conn, vmName);
+            long io_rd = 0;
+            long io_wr = 0;
+            long bytes_rd = 0;
+            long bytes_wr = 0;
+            for (DiskDef disk : disks) {
+                DomainBlockStats blockStats = dm.blockStats(disk.getDiskLabel());
+                io_rd += blockStats.rd_req;
+                io_wr += blockStats.wr_req;
+                bytes_rd += blockStats.rd_bytes;
+                bytes_wr += blockStats.wr_bytes;
+            }
+            
+            if (oldStats != null) {
+                long deltaiord = io_rd - oldStats._io_rd;
+                if (deltaiord > 0)
+                    stats.setDiskReadIOs(deltaiord);
+                long deltaiowr = io_wr - oldStats._io_wr;
+                if (deltaiowr > 0)
+                    stats.setDiskWriteIOs(deltaiowr);
+                long deltabytesrd = bytes_rd - oldStats._bytes_rd;
+                if (deltabytesrd > 0)
+                    stats.setDiskReadKBs(deltabytesrd / 1024);
+                long deltabyteswr = bytes_wr - oldStats._bytes_wr;
+                if (deltabyteswr > 0)
+                    stats.setDiskWriteKBs(deltabyteswr / 1024);
+            }
+            
+            /* save to Hashmap */
             vmStats newStat = new vmStats();
             newStat._usedTime = info.cpuTime;
             newStat._rx = rx;
             newStat._tx = tx;
+            newStat._io_rd = io_rd;
+            newStat._io_wr = io_wr;
+            newStat._bytes_rd = bytes_rd;
+            newStat._bytes_wr = bytes_wr;
             newStat._timestamp = now;
             _vmStats.put(vmName, newStat);
             return stats;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
index 7626d12..a2cceb1 100644
--- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
+++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
@@ -57,6 +57,8 @@ import com.cloud.agent.api.GetHostStatsAnswer;
 import com.cloud.agent.api.GetHostStatsCommand;
 import com.cloud.agent.api.GetStorageStatsAnswer;
 import com.cloud.agent.api.GetStorageStatsCommand;
+import com.cloud.agent.api.GetVmDiskStatsAnswer;
+import com.cloud.agent.api.GetVmDiskStatsCommand;
 import com.cloud.agent.api.GetVmStatsAnswer;
 import com.cloud.agent.api.GetVmStatsCommand;
 import com.cloud.agent.api.GetVncPortAnswer;
@@ -111,6 +113,7 @@ import com.cloud.agent.api.UnPlugNicAnswer;
 import com.cloud.agent.api.UnPlugNicCommand;
 import com.cloud.agent.api.UpdateHostPasswordCommand;
 import com.cloud.agent.api.UpgradeSnapshotCommand;
+import com.cloud.agent.api.VmDiskStatsEntry;
 import com.cloud.agent.api.VmStatsEntry;
 import com.cloud.agent.api.check.CheckSshAnswer;
 import com.cloud.agent.api.check.CheckSshCommand;
@@ -236,6 +239,7 @@ import com.xensource.xenapi.Types.VmBadPowerState;
 import com.xensource.xenapi.Types.VmPowerState;
 import com.xensource.xenapi.Types.XenAPIException;
 import com.xensource.xenapi.VBD;
+import com.xensource.xenapi.VBDMetrics;
 import com.xensource.xenapi.VDI;
 import com.xensource.xenapi.VIF;
 import com.xensource.xenapi.VLAN;
@@ -482,6 +486,8 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
             return execute((GetHostStatsCommand) cmd);
         } else if (clazz == GetVmStatsCommand.class) {
             return execute((GetVmStatsCommand) cmd);
+        } else if (cmd instanceof GetVmDiskStatsCommand) {
+            return execute((GetVmDiskStatsCommand) cmd);
         } else if (clazz == CheckHealthCommand.class) {
             return execute((CheckHealthCommand) cmd);
         } else if (clazz == StopCommand.class) {
@@ -2584,6 +2590,80 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
         return hostStats;
     }
 
+    protected GetVmDiskStatsAnswer execute( GetVmDiskStatsCommand cmd) {
+        Connection conn = getConnection();
+        List<String> vmNames = cmd.getVmNames();
+        HashMap<String, List<VmDiskStatsEntry>> vmDiskStatsNameMap = new HashMap<String, List<VmDiskStatsEntry>>();
+        if( vmNames.size() == 0 ) {
+            return new GetVmDiskStatsAnswer(cmd, "", cmd.getHostName(),vmDiskStatsNameMap);
+        }
+        try {
+
+            // Determine the UUIDs of the requested VMs
+            List<String> vmUUIDs = new ArrayList<String>();
+
+            for (String vmName : vmNames) {
+                VM vm = getVM(conn, vmName);
+                vmUUIDs.add(vm.getUuid(conn));
+            }
+
+            HashMap<String, List<VmDiskStatsEntry>> vmDiskStatsUUIDMap = getVmDiskStats(conn, cmd, vmUUIDs, cmd.getHostGuid());
+            if( vmDiskStatsUUIDMap == null ) {
+                return new GetVmDiskStatsAnswer(cmd, "", cmd.getHostName(), vmDiskStatsNameMap);
+            }
+
+            for (String vmUUID : vmDiskStatsUUIDMap.keySet()) {
+                List<VmDiskStatsEntry> vmDiskStatsUUID = vmDiskStatsUUIDMap.get(vmUUID);
+                String vmName = vmNames.get(vmUUIDs.indexOf(vmUUID));
+                for (VmDiskStatsEntry vmDiskStat : vmDiskStatsUUID) {
+                    vmDiskStat.setVmName(vmName);
+                }
+                vmDiskStatsNameMap.put(vmName, vmDiskStatsUUID);
+            }
+
+            return new GetVmDiskStatsAnswer(cmd, "", cmd.getHostName(),vmDiskStatsNameMap);
+        } catch (XenAPIException e) {
+            String msg = "Unable to get VM disk stats" + e.toString();
+            s_logger.warn(msg, e);
+            return new GetVmDiskStatsAnswer(cmd, "", cmd.getHostName(),vmDiskStatsNameMap);
+        } catch (XmlRpcException e) {
+            String msg = "Unable to get VM disk stats" + e.getMessage();
+            s_logger.warn(msg, e);
+            return new GetVmDiskStatsAnswer(cmd, "", cmd.getHostName(),vmDiskStatsNameMap);
+        }
+    }
+    
+    private HashMap<String, List<VmDiskStatsEntry>> getVmDiskStats(Connection conn, GetVmDiskStatsCommand cmd, List<String> vmUUIDs, String hostGuid) {
+        HashMap<String, List<VmDiskStatsEntry>> vmResponseMap = new HashMap<String, List<VmDiskStatsEntry>>();
+
+        for (String vmUUID : vmUUIDs) {
+            vmResponseMap.put(vmUUID, new ArrayList<VmDiskStatsEntry>());
+        }
+        
+        try {
+            for (String vmUUID : vmUUIDs) {
+                VM vm = VM.getByUuid(conn, vmUUID);
+                List<VmDiskStatsEntry> vmDiskStats = new ArrayList<VmDiskStatsEntry>();
+                for (VBD vbd : vm.getVBDs(conn)) {
+                    if (!vbd.getType(conn).equals(Types.VbdType.CD)) {
+                        VmDiskStatsEntry stats = new VmDiskStatsEntry();
+                        VBDMetrics record = vbd.getMetrics(conn);
+                        stats.setPath(vbd.getVDI(conn).getUuid(conn));
+                        stats.setBytesRead((long)(record.getIoReadKbs(conn) * 1024));
+                        stats.setBytesWrite((long)(record.getIoWriteKbs(conn) * 1024));
+                        vmDiskStats.add(stats);
+                    }
+                }
+                vmResponseMap.put(vmUUID, vmDiskStats);
+            }
+        } catch (Exception e) {
+            s_logger.warn("Error while collecting disk stats from : ", e);
+            return null;
+        }
+        
+        return vmResponseMap;
+    }
+
     protected GetVmStatsAnswer execute( GetVmStatsCommand cmd) {
         Connection conn = getConnection();
         List<String> vmNames = cmd.getVmNames();
@@ -2693,6 +2773,29 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
             }
         }
 
+        try {
+            for (String vmUUID : vmUUIDs) {
+                VM vm = VM.getByUuid(conn, vmUUID);
+                VmStatsEntry stats = vmResponseMap.get(vmUUID);
+                double diskReadKBs = 0;
+                double diskWriteKBs = 0;
+                for (VBD vbd : vm.getVBDs(conn)) {
+                    VBDMetrics record = vbd.getMetrics(conn);
+                    diskReadKBs += record.getIoReadKbs(conn);
+                    diskWriteKBs += record.getIoWriteKbs(conn);
+                }
+                if (stats == null) {
+                    stats = new VmStatsEntry();
+                }
+                stats.setDiskReadKBs(diskReadKBs);
+                stats.setDiskWriteKBs(diskWriteKBs);
+                vmResponseMap.put(vmUUID, stats);
+            }
+        } catch (Exception e) {
+            s_logger.warn("Error while collecting disk stats from : ", e);
+            return null;
+        }
+
         return vmResponseMap;
     }
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/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 c9ca089..029b14c 100755
--- a/server/src/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/com/cloud/api/ApiResponseHelper.java
@@ -3397,6 +3397,17 @@ public class ApiResponseHelper implements ResponseGenerator {
             NetworkVO network = _entityMgr.findByIdIncludingRemoved(NetworkVO.class, usageRecord.getNetworkId().toString());
             usageRecResponse.setNetworkId(network.getUuid());
 
+        } else if(usageRecord.getUsageType() == UsageTypes.VM_DISK_IO_READ || usageRecord.getUsageType() == UsageTypes.VM_DISK_IO_WRITE ||
+                  usageRecord.getUsageType() == UsageTypes.VM_DISK_BYTES_READ || usageRecord.getUsageType() == UsageTypes.VM_DISK_BYTES_WRITE){
+            //Device Type
+            usageRecResponse.setType(usageRecord.getType());
+            //VM Instance Id
+            VMInstanceVO vm = _entityMgr.findByIdIncludingRemoved(VMInstanceVO.class, usageRecord.getUsageId().toString());
+            usageRecResponse.setUsageId(vm.getUuid());
+            //Volume ID
+            VolumeVO volume = _entityMgr.findByIdIncludingRemoved(VolumeVO.class, usageRecord.getUsageId().toString());
+            usageRecResponse.setUsageId(volume.getUuid());
+
         } else if(usageRecord.getUsageType() == UsageTypes.VOLUME){
             //Volume ID
             VolumeVO volume = _entityMgr.findByIdIncludingRemoved(VolumeVO.class, usageRecord.getUsageId().toString());

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java
index 77c3c44..dbfe94d 100644
--- a/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java
+++ b/server/src/com/cloud/api/query/dao/UserVmJoinDaoImpl.java
@@ -160,6 +160,18 @@ public class UserVmJoinDaoImpl extends GenericDaoBase<UserVmJoinVO, Long> implem
 
                 Double networkKbWrite = Double.valueOf(vmStats.getNetworkWriteKBs());
                 userVmResponse.setNetworkKbsWrite(networkKbWrite.longValue());
+
+                Double diskKbsRead = Double.valueOf(vmStats.getDiskReadKBs());
+                userVmResponse.setDiskKbsRead(diskKbsRead.longValue());
+
+                Double diskKbsWrite = Double.valueOf(vmStats.getDiskWriteKBs());
+                userVmResponse.setDiskKbsWrite(diskKbsWrite.longValue());
+
+                Double diskIORead = Double.valueOf(vmStats.getDiskReadIOs());
+                userVmResponse.setDiskIORead(diskIORead.longValue());
+
+                Double diskIOWrite = Double.valueOf(vmStats.getDiskWriteIOs());
+                userVmResponse.setDiskIOWrite(diskIOWrite.longValue());
             }
         }
 

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/b9b0168d/server/src/com/cloud/configuration/Config.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java
index 929d56b..5ee0fad 100755
--- a/server/src/com/cloud/configuration/Config.java
+++ b/server/src/com/cloud/configuration/Config.java
@@ -229,6 +229,7 @@ public enum Config {
 	NetworkGcInterval("Advanced", ManagementServer.class, Integer.class, "network.gc.interval", "600", "Seconds to wait before checking for networks to shutdown", null),
 	CapacitySkipcountingHours("Advanced", ManagementServer.class, Integer.class, "capacity.skipcounting.hours", "3600", "Time (in seconds) to wait before release VM's cpu and memory when VM in stopped state", null),
 	VmStatsInterval("Advanced", ManagementServer.class, Integer.class, "vm.stats.interval", "60000", "The interval (in milliseconds) when vm stats are retrieved from agents.", null),
+	VmDiskStatsInterval("Advanced", ManagementServer.class, Integer.class, "vm.disk.stats.interval", "0", "Interval (in seconds) to report vm disk statistics.", null),
 	VmTransitionWaitInterval("Advanced", ManagementServer.class, Integer.class, "vm.tranisition.wait.interval", "3600", "Time (in seconds) to wait before taking over a VM in transition state", null),
 	VmDestroyForcestop("Advanced", ManagementServer.class, Boolean.class, "vm.destroy.forcestop", "false", "On destroy, force-stop takes this value ", null),