This is an automated email from the ASF dual-hosted git repository.
bhaisaab pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/master by this push:
new 4627fb2 CLOUDSTACK-9972: Enhance listVolume API to include physical
size and … (#2158)
4627fb2 is described below
commit 4627fb2cd7556173bd7e58bf1bec22c93a78f31d
Author: Abhinandan Prateek <[email protected]>
AuthorDate: Sun Nov 5 21:44:43 2017 +0530
CLOUDSTACK-9972: Enhance listVolume API to include physical size and …
(#2158)
* CLOUDSTACK-9972: Enhance listVolume API to include physical size and
utilization.
Also fixed pool, cluster and pod info
* CLOUDSTACK-9972: Fix volume_view and duplicate API constant
* CLOUDSTACK-9972: Backport Do not allow vms to be deployed on hosts that
are in disabled pod
* CLOUDSTACK-9972: Fix localization missing keys
* CLOUDSTACK-9972: Fix sql path
---
.../src/com/cloud/agent/api/BadCommand.java | 26 +---
api/src/com/cloud/storage/VolumeStats.java | 7 +-
.../org/apache/cloudstack/api/ApiConstants.java | 2 +
.../api/command/user/volume/ListVolumesCmd.java | 8 ++
.../cloudstack/api/response/VolumeResponse.java | 88 ++++++++++++-
.../com/cloud/agent/api/GetFileStatsAnswer.java | 41 ------
.../com/cloud/agent/api/GetVmDiskStatsCommand.java | 4 +
.../com/cloud/agent/api/GetVolumeStatsAnswer.java | 73 +++++++++++
.../com/cloud/agent/api/GetVolumeStatsCommand.java | 75 +++++++++++
core/src/com/cloud/agent/api/VolumeStatsEntry.java | 64 ++++++++++
core/src/com/cloud/agent/transport/Request.java | 3 +
.../com/cloud/agent/transport/RequestTest.java | 26 +++-
.../LibvirtGetVolumeStatsCommandWrapper.java | 66 ++++++++++
.../hypervisor/kvm/storage/KVMPhysicalDisk.java | 5 +
.../hypervisor/kvm/storage/LibvirtStoragePool.java | 3 +-
.../hypervisor/vmware/resource/VmwareResource.java | 43 +++++++
.../CitrixGetVolumeStatsCommandWrapper.java | 62 +++++++++
server/src/com/cloud/api/ApiDBUtils.java | 5 +
.../src/com/cloud/api/query/QueryManagerImpl.java | 4 +
.../com/cloud/api/query/ViewResponseHelper.java | 26 ++++
.../com/cloud/api/query/dao/VolumeJoinDaoImpl.java | 6 +
.../src/com/cloud/api/query/vo/VolumeJoinVO.java | 47 +++++++
server/src/com/cloud/configuration/Config.java | 2 +
.../deploy/DeploymentPlanningManagerImpl.java | 88 +++++++------
server/src/com/cloud/server/StatsCollector.java | 68 ++++++++--
server/src/com/cloud/test/DatabaseConfig.java | 2 +-
server/src/com/cloud/vm/UserVmManager.java | 4 +
server/src/com/cloud/vm/UserVmManagerImpl.java | 22 ++++
setup/db/db/schema-41000to41100.sql | 139 ++++++++++++++++++++-
test/integration/smoke/test_volumes.py | 74 ++++++++++-
ui/l10n/ar.js | 3 +
ui/l10n/ca.js | 3 +
ui/l10n/de_DE.js | 3 +
ui/l10n/en.js | 3 +
ui/l10n/es.js | 3 +
ui/l10n/fr_FR.js | 3 +
ui/l10n/hu.js | 3 +
ui/l10n/it_IT.js | 3 +
ui/l10n/ja_JP.js | 3 +
ui/l10n/ko_KR.js | 3 +
ui/l10n/nb_NO.js | 3 +
ui/l10n/nl_NL.js | 3 +
ui/l10n/pl.js | 3 +
ui/l10n/pt_BR.js | 3 +
ui/l10n/ru_RU.js | 3 +
ui/l10n/zh_CN.js | 3 +
ui/scripts/metrics.js | 12 ++
ui/scripts/storage.js | 29 ++++-
.../cloud/hypervisor/vmware/mo/DatastoreMO.java | 32 +++++
.../hypervisor/vmware/mo/VirtualMachineMO.java | 53 ++++++++
50 files changed, 1139 insertions(+), 118 deletions(-)
diff --git a/core/src/com/cloud/agent/api/GetFileStatsCommand.java
b/api/src/com/cloud/agent/api/BadCommand.java
similarity index 71%
rename from core/src/com/cloud/agent/api/GetFileStatsCommand.java
rename to api/src/com/cloud/agent/api/BadCommand.java
index b2da1c3..55976f6 100644
--- a/core/src/com/cloud/agent/api/GetFileStatsCommand.java
+++ b/api/src/com/cloud/agent/api/BadCommand.java
@@ -1,4 +1,3 @@
-//
// 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
@@ -15,30 +14,17 @@
// 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.agent.api.LogLevel.Log4jLevel;
-import com.cloud.storage.Volume;
-
-@LogLevel(Log4jLevel.Trace)
-public class GetFileStatsCommand extends Command {
- protected GetFileStatsCommand() {
- }
-
- String paths;
-
- public GetFileStatsCommand(Volume volume) {
- paths = volume.getPath();
- }
-
- public String getPaths() {
- return paths;
- }
+public class BadCommand extends Command {
@Override
public boolean executeInSequence() {
+ // TODO Auto-generated method stub
return false;
}
+
+ public BadCommand(){
+ super();
+ }
}
diff --git a/api/src/com/cloud/storage/VolumeStats.java
b/api/src/com/cloud/storage/VolumeStats.java
index 70c0b17..81fa7ea 100644
--- a/api/src/com/cloud/storage/VolumeStats.java
+++ b/api/src/com/cloud/storage/VolumeStats.java
@@ -20,5 +20,10 @@ public interface VolumeStats {
/**
* @return bytes used by the volume
*/
- public long getBytesUsed();
+ long getVirtualSize();
+
+ /**
+ * @return bytes allocated
+ */
+ long getPhysicalSize();
}
diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java
b/api/src/org/apache/cloudstack/api/ApiConstants.java
index 1ec340d..2a2d686 100644
--- a/api/src/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/org/apache/cloudstack/api/ApiConstants.java
@@ -85,6 +85,7 @@ public class ApiConstants {
public static final String DEVICE_ID = "deviceid";
public static final String DISK_OFFERING_ID = "diskofferingid";
public static final String DISK_SIZE = "disksize";
+ public static final String UTILIZATION = "utilization";
public static final String DRIVER = "driver";
public static final String ROOT_DISK_SIZE = "rootdisksize";
public static final String DISPLAY_NAME = "displayname";
@@ -205,6 +206,7 @@ public class ApiConstants {
public static final String SSHKEY_ENABLED = "sshkeyenabled";
public static final String PATH = "path";
public static final String POD_ID = "podid";
+ public static final String POD_NAME = "podname";
public static final String POD_IDS = "podids";
public static final String POLICY_ID = "policyid";
public static final String PORT = "port";
diff --git
a/api/src/org/apache/cloudstack/api/command/user/volume/ListVolumesCmd.java
b/api/src/org/apache/cloudstack/api/command/user/volume/ListVolumesCmd.java
index 059def7..554e029 100644
--- a/api/src/org/apache/cloudstack/api/command/user/volume/ListVolumesCmd.java
+++ b/api/src/org/apache/cloudstack/api/command/user/volume/ListVolumesCmd.java
@@ -26,6 +26,7 @@ import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.api.BaseListTaggedResourcesCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ResponseObject.ResponseView;
+import org.apache.cloudstack.api.response.ClusterResponse;
import org.apache.cloudstack.api.response.DiskOfferingResponse;
import org.apache.cloudstack.api.response.HostResponse;
import org.apache.cloudstack.api.response.ListResponse;
@@ -63,6 +64,9 @@ public class ListVolumesCmd extends
BaseListTaggedResourcesCmd {
@Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType
= PodResponse.class, description = "the pod id the disk volume belongs to")
private Long podId;
+ @Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID,
entityType = ClusterResponse.class, description = "the cluster id the disk
volume belongs to", authorized = {RoleType.Admin})
+ private Long clusterId;
+
@Parameter(name = ApiConstants.TYPE, type = CommandType.STRING,
description = "the type of disk volume")
private String type;
@@ -98,6 +102,10 @@ public class ListVolumesCmd extends
BaseListTaggedResourcesCmd {
return hostId;
}
+ public Long getClusterId() {
+ return clusterId;
+ }
+
public Long getId() {
return id;
}
diff --git a/api/src/org/apache/cloudstack/api/response/VolumeResponse.java
b/api/src/org/apache/cloudstack/api/response/VolumeResponse.java
index e25adf6..895e13c 100644
--- a/api/src/org/apache/cloudstack/api/response/VolumeResponse.java
+++ b/api/src/org/apache/cloudstack/api/response/VolumeResponse.java
@@ -228,9 +228,36 @@ public class VolumeResponse extends
BaseResponseWithTagInformation implements Co
String chainInfo;
@SerializedName(ApiConstants.SNAPSHOT_QUIESCEVM)
- @Param(description = "need quiesce vm or not when taking snapshot",
since="4.3")
+ @Param(description = "need quiesce vm or not when taking snapshot", since
= "4.3")
private boolean needQuiescevm;
+ @SerializedName(ApiConstants.PHYSICAL_SIZE)
+ @Param(description = "the bytes alloaated")
+ private Long physicalsize;
+
+ @SerializedName(ApiConstants.VIRTUAL_SIZE)
+ @Param(description = "the bytes actually consumed on disk")
+ private Long virtualsize;
+
+ @SerializedName(ApiConstants.UTILIZATION)
+ @Param(description = "the disk utilization")
+ private String utilization;
+
+ @SerializedName(ApiConstants.CLUSTER_ID)
+ @Param(description = "cluster id of the volume")
+ private String clusterid;
+
+ @SerializedName(ApiConstants.CLUSTER_NAME)
+ @Param(description = "cluster name where the volume is allocated")
+ private String clustername;
+
+ @SerializedName(ApiConstants.POD_ID)
+ @Param(description = "pod id of the volume")
+ private String podid;
+
+ @SerializedName(ApiConstants.POD_NAME)
+ @Param(description = "pod name of the volume")
+ private String podname;
public String getPath() {
return path;
@@ -301,7 +328,7 @@ public class VolumeResponse extends
BaseResponseWithTagInformation implements Co
this.virtualMachineState = virtualMachineState;
}
- public void setProvisioningType(String provisioningType){
+ public void setProvisioningType(String provisioningType) {
this.provisioningType = provisioningType;
}
@@ -649,4 +676,61 @@ public class VolumeResponse extends
BaseResponseWithTagInformation implements Co
public Boolean getDisplayVolume() {
return displayVolume;
}
+
+ public Long getPhysicalsize() {
+ return physicalsize;
+ }
+
+ public void setPhysicalsize(Long physicalsize) {
+ this.physicalsize = physicalsize;
+ }
+
+ public Long getVirtualsize() {
+ return virtualsize;
+ }
+
+ public void setVirtualsize(Long virtualsize) {
+ this.virtualsize = virtualsize;
+ }
+
+ public String getUtilization() {
+ return utilization;
+ }
+
+ public void setUtilization(String utilization) {
+ this.utilization = utilization;
+ }
+
+ public String getClusterId() {
+ return clusterid;
+ }
+
+ public void setClusterId(String clusterid) {
+ this.clusterid = clusterid;
+ }
+
+ public String getClusterName() {
+ return clustername;
+ }
+
+ public void setClusterName(String clustername) {
+ this.clustername = clustername;
+ }
+
+ public String getPodId() {
+ return podid;
+ }
+
+ public void setPodId(String podid) {
+ this.podid = podid;
+ }
+
+ public String getPodName() {
+ return podname;
+ }
+
+ public void setPodName(String podname) {
+ this.podname = podname;
+ }
+
}
diff --git a/core/src/com/cloud/agent/api/GetFileStatsAnswer.java
b/core/src/com/cloud/agent/api/GetFileStatsAnswer.java
deleted file mode 100644
index 5c3f006..0000000
--- a/core/src/com/cloud/agent/api/GetFileStatsAnswer.java
+++ /dev/null
@@ -1,41 +0,0 @@
-//
-// 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.agent.api.LogLevel.Log4jLevel;
-import com.cloud.storage.VolumeStats;
-
-@LogLevel(Log4jLevel.Trace)
-public class GetFileStatsAnswer extends Answer implements VolumeStats {
- long size;
-
- protected GetFileStatsAnswer() {
- }
-
- public GetFileStatsAnswer(GetFileStatsCommand cmd, long value) {
- super(cmd);
- size = value;
- }
-
- @Override
- public long getBytesUsed() {
- return size;
- }
-}
diff --git a/core/src/com/cloud/agent/api/GetVmDiskStatsCommand.java
b/core/src/com/cloud/agent/api/GetVmDiskStatsCommand.java
index 4b2b2e8..8c18e0e 100644
--- a/core/src/com/cloud/agent/api/GetVmDiskStatsCommand.java
+++ b/core/src/com/cloud/agent/api/GetVmDiskStatsCommand.java
@@ -25,6 +25,10 @@ import com.cloud.agent.api.LogLevel.Log4jLevel;
@LogLevel(Log4jLevel.Trace)
public class GetVmDiskStatsCommand extends Command {
+ public String getString() {
+ return "GetVmDiskStatsCommand [vmNames=" + vmNames + ", hostGuid=" +
hostGuid + ", hostName=" + hostName + "]";
+ }
+
List<String> vmNames;
String hostGuid;
String hostName;
diff --git a/core/src/com/cloud/agent/api/GetVolumeStatsAnswer.java
b/core/src/com/cloud/agent/api/GetVolumeStatsAnswer.java
new file mode 100644
index 0000000..8f00a4c
--- /dev/null
+++ b/core/src/com/cloud/agent/api/GetVolumeStatsAnswer.java
@@ -0,0 +1,73 @@
+//
+// 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 com.cloud.agent.api.LogLevel.Log4jLevel;
+import com.cloud.storage.Storage.StoragePoolType;
+
+@LogLevel(Log4jLevel.Trace)
+public class GetVolumeStatsAnswer extends Answer {
+
+ String poolUuid;
+ StoragePoolType poolType;
+ HashMap<String, VolumeStatsEntry> volumeStats;
+
+ public GetVolumeStatsAnswer(GetVolumeStatsCommand cmd, String details,
HashMap<String, VolumeStatsEntry> volumeStats) {
+ super(cmd, true, details);
+ this.poolUuid = cmd.getPoolUuid();
+ this.poolType = cmd.getPoolType();
+ this.volumeStats = volumeStats;
+ }
+
+ protected GetVolumeStatsAnswer() {
+ //no-args constructor for json serialization-deserialization
+ }
+
+ public String getPoolUuid() {
+ return poolUuid;
+ }
+
+ public void setPoolUuid(String poolUuid) {
+ this.poolUuid = poolUuid;
+ }
+
+ public StoragePoolType getPoolType() {
+ return poolType;
+ }
+
+ public void setPoolType(StoragePoolType poolType) {
+ this.poolType = poolType;
+ }
+
+ public HashMap<String, VolumeStatsEntry> getVolumeStats() {
+ return volumeStats;
+ }
+
+ public void setVolumeStats(HashMap<String, VolumeStatsEntry> volumeStats) {
+ this.volumeStats = volumeStats;
+ }
+
+ public String getString() {
+ return "GetVolumeStatsAnswer [poolUuid=" + poolUuid + ", poolType=" +
poolType + ", volumeStats=" + volumeStats + "]";
+ }
+
+}
diff --git a/core/src/com/cloud/agent/api/GetVolumeStatsCommand.java
b/core/src/com/cloud/agent/api/GetVolumeStatsCommand.java
new file mode 100644
index 0000000..a08f0db
--- /dev/null
+++ b/core/src/com/cloud/agent/api/GetVolumeStatsCommand.java
@@ -0,0 +1,75 @@
+//
+// 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;
+import com.cloud.storage.Storage.StoragePoolType;
+
+@LogLevel(Log4jLevel.Trace)
+public class GetVolumeStatsCommand extends Command {
+
+ List<String> volumeUuids;
+ StoragePoolType poolType;
+ String poolUuid;
+
+ protected GetVolumeStatsCommand() {
+ }
+
+ public GetVolumeStatsCommand(StoragePoolType poolType, String storeUuid,
List<String> volumeUuids) {
+ this.volumeUuids = volumeUuids;
+ this.poolType = poolType;
+ this.poolUuid = storeUuid;
+ }
+
+ public List<String> getVolumeUuids() {
+ return volumeUuids;
+ }
+
+ public void setVolumeUuids(List<String> volumeUuids) {
+ this.volumeUuids = volumeUuids;
+ }
+
+ public StoragePoolType getPoolType() {
+ return poolType;
+ }
+
+ public void setPoolType(StoragePoolType poolType) {
+ this.poolType = poolType;
+ }
+
+ public String getPoolUuid() {
+ return poolUuid;
+ }
+
+ public void setPoolUuid(String storeUuid) {
+ this.poolUuid = storeUuid;
+ }
+
+ @Override
+ public boolean executeInSequence() {
+ return false;
+ }
+
+ public String getString() {
+ return "GetVolumeStatsCommand [volumeUuids=" + volumeUuids + ",
poolType=" + poolType + ", poolUuid=" + poolUuid + "]";
+ }
+}
\ No newline at end of file
diff --git a/core/src/com/cloud/agent/api/VolumeStatsEntry.java
b/core/src/com/cloud/agent/api/VolumeStatsEntry.java
new file mode 100644
index 0000000..fb4ecc7
--- /dev/null
+++ b/core/src/com/cloud/agent/api/VolumeStatsEntry.java
@@ -0,0 +1,64 @@
+//
+// 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.storage.VolumeStats;
+
+public class VolumeStatsEntry implements VolumeStats {
+ String volumeUuid;
+ long physicalsize = 0;
+ long virtualSize = 0;
+
+ public VolumeStatsEntry(String volumeUuid, long physicalsize, long
virtualSize) {
+ this.volumeUuid = volumeUuid;
+ this.physicalsize = physicalsize;
+ this.virtualSize = virtualSize;
+ }
+
+ public String getVolumeUuid() {
+ return volumeUuid;
+ }
+
+ public void setVolumeUuid(String volumeUuid) {
+ this.volumeUuid = volumeUuid;
+ }
+
+ public long getPhysicalSize() {
+ return physicalsize;
+ }
+
+ public void setPhysicalSize(long size) {
+ this.physicalsize = size;
+ }
+
+ public long getVirtualSize() {
+ return virtualSize;
+ }
+
+ public void setVirtualSize(long virtualSize) {
+ this.virtualSize = virtualSize;
+ }
+
+ @Override
+ public String toString() {
+ return "VolumeStatsEntry [volumeUuid=" + volumeUuid + ", size=" +
physicalsize + ", virtualSize=" + virtualSize + "]";
+ }
+
+}
diff --git a/core/src/com/cloud/agent/transport/Request.java
b/core/src/com/cloud/agent/transport/Request.java
index f78a96c..09f6bd4 100644
--- a/core/src/com/cloud/agent/transport/Request.java
+++ b/core/src/com/cloud/agent/transport/Request.java
@@ -47,6 +47,7 @@ import com.google.gson.JsonSerializer;
import com.google.gson.stream.JsonReader;
import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.BadCommand;
import com.cloud.agent.api.Command;
import com.cloud.agent.api.SecStorageFirewallCfgCommand.PortConfig;
import com.cloud.exception.UnsupportedVersionException;
@@ -249,6 +250,8 @@ public class Request {
JsonReader jsonReader = new JsonReader(reader);
jsonReader.setLenient(true);
_cmds = s_gson.fromJson(jsonReader, (Type)Command[].class);
+ } catch (JsonParseException e) {
+ _cmds = new Command[] { new BadCommand() };
} catch (RuntimeException e) {
s_logger.error("Caught problem with " + _content, e);
throw e;
diff --git a/core/test/com/cloud/agent/transport/RequestTest.java
b/core/test/com/cloud/agent/transport/RequestTest.java
index ee3b082..21766ba 100644
--- a/core/test/com/cloud/agent/transport/RequestTest.java
+++ b/core/test/com/cloud/agent/transport/RequestTest.java
@@ -20,7 +20,6 @@
package com.cloud.agent.transport;
import java.nio.ByteBuffer;
-
import junit.framework.TestCase;
import org.apache.log4j.Level;
@@ -32,13 +31,16 @@ import
org.apache.cloudstack.storage.command.DownloadCommand;
import org.apache.cloudstack.storage.to.TemplateObjectTO;
import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.BadCommand;
import com.cloud.agent.api.Command;
import com.cloud.agent.api.GetHostStatsCommand;
+import com.cloud.agent.api.GetVolumeStatsCommand;
import com.cloud.agent.api.SecStorageFirewallCfgCommand;
import com.cloud.agent.api.UpdateHostPasswordCommand;
import com.cloud.agent.api.storage.DownloadAnswer;
import com.cloud.agent.api.storage.ListTemplateCommand;
import com.cloud.agent.api.to.NfsTO;
+import com.cloud.agent.transport.Request.Version;
import com.cloud.exception.UnsupportedVersionException;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.serializer.GsonHelper;
@@ -250,4 +252,26 @@ public class RequestTest extends TestCase {
}
}
+ public void testGoodCommand() {
+ s_logger.info("Testing good Command");
+ String content =
"[{\"com.cloud.agent.api.GetVolumeStatsCommand\":{\"volumeUuids\":[\"dcc860ac-4a20-498f-9cb3-bab4d57aa676\"],"
+ +
"\"poolType\":\"NetworkFilesystem\",\"poolUuid\":\"e007c270-2b1b-3ce9-ae92-a98b94eef7eb\",\"contextMap\":{},\"wait\":5}}]";
+ Request sreq = new Request(Version.v2, 1L, 2L, 3L, 1L, (short)1,
content);
+ sreq.setSequence(1);
+ Command cmds[] = sreq.getCommands();
+ s_logger.debug("Command class = " +
cmds[0].getClass().getSimpleName());
+ assert cmds[0].getClass().equals(GetVolumeStatsCommand.class);
+ }
+
+ public void testBadCommand() {
+ s_logger.info("Testing Bad Command");
+ String content =
"[{\"com.cloud.agent.api.SomeJunkCommand\":{\"volumeUuids\":[\"dcc860ac-4a20-498f-9cb3-bab4d57aa676\"],"
+ +
"\"poolType\":\"NetworkFilesystem\",\"poolUuid\":\"e007c270-2b1b-3ce9-ae92-a98b94eef7eb\",\"contextMap\":{},\"wait\":5}}]";
+ Request sreq = new Request(Version.v2, 1L, 2L, 3L, 1L, (short)1,
content);
+ sreq.setSequence(1);
+ Command cmds[] = sreq.getCommands();
+ s_logger.debug("Command class = " +
cmds[0].getClass().getSimpleName());
+ assert cmds[0].getClass().equals(BadCommand.class);
+ }
+
}
diff --git
a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVolumeStatsCommandWrapper.java
b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVolumeStatsCommandWrapper.java
new file mode 100644
index 0000000..6d945b1
--- /dev/null
+++
b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtGetVolumeStatsCommandWrapper.java
@@ -0,0 +1,66 @@
+//
+//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.hypervisor.kvm.resource.wrapper;
+
+import java.util.HashMap;
+
+import org.apache.log4j.Logger;
+import org.libvirt.Connect;
+import org.libvirt.LibvirtException;
+
+import com.cloud.agent.api.Answer;
+import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
+import com.cloud.hypervisor.kvm.resource.LibvirtConnection;
+import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk;
+import com.cloud.hypervisor.kvm.storage.KVMStoragePool;
+import com.cloud.resource.CommandWrapper;
+import com.cloud.resource.ResourceWrapper;
+import com.cloud.storage.Storage.StoragePoolType;
+import com.cloud.agent.api.GetVolumeStatsAnswer;
+import com.cloud.agent.api.GetVolumeStatsCommand;
+import com.cloud.agent.api.VolumeStatsEntry;
+
+@ResourceWrapper(handles = GetVolumeStatsCommand.class)
+public final class LibvirtGetVolumeStatsCommandWrapper extends
CommandWrapper<GetVolumeStatsCommand, Answer, LibvirtComputingResource> {
+ private static final Logger s_logger =
Logger.getLogger(LibvirtGetVmDiskStatsCommandWrapper.class);
+
+ @Override
+ public Answer execute(final GetVolumeStatsCommand cmd, final
LibvirtComputingResource libvirtComputingResource) {
+ try {
+ Connect conn = LibvirtConnection.getConnection();
+ String storeUuid = cmd.getPoolUuid();
+ StoragePoolType poolType = cmd.getPoolType();
+ HashMap<String, VolumeStatsEntry> statEntry = new HashMap<String,
VolumeStatsEntry>();
+ for (String volumeUuid : cmd.getVolumeUuids()) {
+ statEntry.put(volumeUuid,
getVolumeStat(libvirtComputingResource, conn, volumeUuid, storeUuid, poolType));
+ }
+ return new GetVolumeStatsAnswer(cmd, "", statEntry);
+ } catch (LibvirtException e) {
+ return new GetVolumeStatsAnswer(cmd, "Can't get vm disk stats: " +
e.getMessage(), null);
+ }
+ }
+
+
+ private VolumeStatsEntry getVolumeStat(final LibvirtComputingResource
libvirtComputingResource, final Connect conn, final String volumeUuid, final
String storeUuid, final StoragePoolType poolType) throws LibvirtException {
+ KVMStoragePool sourceKVMPool =
libvirtComputingResource.getStoragePoolMgr().getStoragePool(poolType,
storeUuid);
+ KVMPhysicalDisk sourceKVMVolume =
sourceKVMPool.getPhysicalDisk(volumeUuid);
+ return new VolumeStatsEntry(volumeUuid, sourceKVMVolume.getSize(),
sourceKVMVolume.getVirtualSize());
+ }
+}
diff --git
a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMPhysicalDisk.java
b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMPhysicalDisk.java
index c344e8c..eaa143a 100644
---
a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMPhysicalDisk.java
+++
b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMPhysicalDisk.java
@@ -56,6 +56,11 @@ public class KVMPhysicalDisk {
this.pool = pool;
}
+ @Override
+ public String toString() {
+ return "KVMPhysicalDisk [path=" + path + ", name=" + name + ", pool="
+ pool + ", format=" + format + ", size=" + size + ", virtualSize=" +
virtualSize + "]";
+ }
+
public void setFormat(PhysicalDiskFormat format) {
this.format = format;
}
diff --git
a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java
b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java
index 66018dd..1b554f7 100644
---
a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java
+++
b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java
@@ -140,7 +140,7 @@ public class LibvirtStoragePool implements KVMStoragePool {
if (disk != null) {
return disk;
}
- s_logger.debug("find volume bypass libvirt");
+ s_logger.debug("find volume bypass libvirt volumeUid " + volumeUid);
//For network file system or file system, try to use java file to find
the volume, instead of through libvirt. BUG:CLOUDSTACK-4459
String localPoolPath = this.getLocalPath();
File f = new File(localPoolPath + File.separator + volumeUuid);
@@ -152,6 +152,7 @@ public class LibvirtStoragePool implements KVMStoragePool {
disk.setFormat(PhysicalDiskFormat.QCOW2);
disk.setSize(f.length());
disk.setVirtualSize(f.length());
+ s_logger.debug("find volume bypass libvirt disk " + disk.toString());
return disk;
}
diff --git
a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java
b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java
index 9d32f34..40ffdf4 100644
---
a/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java
+++
b/plugins/hypervisors/vmware/src/com/cloud/hypervisor/vmware/resource/VmwareResource.java
@@ -146,6 +146,8 @@ import com.cloud.agent.api.GetVmStatsAnswer;
import com.cloud.agent.api.GetVmStatsCommand;
import com.cloud.agent.api.GetVncPortAnswer;
import com.cloud.agent.api.GetVncPortCommand;
+import com.cloud.agent.api.GetVolumeStatsAnswer;
+import com.cloud.agent.api.GetVolumeStatsCommand;
import com.cloud.agent.api.HostStatsEntry;
import com.cloud.agent.api.HostVmStateReportEntry;
import com.cloud.agent.api.MaintainAnswer;
@@ -199,6 +201,7 @@ import com.cloud.agent.api.UpgradeSnapshotCommand;
import com.cloud.agent.api.ValidateSnapshotAnswer;
import com.cloud.agent.api.ValidateSnapshotCommand;
import com.cloud.agent.api.VmStatsEntry;
+import com.cloud.agent.api.VolumeStatsEntry;
import com.cloud.agent.api.check.CheckSshAnswer;
import com.cloud.agent.api.check.CheckSshCommand;
import com.cloud.agent.api.routing.IpAssocCommand;
@@ -414,6 +417,8 @@ public class VmwareResource implements StoragePoolResource,
ServerResource, Vmwa
answer = execute((GetVmNetworkStatsCommand) cmd);
} else if (clz == GetVmDiskStatsCommand.class) {
answer = execute((GetVmDiskStatsCommand)cmd);
+ } else if (cmd instanceof GetVolumeStatsCommand) {
+ return execute((GetVolumeStatsCommand)cmd);
} else if (clz == CheckHealthCommand.class) {
answer = execute((CheckHealthCommand)cmd);
} else if (clz == StopCommand.class) {
@@ -3275,6 +3280,44 @@ public class VmwareResource implements
StoragePoolResource, ServerResource, Vmwa
return new GetVmNetworkStatsAnswer(cmd, null, null, null);
}
+ protected GetVolumeStatsAnswer execute(GetVolumeStatsCommand cmd) {
+ try {
+ VmwareHypervisorHost srcHyperHost =
getHyperHost(getServiceContext());
+ ManagedObjectReference morDs =
HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(srcHyperHost,
cmd.getPoolUuid());
+ assert (morDs != null);
+ DatastoreMO primaryStorageDatastoreMo = new
DatastoreMO(getServiceContext(), morDs);
+ VmwareHypervisorHost hyperHost = getHyperHost(getServiceContext());
+ ManagedObjectReference dcMor = hyperHost.getHyperHostDatacenter();
+ DatacenterMO dcMo = new DatacenterMO(getServiceContext(), dcMor);
+ HashMap<String, VolumeStatsEntry> statEntry = new HashMap<String,
VolumeStatsEntry>();
+
+ for (String chainInfo : cmd.getVolumeUuids()){
+ if (chainInfo != null) {
+ VirtualMachineDiskInfo infoInChain =
_gson.fromJson(chainInfo, VirtualMachineDiskInfo.class);
+ if (infoInChain != null) {
+ String[] disks = infoInChain.getDiskChain();
+ if (disks.length > 0) {
+ for (String diskPath : disks) {
+ DatastoreFile file = new
DatastoreFile(diskPath);
+ VirtualMachineMO vmMo =
dcMo.findVm(file.getDir());
+ Pair<VirtualDisk, String> vds =
vmMo.getDiskDevice(file.getFileName(), true);
+ long virtualsize =
vds.first().getCapacityInKB() * 1024;
+ long physicalsize =
primaryStorageDatastoreMo.fileDiskSize(file.getPath());
+ VolumeStatsEntry vse = new
VolumeStatsEntry(chainInfo, physicalsize, virtualsize);
+ statEntry.put(chainInfo, vse);
+ }
+ }
+ }
+ }
+ }
+ return new GetVolumeStatsAnswer(cmd, "", statEntry);
+ } catch (Exception e) {
+ s_logger.info("VOLSTAT GetVolumeStatsCommand failed " +
e.getMessage());
+ }
+
+ return new GetVolumeStatsAnswer(cmd, "", null);
+ }
+
protected Answer execute(CheckHealthCommand cmd) {
if (s_logger.isInfoEnabled()) {
s_logger.info("Executing resource CheckHealthCommand: " +
_gson.toJson(cmd));
diff --git
a/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixGetVolumeStatsCommandWrapper.java
b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixGetVolumeStatsCommandWrapper.java
new file mode 100644
index 0000000..f5d6604
--- /dev/null
+++
b/plugins/hypervisors/xenserver/src/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixGetVolumeStatsCommandWrapper.java
@@ -0,0 +1,62 @@
+//
+//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.hypervisor.xenserver.resource.wrapper.xenbase;
+
+import java.util.HashMap;
+
+import org.apache.log4j.Logger;
+
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.GetVolumeStatsAnswer;
+import com.cloud.agent.api.GetVolumeStatsCommand;
+import com.cloud.agent.api.VolumeStatsEntry;
+import com.cloud.hypervisor.xenserver.resource.CitrixResourceBase;
+import com.cloud.resource.CommandWrapper;
+import com.cloud.resource.ResourceWrapper;
+import com.xensource.xenapi.Connection;
+import com.xensource.xenapi.VDI;
+
+@ResourceWrapper(handles = GetVolumeStatsCommand.class)
+public final class CitrixGetVolumeStatsCommandWrapper extends
CommandWrapper<GetVolumeStatsCommand, Answer, CitrixResourceBase> {
+ private static final Logger s_logger =
Logger.getLogger(CitrixGetVolumeStatsCommandWrapper.class);
+
+ @Override
+ public Answer execute(final GetVolumeStatsCommand cmd, final
CitrixResourceBase citrixResourceBase) {
+ Connection conn = citrixResourceBase.getConnection();
+ HashMap<String, VolumeStatsEntry> statEntry = new HashMap<String,
VolumeStatsEntry>();
+ for (String volumeUuid : cmd.getVolumeUuids()) {
+ VDI vdi = citrixResourceBase.getVDIbyUuid(conn, volumeUuid, false);
+ if (vdi != null) {
+ try {
+ VolumeStatsEntry vse = new VolumeStatsEntry(volumeUuid,
vdi.getPhysicalUtilisation(conn), vdi.getVirtualSize(conn));
+ statEntry.put(volumeUuid, vse);
+ } catch (Exception e) {
+ s_logger.warn("Unable to get volume stats", e);
+ statEntry.put(volumeUuid, new VolumeStatsEntry(volumeUuid,
-1, -1));
+ }
+ } else {
+ s_logger.warn("VDI not found for path " + volumeUuid);
+ statEntry.put(volumeUuid, new VolumeStatsEntry(volumeUuid,
-1L, -1L));
+ }
+ }
+ return new GetVolumeStatsAnswer(cmd, "", statEntry);
+ }
+
+}
\ No newline at end of file
diff --git a/server/src/com/cloud/api/ApiDBUtils.java
b/server/src/com/cloud/api/ApiDBUtils.java
index e88a102..9f67aa7 100644
--- a/server/src/com/cloud/api/ApiDBUtils.java
+++ b/server/src/com/cloud/api/ApiDBUtils.java
@@ -260,6 +260,7 @@ import com.cloud.storage.UploadVO;
import com.cloud.storage.VMTemplateVO;
import com.cloud.storage.Volume;
import com.cloud.storage.Volume.Type;
+import com.cloud.storage.VolumeStats;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.DiskOfferingDao;
import com.cloud.storage.dao.GuestOSCategoryDao;
@@ -923,6 +924,10 @@ public class ApiDBUtils {
return s_statsCollector.getVmStats(hostId);
}
+ public static VolumeStats getVolumeStatistics(String volumeUuid) {
+ return s_statsCollector.getVolumeStats(volumeUuid);
+ }
+
public static StorageStats getSecondaryStorageStatistics(long id) {
return s_statsCollector.getStorageStats(id);
}
diff --git a/server/src/com/cloud/api/query/QueryManagerImpl.java
b/server/src/com/cloud/api/query/QueryManagerImpl.java
index 1c5c70c..42bef79 100644
--- a/server/src/com/cloud/api/query/QueryManagerImpl.java
+++ b/server/src/com/cloud/api/query/QueryManagerImpl.java
@@ -1736,6 +1736,7 @@ public class QueryManagerImpl extends
MutualExclusiveIdsManagerBase implements Q
String type = cmd.getType();
Map<String, String> tags = cmd.getTags();
Long storageId = cmd.getStorageId();
+ Long clusterId = cmd.getClusterId();
Long diskOffId = cmd.getDiskOfferingId();
Boolean display = cmd.getDisplay();
@@ -1845,6 +1846,9 @@ public class QueryManagerImpl extends
MutualExclusiveIdsManagerBase implements Q
sc.setParameters("storageId", storageId);
}
+ if (clusterId != null) {
+ sc.setParameters("clusterId", clusterId);
+ }
// Don't return DomR and ConsoleProxy volumes
sc.setParameters("type", VirtualMachine.Type.ConsoleProxy,
VirtualMachine.Type.SecondaryStorageVm, VirtualMachine.Type.DomainRouter);
diff --git a/server/src/com/cloud/api/query/ViewResponseHelper.java
b/server/src/com/cloud/api/query/ViewResponseHelper.java
index dfed7ba..11af5a9 100644
--- a/server/src/com/cloud/api/query/ViewResponseHelper.java
+++ b/server/src/com/cloud/api/query/ViewResponseHelper.java
@@ -16,6 +16,7 @@
// under the License.
package com.cloud.api.query;
+import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.Hashtable;
@@ -79,6 +80,8 @@ import com.cloud.api.query.vo.UserAccountJoinVO;
import com.cloud.api.query.vo.UserVmJoinVO;
import com.cloud.api.query.vo.VolumeJoinVO;
import com.cloud.storage.StoragePoolTagVO;
+import com.cloud.storage.Storage.ImageFormat;
+import com.cloud.storage.VolumeStats;
import com.cloud.user.Account;
/**
@@ -263,6 +266,7 @@ public class ViewResponseHelper {
public static List<VolumeResponse> createVolumeResponse(ResponseView view,
VolumeJoinVO... volumes) {
Hashtable<Long, VolumeResponse> vrDataList = new Hashtable<Long,
VolumeResponse>();
+ DecimalFormat df = new DecimalFormat("0.00");
for (VolumeJoinVO vr : volumes) {
VolumeResponse vrData = vrDataList.get(vr.getId());
if (vrData == null) {
@@ -274,6 +278,28 @@ public class ViewResponseHelper {
vrData = ApiDBUtils.fillVolumeDetails(view, vrData, vr);
}
vrDataList.put(vr.getId(), vrData);
+
+ if (view == ResponseView.Full) {
+ VolumeStats vs = null;
+ if (vr.getFormat() == ImageFormat.QCOW2) {
+ vs = ApiDBUtils.getVolumeStatistics(vrData.getId());
+ }
+ else if (vr.getFormat() == ImageFormat.VHD){
+ vs = ApiDBUtils.getVolumeStatistics(vrData.getPath());
+ }
+ else if (vr.getFormat() == ImageFormat.OVA){
+ vs = ApiDBUtils.getVolumeStatistics(vrData.getChainInfo());
+ }
+ if (vs != null){
+ long vsz = vs.getVirtualSize();
+ long psz = vs.getPhysicalSize() ;
+ double util = (double)psz/vsz;
+ vrData.setVirtualsize(vsz);
+ vrData.setPhysicalsize(psz);
+ vrData.setUtilization(df.format(util));
+ }
+ }
+
}
return new ArrayList<VolumeResponse>(vrDataList.values());
}
diff --git a/server/src/com/cloud/api/query/dao/VolumeJoinDaoImpl.java
b/server/src/com/cloud/api/query/dao/VolumeJoinDaoImpl.java
index 73e0c6d..6ed9be9 100644
--- a/server/src/com/cloud/api/query/dao/VolumeJoinDaoImpl.java
+++ b/server/src/com/cloud/api/query/dao/VolumeJoinDaoImpl.java
@@ -78,6 +78,12 @@ public class VolumeJoinDaoImpl extends
GenericDaoBaseWithTagInformation<VolumeJo
volResponse.setZoneId(volume.getDataCenterUuid());
volResponse.setZoneName(volume.getDataCenterName());
+ if (view == ResponseView.Full) {
+ volResponse.setClusterId(volume.getClusterUuid());
+ volResponse.setClusterName(volume.getClusterName());
+ volResponse.setPodId(volume.getPodUuid());
+ volResponse.setPodName(volume.getPodName());
+ }
if (volume.getVolumeType() != null) {
volResponse.setVolumeType(volume.getVolumeType().toString());
diff --git a/server/src/com/cloud/api/query/vo/VolumeJoinVO.java
b/server/src/com/cloud/api/query/vo/VolumeJoinVO.java
index 77785b1..cf361df 100644
--- a/server/src/com/cloud/api/query/vo/VolumeJoinVO.java
+++ b/server/src/com/cloud/api/query/vo/VolumeJoinVO.java
@@ -113,6 +113,21 @@ public class VolumeJoinVO extends
BaseViewWithTagInformationVO implements Contro
@Column(name = "pod_id")
private long podId;
+ @Column(name = "pod_name")
+ private String podName;
+
+ @Column(name = "pod_uuid")
+ private String podUuid;
+
+ @Column(name = "cluster_id")
+ private long clusterId;
+
+ @Column(name = "cluster_name")
+ private String clusterName;
+
+ @Column(name = "cluster_uuid")
+ private String clusterUuid;
+
@Column(name = "data_center_id")
private long dataCenterId;
@@ -469,6 +484,38 @@ public class VolumeJoinVO extends
BaseViewWithTagInformationVO implements Contro
return poolName;
}
+ public String getPodName() {
+ return podName;
+ }
+
+ public void setPodName(String podName) {
+ this.podName = podName;
+ }
+
+ public String getPodUuid() {
+ return podUuid;
+ }
+
+ public void setPodUuid(String podUuid) {
+ this.podUuid = podUuid;
+ }
+
+ public void setPodId(long podId) {
+ this.podId = podId;
+ }
+
+ public long getClusterId() {
+ return clusterId;
+ }
+
+ public String getClusterName() {
+ return clusterName;
+ }
+
+ public String getClusterUuid() {
+ return clusterUuid;
+ }
+
public long getTemplateId() {
return templateId;
}
diff --git a/server/src/com/cloud/configuration/Config.java
b/server/src/com/cloud/configuration/Config.java
index 6f48eab..bc8272a 100644
--- a/server/src/com/cloud/configuration/Config.java
+++ b/server/src/com/cloud/configuration/Config.java
@@ -853,6 +853,8 @@ public enum Config {
"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),
+ VolumeStatsInterval("Advanced", ManagementServer.class, Integer.class,
"volume.stats.interval", "60000", "Interval (in seconds) to report volume
statistics.", null),
VmTransitionWaitInterval(
"Advanced",
ManagementServer.class,
diff --git a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java
b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java
index 3363b0e..6cb5381 100644
--- a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java
+++ b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java
@@ -282,12 +282,22 @@ StateListener<State, VirtualMachine.Event,
VirtualMachine> {
s_logger.debug("The specified host is in avoid set");
} else {
if (s_logger.isDebugEnabled()) {
- s_logger.debug("Looking for suitable pools for this host
under zone: " + host.getDataCenterId() + ", pod: " + host.getPodId() + ",
cluster: " +
- host.getClusterId());
+ s_logger.debug(
+ "Looking for suitable pools for this host under
zone: " + host.getDataCenterId() + ", pod: " + host.getPodId() + ", cluster: "
+ host.getClusterId());
}
Pod pod = _podDao.findById(host.getPodId());
+ // check if the cluster or the pod is disabled
+ if (pod.getAllocationState() !=
Grouping.AllocationState.Enabled) {
+ s_logger.warn("The Pod containing this host is in disabled
state, PodId= " + pod.getId());
+ return null;
+ }
+
Cluster cluster = _clusterDao.findById(host.getClusterId());
+ if (cluster.getAllocationState() !=
Grouping.AllocationState.Enabled) {
+ s_logger.warn("The Cluster containing this host is in
disabled state, PodId= " + cluster.getId());
+ return null;
+ }
if (vm.getHypervisorType() == HypervisorType.BareMetal) {
DeployDestination dest = new DeployDestination(dc, pod,
cluster, host, new HashMap<Volume, StoragePool>());
@@ -1041,47 +1051,49 @@ StateListener<State, VirtualMachine.Event,
VirtualMachine> {
DataCenterDeployment potentialPlan =
new DataCenterDeployment(plan.getDataCenterId(),
clusterVO.getPodId(), clusterVO.getId(), null, plan.getPoolId(), null,
plan.getReservationContext());
- // find suitable hosts under this cluster, need as many hosts as we
- // get.
- List<Host> suitableHosts = findSuitableHosts(vmProfile,
potentialPlan, avoid, HostAllocator.RETURN_UPTO_ALL);
- // if found suitable hosts in this cluster, find suitable storage
- // pools for each volume of the VM
- if (suitableHosts != null && !suitableHosts.isEmpty()) {
- if (vmProfile.getHypervisorType() == HypervisorType.BareMetal)
{
- Pod pod = _podDao.findById(clusterVO.getPodId());
- DeployDestination dest = new DeployDestination(dc, pod,
clusterVO, suitableHosts.get(0));
- return dest;
- }
-
- Pair<Map<Volume, List<StoragePool>>, List<Volume>> result =
- findSuitablePoolsForVolumes(vmProfile, potentialPlan,
avoid, StoragePoolAllocator.RETURN_UPTO_ALL);
- Map<Volume, List<StoragePool>> suitableVolumeStoragePools =
result.first();
- List<Volume> readyAndReusedVolumes = result.second();
-
- // choose the potential host and pool for the VM
- if (!suitableVolumeStoragePools.isEmpty()) {
- Pair<Host, Map<Volume, StoragePool>> potentialResources =
findPotentialDeploymentResources(
- suitableHosts, suitableVolumeStoragePools, avoid,
resourceUsageRequired,
- readyAndReusedVolumes);
+ Pod pod = _podDao.findById(clusterVO.getPodId());
+ if (pod.getAllocationState() == Grouping.AllocationState.Enabled )
{
+ // find suitable hosts under this cluster, need as many hosts
as we
+ // get.
+ List<Host> suitableHosts = findSuitableHosts(vmProfile,
potentialPlan, avoid, HostAllocator.RETURN_UPTO_ALL);
+ // if found suitable hosts in this cluster, find suitable
storage
+ // pools for each volume of the VM
+ if (suitableHosts != null && !suitableHosts.isEmpty()) {
+ if (vmProfile.getHypervisorType() ==
HypervisorType.BareMetal) {
+ DeployDestination dest = new DeployDestination(dc,
pod, clusterVO, suitableHosts.get(0));
+ return dest;
+ }
- if (potentialResources != null) {
- Pod pod = _podDao.findById(clusterVO.getPodId());
- Host host =
_hostDao.findById(potentialResources.first().getId());
- Map<Volume, StoragePool> storageVolMap =
potentialResources.second();
- // remove the reused vol<->pool from destination, since
- // we don't have to prepare this volume.
- for (Volume vol : readyAndReusedVolumes) {
- storageVolMap.remove(vol);
+ Pair<Map<Volume, List<StoragePool>>, List<Volume>> result
= findSuitablePoolsForVolumes(vmProfile, potentialPlan, avoid,
StoragePoolAllocator.RETURN_UPTO_ALL);
+ Map<Volume, List<StoragePool>> suitableVolumeStoragePools
= result.first();
+ List<Volume> readyAndReusedVolumes = result.second();
+
+ // choose the potential host and pool for the VM
+ if (!suitableVolumeStoragePools.isEmpty()) {
+ Pair<Host, Map<Volume, StoragePool>>
potentialResources = findPotentialDeploymentResources(suitableHosts,
suitableVolumeStoragePools, avoid,
+ resourceUsageRequired, readyAndReusedVolumes);
+
+ if (potentialResources != null) {
+ Host host =
_hostDao.findById(potentialResources.first().getId());
+ Map<Volume, StoragePool> storageVolMap =
potentialResources.second();
+ // remove the reused vol<->pool from destination,
since
+ // we don't have to prepare this volume.
+ for (Volume vol : readyAndReusedVolumes) {
+ storageVolMap.remove(vol);
+ }
+ DeployDestination dest = new DeployDestination(dc,
pod, clusterVO, host, storageVolMap);
+ s_logger.debug("Returning Deployment Destination:
" + dest);
+ return dest;
}
- DeployDestination dest = new DeployDestination(dc,
pod, clusterVO, host, storageVolMap);
- s_logger.debug("Returning Deployment Destination: " +
dest);
- return dest;
+ } else {
+ s_logger.debug("No suitable storagePools found under
this Cluster: " + clusterId);
}
} else {
- s_logger.debug("No suitable storagePools found under this
Cluster: " + clusterId);
+ s_logger.debug("No suitable hosts found under this
Cluster: " + clusterId);
}
- } else {
- s_logger.debug("No suitable hosts found under this Cluster: "
+ clusterId);
+ }
+ else {
+ s_logger.debug("The cluster is in a disabled pod : " +
pod.getId());
}
if (canAvoidCluster(clusterVO, avoid, plannerAvoidOutput,
vmProfile)) {
diff --git a/server/src/com/cloud/server/StatsCollector.java
b/server/src/com/cloud/server/StatsCollector.java
index 048b3b1..305711e 100644
--- a/server/src/com/cloud/server/StatsCollector.java
+++ b/server/src/com/cloud/server/StatsCollector.java
@@ -41,7 +41,6 @@ import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.Configurable;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
-import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.utils.graphite.GraphiteClient;
@@ -60,6 +59,7 @@ import com.cloud.agent.api.VgpuTypesInfo;
import com.cloud.agent.api.VmDiskStatsEntry;
import com.cloud.agent.api.VmNetworkStatsEntry;
import com.cloud.agent.api.VmStatsEntry;
+import com.cloud.agent.api.VolumeStatsEntry;
import com.cloud.cluster.ManagementServerHostVO;
import com.cloud.cluster.dao.ManagementServerHostDao;
import com.cloud.dc.Vlan.VlanType;
@@ -101,9 +101,9 @@ import com.cloud.storage.StorageManager;
import com.cloud.storage.StorageStats;
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.user.UserStatisticsVO;
+import com.cloud.storage.Storage.ImageFormat;
import com.cloud.user.VmDiskStatisticsVO;
import com.cloud.user.dao.UserStatisticsDao;
import com.cloud.user.dao.VmDiskStatisticsDao;
@@ -160,6 +160,8 @@ public class StatsCollector extends ManagerBase implements
ComponentMethodInterc
"Interval (in seconds) to report vm network statistics (for Shared
networks). Vm network statistics will be disabled if this is set to 0 or less
than 0.", false);
static final ConfigKey<Integer> vmNetworkStatsIntervalMin = new
ConfigKey<Integer>("Advanced", Integer.class, "vm.network.stats.interval.min",
"300",
"Minimal Interval (in seconds) to report vm network statistics
(for Shared networks). If vm.network.stats.interval is smaller than this, use
this to report vm network statistics.", false);
+ static final ConfigKey<Integer> StatsTimeout = new
ConfigKey<Integer>("Advanced", Integer.class, "stats.timeout", "60000",
+ "The timeout for stats call in milli seconds.", true,
ConfigKey.Scope.Cluster);
private static StatsCollector s_instance = null;
@@ -177,12 +179,8 @@ public class StatsCollector extends ManagerBase implements
ComponentMethodInterc
@Inject
private PrimaryDataStoreDao _storagePoolDao;
@Inject
- private ImageStoreDao _imageStoreDao;
- @Inject
private StorageManager _storageManager;
@Inject
- private StoragePoolHostDao _storagePoolHostDao;
- @Inject
private DataStoreManager _dataStoreMgr;
@Inject
private ResourceManager _resourceMgr;
@@ -229,7 +227,7 @@ public class StatsCollector extends ManagerBase implements
ComponentMethodInterc
private ConcurrentHashMap<Long, HostStats> _hostStats = new
ConcurrentHashMap<Long, HostStats>();
private final ConcurrentHashMap<Long, VmStats> _VmStats = new
ConcurrentHashMap<Long, VmStats>();
- private final ConcurrentHashMap<Long, VolumeStats> _volumeStats = new
ConcurrentHashMap<Long, VolumeStats>();
+ private final Map<String, VolumeStats> _volumeStats = new
ConcurrentHashMap<String, VolumeStats>();
private ConcurrentHashMap<Long, StorageStats> _storageStats = new
ConcurrentHashMap<Long, StorageStats>();
private ConcurrentHashMap<Long, StorageStats> _storagePoolStats = new
ConcurrentHashMap<Long, StorageStats>();
@@ -282,7 +280,7 @@ public class StatsCollector extends ManagerBase implements
ComponentMethodInterc
hostStatsInterval =
NumbersUtil.parseLong(configs.get("host.stats.interval"), 60000L);
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);
+ volumeStatsInterval =
NumbersUtil.parseLong(configs.get("volume.stats.interval"), 600000L);
autoScaleStatsInterval =
NumbersUtil.parseLong(configs.get("autoscale.stats.interval"), 60000L);
/* URI to send statistics to. Currently only Graphite is supported */
@@ -359,6 +357,10 @@ public class StatsCollector extends ManagerBase implements
ComponentMethodInterc
s_logger.debug("vm.network.stats.interval - " +
vmNetworkStatsInterval.value() + " is 0 or less than 0, so not scheduling the
vm network stats thread");
}
+ if (volumeStatsInterval > 0) {
+ _executor.scheduleAtFixedRate(new VolumeStatsTask(), 15000L,
volumeStatsInterval, TimeUnit.MILLISECONDS);
+ }
+
//Schedule disk stats update task
_diskStatsUpdateExecutor = Executors.newScheduledThreadPool(1, new
NamedThreadFactory("DiskStatsUpdater"));
@@ -644,6 +646,7 @@ public class StatsCollector extends ManagerBase implements
ComponentMethodInterc
return;
}
// collect the vm disk statistics(total) from hypervisor. added by
weizhou, 2013.03.
+ s_logger.trace("Running VM disk stats ...");
try {
Transaction.execute(new TransactionCallbackNoReturn() {
@Override
@@ -887,6 +890,51 @@ public class StatsCollector extends ManagerBase implements
ComponentMethodInterc
}
}
+
+ class VolumeStatsTask extends ManagedContextRunnable {
+ @Override
+ protected void runInContext() {
+ try {
+ List<StoragePoolVO> pools = _storagePoolDao.listAll();
+
+ for (StoragePoolVO pool : pools) {
+ List<VolumeVO> volumes =
_volsDao.findByPoolId(pool.getId(), null);
+ List<String> volumeLocators = new ArrayList<String>();
+ for (VolumeVO volume: volumes){
+ if (volume.getFormat() == ImageFormat.QCOW2) {
+ volumeLocators.add(volume.getUuid());
+ }
+ else if (volume.getFormat() == ImageFormat.VHD){
+ volumeLocators.add(volume.getPath());
+ }
+ else if (volume.getFormat() == ImageFormat.OVA){
+ volumeLocators.add(volume.getChainInfo());
+ }
+ else {
+ s_logger.warn("Volume stats not implemented for
this format type " + volume.getFormat() );
+ break;
+ }
+ }
+ try {
+ HashMap<String, VolumeStatsEntry> volumeStatsByUuid =
_userVmMgr.getVolumeStatistics(pool.getClusterId(), pool.getUuid(),
pool.getPoolType(), volumeLocators, StatsTimeout.value());
+ if (volumeStatsByUuid != null){
+ _volumeStats.putAll(volumeStatsByUuid);
+ }
+ } catch (Exception e) {
+ s_logger.warn("Failed to get volume stats for cluster
with ID: " + pool.getClusterId(), e);
+ continue;
+ }
+ }
+ } catch (Throwable t) {
+ s_logger.error("Error trying to retrieve volume stats", t);
+ }
+ }
+ }
+
+ public VolumeStats getVolumeStats(String volumeLocator) {
+ return _volumeStats.get(volumeLocator);
+ }
+
class StorageCollector extends ManagedContextRunnable {
@Override
protected void runInContext() {
@@ -1257,11 +1305,11 @@ public class StatsCollector extends ManagerBase
implements ComponentMethodInterc
@Override
public String getConfigComponentName() {
- return this.getClass().getSimpleName();
+ return StatsCollector.class.getSimpleName();
}
@Override
public ConfigKey<?>[] getConfigKeys() {
- return new ConfigKey<?>[] { vmDiskStatsInterval,
vmDiskStatsIntervalMin, vmNetworkStatsInterval, vmNetworkStatsIntervalMin };
+ return new ConfigKey<?>[] { vmDiskStatsInterval,
vmDiskStatsIntervalMin, vmNetworkStatsInterval, vmNetworkStatsIntervalMin,
StatsTimeout };
}
}
diff --git a/server/src/com/cloud/test/DatabaseConfig.java
b/server/src/com/cloud/test/DatabaseConfig.java
index 7240374..f93692c 100644
--- a/server/src/com/cloud/test/DatabaseConfig.java
+++ b/server/src/com/cloud/test/DatabaseConfig.java
@@ -315,7 +315,7 @@ public class DatabaseConfig {
s_defaultConfigurationValues.put("host.stats.interval", "60000");
s_defaultConfigurationValues.put("storage.stats.interval", "60000");
- //s_defaultConfigurationValues.put("volume.stats.interval", "-1");
+ s_defaultConfigurationValues.put("volume.stats.interval", "60000");
s_defaultConfigurationValues.put("port", "8250");
s_defaultConfigurationValues.put("integration.api.port", "8096");
s_defaultConfigurationValues.put("usage.stats.job.exec.time",
"00:15"); // run at 12:15am
diff --git a/server/src/com/cloud/vm/UserVmManager.java
b/server/src/com/cloud/vm/UserVmManager.java
index 51cce9d..6a384f1 100644
--- a/server/src/com/cloud/vm/UserVmManager.java
+++ b/server/src/com/cloud/vm/UserVmManager.java
@@ -26,6 +26,7 @@ import org.apache.cloudstack.framework.config.ConfigKey;
import com.cloud.agent.api.VmDiskStatsEntry;
import com.cloud.agent.api.VmNetworkStatsEntry;
import com.cloud.agent.api.VmStatsEntry;
+import com.cloud.agent.api.VolumeStatsEntry;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.ManagementServerException;
@@ -33,6 +34,7 @@ import com.cloud.exception.ResourceUnavailableException;
import com.cloud.exception.VirtualMachineMigrationException;
import com.cloud.offering.ServiceOffering;
import com.cloud.service.ServiceOfferingVO;
+import com.cloud.storage.Storage.StoragePoolType;
import com.cloud.user.Account;
import com.cloud.uservm.UserVm;
import com.cloud.utils.Pair;
@@ -82,6 +84,8 @@ public interface UserVmManager extends UserVmService {
HashMap<Long, List<VmDiskStatsEntry>> getVmDiskStatistics(long hostId,
String hostName, List<Long> vmIds);
+ HashMap<String, VolumeStatsEntry> getVolumeStatistics(long clusterId,
String poolUuid, StoragePoolType poolType, List<String> volumeLocator, int
timout);
+
boolean deleteVmGroup(long groupId);
boolean addInstanceToGroup(long userVmId, String group);
diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java
b/server/src/com/cloud/vm/UserVmManagerImpl.java
index 07dd0ea..791ad95 100644
--- a/server/src/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/com/cloud/vm/UserVmManagerImpl.java
@@ -103,6 +103,8 @@ import com.cloud.agent.api.GetVmNetworkStatsAnswer;
import com.cloud.agent.api.GetVmNetworkStatsCommand;
import com.cloud.agent.api.GetVmStatsAnswer;
import com.cloud.agent.api.GetVmStatsCommand;
+import com.cloud.agent.api.GetVolumeStatsAnswer;
+import com.cloud.agent.api.GetVolumeStatsCommand;
import com.cloud.agent.api.PvlanSetupCommand;
import com.cloud.agent.api.RestoreVMSnapshotAnswer;
import com.cloud.agent.api.RestoreVMSnapshotCommand;
@@ -110,6 +112,7 @@ import com.cloud.agent.api.StartAnswer;
import com.cloud.agent.api.VmDiskStatsEntry;
import com.cloud.agent.api.VmNetworkStatsEntry;
import com.cloud.agent.api.VmStatsEntry;
+import com.cloud.agent.api.VolumeStatsEntry;
import com.cloud.agent.api.to.DiskTO;
import com.cloud.agent.api.to.NicTO;
import com.cloud.agent.api.to.VirtualMachineTO;
@@ -167,6 +170,7 @@ import com.cloud.gpu.GPU;
import com.cloud.ha.HighAvailabilityManager;
import com.cloud.host.Host;
import com.cloud.host.HostVO;
+import com.cloud.host.Status;
import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.hypervisor.HypervisorCapabilitiesVO;
@@ -226,6 +230,7 @@ import com.cloud.storage.GuestOSVO;
import com.cloud.storage.SnapshotVO;
import com.cloud.storage.Storage;
import com.cloud.storage.Storage.ImageFormat;
+import com.cloud.storage.Storage.StoragePoolType;
import com.cloud.storage.Storage.TemplateType;
import com.cloud.storage.Snapshot;
import com.cloud.storage.StorageManager;
@@ -1869,6 +1874,23 @@ public class UserVmManagerImpl extends ManagerBase
implements UserVmManager, Vir
}
@Override
+ public HashMap<String, VolumeStatsEntry> getVolumeStatistics(long
clusterId, String poolUuid, StoragePoolType poolType, List<String>
volumeLocator, int timeout) {
+ List<HostVO> neighbors =
_resourceMgr.listHostsInClusterByStatus(clusterId, Status.Up);
+ for (HostVO neighbor : neighbors) {
+ GetVolumeStatsCommand cmd = new GetVolumeStatsCommand(poolType,
poolUuid, volumeLocator);
+ if (timeout > 0) {
+ cmd.setWait(timeout/1000);
+ }
+ Answer answer = _agentMgr.easySend(neighbor.getId(), cmd);
+ if (answer instanceof GetVolumeStatsAnswer){
+ GetVolumeStatsAnswer volstats = (GetVolumeStatsAnswer)answer;
+ return volstats.getVolumeStats();
+ }
+ }
+ return null;
+ }
+
+ @Override
@DB
public UserVm recoverVirtualMachine(RecoverVMCmd cmd) throws
ResourceAllocationException, CloudRuntimeException {
diff --git a/setup/db/db/schema-41000to41100.sql
b/setup/db/db/schema-41000to41100.sql
index e5e6c05..db98f2e 100644
--- a/setup/db/db/schema-41000to41100.sql
+++ b/setup/db/db/schema-41000to41100.sql
@@ -295,4 +295,141 @@ ALTER TABLE `cloud`.`oobm` MODIFY COLUMN port
VARCHAR(255);
INSERT IGNORE INTO `cloud`.`configuration` (`category`, `instance`,
`component`, `name`, `value`, `description`, `default_value`, `is_dynamic`)
VALUES ('Console Proxy', 'DEFAULT', 'AgentManager', 'consoleproxy.sslEnabled',
'false', 'Enable SSL for console proxy', 'false', 0);
-- CLOUDSTACK-9859: Retirement of midonet plugin (final removal)
-delete from `cloud`.`configuration` where name in
('midonet.apiserver.address', 'midonet.providerrouter.id');
\ No newline at end of file
+delete from `cloud`.`configuration` where name in
('midonet.apiserver.address', 'midonet.providerrouter.id');
+
+-- CLOUDSTACK-9972: Enhance listVolumes API
+INSERT IGNORE INTO `cloud`.`configuration` VALUES ('Premium', 'DEFAULT',
'management-server', 'volume.stats.interval', '600000', 'Interval (in seconds)
to report volume statistics', '600000', now(), NULL, NULL);
+
+DROP VIEW IF EXISTS `cloud`.`volume_view`;
+CREATE VIEW `cloud`.`volume_view` AS
+ select
+ volumes.id,
+ volumes.uuid,
+ volumes.name,
+ volumes.device_id,
+ volumes.volume_type,
+ volumes.provisioning_type,
+ volumes.size,
+ volumes.min_iops,
+ volumes.max_iops,
+ volumes.created,
+ volumes.state,
+ volumes.attached,
+ volumes.removed,
+ volumes.display_volume,
+ volumes.format,
+ volumes.path,
+ volumes.chain_info,
+ account.id account_id,
+ account.uuid account_uuid,
+ account.account_name account_name,
+ account.type account_type,
+ domain.id domain_id,
+ domain.uuid domain_uuid,
+ domain.name domain_name,
+ domain.path domain_path,
+ projects.id project_id,
+ projects.uuid project_uuid,
+ projects.name project_name,
+ data_center.id data_center_id,
+ data_center.uuid data_center_uuid,
+ data_center.name data_center_name,
+ data_center.networktype data_center_type,
+ vm_instance.id vm_id,
+ vm_instance.uuid vm_uuid,
+ vm_instance.name vm_name,
+ vm_instance.state vm_state,
+ vm_instance.vm_type,
+ user_vm.display_name vm_display_name,
+ volume_store_ref.size volume_store_size,
+ volume_store_ref.download_pct,
+ volume_store_ref.download_state,
+ volume_store_ref.error_str,
+ volume_store_ref.created created_on_store,
+ disk_offering.id disk_offering_id,
+ disk_offering.uuid disk_offering_uuid,
+ disk_offering.name disk_offering_name,
+ disk_offering.display_text disk_offering_display_text,
+ disk_offering.use_local_storage,
+ disk_offering.system_use,
+ disk_offering.bytes_read_rate,
+ disk_offering.bytes_write_rate,
+ disk_offering.iops_read_rate,
+ disk_offering.iops_write_rate,
+ disk_offering.cache_mode,
+ storage_pool.id pool_id,
+ storage_pool.uuid pool_uuid,
+ storage_pool.name pool_name,
+ cluster.id cluster_id,
+ cluster.name cluster_name,
+ cluster.uuid cluster_uuid,
+ cluster.hypervisor_type,
+ vm_template.id template_id,
+ vm_template.uuid template_uuid,
+ vm_template.extractable,
+ vm_template.type template_type,
+ vm_template.name template_name,
+ vm_template.display_text template_display_text,
+ iso.id iso_id,
+ iso.uuid iso_uuid,
+ iso.name iso_name,
+ iso.display_text iso_display_text,
+ resource_tags.id tag_id,
+ resource_tags.uuid tag_uuid,
+ resource_tags.key tag_key,
+ resource_tags.value tag_value,
+ resource_tags.domain_id tag_domain_id,
+ resource_tags.account_id tag_account_id,
+ resource_tags.resource_id tag_resource_id,
+ resource_tags.resource_uuid tag_resource_uuid,
+ resource_tags.resource_type tag_resource_type,
+ resource_tags.customer tag_customer,
+ async_job.id job_id,
+ async_job.uuid job_uuid,
+ async_job.job_status job_status,
+ async_job.account_id job_account_id,
+ host_pod_ref.id pod_id,
+ host_pod_ref.uuid pod_uuid,
+ host_pod_ref.name pod_name,
+ resource_tag_account.account_name tag_account_name,
+ resource_tag_domain.uuid tag_domain_uuid,
+ resource_tag_domain.name tag_domain_name
+ from
+ `cloud`.`volumes`
+ inner join
+ `cloud`.`account` ON volumes.account_id = account.id
+ inner join
+ `cloud`.`domain` ON volumes.domain_id = domain.id
+ left join
+ `cloud`.`projects` ON projects.project_account_id = account.id
+ left join
+ `cloud`.`data_center` ON volumes.data_center_id = data_center.id
+ left join
+ `cloud`.`vm_instance` ON volumes.instance_id = vm_instance.id
+ left join
+ `cloud`.`user_vm` ON user_vm.id = vm_instance.id
+ left join
+ `cloud`.`volume_store_ref` ON volumes.id = volume_store_ref.volume_id
+ left join
+ `cloud`.`disk_offering` ON volumes.disk_offering_id = disk_offering.id
+ left join
+ `cloud`.`storage_pool` ON volumes.pool_id = storage_pool.id
+ left join
+ `cloud`.`host_pod_ref` ON storage_pool.pod_id = host_pod_ref.id
+ left join
+ `cloud`.`cluster` ON storage_pool.cluster_id = cluster.id
+ left join
+ `cloud`.`vm_template` ON volumes.template_id = vm_template.id
+ left join
+ `cloud`.`vm_template` iso ON iso.id = volumes.iso_id
+ left join
+ `cloud`.`resource_tags` ON resource_tags.resource_id = volumes.id
+ and resource_tags.resource_type = 'Volume'
+ left join
+ `cloud`.`async_job` ON async_job.instance_id = volumes.id
+ and async_job.instance_type = 'Volume'
+ and async_job.job_status = 0
+ left join
+ `cloud`.`account` resource_tag_account ON resource_tag_account.id =
resource_tags.account_id
+ left join
+ `cloud`.`domain` resource_tag_domain ON resource_tag_domain.id =
resource_tags.domain_id;
diff --git a/test/integration/smoke/test_volumes.py
b/test/integration/smoke/test_volumes.py
index fca65f2..588b762 100644
--- a/test/integration/smoke/test_volumes.py
+++ b/test/integration/smoke/test_volumes.py
@@ -35,7 +35,8 @@ from marvin.lib.base import (ServiceOffering,
from marvin.lib.common import (get_domain,
get_zone,
get_template,
- find_storage_pool_type)
+ find_storage_pool_type,
+ get_pod)
from marvin.lib.utils import checkVolumeSize
from marvin.codes import SUCCESS, FAILED, XEN_SERVER
from nose.plugins.attrib import attr
@@ -797,3 +798,74 @@ class TestVolumes(cloudstackTestCase):
"Check if volume exists in ListVolumes"
)
return
+
+ @attr(tags = ["advanced", "advancedns", "smoke", "basic"],
required_hardware="true")
+ def test_10_list_volumes(self):
+
+ # Validate the following
+ #
+ # 1. List Root Volume and waits until it has the newly introduced
attributes
+ #
+ # 2. Verifies return attributes has values different from none, when
instance is running
+ #
+
+ list_vm = VirtualMachine.list(self.apiclient,
id=self.virtual_machine.id)[0]
+
+ host = Host.list(
+ self.apiclient,
+ type='Routing',
+ virtualmachineid=list_vm.id
+ )[0]
+ list_pods = get_pod(self.apiclient, self.zone.id, host.podid)
+
+ root_volume = self.wait_for_attributes_and_return_root_vol()
+
+ self.assertTrue(hasattr(root_volume, "utilization"))
+ self.assertTrue(root_volume.utilization is not None)
+
+ self.assertTrue(hasattr(root_volume, "virtualsize"))
+ self.assertTrue(root_volume.virtualsize is not None)
+
+ self.assertTrue(hasattr(root_volume, "physicalsize"))
+ self.assertTrue(root_volume.physicalsize is not None)
+
+ self.assertTrue(hasattr(root_volume, "vmname"))
+ self.assertEqual(root_volume.vmname, list_vm.name)
+
+ self.assertTrue(hasattr(root_volume, "clustername"))
+ self.assertTrue(root_volume.clustername is not None)
+
+ self.assertTrue(hasattr(root_volume, "clusterid"))
+ self.assertTrue(root_volume.clusterid is not None)
+
+ self.assertTrue(hasattr(root_volume, "storageid"))
+ self.assertTrue(root_volume.storageid is not None)
+
+ self.assertTrue(hasattr(root_volume, "storage"))
+ self.assertTrue(root_volume.storage is not None)
+
+ self.assertTrue(hasattr(root_volume, "zoneid"))
+ self.assertEqual(root_volume.zoneid, self.zone.id)
+
+ self.assertTrue(hasattr(root_volume, "zonename"))
+ self.assertEqual(root_volume.zonename, self.zone.name)
+
+ self.assertTrue(hasattr(root_volume, "podid"))
+ self.assertEqual(root_volume.podid, list_pods.id)
+
+ self.assertTrue(hasattr(root_volume, "podname"))
+ self.assertEqual(root_volume.podname, list_pods.name)
+
+ def wait_for_attributes_and_return_root_vol(self):
+
+ for i in range(60):
+ list_volume_response = Volume.list(
+ self.apiClient,
+ virtualmachineid=self.virtual_machine.id,
+ type='ROOT',
+ listall=True
+ )
+ if list_volume_response[0].virtualsize is not None:
+ return list_volume_response[0]
+
+ time.sleep(1)
diff --git a/ui/l10n/ar.js b/ui/l10n/ar.js
index 538fd4b..afb4269 100644
--- a/ui/l10n/ar.js
+++ b/ui/l10n/ar.js
@@ -657,12 +657,15 @@ var dictionary = {
"label.disk.iops.write.rate": "Disk Write Rate (IOPS)",
"label.disk.offering": "Disk Offering",
"label.disk.offering.details": "Disk offering details",
+ "label.disk.physicalsize":"Physical Size",
"label.disk.provisioningtype": "Provisioning Type",
"label.disk.read.bytes": "Disk Read (Bytes)",
"label.disk.read.io": "Disk Read (IO)",
"label.disk.size": "Disk Size",
"label.disk.size.gb": "Disk Size (in GB)",
"label.disk.total": "Disk Total",
+ "label.disk.utilisation":"Utilisation",
+ "label.disk.virtualsize":"Virtual Size",
"label.disk.volume": "Disk Volume",
"label.disk.write.bytes": "Disk Write (Bytes)",
"label.disk.write.io": "Disk Write (IO)",
diff --git a/ui/l10n/ca.js b/ui/l10n/ca.js
index d97a948..3b1f04c 100644
--- a/ui/l10n/ca.js
+++ b/ui/l10n/ca.js
@@ -657,12 +657,15 @@ var dictionary = {
"label.disk.iops.write.rate": "Disk Write Rate (IOPS)",
"label.disk.offering": "Disk Offering",
"label.disk.offering.details": "Disk offering details",
+ "label.disk.physicalsize":"Physical Size",
"label.disk.provisioningtype": "Provisioning Type",
"label.disk.read.bytes": "Disk Read (Bytes)",
"label.disk.read.io": "Disk Read (IO)",
"label.disk.size": "Disk Size",
"label.disk.size.gb": "Disk Size (in GB)",
"label.disk.total": "Disk Total",
+ "label.disk.utilisation":"Utilisation",
+ "label.disk.virtualsize":"Virtual Size",
"label.disk.volume": "Disk Volume",
"label.disk.write.bytes": "Disk Write (Bytes)",
"label.disk.write.io": "Disk Write (IO)",
diff --git a/ui/l10n/de_DE.js b/ui/l10n/de_DE.js
index f3d93bf..c0f2e0a 100644
--- a/ui/l10n/de_DE.js
+++ b/ui/l10n/de_DE.js
@@ -657,12 +657,15 @@ var dictionary = {
"label.disk.iops.write.rate": "Festplatten-Schreibrate (IOPS)",
"label.disk.offering": "Festplattenangebot",
"label.disk.offering.details": "Festplattenangebotdetails",
+ "label.disk.physicalsize":"Physical Size",
"label.disk.provisioningtype": "Provisionierungstyp",
"label.disk.read.bytes": "Festplatte Lesen (Bytes)",
"label.disk.read.io": "Festplatte Lesen (EA)",
"label.disk.size": "Festplattengröße",
"label.disk.size.gb": "Festplattengröße (in GB)",
"label.disk.total": "Gesamtzahl der Festplatten",
+ "label.disk.utilisation":"Utilisation",
+ "label.disk.virtualsize":"Virtual Size",
"label.disk.volume": "Festplattenvolumen",
"label.disk.write.bytes": "Festplatte Schreiben (Bytes)",
"label.disk.write.io": "Festplatte Schreiben (EA)",
diff --git a/ui/l10n/en.js b/ui/l10n/en.js
index 3727dc6..003c93f 100644
--- a/ui/l10n/en.js
+++ b/ui/l10n/en.js
@@ -667,12 +667,15 @@ var dictionary = {"ICMP.code":"ICMP Code",
"label.disk.iops.write.rate":"Disk Write Rate (IOPS)",
"label.disk.offering":"Disk Offering",
"label.disk.offering.details":"Disk offering details",
+"label.disk.physicalsize":"Physical Size",
"label.disk.provisioningtype":"Provisioning Type",
"label.disk.read.bytes":"Disk Read (Bytes)",
"label.disk.read.io":"Disk Read (IO)",
"label.disk.size":"Disk Size",
"label.disk.size.gb":"Disk Size (in GB)",
"label.disk.total":"Disk Total",
+"label.disk.utilisation":"Utilisation",
+"label.disk.virtualsize":"Virtual Size",
"label.disk.volume":"Disk Volume",
"label.disk.write.bytes":"Disk Write (Bytes)",
"label.disk.write.io":"Disk Write (IO)",
diff --git a/ui/l10n/es.js b/ui/l10n/es.js
index 38ec9c0..6abfd8e 100644
--- a/ui/l10n/es.js
+++ b/ui/l10n/es.js
@@ -657,12 +657,15 @@ var dictionary = {
"label.disk.iops.write.rate": "Tasa Escritura de Disco (IOPS)",
"label.disk.offering": "Oferta de Disco",
"label.disk.offering.details": "Detalles de Oferta de Disco",
+ "label.disk.physicalsize":"Physical Size",
"label.disk.provisioningtype": "Tipo de Aprovisionamiento",
"label.disk.read.bytes": "Lectura Disco (Bytes)",
"label.disk.read.io": "Lectura Disco (IO)",
"label.disk.size": "tamaño de disco",
"label.disk.size.gb": "tamaño de disco (en GB)",
"label.disk.total": "disco Total",
+ "label.disk.utilisation":"Utilisation",
+ "label.disk.virtualsize":"Virtual Size",
"label.disk.volume": "volumen de disco",
"label.disk.write.bytes": "Escritura Disco (Bytes)",
"label.disk.write.io": "Escritura Disco (IO)",
diff --git a/ui/l10n/fr_FR.js b/ui/l10n/fr_FR.js
index 0bb6a0d..dd5e85a 100644
--- a/ui/l10n/fr_FR.js
+++ b/ui/l10n/fr_FR.js
@@ -657,12 +657,15 @@ var dictionary = {
"label.disk.iops.write.rate": "Débit écriture disque (IOPS)",
"label.disk.offering": "Offre de Disque",
"label.disk.offering.details": "Détails offre de disque",
+ "label.disk.physicalsize":"Physical Size",
"label.disk.provisioningtype": "Type de provisionnement",
"label.disk.read.bytes": "Lecture Disque (Octets)",
"label.disk.read.io": "Lecture Disque (IO)",
"label.disk.size": "Capacité disque",
"label.disk.size.gb": "Capacité disque (Go)",
"label.disk.total": "Espace disque total",
+ "label.disk.utilisation":"Utilisation",
+ "label.disk.virtualsize":"Virtual Size",
"label.disk.volume": "Volume disque",
"label.disk.write.bytes": "Écriture Disque (Octets)",
"label.disk.write.io": "Écriture Disque (IO)",
diff --git a/ui/l10n/hu.js b/ui/l10n/hu.js
index 3da6941..23519b9 100644
--- a/ui/l10n/hu.js
+++ b/ui/l10n/hu.js
@@ -657,12 +657,15 @@ var dictionary = {
"label.disk.iops.write.rate": "Írási ráta (IOPS)",
"label.disk.offering": "Merevlemez ajánlat",
"label.disk.offering.details": "Merevlemez ajánlat részletei",
+ "label.disk.physicalsize":"Physical Size",
"label.disk.provisioningtype": "Létrehozás típusa",
"label.disk.read.bytes": "Merevlemez olvasás (Byte)",
"label.disk.read.io": "Merevlemez írás (IO)",
"label.disk.size": "Merevlemez méret",
"label.disk.size.gb": "Merevlemez méret (GB)",
"label.disk.total": "Merevlemez összes",
+ "label.disk.utilisation":"Utilisation",
+ "label.disk.virtualsize":"Virtual Size",
"label.disk.volume": "Merevlemez kötet",
"label.disk.write.bytes": "Merevlemez írás (byte)",
"label.disk.write.io": "Merevlemez írás (IO)",
diff --git a/ui/l10n/it_IT.js b/ui/l10n/it_IT.js
index 247b8e0..c4501e6 100644
--- a/ui/l10n/it_IT.js
+++ b/ui/l10n/it_IT.js
@@ -657,12 +657,15 @@ var dictionary = {
"label.disk.iops.write.rate": "Disk Write Rate (IOPS)",
"label.disk.offering": "Offerta Disco",
"label.disk.offering.details": "Disk offering details",
+ "label.disk.physicalsize":"Physical Size",
"label.disk.provisioningtype": "Tipo di Provisioning",
"label.disk.read.bytes": "Disk Read (Bytes)",
"label.disk.read.io": "Disk Read (IO)",
"label.disk.size": "Disk Size",
"label.disk.size.gb": "Disk Size (in GB)",
"label.disk.total": "Disk Total",
+ "label.disk.utilisation":"Utilisation",
+ "label.disk.virtualsize":"Virtual Size",
"label.disk.volume": "Disk Volume",
"label.disk.write.bytes": "Disk Write (Bytes)",
"label.disk.write.io": "Disk Write (IO)",
diff --git a/ui/l10n/ja_JP.js b/ui/l10n/ja_JP.js
index a5da1a3..a2f42f9 100644
--- a/ui/l10n/ja_JP.js
+++ b/ui/l10n/ja_JP.js
@@ -657,12 +657,15 @@ var dictionary = {
"label.disk.iops.write.rate": "ディスク書き込み速度 (IOPS)",
"label.disk.offering": "ディスク オファリング",
"label.disk.offering.details": "ディスクオファリングの詳細",
+ "label.disk.physicalsize":"Physical Size",
"label.disk.provisioningtype": "プロビジョニングの種類",
"label.disk.read.bytes": "ディスク読み取り (バイト)",
"label.disk.read.io": "ディスク読み取り (IO)",
"label.disk.size": "ディスク サイズ",
"label.disk.size.gb": "ディスク サイズ (GB)",
"label.disk.total": "ディスク合計",
+ "label.disk.utilisation":"Utilisation",
+ "label.disk.virtualsize":"Virtual Size",
"label.disk.volume": "ディスク ボリューム",
"label.disk.write.bytes": "ディスク書き込み (バイト)",
"label.disk.write.io": "ディスク書き込み (IO)",
diff --git a/ui/l10n/ko_KR.js b/ui/l10n/ko_KR.js
index f592a7c..9655052 100644
--- a/ui/l10n/ko_KR.js
+++ b/ui/l10n/ko_KR.js
@@ -657,12 +657,15 @@ var dictionary = {
"label.disk.iops.write.rate": "Disk Write Rate (IOPS)",
"label.disk.offering": "디스크 제공",
"label.disk.offering.details": "Disk offering details",
+ "label.disk.physicalsize":"Physical Size",
"label.disk.provisioningtype": "Provisioning Type",
"label.disk.read.bytes": "Disk Read (Bytes)",
"label.disk.read.io": "Disk Read (IO)",
"label.disk.size": "디스크 크기",
"label.disk.size.gb": "디스크 크기(GB 단위)",
"label.disk.total": "디스크 합계",
+ "label.disk.utilisation":"Utilisation",
+ "label.disk.virtualsize":"Virtual Size",
"label.disk.volume": "디스크 볼륨",
"label.disk.write.bytes": "Disk Write (Bytes)",
"label.disk.write.io": "Disk Write (IO)",
diff --git a/ui/l10n/nb_NO.js b/ui/l10n/nb_NO.js
index 1ef4145..c9836ea 100644
--- a/ui/l10n/nb_NO.js
+++ b/ui/l10n/nb_NO.js
@@ -657,12 +657,15 @@ var dictionary = {
"label.disk.iops.write.rate": "Diskskrivehastighet (IOPS)",
"label.disk.offering": "Disktilbud",
"label.disk.offering.details": "Disktilbud detaljer",
+ "label.disk.physicalsize":"Physical Size",
"label.disk.provisioningtype": "Provisjoneringstype",
"label.disk.read.bytes": "Disk lese (Bytes)",
"label.disk.read.io": "Disk lese (IO)",
"label.disk.size": "Diskstørrelse",
"label.disk.size.gb": "Diskstørrelse (i GB)",
"label.disk.total": "Disk Totalt",
+ "label.disk.utilisation":"Utilisation",
+ "label.disk.virtualsize":"Virtual Size",
"label.disk.volume": "Disk Volum",
"label.disk.write.bytes": "Disk skrive (Bytes)",
"label.disk.write.io": "Disk skrive (IO)",
diff --git a/ui/l10n/nl_NL.js b/ui/l10n/nl_NL.js
index 58465f3..5e43889 100644
--- a/ui/l10n/nl_NL.js
+++ b/ui/l10n/nl_NL.js
@@ -657,12 +657,15 @@ var dictionary = {
"label.disk.iops.write.rate": "Schrijf snelheid Schijf (IOPS)",
"label.disk.offering": "Schijf Aanbieding",
"label.disk.offering.details": "schijfe offerte gegevens",
+ "label.disk.physicalsize":"Physical Size",
"label.disk.provisioningtype": "Provisioning type",
"label.disk.read.bytes": "Schijf lezen (Bytes)",
"label.disk.read.io": "Schijf Lezen (IO)",
"label.disk.size": "Schijf Grootte",
"label.disk.size.gb": "Schijf Grootte (in GB)",
"label.disk.total": "Schijf Totaal",
+ "label.disk.utilisation":"Utilisation",
+ "label.disk.virtualsize":"Virtual Size",
"label.disk.volume": "Schijf Volume",
"label.disk.write.bytes": "Schijf Schrijven (Bytes)",
"label.disk.write.io": "Schijf Schrijven (IO)",
diff --git a/ui/l10n/pl.js b/ui/l10n/pl.js
index 1ba89b2..8b1cb42 100644
--- a/ui/l10n/pl.js
+++ b/ui/l10n/pl.js
@@ -657,12 +657,15 @@ var dictionary = {
"label.disk.iops.write.rate": "Disk Write Rate (IOPS)",
"label.disk.offering": "Disk Offering",
"label.disk.offering.details": "Disk offering details",
+ "label.disk.physicalsize":"Physical Size",
"label.disk.provisioningtype": "Provisioning Type",
"label.disk.read.bytes": "Disk Read (Bytes)",
"label.disk.read.io": "Disk Read (IO)",
"label.disk.size": "Wielkość dysku",
"label.disk.size.gb": "Wielkość dysku (w GB)",
"label.disk.total": "Disk Total",
+ "label.disk.utilisation":"Utilisation",
+ "label.disk.virtualsize":"Virtual Size",
"label.disk.volume": "Disk Volume",
"label.disk.write.bytes": "Disk Write (Bytes)",
"label.disk.write.io": "Disk Write (IO)",
diff --git a/ui/l10n/pt_BR.js b/ui/l10n/pt_BR.js
index 77bfe53..fbaafcb 100644
--- a/ui/l10n/pt_BR.js
+++ b/ui/l10n/pt_BR.js
@@ -657,12 +657,15 @@ var dictionary = {
"label.disk.iops.write.rate": "Taxa de Escrita no Disco (IOPS)",
"label.disk.offering": "Oferta de Disco",
"label.disk.offering.details": "Detalhes da oferta de disco",
+ "label.disk.physicalsize":"Physical Size",
"label.disk.provisioningtype": "Tipo de Provisionamento",
"label.disk.read.bytes": "Leitura do Disco (Bytes)",
"label.disk.read.io": "Leitura do Disk (I/O)",
"label.disk.size": "Tamanho do Disco",
"label.disk.size.gb": "Tamanho (em GB)",
"label.disk.total": "Disco Total",
+ "label.disk.utilisation":"Utilisation",
+ "label.disk.virtualsize":"Virtual Size",
"label.disk.volume": "Disco",
"label.disk.write.bytes": "Escrita no Disco (Bytes)",
"label.disk.write.io": "Escrita no Disco (I/O)",
diff --git a/ui/l10n/ru_RU.js b/ui/l10n/ru_RU.js
index aaa40b8..649e5fe 100644
--- a/ui/l10n/ru_RU.js
+++ b/ui/l10n/ru_RU.js
@@ -657,12 +657,15 @@ var dictionary = {
"label.disk.iops.write.rate": "Скорость записи диска (IOPS)",
"label.disk.offering": "Услуга дискового пространства",
"label.disk.offering.details": "Disk offering details",
+ "label.disk.physicalsize":"Physical Size",
"label.disk.provisioningtype": "Provisioning Type",
"label.disk.read.bytes": "Прочитано с диска (Байт)",
"label.disk.read.io": "Прочитано с диска (IO)",
"label.disk.size": "Размер диска",
"label.disk.size.gb": "Размер диска (в ГБ)",
"label.disk.total": "Всего в дисках",
+ "label.disk.utilisation":"Utilisation",
+ "label.disk.virtualsize":"Virtual Size",
"label.disk.volume": "Объем диска",
"label.disk.write.bytes": "Записано на диск (Байт)",
"label.disk.write.io": "Записано на диск (IO)",
diff --git a/ui/l10n/zh_CN.js b/ui/l10n/zh_CN.js
index 2131c98..c4a663c4 100644
--- a/ui/l10n/zh_CN.js
+++ b/ui/l10n/zh_CN.js
@@ -657,12 +657,15 @@ var dictionary = {
"label.disk.iops.write.rate": "磁盘写入速度(IOPS)",
"label.disk.offering": "磁盘方案",
"label.disk.offering.details": "磁盘方案详情",
+ "label.disk.physicalsize":"Physical Size",
"label.disk.provisioningtype": "置备类型",
"label.disk.read.bytes": "磁盘读取(字节)",
"label.disk.read.io": "磁盘读取(IO)",
"label.disk.size": "磁盘大小",
"label.disk.size.gb": "磁盘大小(GB)",
"label.disk.total": "磁盘总量",
+ "label.disk.utilisation":"Utilisation",
+ "label.disk.virtualsize":"Virtual Size",
"label.disk.volume": "磁盘卷",
"label.disk.write.bytes": "磁盘写入(字节)",
"label.disk.write.io": "磁盘写入(IO)",
diff --git a/ui/scripts/metrics.js b/ui/scripts/metrics.js
index 3152af7..bc73934 100644
--- a/ui/scripts/metrics.js
+++ b/ui/scripts/metrics.js
@@ -577,6 +577,18 @@
sizegb: {
label: 'label.metrics.disk.size'
},
+ physicalsize: {
+ label: 'label.disk.physicalsize',
+ converter: function(args) {
+ if (args == null || args == 0)
+ return "";
+ else
+ return cloudStack.converters.convertBytes(args);
+ }
+ },
+ utilization: {
+ label: 'label.disk.utilisation'
+ },
storagetype: {
label: 'label.metrics.disk.storagetype'
},
diff --git a/ui/scripts/storage.js b/ui/scripts/storage.js
index 639f8a9..9c017b1 100644
--- a/ui/scripts/storage.js
+++ b/ui/scripts/storage.js
@@ -1752,7 +1752,7 @@
if (isAdmin()) {
hiddenFields = [];
} else {
- hiddenFields = ['storage',
'hypervisor'];
+ hiddenFields = ['storage',
'hypervisor', 'virtualsize', 'physicalsize', 'utilization', 'clusterid',
'clustername'];
}
return hiddenFields;
},
@@ -1817,6 +1817,33 @@
return
cloudStack.converters.convertBytes(args);
}
},
+ clusterid: {
+ label: 'label.cluster'
+ },
+ clustername: {
+ label: 'label.cluster.name'
+ },
+ physicalsize: {
+ label: 'label.disk.physicalsize',
+ converter: function(args) {
+ if (args == null || args == 0)
+ return "";
+ else
+ return
cloudStack.converters.convertBytes(args);
+ }
+ },
+ utilization: {
+ label: 'label.disk.utilisation'
+ },
+ virtualsize: {
+ label: 'label.disk.virtualsize',
+ converter: function(args) {
+ if (args == null || args == 0)
+ return "";
+ else
+ return
cloudStack.converters.convertBytes(args);
+ }
+ },
miniops: {
label: 'label.disk.iops.min',
converter: function(args) {
diff --git a/vmware-base/src/com/cloud/hypervisor/vmware/mo/DatastoreMO.java
b/vmware-base/src/com/cloud/hypervisor/vmware/mo/DatastoreMO.java
index 1152ba5..3659cf5 100644
--- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/DatastoreMO.java
+++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/DatastoreMO.java
@@ -24,7 +24,9 @@ import org.apache.log4j.Logger;
import com.vmware.vim25.DatastoreHostMount;
import com.vmware.vim25.DatastoreSummary;
import com.vmware.vim25.FileInfo;
+import com.vmware.vim25.FileQueryFlags;
import com.vmware.vim25.HostDatastoreBrowserSearchResults;
+import com.vmware.vim25.HostDatastoreBrowserSearchSpec;
import com.vmware.vim25.HostMountInfo;
import com.vmware.vim25.ManagedObjectReference;
import com.vmware.vim25.ObjectContent;
@@ -339,6 +341,36 @@ public class DatastoreMO extends BaseMO {
return false;
}
+ public long fileDiskSize(String fileFullPath) throws Exception {
+ long size = 0;
+ DatastoreFile file = new DatastoreFile(fileFullPath);
+ DatastoreFile dirFile = new DatastoreFile(file.getDatastoreName(),
file.getDir());
+
+ HostDatastoreBrowserMO browserMo = getHostDatastoreBrowserMO();
+
+ HostDatastoreBrowserSearchSpec searchSpec = new
HostDatastoreBrowserSearchSpec();
+ FileQueryFlags fqf = new FileQueryFlags();
+ fqf.setFileSize(true);
+ fqf.setFileOwner(true);
+ fqf.setModification(true);
+ searchSpec.setDetails(fqf);
+ searchSpec.setSearchCaseInsensitive(false);
+ searchSpec.getMatchPattern().add(file.getFileName());
+ s_logger.debug("Search file " + file.getFileName() + " on " +
dirFile.getPath()); //ROOT-2.vmdk, [3ecf7a579d3b3793b86d9d019a97ae27] s-2-VM
+ HostDatastoreBrowserSearchResults result =
browserMo.searchDatastore(dirFile.getPath(), searchSpec);
+ if (result != null) {
+ List<FileInfo> info = result.getFile();
+ for (FileInfo fi : info) {
+ if (file.getFileName().equals(fi.getPath())) {
+ s_logger.debug("File found = " + fi.getPath() + ", size="
+ fi.getFileSize());
+ return fi.getFileSize();
+ }
+ }
+ }
+ s_logger.debug("File " + fileFullPath + " does not exist on
datastore");
+ return size;
+ }
+
public boolean folderExists(String folderParentDatastorePath, String
folderName) throws Exception {
HostDatastoreBrowserMO browserMo = getHostDatastoreBrowserMO();
diff --git
a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java
b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java
index c7bdbcd..a4f26db 100644
--- a/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java
+++ b/vmware-base/src/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java
@@ -2376,6 +2376,59 @@ public class VirtualMachineMO extends BaseMO {
return null;
}
+ // return pair of VirtualDisk and disk device bus name(ide0:0, etc)
+ public Pair<VirtualDisk, String> getDiskDevice(String vmdkDatastorePath,
boolean matchExactly) throws Exception {
+ List<VirtualDevice> devices =
_context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
+
+ DatastoreFile dsSrcFile = new DatastoreFile(vmdkDatastorePath);
+ String srcBaseName = dsSrcFile.getFileBaseName();
+ String trimmedSrcBaseName =
VmwareHelper.trimSnapshotDeltaPostfix(srcBaseName);
+
+ if (matchExactly) {
+ s_logger.info("Look for disk device info from volume : " +
vmdkDatastorePath + " with base name: " + srcBaseName);
+ } else {
+ s_logger.info("Look for disk device info from volume : " +
vmdkDatastorePath + " with trimmed base name: " + trimmedSrcBaseName);
+ }
+
+ if (devices != null && devices.size() > 0) {
+ for (VirtualDevice device : devices) {
+ if (device instanceof VirtualDisk) {
+ s_logger.info("Test against disk device, controller key: "
+ device.getControllerKey() + ", unit number: " + device.getUnitNumber());
+
+ VirtualDeviceBackingInfo backingInfo =
((VirtualDisk)device).getBacking();
+ if (backingInfo instanceof VirtualDiskFlatVer2BackingInfo)
{
+ VirtualDiskFlatVer2BackingInfo diskBackingInfo =
(VirtualDiskFlatVer2BackingInfo)backingInfo;
+ do {
+ s_logger.info("Test against disk backing : " +
diskBackingInfo.getFileName());
+
+ DatastoreFile dsBackingFile = new
DatastoreFile(diskBackingInfo.getFileName());
+ String backingBaseName =
dsBackingFile.getFileBaseName();
+ if (matchExactly) {
+ if
(backingBaseName.equalsIgnoreCase(srcBaseName)) {
+ String deviceNumbering =
getDeviceBusName(devices, device);
+
+ s_logger.info("Disk backing : " +
diskBackingInfo.getFileName() + " matches ==> " + deviceNumbering);
+ return new Pair<VirtualDisk,
String>((VirtualDisk)device, deviceNumbering);
+ }
+ } else {
+ if
(backingBaseName.contains(trimmedSrcBaseName)) {
+ String deviceNumbering =
getDeviceBusName(devices, device);
+
+ s_logger.info("Disk backing : " +
diskBackingInfo.getFileName() + " matches ==> " + deviceNumbering);
+ return new Pair<VirtualDisk,
String>((VirtualDisk)device, deviceNumbering);
+ }
+ }
+
+ diskBackingInfo = diskBackingInfo.getParent();
+ } while (diskBackingInfo != null);
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
public String getDiskCurrentTopBackingFileInChain(String deviceBusName)
throws Exception {
List<VirtualDevice> devices =
_context.getVimClient().getDynamicProperty(_mor, "config.hardware.device");
if (devices != null && devices.size() > 0) {
--
To stop receiving notification emails like this one, please contact
['"[email protected]" <[email protected]>'].