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:44 UTC

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

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),