SolidFire plug-in and related changes

SolidFire plug-in

SolidFire plug-in related
This commit is contained in:
Mike Tutkowski 2013-06-28 14:05:12 -06:00
parent 02ab2eb384
commit 99227f7b3e
90 changed files with 3434 additions and 294 deletions

View File

@ -23,14 +23,16 @@ import com.cloud.storage.Volume;
public class DiskTO { public class DiskTO {
private DataTO data; private DataTO data;
private Long diskSeq; private Long diskSeq;
private String vdiUuid;
private Volume.Type type; private Volume.Type type;
public DiskTO() { public DiskTO() {
} }
public DiskTO(DataTO data, Long diskSeq, Volume.Type type) { public DiskTO(DataTO data, Long diskSeq, String vdiUuid, Volume.Type type) {
this.data = data; this.data = data;
this.diskSeq = diskSeq; this.diskSeq = diskSeq;
this.vdiUuid = vdiUuid;
this.type = type; this.type = type;
} }
@ -50,6 +52,14 @@ public class DiskTO {
this.diskSeq = diskSeq; this.diskSeq = diskSeq;
} }
public String getVdiUuid() {
return vdiUuid;
}
public void setVdiUuid(String vdiUuid) {
this.vdiUuid = vdiUuid;
}
public Volume.Type getType() { public Volume.Type getType() {
return type; return type;
} }

View File

@ -47,12 +47,24 @@ public interface DiskOffering extends InfrastructureEntity, Identity, InternalId
Date getCreated(); Date getCreated();
long getDiskSize();
boolean isCustomized(); boolean isCustomized();
void setDiskSize(long diskSize); void setDiskSize(long diskSize);
long getDiskSize();
void setCustomizedIops(Boolean customizedIops);
Boolean isCustomizedIops();
void setMinIops(Long minIops);
Long getMinIops();
void setMaxIops(Long maxIops);
Long getMaxIops();
void setBytesReadRate(Long bytesReadRate); void setBytesReadRate(Long bytesReadRate);
Long getBytesReadRate(); Long getBytesReadRate();

View File

@ -60,6 +60,8 @@ public interface StoragePool extends Identity, InternalIdentity {
*/ */
long getUsedBytes(); long getUsedBytes();
Long getCapacityIops();
Long getClusterId(); Long getClusterId();
/** /**

View File

@ -122,6 +122,12 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba
*/ */
Long getSize(); Long getSize();
Long getMinIops();
Long getMaxIops();
String get_iScsiName();
/** /**
* @return the vm instance id * @return the vm instance id
*/ */

View File

@ -51,6 +51,9 @@ public class ApiConstants {
public static final String CPU_OVERCOMMIT_RATIO="cpuovercommitratio"; public static final String CPU_OVERCOMMIT_RATIO="cpuovercommitratio";
public static final String CREATED = "created"; public static final String CREATED = "created";
public static final String CUSTOMIZED = "customized"; public static final String CUSTOMIZED = "customized";
public static final String CUSTOMIZED_IOPS = "customizediops";
public static final String MIN_IOPS = "miniops";
public static final String MAX_IOPS = "maxiops";
public static final String DESCRIPTION = "description"; public static final String DESCRIPTION = "description";
public static final String DESTINATION_ZONE_ID = "destzoneid"; public static final String DESTINATION_ZONE_ID = "destzoneid";
public static final String DETAILS = "details"; public static final String DETAILS = "details";
@ -326,6 +329,9 @@ public class ApiConstants {
public static final String SERVICE_CAPABILITY_LIST = "servicecapabilitylist"; public static final String SERVICE_CAPABILITY_LIST = "servicecapabilitylist";
public static final String CAN_CHOOSE_SERVICE_CAPABILITY = "canchooseservicecapability"; public static final String CAN_CHOOSE_SERVICE_CAPABILITY = "canchooseservicecapability";
public static final String PROVIDER = "provider"; public static final String PROVIDER = "provider";
public static final String MANAGED = "managed";
public static final String CAPACITY_BYTES = "capacitybytes";
public static final String CAPACITY_IOPS = "capacityiops";
public static final String NETWORK_SPEED = "networkspeed"; public static final String NETWORK_SPEED = "networkspeed";
public static final String BROADCAST_DOMAIN_RANGE = "broadcastdomainrange"; public static final String BROADCAST_DOMAIN_RANGE = "broadcastdomainrange";
public static final String ISOLATION_METHODS = "isolationmethods"; public static final String ISOLATION_METHODS = "isolationmethods";

View File

@ -52,7 +52,7 @@ public class CreateDiskOfferingCmd extends BaseCmd {
@Parameter(name=ApiConstants.TAGS, type=CommandType.STRING, description="tags for the disk offering", length=4096) @Parameter(name=ApiConstants.TAGS, type=CommandType.STRING, description="tags for the disk offering", length=4096)
private String tags; private String tags;
@Parameter(name=ApiConstants.CUSTOMIZED, type=CommandType.BOOLEAN, description="whether disk offering is custom or not") @Parameter(name=ApiConstants.CUSTOMIZED, type=CommandType.BOOLEAN, description="whether disk offering size is custom or not")
private Boolean customized; private Boolean customized;
@Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.UUID, entityType=DomainResponse.class, @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.UUID, entityType=DomainResponse.class,
@ -62,6 +62,9 @@ public class CreateDiskOfferingCmd extends BaseCmd {
@Parameter(name=ApiConstants.STORAGE_TYPE, type=CommandType.STRING, description="the storage type of the disk offering. Values are local and shared.") @Parameter(name=ApiConstants.STORAGE_TYPE, type=CommandType.STRING, description="the storage type of the disk offering. Values are local and shared.")
private String storageType = ServiceOffering.StorageType.shared.toString(); private String storageType = ServiceOffering.StorageType.shared.toString();
@Parameter(name=ApiConstants.DISPLAY_OFFERING, type=CommandType.BOOLEAN, description="an optional field, whether to display the offering to the end user or not.")
private Boolean displayOffering;
@Parameter(name=ApiConstants.BYTES_READ_RATE, type=CommandType.LONG, required=false, description="bytes read rate of the disk offering") @Parameter(name=ApiConstants.BYTES_READ_RATE, type=CommandType.LONG, required=false, description="bytes read rate of the disk offering")
private Long bytesReadRate; private Long bytesReadRate;
@ -74,8 +77,14 @@ public class CreateDiskOfferingCmd extends BaseCmd {
@Parameter(name=ApiConstants.IOPS_WRITE_RATE, type=CommandType.LONG, required=false, description="io requests write rate of the disk offering") @Parameter(name=ApiConstants.IOPS_WRITE_RATE, type=CommandType.LONG, required=false, description="io requests write rate of the disk offering")
private Long iopsWriteRate; private Long iopsWriteRate;
@Parameter(name=ApiConstants.DISPLAY_OFFERING, type=CommandType.BOOLEAN, description="an optional field, whether to display the offering to the end user or not.") @Parameter(name=ApiConstants.CUSTOMIZED_IOPS, type=CommandType.BOOLEAN, required=false, description="whether disk offering iops is custom or not")
private Boolean displayOffering; private Boolean customizedIops;
@Parameter(name=ApiConstants.MIN_IOPS, type=CommandType.LONG, required=false, description="min iops of the disk offering")
private Long minIops;
@Parameter(name=ApiConstants.MAX_IOPS, type=CommandType.LONG, required=false, description="max iops of the disk offering")
private Long maxIops;
///////////////////////////////////////////////////// /////////////////////////////////////////////////////
/////////////////// Accessors /////////////////////// /////////////////// Accessors ///////////////////////
@ -101,6 +110,18 @@ public class CreateDiskOfferingCmd extends BaseCmd {
return customized; return customized;
} }
public Boolean isCustomizedIops() {
return customizedIops;
}
public Long getMinIops() {
return minIops;
}
public Long getMaxIops() {
return maxIops;
}
public Long getDomainId(){ public Long getDomainId(){
return domainId; return domainId;
} }

View File

@ -80,6 +80,18 @@ public class CreateStoragePoolCmd extends BaseCmd {
required=false, description="the scope of the storage: cluster or zone") required=false, description="the scope of the storage: cluster or zone")
private String scope; private String scope;
@Parameter(name=ApiConstants.MANAGED, type=CommandType.BOOLEAN,
required=false, description="whether the storage should be managed by CloudStack")
private Boolean managed;
@Parameter(name=ApiConstants.CAPACITY_IOPS, type=CommandType.LONG,
required=false, description="IOPS CloudStack can provision from this storage pool")
private Long capacityIops;
@Parameter(name=ApiConstants.CAPACITY_BYTES, type=CommandType.LONG,
required=false, description="bytes CloudStack can provision from this storage pool")
private Long capacityBytes;
@Parameter(name=ApiConstants.HYPERVISOR, type=CommandType.STRING, required=false, @Parameter(name=ApiConstants.HYPERVISOR, type=CommandType.STRING, required=false,
description="hypervisor type of the hosts in zone that will be attached to this storage pool. KVM, VMware supported as of now.") description="hypervisor type of the hosts in zone that will be attached to this storage pool. KVM, VMware supported as of now.")
private String hypervisor; private String hypervisor;
@ -124,6 +136,18 @@ public class CreateStoragePoolCmd extends BaseCmd {
return this.scope; return this.scope;
} }
public Boolean isManaged() {
return managed;
}
public Long getCapacityIops() {
return capacityIops;
}
public Long getCapacityBytes() {
return capacityBytes;
}
public String getHypervisor() { public String getHypervisor() {
return hypervisor; return hypervisor;
} }

View File

@ -68,6 +68,12 @@ public class CreateVolumeCmd extends BaseAsyncCreateCmd {
@Parameter(name=ApiConstants.SIZE, type=CommandType.LONG, description="Arbitrary volume size") @Parameter(name=ApiConstants.SIZE, type=CommandType.LONG, description="Arbitrary volume size")
private Long size; private Long size;
@Parameter(name=ApiConstants.MIN_IOPS, type=CommandType.LONG, description="min iops")
private Long minIops;
@Parameter(name=ApiConstants.MAX_IOPS, type=CommandType.LONG, description="max iops")
private Long maxIops;
@Parameter(name=ApiConstants.SNAPSHOT_ID, type=CommandType.UUID, entityType=SnapshotResponse.class, @Parameter(name=ApiConstants.SNAPSHOT_ID, type=CommandType.UUID, entityType=SnapshotResponse.class,
description="the snapshot ID for the disk volume. Either diskOfferingId or snapshotId must be passed in.") description="the snapshot ID for the disk volume. Either diskOfferingId or snapshotId must be passed in.")
private Long snapshotId; private Long snapshotId;
@ -104,6 +110,14 @@ public class CreateVolumeCmd extends BaseAsyncCreateCmd {
return size; return size;
} }
public Long getMinIops() {
return minIops;
}
public Long getMaxIops() {
return maxIops;
}
public Long getSnapshotId() { public Long getSnapshotId() {
return snapshotId; return snapshotId;
} }

View File

@ -52,6 +52,15 @@ public class DiskOfferingResponse extends BaseResponse {
@SerializedName("iscustomized") @Param(description="true if disk offering uses custom size, false otherwise") @SerializedName("iscustomized") @Param(description="true if disk offering uses custom size, false otherwise")
private Boolean customized; private Boolean customized;
@SerializedName("iscustomizediops") @Param(description="true if disk offering uses custom iops, false otherwise")
private Boolean customizedIops;
@SerializedName(ApiConstants.MIN_IOPS) @Param(description="the min iops of the disk offering")
private Long minIops;
@SerializedName(ApiConstants.MAX_IOPS) @Param(description="the max iops of the disk offering")
private Long maxIops;
@SerializedName(ApiConstants.TAGS) @Param(description="the tags for the disk offering") @SerializedName(ApiConstants.TAGS) @Param(description="the tags for the disk offering")
private String tags; private String tags;
@ -154,6 +163,30 @@ public class DiskOfferingResponse extends BaseResponse {
this.customized = customized; this.customized = customized;
} }
public Boolean isCustomizedIops() {
return customizedIops;
}
public void setCustomizedIops(Boolean customizedIops) {
this.customizedIops = customizedIops;
}
public Long getMinIops() {
return minIops;
}
public void setMinIops(Long minIops) {
this.minIops = minIops;
}
public Long getMaxIops() {
return maxIops;
}
public void setMaxIops(Long maxIops) {
this.maxIops = maxIops;
}
public String getStorageType() { public String getStorageType() {
return storageType; return storageType;
} }

View File

@ -74,6 +74,9 @@ public class StoragePoolResponse extends BaseResponse {
@SerializedName("disksizeused") @Param(description="the host's currently used disk size") @SerializedName("disksizeused") @Param(description="the host's currently used disk size")
private Long diskSizeUsed; private Long diskSizeUsed;
@SerializedName("capacityiops") @Param(description="IOPS CloudStack can provision from this storage pool")
private Long capacityIops;
@SerializedName("tags") @Param(description="the tags for the storage pool") @SerializedName("tags") @Param(description="the tags for the storage pool")
private String tags; private String tags;
@ -237,6 +240,14 @@ public class StoragePoolResponse extends BaseResponse {
this.diskSizeUsed = diskSizeUsed; this.diskSizeUsed = diskSizeUsed;
} }
public Long getCapacityIops() {
return capacityIops;
}
public void setCapacityIops(Long capacityIops) {
this.capacityIops = capacityIops;
}
public String getTags() { public String getTags() {
return tags; return tags;
} }

View File

@ -76,6 +76,14 @@ public class VolumeResponse extends BaseResponse implements ControlledViewEntity
@Param(description = "size of the disk volume") @Param(description = "size of the disk volume")
private Long size; private Long size;
@SerializedName(ApiConstants.MIN_IOPS)
@Param(description = "min iops of the disk volume")
private Long minIops;
@SerializedName(ApiConstants.MAX_IOPS)
@Param(description = "max iops of the disk volume")
private Long maxIops;
@SerializedName(ApiConstants.CREATED) @SerializedName(ApiConstants.CREATED)
@Param(description = "the date the disk volume was created") @Param(description = "the date the disk volume was created")
private Date created; private Date created;
@ -241,6 +249,14 @@ public class VolumeResponse extends BaseResponse implements ControlledViewEntity
this.size = size; this.size = size;
} }
public void setMinIops(Long minIops) {
this.minIops = minIops;
}
public void setMaxIops(Long maxIops) {
this.maxIops = maxIops;
}
public void setCreated(Date created) { public void setCreated(Date created) {
this.created = created; this.created = created;
} }

View File

@ -14,6 +14,10 @@
# KIND, either express or implied. See the License for the # KIND, either express or implied. See the License for the
# specific language governing permissions and limitations # specific language governing permissions and limitations
# under the License. # under the License.
label.custom.disk.iops=Custom IOPS
label.disk.iops.min=Min IOPS
label.disk.iops.max=Max IOPS
label.disk.iops.total=IOPS Total
label.view.secondary.ips=View secondary IPs label.view.secondary.ips=View secondary IPs
message.acquire.ip.nic=Please confirm that you would like to acquire a new secondary IP for this NIC.<br/>NOTE: You need to manually configure the newly-acquired secondary IP inside the virtual machine. message.acquire.ip.nic=Please confirm that you would like to acquire a new secondary IP for this NIC.<br/>NOTE: You need to manually configure the newly-acquired secondary IP inside the virtual machine.
message.select.affinity.groups=Please select any affinity groups you want this VM to belong to: message.select.affinity.groups=Please select any affinity groups you want this VM to belong to:
@ -395,7 +399,7 @@ label.code=Code
label.community=Community label.community=Community
label.compute.and.storage=Compute and Storage label.compute.and.storage=Compute and Storage
label.compute.offering=Compute offering label.compute.offering=Compute offering
label.compute.offerings=Compute offerings label.compute.offerings=Compute Offerings
label.compute=Compute label.compute=Compute
label.configuration=Configuration label.configuration=Configuration
label.configure.network.ACLs=Configure Network ACLs label.configure.network.ACLs=Configure Network ACLs
@ -1046,6 +1050,7 @@ label.stopped.vms=Stopped VMs
label.storage.tags=Storage Tags label.storage.tags=Storage Tags
label.storage.traffic=Storage Traffic label.storage.traffic=Storage Traffic
label.storage.type=Storage Type label.storage.type=Storage Type
label.qos.type=QoS Type
label.storage=Storage label.storage=Storage
label.subdomain.access=Subdomain Access label.subdomain.access=Subdomain Access
label.submit=Submit label.submit=Submit

View File

@ -20,6 +20,11 @@
<version>4.2.0-SNAPSHOT</version> <version>4.2.0-SNAPSHOT</version>
</parent> </parent>
<dependencies> <dependencies>
<dependency>
<groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-plugin-storage-volume-solidfire</artifactId>
<version>${project.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.apache.cloudstack</groupId> <groupId>org.apache.cloudstack</groupId>
<artifactId>cloud-server</artifactId> <artifactId>cloud-server</artifactId>

View File

@ -806,6 +806,7 @@
<bean id="cloudStackImageStoreProviderImpl" class="org.apache.cloudstack.storage.datastore.provider.CloudStackImageStoreProviderImpl" /> <bean id="cloudStackImageStoreProviderImpl" class="org.apache.cloudstack.storage.datastore.provider.CloudStackImageStoreProviderImpl" />
<bean id="s3ImageStoreProviderImpl" class="org.apache.cloudstack.storage.datastore.provider.S3ImageStoreProviderImpl" /> <bean id="s3ImageStoreProviderImpl" class="org.apache.cloudstack.storage.datastore.provider.S3ImageStoreProviderImpl" />
<bean id="swiftImageStoreProviderImpl" class="org.apache.cloudstack.storage.datastore.provider.SwiftImageStoreProviderImpl" /> <bean id="swiftImageStoreProviderImpl" class="org.apache.cloudstack.storage.datastore.provider.SwiftImageStoreProviderImpl" />
<bean id="solidFireDataStoreProvider" class="org.apache.cloudstack.storage.datastore.provider.SolidfirePrimaryDataStoreProvider" />
<bean id="ApplicationLoadBalancerService" class="org.apache.cloudstack.network.lb.ApplicationLoadBalancerManagerImpl" /> <bean id="ApplicationLoadBalancerService" class="org.apache.cloudstack.network.lb.ApplicationLoadBalancerManagerImpl" />
<bean id="InternalLoadBalancerVMManager" class="org.apache.cloudstack.network.lb.InternalLoadBalancerVMManagerImpl" /> <bean id="InternalLoadBalancerVMManager" class="org.apache.cloudstack.network.lb.InternalLoadBalancerVMManagerImpl" />
<bean id="StorageCacheReplacementAlgorithm" class="org.apache.cloudstack.storage.cache.manager.StorageCacheReplacementAlgorithmLRU" /> <bean id="StorageCacheReplacementAlgorithm" class="org.apache.cloudstack.storage.cache.manager.StorageCacheReplacementAlgorithmLRU" />

View File

@ -19,35 +19,33 @@ package com.cloud.agent.api;
public class AttachVolumeAnswer extends Answer { public class AttachVolumeAnswer extends Answer {
private Long deviceId; private Long deviceId;
private String vdiUuid;
private String chainInfo; private String chainInfo;
protected AttachVolumeAnswer() {
}
public AttachVolumeAnswer(AttachVolumeCommand cmd, String result) { public AttachVolumeAnswer(AttachVolumeCommand cmd, String result) {
super(cmd, false, result); super(cmd, false, result);
this.deviceId = null; this.deviceId = null;
} }
public AttachVolumeAnswer(AttachVolumeCommand cmd, Long deviceId) { public AttachVolumeAnswer(AttachVolumeCommand cmd, Long deviceId, String vdiUuid) {
super(cmd); super(cmd);
this.deviceId = deviceId; this.deviceId = deviceId;
this.vdiUuid = vdiUuid;
} }
public AttachVolumeAnswer(AttachVolumeCommand cmd) { public AttachVolumeAnswer(AttachVolumeCommand cmd) {
super(cmd); super(cmd);
this.deviceId = null; this.deviceId = null;
} }
/**
* @return the deviceId
*/
public Long getDeviceId() { public Long getDeviceId() {
return deviceId; return deviceId;
} }
public String getVdiUuid() {
return vdiUuid;
}
public void setChainInfo(String chainInfo) { public void setChainInfo(String chainInfo) {
this.chainInfo = chainInfo; this.chainInfo = chainInfo;
} }

View File

@ -19,29 +19,37 @@ package com.cloud.agent.api;
import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.Storage.StoragePoolType;
public class AttachVolumeCommand extends Command { public class AttachVolumeCommand extends Command {
private boolean attach;
boolean attach; private boolean _managed;
String vmName; private String vmName;
StoragePoolType pooltype; private StoragePoolType pooltype;
String poolUuid; private String volumePath;
String volumeFolder; private String volumeName;
String volumePath; private Long deviceId;
String volumeName; private String chainInfo;
Long deviceId; private String poolUuid;
String chainInfo; private String _storageHost;
Long bytesReadRate; private int _storagePort;
Long bytesWriteRate; private String _iScsiName;
Long iopsReadRate; private String _chapInitiatorUsername;
Long iopsWriteRate; private String _chapInitiatorPassword;
private String _chapTargetUsername;
private String _chapTargetPassword;
private Long bytesReadRate;
private Long bytesWriteRate;
private Long iopsReadRate;
private Long iopsWriteRate;
protected AttachVolumeCommand() { protected AttachVolumeCommand() {
} }
public AttachVolumeCommand(boolean attach, String vmName, StoragePoolType pooltype, String volumeFolder, String volumePath, String volumeName, Long deviceId, String chainInfo) { public AttachVolumeCommand(boolean attach, boolean managed, String vmName,
StoragePoolType pooltype, String volumePath, String volumeName,
Long deviceId, String chainInfo) {
this.attach = attach; this.attach = attach;
this._managed = managed;
this.vmName = vmName; this.vmName = vmName;
this.pooltype = pooltype; this.pooltype = pooltype;
this.volumeFolder = volumeFolder;
this.volumePath = volumePath; this.volumePath = volumePath;
this.volumeName = volumeName; this.volumeName = volumeName;
this.deviceId = deviceId; this.deviceId = deviceId;
@ -54,7 +62,7 @@ public class AttachVolumeCommand extends Command {
} }
public boolean getAttach() { public boolean getAttach() {
return attach; return attach;
} }
public String getVmName() { public String getVmName() {
@ -69,16 +77,12 @@ public class AttachVolumeCommand extends Command {
this.pooltype = pooltype; this.pooltype = pooltype;
} }
public String getVolumeFolder() {
return volumeFolder;
}
public String getVolumePath() { public String getVolumePath() {
return volumePath; return volumePath;
} }
public String getVolumeName() { public String getVolumeName() {
return volumeName; return volumeName;
} }
public Long getDeviceId() { public Long getDeviceId() {
@ -90,17 +94,77 @@ public class AttachVolumeCommand extends Command {
} }
public String getPoolUuid() { public String getPoolUuid() {
return poolUuid; return poolUuid;
} }
public void setPoolUuid(String poolUuid) { public void setPoolUuid(String poolUuid) {
this.poolUuid = poolUuid; this.poolUuid = poolUuid;
} }
public String getChainInfo() { public String getChainInfo() {
return chainInfo; return chainInfo;
} }
public void setStorageHost(String storageHost) {
_storageHost = storageHost;
}
public String getStorageHost() {
return _storageHost;
}
public void setStoragePort(int storagePort) {
_storagePort = storagePort;
}
public int getStoragePort() {
return _storagePort;
}
public boolean isManaged() {
return _managed;
}
public void set_iScsiName(String iScsiName) {
this._iScsiName = iScsiName;
}
public String get_iScsiName() {
return _iScsiName;
}
public void setChapInitiatorUsername(String chapInitiatorUsername) {
_chapInitiatorUsername = chapInitiatorUsername;
}
public String getChapInitiatorUsername() {
return _chapInitiatorUsername;
}
public void setChapInitiatorPassword(String chapInitiatorPassword) {
_chapInitiatorPassword = chapInitiatorPassword;
}
public String getChapInitiatorPassword() {
return _chapInitiatorPassword;
}
public void setChapTargetUsername(String chapTargetUsername) {
_chapTargetUsername = chapTargetUsername;
}
public String getChapTargetUsername() {
return _chapTargetUsername;
}
public void setChapTargetPassword(String chapTargetPassword) {
_chapTargetPassword = chapTargetPassword;
}
public String getChapTargetPassword() {
return _chapTargetPassword;
}
public void setBytesReadRate(Long bytesReadRate) { public void setBytesReadRate(Long bytesReadRate) {
this.bytesReadRate = bytesReadRate; this.bytesReadRate = bytesReadRate;
} }

View File

@ -26,14 +26,14 @@ import com.cloud.agent.api.AttachVolumeCommand;
import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.Storage.StoragePoolType;
public class AttachVolumeAnswerTest { public class AttachVolumeAnswerTest {
AttachVolumeCommand avc = new AttachVolumeCommand(true, "vmname", AttachVolumeCommand avc = new AttachVolumeCommand(true, false, "vmname",
StoragePoolType.Filesystem, "vFolder", "vPath", "vName", StoragePoolType.Filesystem, "vPath", "vName",
123456789L, "chainInfo"); 123456789L, "chainInfo");
AttachVolumeAnswer ava1 = new AttachVolumeAnswer(avc); AttachVolumeAnswer ava1 = new AttachVolumeAnswer(avc);
String results = ""; String results = "";
AttachVolumeAnswer ava2 = new AttachVolumeAnswer(avc, results); AttachVolumeAnswer ava2 = new AttachVolumeAnswer(avc, results);
Long deviceId = 10L; Long deviceId = 10L;
AttachVolumeAnswer ava3 = new AttachVolumeAnswer(avc, deviceId); AttachVolumeAnswer ava3 = new AttachVolumeAnswer(avc, deviceId, "");
@Test @Test
public void testGetDeviceId() { public void testGetDeviceId() {

View File

@ -25,8 +25,8 @@ import com.cloud.agent.api.AttachVolumeCommand;
import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.Storage.StoragePoolType;
public class AttachVolumeCommandTest { public class AttachVolumeCommandTest {
AttachVolumeCommand avc = new AttachVolumeCommand(true, "vmname", AttachVolumeCommand avc = new AttachVolumeCommand(true, false, "vmname",
StoragePoolType.Filesystem, "vFolder", "vPath", "vName", StoragePoolType.Filesystem, "vPath", "vName",
123456789L, "chainInfo"); 123456789L, "chainInfo");
@Test @Test
@ -65,12 +65,6 @@ public class AttachVolumeCommandTest {
assertTrue(pt.equals(StoragePoolType.Iscsi)); assertTrue(pt.equals(StoragePoolType.Iscsi));
} }
@Test
public void testGetVolumeFolder() {
String vFolder = avc.getVolumeFolder();
assertTrue(vFolder.equals("vFolder"));
}
@Test @Test
public void testGetVolumePath() { public void testGetVolumePath() {
String vPath = avc.getVolumePath(); String vPath = avc.getVolumePath();

View File

@ -87,6 +87,11 @@ public class BackupSnapshotCommandTest {
return 0L; return 0L;
}; };
@Override
public Long getCapacityIops() {
return 0L;
}
@Override @Override
public Long getClusterId() { public Long getClusterId() {
return 0L; return 0L;

View File

@ -125,6 +125,11 @@ public class CheckNetworkAnswerTest {
return 0L; return 0L;
}; };
@Override
public Long getCapacityIops() {
return 0L;
};
@Override @Override
public Long getClusterId() { public Long getClusterId() {
return 0L; return 0L;

View File

@ -78,6 +78,10 @@ public class SnapshotCommandTest {
return 0L; return 0L;
}; };
public Long getCapacityIops() {
return 0L;
};
public Long getClusterId() { public Long getClusterId() {
return 0L; return 0L;
}; };

View File

@ -0,0 +1,26 @@
/*
* 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 org.apache.cloudstack.engine.subsystem.api.storage;
public interface ChapInfo {
String getInitiatorUsername();
String getInitiatorSecret();
String getTargetUsername();
String getTargetSecret();
}

View File

@ -24,17 +24,11 @@ import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
import org.apache.cloudstack.storage.command.CommandResult; import org.apache.cloudstack.storage.command.CommandResult;
public interface DataStoreDriver { public interface DataStoreDriver {
void createAsync(DataObject data, AsyncCompletionCallback<CreateCmdResult> callback);
void deleteAsync(DataObject data, AsyncCompletionCallback<CommandResult> callback);
void copyAsync(DataObject srcdata, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback);
boolean canCopy(DataObject srcData, DataObject destData);
void resize(DataObject data, AsyncCompletionCallback<CreateCmdResult> callback);
DataTO getTO(DataObject data); DataTO getTO(DataObject data);
DataStoreTO getStoreTO(DataStore store); DataStoreTO getStoreTO(DataStore store);
void createAsync(DataStore store, DataObject data, AsyncCompletionCallback<CreateCmdResult> callback);
void deleteAsync(DataStore store, DataObject data, AsyncCompletionCallback<CommandResult> callback);
void copyAsync(DataObject srcdata, DataObject destData, AsyncCompletionCallback<CopyCommandResult> callback);
boolean canCopy(DataObject srcData, DataObject destData);
void resize(DataObject data, AsyncCompletionCallback<CreateCmdResult> callback);
} }

View File

@ -22,7 +22,7 @@ import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
import org.apache.cloudstack.storage.command.CommandResult; import org.apache.cloudstack.storage.command.CommandResult;
public interface PrimaryDataStoreDriver extends DataStoreDriver { public interface PrimaryDataStoreDriver extends DataStoreDriver {
void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback); public ChapInfo getChapInfo(VolumeInfo volumeInfo);
public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback);
void revertSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CommandResult> callback); public void revertSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CommandResult> callback);
} }

View File

@ -20,6 +20,7 @@ package org.apache.cloudstack.engine.subsystem.api.storage;
import java.util.Map; import java.util.Map;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.Storage.StoragePoolType;
public class PrimaryDataStoreParameters { public class PrimaryDataStoreParameters {
@ -30,12 +31,17 @@ public class PrimaryDataStoreParameters {
private Map<String, String> details; private Map<String, String> details;
private String tags; private String tags;
private StoragePoolType type; private StoragePoolType type;
private HypervisorType hypervisorType;
private String host; private String host;
private String path; private String path;
private int port; private int port;
private String uuid; private String uuid;
private String name; private String name;
private String userInfo; private String userInfo;
private long capacityBytes;
private long usedBytes;
private boolean managed;
private Long capacityIops;
/** /**
* @return the userInfo * @return the userInfo
@ -187,6 +193,30 @@ public class PrimaryDataStoreParameters {
this.providerName = providerName; this.providerName = providerName;
} }
public void setManaged(boolean managed) {
this.managed = managed;
}
public boolean isManaged() {
return managed;
}
public void setCapacityIops(Long capacityIops) {
this.capacityIops = capacityIops;
}
public Long getCapacityIops() {
return capacityIops;
}
public void setHypervisorType(HypervisorType hypervisorType) {
this.hypervisorType = hypervisorType;
}
public HypervisorType getHypervisorType() {
return hypervisorType;
}
/** /**
* @return the clusterId * @return the clusterId
*/ */
@ -231,4 +261,24 @@ public class PrimaryDataStoreParameters {
public void setZoneId(Long zoneId) { public void setZoneId(Long zoneId) {
this.zoneId = zoneId; this.zoneId = zoneId;
} }
public long getCapacityBytes()
{
return capacityBytes;
}
public void setCapacityBytes(long capacityBytes)
{
this.capacityBytes = capacityBytes;
}
public long getUsedBytes()
{
return usedBytes;
}
public void setUsedBytes(long usedBytes)
{
this.usedBytes = usedBytes;
}
} }

View File

@ -42,6 +42,8 @@ public interface VolumeService {
} }
} }
ChapInfo getChapInfo(VolumeInfo volumeInfo, DataStore dataStore);
/** /**
* Creates the volume based on the given criteria * Creates the volume based on the given criteria
* *

View File

@ -24,6 +24,14 @@ import com.cloud.agent.api.to.DiskTO;
public final class AttachCommand extends Command implements StorageSubSystemCommand { public final class AttachCommand extends Command implements StorageSubSystemCommand {
private DiskTO disk; private DiskTO disk;
private String vmName; private String vmName;
private String _storageHost;
private int _storagePort;
private boolean _managed;
private String _iScsiName;
private String _chapInitiatorUsername;
private String _chapInitiatorPassword;
private String _chapTargetUsername;
private String _chapTargetPassword;
public AttachCommand(DiskTO disk, String vmName) { public AttachCommand(DiskTO disk, String vmName) {
super(); super();
@ -52,4 +60,67 @@ public final class AttachCommand extends Command implements StorageSubSystemComm
this.vmName = vmName; this.vmName = vmName;
} }
public void setStorageHost(String storageHost) {
_storageHost = storageHost;
}
public String getStorageHost() {
return _storageHost;
}
public void setStoragePort(int storagePort) {
_storagePort = storagePort;
}
public int getStoragePort() {
return _storagePort;
}
public void setManaged(boolean managed) {
_managed = managed;
}
public boolean isManaged() {
return _managed;
}
public void set_iScsiName(String iScsiName) {
this._iScsiName = iScsiName;
}
public String get_iScsiName() {
return _iScsiName;
}
public void setChapInitiatorUsername(String chapInitiatorUsername) {
_chapInitiatorUsername = chapInitiatorUsername;
}
public String getChapInitiatorUsername() {
return _chapInitiatorUsername;
}
public void setChapInitiatorPassword(String chapInitiatorPassword) {
_chapInitiatorPassword = chapInitiatorPassword;
}
public String getChapInitiatorPassword() {
return _chapInitiatorPassword;
}
public void setChapTargetUsername(String chapTargetUsername) {
_chapTargetUsername = chapTargetUsername;
}
public String getChapTargetUsername() {
return _chapTargetUsername;
}
public void setChapTargetPassword(String chapTargetPassword) {
_chapTargetPassword = chapTargetPassword;
}
public String getChapTargetPassword() {
return _chapTargetPassword;
}
} }

View File

@ -24,6 +24,8 @@ import com.cloud.agent.api.to.DiskTO;
public class DettachCommand extends Command implements StorageSubSystemCommand { public class DettachCommand extends Command implements StorageSubSystemCommand {
private DiskTO disk; private DiskTO disk;
private String vmName; private String vmName;
private boolean _managed;
private String _iScsiName;
public DettachCommand(DiskTO disk, String vmName) { public DettachCommand(DiskTO disk, String vmName) {
super(); super();
@ -52,4 +54,19 @@ public class DettachCommand extends Command implements StorageSubSystemCommand {
this.vmName = vmName; this.vmName = vmName;
} }
public void setManaged(boolean managed) {
_managed = managed;
}
public boolean isManaged() {
return _managed;
}
public void set_iScsiName(String iScsiName) {
_iScsiName = iScsiName;
}
public String get_iScsiName() {
return _iScsiName;
}
} }

View File

@ -103,6 +103,12 @@ public class StoragePoolVO implements StoragePool {
@Enumerated(value = EnumType.STRING) @Enumerated(value = EnumType.STRING)
private ScopeType scope; private ScopeType scope;
@Column(name = "managed")
private boolean managed;
@Column(name = "capacity_iops", updatable = true, nullable = true)
private Long capacityIops;
@Column(name = "hypervisor") @Column(name = "hypervisor")
@Enumerated(value = EnumType.STRING) @Enumerated(value = EnumType.STRING)
private HypervisorType hypervisor; private HypervisorType hypervisor;
@ -201,8 +207,24 @@ public class StoragePoolVO implements StoragePool {
usedBytes = available; usedBytes = available;
} }
public void setCapacityBytes(long capacity) { public void setCapacityBytes(long capacityBytes) {
capacityBytes = capacity; this.capacityBytes = capacityBytes;
}
public void setManaged(boolean managed) {
this.managed = managed;
}
public boolean isManaged() {
return managed;
}
public void setCapacityIops(Long capacityIops) {
this.capacityIops = capacityIops;
}
public Long getCapacityIops() {
return capacityIops;
} }
public Long getClusterId() { public Long getClusterId() {

View File

@ -94,6 +94,15 @@ public class DiskOfferingVO implements DiskOffering {
@Column(name = "uuid") @Column(name = "uuid")
private String uuid; private String uuid;
@Column(name="customized_iops")
private Boolean customizedIops;
@Column(name="min_iops")
Long minIops;
@Column(name="max_iops")
Long maxIops;
@Column(name = "sort_key") @Column(name = "sort_key")
int sortKey; int sortKey;
@ -116,8 +125,8 @@ public class DiskOfferingVO implements DiskOffering {
this.uuid = UUID.randomUUID().toString(); this.uuid = UUID.randomUUID().toString();
} }
public DiskOfferingVO(Long domainId, String name, String displayText, long diskSize, String tags, public DiskOfferingVO(Long domainId, String name, String displayText, long diskSize, String tags, boolean isCustomized,
boolean isCustomized) { Boolean isCustomizedIops, Long minIops, Long maxIops) {
this.domainId = domainId; this.domainId = domainId;
this.name = name; this.name = name;
this.displayText = displayText; this.displayText = displayText;
@ -128,6 +137,9 @@ public class DiskOfferingVO implements DiskOffering {
this.useLocalStorage = false; this.useLocalStorage = false;
this.customized = isCustomized; this.customized = isCustomized;
this.uuid = UUID.randomUUID().toString(); this.uuid = UUID.randomUUID().toString();
this.customizedIops = isCustomizedIops;
this.minIops = minIops;
this.maxIops = maxIops;
} }
public DiskOfferingVO(String name, String displayText, boolean mirrored, String tags, boolean recreatable, public DiskOfferingVO(String name, String displayText, boolean mirrored, String tags, boolean recreatable,
@ -175,6 +187,36 @@ public class DiskOfferingVO implements DiskOffering {
} }
@Override @Override
public Boolean isCustomizedIops() {
return customizedIops;
}
@Override
public void setCustomizedIops(Boolean customizedIops) {
this.customizedIops = customizedIops;
}
@Override
public Long getMinIops() {
return minIops;
}
@Override
public void setMinIops(Long minIops) {
this.minIops = minIops;
}
@Override
public Long getMaxIops() {
return maxIops;
}
@Override
public void setMaxIops(Long maxIops) {
this.maxIops = maxIops;
}
@Override
public String getUniqueName() { public String getUniqueName() {
return uniqueName; return uniqueName;
} }

View File

@ -70,6 +70,12 @@ public class VolumeVO implements Volume {
@Column(name = "size") @Column(name = "size")
Long size; Long size;
@Column(name = "min_iops")
Long minIops;
@Column(name = "max_iops")
Long maxIops;
@Column(name = "folder") @Column(name = "folder")
String folder; String folder;
@ -141,25 +147,32 @@ public class VolumeVO implements Volume {
@Column(name = "display_volume", updatable = true, nullable = false) @Column(name = "display_volume", updatable = true, nullable = false)
protected boolean displayVolume; protected boolean displayVolume;
@Column(name = "iscsi_name")
private String _iScsiName;
@Transient @Transient
// @Column(name="reservation") // @Column(name="reservation")
String reservationId; String reservationId;
// Real Constructor // Real Constructor
public VolumeVO(Type type, String name, long dcId, long domainId, long accountId, long diskOfferingId, long size) { public VolumeVO(Type type, String name, long dcId, long domainId, long accountId, long diskOfferingId, long size,
Long minIops, Long maxIops, String iScsiName) {
this.volumeType = type; this.volumeType = type;
this.name = name; this.name = name;
this.dataCenterId = dcId; this.dataCenterId = dcId;
this.accountId = accountId; this.accountId = accountId;
this.domainId = domainId; this.domainId = domainId;
this.size = size; this.size = size;
this.minIops = minIops;
this.maxIops = maxIops;
this._iScsiName = iScsiName;
this.diskOfferingId = diskOfferingId; this.diskOfferingId = diskOfferingId;
this.state = State.Allocated; this.state = State.Allocated;
this.uuid = UUID.randomUUID().toString(); this.uuid = UUID.randomUUID().toString();
} }
public VolumeVO(String name, Long dcId, Long podId, long accountId, long domainId, Long instanceId, String folder, public VolumeVO(String name, long dcId, long podId, long accountId, long domainId, Long instanceId, String folder, String path,
String path, long size, Volume.Type vType) { long size, Long minIops, Long maxIops, String iScsiName, Volume.Type vType) {
this.name = name; this.name = name;
this.accountId = accountId; this.accountId = accountId;
this.domainId = domainId; this.domainId = domainId;
@ -167,6 +180,9 @@ public class VolumeVO implements Volume {
this.folder = folder; this.folder = folder;
this.path = path; this.path = path;
this.size = size; this.size = size;
this.minIops = minIops;
this.maxIops = maxIops;
this._iScsiName = iScsiName;
this.podId = podId; this.podId = podId;
this.dataCenterId = dcId; this.dataCenterId = dcId;
this.volumeType = vType; this.volumeType = vType;
@ -177,11 +193,15 @@ public class VolumeVO implements Volume {
// Copy Constructor // Copy Constructor
public VolumeVO(Volume that) { public VolumeVO(Volume that) {
this(that.getName(), that.getDataCenterId(), that.getPodId(), that.getAccountId(), that.getDomainId(), that this(that.getName(), that.getDataCenterId(), that.getPodId(), that.getAccountId(), that.getDomainId(), that.getInstanceId(),
.getInstanceId(), that.getFolder(), that.getPath(), that.getSize(), that.getVolumeType()); that.getFolder(), that.getPath(), that.getSize(), that.getMinIops(), that.getMaxIops(),
that.get_iScsiName(), that.getVolumeType());
this.recreatable = that.isRecreatable(); this.recreatable = that.isRecreatable();
this.state = that.getState(); this.state = that.getState();
this.size = that.getSize(); this.size = that.getSize();
this.minIops = that.getMinIops();
this.maxIops = that.getMaxIops();
this._iScsiName = that.get_iScsiName();
this.diskOfferingId = that.getDiskOfferingId(); this.diskOfferingId = that.getDiskOfferingId();
this.poolId = that.getPoolId(); this.poolId = that.getPoolId();
this.attached = that.getAttached(); this.attached = that.getAttached();
@ -274,6 +294,24 @@ public class VolumeVO implements Volume {
this.size = size; this.size = size;
} }
@Override
public Long getMinIops() {
return minIops;
}
public void setMinIops(Long minIops) {
this.minIops = minIops;
}
@Override
public Long getMaxIops() {
return maxIops;
}
public void setMaxIops(Long maxIops) {
this.maxIops = maxIops;
}
@Override @Override
public Long getInstanceId() { public Long getInstanceId() {
return instanceId; return instanceId;
@ -464,6 +502,15 @@ public class VolumeVO implements Volume {
this.uuid = uuid; this.uuid = uuid;
} }
@Override
public String get_iScsiName() {
return this._iScsiName;
}
public void set_iScsiName(String iScsiName) {
this._iScsiName = iScsiName;
}
public boolean isDisplayVolume() { public boolean isDisplayVolume() {
return displayVolume; return displayVolume;
} }

View File

@ -58,6 +58,8 @@ public interface VolumeDao extends GenericDao<VolumeVO, Long>, StateDao<Volume.S
List<VolumeVO> findByPoolId(long poolId); List<VolumeVO> findByPoolId(long poolId);
List<VolumeVO> findByPoolId(long poolId, Volume.Type volumeType);
List<VolumeVO> findByInstanceAndDeviceId(long instanceId, long deviceId); List<VolumeVO> findByInstanceAndDeviceId(long instanceId, long deviceId);
List<VolumeVO> findUsableVolumesForInstance(long instanceId); List<VolumeVO> findUsableVolumesForInstance(long instanceId);

View File

@ -109,6 +109,19 @@ public class VolumeDaoImpl extends GenericDaoBase<VolumeVO, Long> implements Vol
sc.setParameters("poolId", poolId); sc.setParameters("poolId", poolId);
sc.setParameters("notDestroyed", Volume.State.Destroy); sc.setParameters("notDestroyed", Volume.State.Destroy);
sc.setParameters("vType", Volume.Type.ROOT.toString()); sc.setParameters("vType", Volume.Type.ROOT.toString());
return listBy(sc);
}
@Override
public List<VolumeVO> findByPoolId(long poolId, Volume.Type volumeType) {
SearchCriteria<VolumeVO> sc = AllFieldsSearch.create();
sc.setParameters("poolId", poolId);
sc.setParameters("notDestroyed", Volume.State.Destroy);
if (volumeType != null) {
sc.setParameters("vType", volumeType.toString());
}
return listBy(sc); return listBy(sc);
} }

View File

@ -166,7 +166,7 @@ public class TemplateServiceImpl implements TemplateService {
AsyncCallbackDispatcher<TemplateServiceImpl, CreateCmdResult> caller = AsyncCallbackDispatcher.create(this); AsyncCallbackDispatcher<TemplateServiceImpl, CreateCmdResult> caller = AsyncCallbackDispatcher.create(this);
caller.setCallback(caller.getTarget().createTemplateCallback(null, null)).setContext(context); caller.setCallback(caller.getTarget().createTemplateCallback(null, null)).setContext(context);
store.getDriver().createAsync(templateOnStore, caller); store.getDriver().createAsync(store, templateOnStore, caller);
} }
@Override @Override
@ -511,7 +511,7 @@ public class TemplateServiceImpl implements TemplateService {
TemplateOpContext<TemplateApiResult> context = new TemplateOpContext<TemplateApiResult>(null, to, future); TemplateOpContext<TemplateApiResult> context = new TemplateOpContext<TemplateApiResult>(null, to, future);
AsyncCallbackDispatcher<TemplateServiceImpl, CommandResult> caller = AsyncCallbackDispatcher.create(this); AsyncCallbackDispatcher<TemplateServiceImpl, CommandResult> caller = AsyncCallbackDispatcher.create(this);
caller.setCallback(caller.getTarget().deleteTemplateCallback(null, null)).setContext(context); caller.setCallback(caller.getTarget().deleteTemplateCallback(null, null)).setContext(context);
to.getDataStore().getDriver().deleteAsync(to, caller); to.getDataStore().getDriver().deleteAsync(to.getDataStore(), to, caller);
return future; return future;
} }

View File

@ -145,7 +145,7 @@ public class ImageStoreImpl implements ImageStoreEntity {
@Override @Override
public boolean delete(DataObject obj) { public boolean delete(DataObject obj) {
AsyncCallFuture<CommandResult> future = new AsyncCallFuture<CommandResult>(); AsyncCallFuture<CommandResult> future = new AsyncCallFuture<CommandResult>();
this.driver.deleteAsync(obj, future); this.driver.deleteAsync(obj.getDataStore(), obj, future);
try { try {
future.get(); future.get();
} catch (InterruptedException e) { } catch (InterruptedException e) {

View File

@ -148,7 +148,8 @@ public class StorageAllocatorTest {
diskOffering = diskOfferingDao.persist(diskOffering); diskOffering = diskOfferingDao.persist(diskOffering);
diskOfferingId = diskOffering.getId(); diskOfferingId = diskOffering.getId();
volume = new VolumeVO(Volume.Type.ROOT, "volume", dcId, 1, 1, diskOffering.getId(), diskOffering.getDiskSize()); volume = new VolumeVO(Volume.Type.ROOT, "volume", dcId, 1, 1, diskOffering.getId(), diskOffering.getDiskSize(),
diskOffering.getMinIops(), diskOffering.getMaxIops(), "");
volume = volumeDao.persist(volume); volume = volumeDao.persist(volume);
volumeId = volume.getId(); volumeId = volume.getId();
} }

View File

@ -347,7 +347,7 @@ public class SnapshotTest extends CloudStackTestNGBase {
private VolumeVO createVolume(Long templateId, long dataStoreId) { private VolumeVO createVolume(Long templateId, long dataStoreId) {
VolumeVO volume = new VolumeVO(Volume.Type.DATADISK, UUID.randomUUID().toString(), this.dcId, 1L, 1L, 1L, 1000); VolumeVO volume = new VolumeVO(Volume.Type.DATADISK, UUID.randomUUID().toString(), this.dcId, 1L, 1L, 1L, 1000, 0L, 0L, "");
volume.setDataCenterId(this.dcId); volume.setDataCenterId(this.dcId);
volume.setPoolId(dataStoreId); volume.setPoolId(dataStoreId);
volume = volumeDao.persist(volume); volume = volumeDao.persist(volume);

View File

@ -317,7 +317,7 @@ public class VolumeTest extends CloudStackTestNGBase {
} }
private VolumeVO createVolume(Long templateId, long dataStoreId) { private VolumeVO createVolume(Long templateId, long dataStoreId) {
VolumeVO volume = new VolumeVO(Volume.Type.DATADISK, UUID.randomUUID().toString(), this.dcId, 1L, 1L, 1L, 1000); VolumeVO volume = new VolumeVO(Volume.Type.DATADISK, UUID.randomUUID().toString(), this.dcId, 1L, 1L, 1L, 1000, 0L, 0L, "");
; ;
volume.setPoolId(dataStoreId); volume.setPoolId(dataStoreId);
volume = volumeDao.persist(volume); volume = volumeDao.persist(volume);

View File

@ -317,7 +317,7 @@ public class VolumeTestVmware extends CloudStackTestNGBase {
} }
private VolumeVO createVolume(Long templateId, long dataStoreId) { private VolumeVO createVolume(Long templateId, long dataStoreId) {
VolumeVO volume = new VolumeVO(Volume.Type.DATADISK, UUID.randomUUID().toString(), this.dcId, 1L, 1L, 1L, 1000); VolumeVO volume = new VolumeVO(Volume.Type.DATADISK, UUID.randomUUID().toString(), this.dcId, 1L, 1L, 1L, 1000, 0L, 0L, "");
; ;
volume.setPoolId(dataStoreId); volume.setPoolId(dataStoreId);
volume = volumeDao.persist(volume); volume = volumeDao.persist(volume);

View File

@ -363,7 +363,7 @@ public class volumeServiceTest extends CloudStackTestNGBase {
} }
private VolumeVO createVolume(Long templateId, long dataStoreId) { private VolumeVO createVolume(Long templateId, long dataStoreId) {
VolumeVO volume = new VolumeVO(Volume.Type.DATADISK, UUID.randomUUID().toString(), this.dcId, 1L, 1L, 1L, 1000); VolumeVO volume = new VolumeVO(Volume.Type.DATADISK, UUID.randomUUID().toString(), this.dcId, 1L, 1L, 1L, 1000, 0L, 0L, "");
volume.setPoolId(dataStoreId); volume.setPoolId(dataStoreId);
volume = volumeDao.persist(volume); volume = volumeDao.persist(volume);
return volume; return volume;

View File

@ -355,7 +355,7 @@ public class SnapshotServiceImpl implements SnapshotService {
AsyncCallbackDispatcher<SnapshotServiceImpl, CommandResult> caller = AsyncCallbackDispatcher.create(this); AsyncCallbackDispatcher<SnapshotServiceImpl, CommandResult> caller = AsyncCallbackDispatcher.create(this);
caller.setCallback(caller.getTarget().deleteSnapshotCallback(null, null)).setContext(context); caller.setCallback(caller.getTarget().deleteSnapshotCallback(null, null)).setContext(context);
DataStore store = snapInfo.getDataStore(); DataStore store = snapInfo.getDataStore();
store.getDriver().deleteAsync(snapInfo, caller); store.getDriver().deleteAsync(store, snapInfo, caller);
SnapshotResult result = null; SnapshotResult result = null;
try { try {

View File

@ -49,26 +49,38 @@ public class ZoneWideStoragePoolAllocator extends AbstractStoragePoolAllocator {
Volume volume = _volumeDao.findById(dskCh.getVolumeId()); Volume volume = _volumeDao.findById(dskCh.getVolumeId());
List<Volume> requestVolumes = new ArrayList<Volume>(); List<Volume> requestVolumes = new ArrayList<Volume>();
requestVolumes.add(volume); requestVolumes.add(volume);
return storageMgr.storagePoolHasEnoughSpace(requestVolumes, pool);
return storageMgr.storagePoolHasEnoughIops(requestVolumes, pool) &&
storageMgr.storagePoolHasEnoughSpace(requestVolumes, pool);
} }
@Override @Override
protected List<StoragePool> select(DiskProfile dskCh, VirtualMachineProfile<? extends VirtualMachine> vmProfile, protected List<StoragePool> select(DiskProfile dskCh,
DeploymentPlan plan, ExcludeList avoid, int returnUpTo) { VirtualMachineProfile<? extends VirtualMachine> vmProfile,
s_logger.debug("ZoneWideStoragePoolAllocator to find storage pool"); DeploymentPlan plan, ExcludeList avoid, int returnUpTo) {
List<StoragePool> suitablePools = new ArrayList<StoragePool>(); s_logger.debug("ZoneWideStoragePoolAllocator to find storage pool");
HypervisorType hypervisor = dskCh.getHypervisorType(); List<StoragePool> suitablePools = new ArrayList<StoragePool>();
if (hypervisor != null) {
if (hypervisor != HypervisorType.KVM && hypervisor != HypervisorType.VMware) { List<StoragePoolVO> storagePools = _storagePoolDao.findZoneWideStoragePoolsByTags(plan.getDataCenterId(), dskCh.getTags());
s_logger.debug("Only kvm, VMware hypervisors are enabled to support zone wide storage");
return suitablePools; if (storagePools == null) {
storagePools = new ArrayList<StoragePoolVO>();
}
List<StoragePoolVO> anyHypervisorStoragePools = new ArrayList<StoragePoolVO>();
for (StoragePoolVO storagePool : storagePools) {
if (HypervisorType.Any.equals(storagePool.getHypervisor())) {
anyHypervisorStoragePools.add(storagePool);
} }
} }
List<StoragePoolVO> storagePools = _storagePoolDao.findZoneWideStoragePoolsByTags(plan.getDataCenterId(), dskCh.getTags());
List<StoragePoolVO> storagePoolsByHypervisor = _storagePoolDao.findZoneWideStoragePoolsByHypervisor(plan.getDataCenterId(), dskCh.getHypervisorType()); List<StoragePoolVO> storagePoolsByHypervisor = _storagePoolDao.findZoneWideStoragePoolsByHypervisor(plan.getDataCenterId(), dskCh.getHypervisorType());
storagePools.retainAll(storagePoolsByHypervisor); storagePools.retainAll(storagePoolsByHypervisor);
storagePools.addAll(anyHypervisorStoragePools);
// add remaining pools in zone, that did not match tags, to avoid set // add remaining pools in zone, that did not match tags, to avoid set
List<StoragePoolVO> allPools = _storagePoolDao.findZoneWideStoragePoolsByTags(plan.getDataCenterId(), null); List<StoragePoolVO> allPools = _storagePoolDao.findZoneWideStoragePoolsByTags(plan.getDataCenterId(), null);
allPools.removeAll(storagePools); allPools.removeAll(storagePools);

View File

@ -162,7 +162,7 @@ public class DataObjectManagerImpl implements DataObjectManager {
AsyncCallbackDispatcher<DataObjectManagerImpl, CreateCmdResult> caller = AsyncCallbackDispatcher.create(this); AsyncCallbackDispatcher<DataObjectManagerImpl, CreateCmdResult> caller = AsyncCallbackDispatcher.create(this);
caller.setCallback(caller.getTarget().createAsynCallback(null, null)).setContext(context); caller.setCallback(caller.getTarget().createAsynCallback(null, null)).setContext(context);
store.getDriver().createAsync(objInStore, caller); store.getDriver().createAsync(store, objInStore, caller);
return; return;
} }
@ -321,7 +321,7 @@ public class DataObjectManagerImpl implements DataObjectManager {
AsyncCallbackDispatcher<DataObjectManagerImpl, CommandResult> caller = AsyncCallbackDispatcher.create(this); AsyncCallbackDispatcher<DataObjectManagerImpl, CommandResult> caller = AsyncCallbackDispatcher.create(this);
caller.setCallback(caller.getTarget().deleteAsynCallback(null, null)).setContext(context); caller.setCallback(caller.getTarget().deleteAsynCallback(null, null)).setContext(context);
data.getDataStore().getDriver().deleteAsync(data, caller); data.getDataStore().getDriver().deleteAsync(data.getDataStore(), data, caller);
return; return;
} }

View File

@ -175,6 +175,12 @@ public class PrimaryDataStoreEntityImpl implements StorageEntity {
return 0; return 0;
} }
@Override
public Long getCapacityIops() {
// TODO Auto-generated method stub
return 0L;
}
@Override @Override
public Long getClusterId() { public Long getClusterId() {
// TODO Auto-generated method stub // TODO Auto-generated method stub

View File

@ -81,7 +81,7 @@ public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver {
} }
@Override @Override
public void createAsync(DataObject data, AsyncCompletionCallback<CreateCmdResult> callback) { public void createAsync(DataStore dataStore, DataObject data, AsyncCompletionCallback<CreateCmdResult> callback) {
CreateContext<CreateCmdResult> context = new CreateContext<CreateCmdResult>(callback, data); CreateContext<CreateCmdResult> context = new CreateContext<CreateCmdResult>(callback, data);
AsyncCallbackDispatcher<BaseImageStoreDriverImpl, DownloadAnswer> caller = AsyncCallbackDispatcher AsyncCallbackDispatcher<BaseImageStoreDriverImpl, DownloadAnswer> caller = AsyncCallbackDispatcher
.create(this); .create(this);
@ -184,7 +184,7 @@ public abstract class BaseImageStoreDriverImpl implements ImageStoreDriver {
} }
@Override @Override
public void deleteAsync(DataObject data, AsyncCompletionCallback<CommandResult> callback) { public void deleteAsync(DataStore dataStore, DataObject data, AsyncCompletionCallback<CommandResult> callback) {
DeleteCommand cmd = new DeleteCommand(data.getTO()); DeleteCommand cmd = new DeleteCommand(data.getTO());
CommandResult result = new CommandResult(); CommandResult result = new CommandResult();

View File

@ -79,6 +79,11 @@ public class PrimaryDataStoreHelper {
dataStoreVO.setClusterId(params.getClusterId()); dataStoreVO.setClusterId(params.getClusterId());
dataStoreVO.setStatus(StoragePoolStatus.Initialized); dataStoreVO.setStatus(StoragePoolStatus.Initialized);
dataStoreVO.setUserInfo(params.getUserInfo()); dataStoreVO.setUserInfo(params.getUserInfo());
dataStoreVO.setManaged(params.isManaged());
dataStoreVO.setCapacityIops(params.getCapacityIops());
dataStoreVO.setCapacityBytes(params.getCapacityBytes());
dataStoreVO.setUsedBytes(params.getUsedBytes());
dataStoreVO.setHypervisor(params.getHypervisorType());
Map<String, String> details = params.getDetails(); Map<String, String> details = params.getDetails();
String tags = params.getTags(); String tags = params.getTags();

View File

@ -287,6 +287,11 @@ public class PrimaryDataStoreImpl implements PrimaryDataStore {
return this.pdsv.getUsedBytes(); return this.pdsv.getUsedBytes();
} }
@Override
public Long getCapacityIops() {
return this.pdsv.getCapacityIops();
}
@Override @Override
public Long getClusterId() { public Long getClusterId() {
return this.pdsv.getClusterId(); return this.pdsv.getClusterId();

View File

@ -107,6 +107,11 @@ public class VolumeObject implements VolumeInfo {
volumeVO.setUuid(uuid); volumeVO.setUuid(uuid);
} }
@Override
public String get_iScsiName() {
return volumeVO.get_iScsiName();
}
public void setSize(Long size) { public void setSize(Long size) {
volumeVO.setSize(size); volumeVO.setSize(size);
} }
@ -126,6 +131,16 @@ public class VolumeObject implements VolumeInfo {
return volumeVO.getSize(); return volumeVO.getSize();
} }
@Override
public Long getMinIops() {
return volumeVO.getMinIops();
}
@Override
public Long getMaxIops() {
return volumeVO.getMaxIops();
}
public long getVolumeId() { public long getVolumeId() {
return volumeVO.getId(); return volumeVO.getId();
} }

View File

@ -35,8 +35,11 @@ import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine;
import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event; import org.apache.cloudstack.engine.subsystem.api.storage.ObjectInDataStoreStateMachine.Event;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreDriver;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo; import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService;
@ -143,6 +146,16 @@ public class VolumeServiceImpl implements VolumeService {
} }
public ChapInfo getChapInfo(VolumeInfo volumeInfo, DataStore dataStore) {
DataStoreDriver dataStoreDriver = dataStore.getDriver();
if (dataStoreDriver instanceof PrimaryDataStoreDriver) {
return ((PrimaryDataStoreDriver)dataStoreDriver).getChapInfo(volumeInfo);
}
return null;
}
@Override @Override
public AsyncCallFuture<VolumeApiResult> createVolumeAsync(VolumeInfo volume, DataStore dataStore) { public AsyncCallFuture<VolumeApiResult> createVolumeAsync(VolumeInfo volume, DataStore dataStore) {
AsyncCallFuture<VolumeApiResult> future = new AsyncCallFuture<VolumeApiResult>(); AsyncCallFuture<VolumeApiResult> future = new AsyncCallFuture<VolumeApiResult>();
@ -154,7 +167,7 @@ public class VolumeServiceImpl implements VolumeService {
AsyncCallbackDispatcher<VolumeServiceImpl, CreateCmdResult> caller = AsyncCallbackDispatcher.create(this); AsyncCallbackDispatcher<VolumeServiceImpl, CreateCmdResult> caller = AsyncCallbackDispatcher.create(this);
caller.setCallback(caller.getTarget().createVolumeCallback(null, null)).setContext(context); caller.setCallback(caller.getTarget().createVolumeCallback(null, null)).setContext(context);
dataStore.getDriver().createAsync(volumeOnStore, caller); dataStore.getDriver().createAsync(dataStore, volumeOnStore, caller);
return future; return future;
} }
@ -238,7 +251,7 @@ public class VolumeServiceImpl implements VolumeService {
AsyncCallbackDispatcher<VolumeServiceImpl, CommandResult> caller = AsyncCallbackDispatcher.create(this); AsyncCallbackDispatcher<VolumeServiceImpl, CommandResult> caller = AsyncCallbackDispatcher.create(this);
caller.setCallback(caller.getTarget().deleteVolumeCallback(null, null)).setContext(context); caller.setCallback(caller.getTarget().deleteVolumeCallback(null, null)).setContext(context);
volume.getDataStore().getDriver().deleteAsync(volume, caller); volume.getDataStore().getDriver().deleteAsync(volume.getDataStore(), volume, caller);
return future; return future;
} }
@ -935,7 +948,7 @@ public class VolumeServiceImpl implements VolumeService {
caller.setCallback(caller.getTarget().registerVolumeCallback(null, null)); caller.setCallback(caller.getTarget().registerVolumeCallback(null, null));
caller.setContext(context); caller.setContext(context);
store.getDriver().createAsync(volumeOnStore, caller); store.getDriver().createAsync(store, volumeOnStore, caller);
return future; return future;
} }

View File

@ -2573,7 +2573,7 @@ ServerResource {
return new AttachVolumeAnswer(cmd, e.toString()); return new AttachVolumeAnswer(cmd, e.toString());
} }
return new AttachVolumeAnswer(cmd, cmd.getDeviceId()); return new AttachVolumeAnswer(cmd, cmd.getDeviceId(), cmd.getVolumePath());
} }
private Answer execute(ReadyCommand cmd) { private Answer execute(ReadyCommand cmd) {

View File

@ -258,7 +258,7 @@ public class MockStorageManagerImpl extends ManagerBase implements MockStorageMa
} }
txn.commit(); txn.commit();
return new AttachVolumeAnswer(cmd, cmd.getDeviceId()); return new AttachVolumeAnswer(cmd, cmd.getDeviceId(), cmd.getVolumePath());
} catch (Exception ex) { } catch (Exception ex) {
txn.rollback(); txn.rollback();
throw new CloudRuntimeException("Error when attaching volume " + cmd.getVolumeName() + " to VM " throw new CloudRuntimeException("Error when attaching volume " + cmd.getVolumeName() + " to VM "

View File

@ -36,6 +36,9 @@ import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.TimeZone; import java.util.TimeZone;
import java.util.UUID; import java.util.UUID;
import java.util.HashSet;
import java.util.Set;
import java.util.Map.Entry;
import javax.inject.Inject; import javax.inject.Inject;
import javax.naming.ConfigurationException; import javax.naming.ConfigurationException;
@ -216,6 +219,8 @@ import com.cloud.hypervisor.vmware.mo.HostMO;
import com.cloud.hypervisor.vmware.mo.HypervisorHostHelper; import com.cloud.hypervisor.vmware.mo.HypervisorHostHelper;
import com.cloud.hypervisor.vmware.mo.NetworkDetails; import com.cloud.hypervisor.vmware.mo.NetworkDetails;
import com.cloud.hypervisor.vmware.mo.VirtualEthernetCardType; import com.cloud.hypervisor.vmware.mo.VirtualEthernetCardType;
import com.cloud.hypervisor.vmware.mo.HostDatastoreSystemMO;
import com.cloud.hypervisor.vmware.mo.HostStorageSystemMO;
import com.cloud.hypervisor.vmware.mo.VirtualMachineMO; import com.cloud.hypervisor.vmware.mo.VirtualMachineMO;
import com.cloud.hypervisor.vmware.mo.VirtualSwitchType; import com.cloud.hypervisor.vmware.mo.VirtualSwitchType;
import com.cloud.hypervisor.vmware.mo.VmwareHypervisorHost; import com.cloud.hypervisor.vmware.mo.VmwareHypervisorHost;
@ -229,7 +234,6 @@ import com.cloud.network.HAProxyConfigurator;
import com.cloud.network.LoadBalancerConfigurator; import com.cloud.network.LoadBalancerConfigurator;
import com.cloud.network.Networks; import com.cloud.network.Networks;
import com.cloud.network.Networks.BroadcastDomainType; import com.cloud.network.Networks.BroadcastDomainType;
import com.cloud.network.Networks.IsolationType;
import com.cloud.network.Networks.TrafficType; import com.cloud.network.Networks.TrafficType;
import com.cloud.network.VmwareTrafficLabel; import com.cloud.network.VmwareTrafficLabel;
import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.FirewallRule;
@ -272,8 +276,16 @@ import com.vmware.vim25.GuestInfo;
import com.vmware.vim25.HostCapability; import com.vmware.vim25.HostCapability;
import com.vmware.vim25.HostFirewallInfo; import com.vmware.vim25.HostFirewallInfo;
import com.vmware.vim25.HostFirewallRuleset; import com.vmware.vim25.HostFirewallRuleset;
import com.vmware.vim25.HostNetworkTrafficShapingPolicy; import com.vmware.vim25.HostHostBusAdapter;
import com.vmware.vim25.HostPortGroupSpec; import com.vmware.vim25.HostInternetScsiTargetTransport;
import com.vmware.vim25.HostScsiTopology;
import com.vmware.vim25.HostInternetScsiHba;
import com.vmware.vim25.HostInternetScsiHbaAuthenticationProperties;
import com.vmware.vim25.HostInternetScsiHbaStaticTarget;
import com.vmware.vim25.HostScsiDisk;
import com.vmware.vim25.HostScsiTopologyInterface;
import com.vmware.vim25.HostScsiTopologyLun;
import com.vmware.vim25.HostScsiTopologyTarget;
import com.vmware.vim25.ManagedObjectReference; import com.vmware.vim25.ManagedObjectReference;
import com.vmware.vim25.ObjectContent; import com.vmware.vim25.ObjectContent;
import com.vmware.vim25.OptionValue; import com.vmware.vim25.OptionValue;
@ -304,10 +316,6 @@ import com.vmware.vim25.VirtualMachineRelocateSpecDiskLocator;
import com.vmware.vim25.VirtualMachineRuntimeInfo; import com.vmware.vim25.VirtualMachineRuntimeInfo;
import com.vmware.vim25.VirtualSCSISharing; import com.vmware.vim25.VirtualSCSISharing;
import java.util.HashSet;
import java.util.Set;
import java.util.Map.Entry;
public class VmwareResource implements StoragePoolResource, ServerResource, VmwareHostService { public class VmwareResource implements StoragePoolResource, ServerResource, VmwareHostService {
private static final Logger s_logger = Logger.getLogger(VmwareResource.class); private static final Logger s_logger = Logger.getLogger(VmwareResource.class);
@ -593,7 +601,6 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
try { try {
VmwareHypervisorHost hyperHost = getHyperHost(getServiceContext()); VmwareHypervisorHost hyperHost = getHyperHost(getServiceContext());
ManagedObjectReference morDc = hyperHost.getHyperHostDatacenter();
// find VM through datacenter (VM is not at the target host yet) // find VM through datacenter (VM is not at the target host yet)
VirtualMachineMO vmMo = hyperHost.findVmOnPeerHyperHost(vmName); VirtualMachineMO vmMo = hyperHost.findVmOnPeerHyperHost(vmName);
if (vmMo == null) { if (vmMo == null) {
@ -3244,7 +3251,7 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
HashMap<String, State> newStates = getVmStates(); HashMap<String, State> newStates = getVmStates();
List<String> requestedVmNames = cmd.getVmNames(); List<String> requestedVmNames = cmd.getVmNames();
List<String> vmNames = new ArrayList(); List<String> vmNames = new ArrayList<String>();
if (requestedVmNames != null) { if (requestedVmNames != null) {
for (String vmName : requestedVmNames) { for (String vmName : requestedVmNames) {
@ -3750,8 +3757,6 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
s_logger.info("Executing resource MigrateVolumeCommand: " + _gson.toJson(cmd)); s_logger.info("Executing resource MigrateVolumeCommand: " + _gson.toJson(cmd));
} }
VmwareContext context = getServiceContext();
VmwareManager mgr = context.getStockObject(VmwareManager.CONTEXT_STOCK_NAME);
final String vmName = volMgr.getVmNameFromVolumeId(cmd.getVolumeId()); final String vmName = volMgr.getVmNameFromVolumeId(cmd.getVolumeId());
VirtualMachineMO vmMo = null; VirtualMachineMO vmMo = null;
@ -3903,6 +3908,45 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
} }
} }
protected ManagedObjectReference handleDatastoreAndVmdk(AttachVolumeCommand cmd) throws Exception {
ManagedObjectReference morDs = null;
VmwareContext context = getServiceContext();
VmwareHypervisorHost hyperHost = getHyperHost(context);
String iqn = cmd.get_iScsiName();
if (cmd.getAttach()) {
morDs = createVmfsDatastore(hyperHost, iqn,
cmd.getStorageHost(), cmd.getStoragePort(), iqn,
cmd.getChapInitiatorUsername(), cmd.getChapInitiatorPassword(),
cmd.getChapTargetUsername(), cmd.getChapTargetPassword());
DatastoreMO dsMo = new DatastoreMO(context, morDs);
String volumeDatastorePath = String.format("[%s] %s.vmdk", dsMo.getName(), dsMo.getName());
if (!dsMo.fileExists(volumeDatastorePath)) {
String dummyVmName = getWorkerName(context, cmd, 0);
VirtualMachineMO vmMo = prepareVolumeHostDummyVm(hyperHost, dsMo, dummyVmName);
if (vmMo == null) {
throw new Exception("Unable to create a dummy VM for volume creation");
}
vmMo.createDisk(volumeDatastorePath, (int)(dsMo.getSummary().getFreeSpace() / (1024L * 1024L)),
morDs, vmMo.getScsiDeviceControllerKey());
vmMo.detachDisk(volumeDatastorePath, false);
}
}
else {
deleteVmfsDatastore(hyperHost, iqn, cmd.getStorageHost(), cmd.getStoragePort(), iqn);
}
return morDs;
}
protected Answer execute(AttachVolumeCommand cmd) { protected Answer execute(AttachVolumeCommand cmd) {
if (s_logger.isInfoEnabled()) { if (s_logger.isInfoEnabled()) {
s_logger.info("Executing resource AttachVolumeCommand: " + _gson.toJson(cmd)); s_logger.info("Executing resource AttachVolumeCommand: " + _gson.toJson(cmd));
@ -3922,7 +3966,15 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
throw new Exception(msg); throw new Exception(msg);
} }
ManagedObjectReference morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, cmd.getPoolUuid()); ManagedObjectReference morDs = null;
if (cmd.getAttach() && cmd.isManaged()) {
morDs = handleDatastoreAndVmdk(cmd);
}
else {
morDs = HypervisorHostHelper.findDatastoreWithBackwardsCompatibility(hyperHost, cmd.getPoolUuid());
}
if (morDs == null) { if (morDs == null) {
String msg = "Unable to find the mounted datastore to execute AttachVolumeCommand, vmName: " + cmd.getVmName(); String msg = "Unable to find the mounted datastore to execute AttachVolumeCommand, vmName: " + cmd.getVmName();
s_logger.error(msg); s_logger.error(msg);
@ -3933,12 +3985,16 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
String datastoreVolumePath = dsMo.searchFileInSubFolders(cmd.getVolumePath() + ".vmdk", true); String datastoreVolumePath = dsMo.searchFileInSubFolders(cmd.getVolumePath() + ".vmdk", true);
assert (datastoreVolumePath != null) : "Virtual disk file must exist in specified datastore for attach/detach operations."; assert (datastoreVolumePath != null) : "Virtual disk file must exist in specified datastore for attach/detach operations.";
AttachVolumeAnswer answer = new AttachVolumeAnswer(cmd, cmd.getDeviceId()); AttachVolumeAnswer answer = new AttachVolumeAnswer(cmd, cmd.getDeviceId(), datastoreVolumePath);
if (cmd.getAttach()) { if (cmd.getAttach()) {
vmMo.attachDisk(new String[] { datastoreVolumePath }, morDs); vmMo.attachDisk(new String[] { datastoreVolumePath }, morDs);
} else { } else {
vmMo.removeAllSnapshots(); vmMo.removeAllSnapshots();
vmMo.detachDisk(datastoreVolumePath, false); vmMo.detachDisk(datastoreVolumePath, false);
if (cmd.isManaged()) {
handleDatastoreAndVmdk(cmd);
}
} }
return answer; return answer;
@ -3954,6 +4010,198 @@ public class VmwareResource implements StoragePoolResource, ServerResource, Vmwa
} }
} }
private ManagedObjectReference createVmfsDatastore(VmwareHypervisorHost hyperHost, String datastoreName, String storageIpAddress,
int storagePortNumber, String iqn, String chapName, String chapSecret, String mutualChapName, String mutualChapSecret) throws Exception {
VmwareContext context = getServiceContext();
ManagedObjectReference morCluster = hyperHost.getHyperHostCluster();
ClusterMO cluster = new ClusterMO(context, morCluster);
List<Pair<ManagedObjectReference, String>> lstHosts = cluster.getClusterHosts();
HostInternetScsiHbaStaticTarget target = new HostInternetScsiHbaStaticTarget();
target.setAddress(storageIpAddress);
target.setPort(storagePortNumber);
target.setIScsiName(iqn);
HostInternetScsiHbaAuthenticationProperties auth = new HostInternetScsiHbaAuthenticationProperties();
String strAuthType = "chapRequired";
auth.setChapAuthEnabled(true);
auth.setChapInherited(false);
auth.setChapAuthenticationType(strAuthType);
auth.setChapName(chapName);
auth.setChapSecret(chapSecret);
auth.setMutualChapInherited(false);
auth.setMutualChapAuthenticationType(strAuthType);
auth.setMutualChapName(mutualChapName);
auth.setMutualChapSecret(mutualChapSecret);
target.setAuthenticationProperties(auth);
final List<HostInternetScsiHbaStaticTarget> lstTargets = new ArrayList<HostInternetScsiHbaStaticTarget>();
lstTargets.add(target);
HostDatastoreSystemMO hostDatastoreSystem = null;
HostStorageSystemMO hostStorageSystem = null;
final List<Thread> threads = new ArrayList<Thread>();
final List<Exception> exceptions = new ArrayList<Exception>();
for (Pair<ManagedObjectReference, String> hostPair : lstHosts) {
HostMO host = new HostMO(context, hostPair.first());
hostDatastoreSystem = host.getHostDatastoreSystemMO();
hostStorageSystem = host.getHostStorageSystemMO();
boolean iScsiHbaConfigured = false;
for (HostHostBusAdapter hba : hostStorageSystem.getStorageDeviceInfo().getHostBusAdapter()) {
if (hba instanceof HostInternetScsiHba) {
// just finding an instance of HostInternetScsiHba means that we have found at least one configured iSCSI HBA
// at least one iSCSI HBA must be configured before a CloudStack user can use this host for iSCSI storage
iScsiHbaConfigured = true;
final String iScsiHbaDevice = hba.getDevice();
final HostStorageSystemMO hss = hostStorageSystem;
threads.add(new Thread() {
public void run() {
try {
hss.addInternetScsiStaticTargets(iScsiHbaDevice, lstTargets);
hss.rescanHba(iScsiHbaDevice);
}
catch (Exception ex) {
synchronized (exceptions) {
exceptions.add(ex);
}
}
}
});
}
}
if (!iScsiHbaConfigured) {
throw new Exception("An iSCSI HBA must be configured before a host can use iSCSI storage.");
}
}
for (Thread thread : threads) {
thread.start();
}
for (Thread thread : threads) {
thread.join();
}
if (exceptions.size() > 0) {
throw new Exception(exceptions.get(0).getMessage());
}
ManagedObjectReference morDs = hostDatastoreSystem.findDatastore(iqn);
if (morDs != null) {
return morDs;
}
List<HostScsiDisk> lstHostScsiDisks = hostDatastoreSystem.queryAvailableDisksForVmfs();
HostScsiDisk hostScsiDisk = getHostScsiDisk(hostStorageSystem.getStorageDeviceInfo().getScsiTopology(), lstHostScsiDisks, iqn);
if (hostScsiDisk == null) {
throw new Exception("A relevant SCSI disk could not be located to use to create a datastore.");
}
return hostDatastoreSystem.createVmfsDatastore(datastoreName, hostScsiDisk);
}
// the purpose of this method is to find the HostScsiDisk in the passed-in array that exists (if any) because
// we added the static iqn to an iSCSI HBA
private static HostScsiDisk getHostScsiDisk(HostScsiTopology hst, List<HostScsiDisk> lstHostScsiDisks, String iqn) {
for (HostScsiTopologyInterface adapter : hst.getAdapter()) {
if (adapter.getTarget() != null) {
for (HostScsiTopologyTarget target : adapter.getTarget()) {
if (target.getTransport() instanceof HostInternetScsiTargetTransport) {
String iScsiName = ((HostInternetScsiTargetTransport)target.getTransport()).getIScsiName();
if (iqn.equals(iScsiName)) {
for (HostScsiDisk hostScsiDisk : lstHostScsiDisks) {
for (HostScsiTopologyLun hstl : target.getLun()) {
if (hstl.getScsiLun().contains(hostScsiDisk.getUuid())) {
return hostScsiDisk;
}
}
}
}
}
}
}
}
return null;
}
private void deleteVmfsDatastore(VmwareHypervisorHost hyperHost, String volumeUuid,
String storageIpAddress, int storagePortNumber, String iqn) throws Exception {
// hyperHost.unmountDatastore(volumeUuid);
VmwareContext context = getServiceContext();
ManagedObjectReference morCluster = hyperHost.getHyperHostCluster();
ClusterMO cluster = new ClusterMO(context, morCluster);
List<Pair<ManagedObjectReference, String>> lstHosts = cluster.getClusterHosts();
HostInternetScsiHbaStaticTarget target = new HostInternetScsiHbaStaticTarget();
target.setAddress(storageIpAddress);
target.setPort(storagePortNumber);
target.setIScsiName(iqn);
final List<HostInternetScsiHbaStaticTarget> lstTargets = new ArrayList<HostInternetScsiHbaStaticTarget>();
lstTargets.add(target);
final List<Thread> threads = new ArrayList<Thread>();
final List<Exception> exceptions = new ArrayList<Exception>();
for (Pair<ManagedObjectReference, String> hostPair : lstHosts) {
final HostMO host = new HostMO(context, hostPair.first());
final HostStorageSystemMO hostStorageSystem = host.getHostStorageSystemMO();
for (HostHostBusAdapter hba : hostStorageSystem.getStorageDeviceInfo().getHostBusAdapter()) {
if (hba instanceof HostInternetScsiHba) {
final String iScsiHbaDevice = hba.getDevice();
Thread thread = new Thread() {
public void run() {
try {
hostStorageSystem.removeInternetScsiStaticTargets(iScsiHbaDevice, lstTargets);
hostStorageSystem.rescanHba(iScsiHbaDevice);
}
catch (Exception ex) {
exceptions.add(ex);
}
}
};
threads.add(thread);
thread.start();
}
}
}
for (Thread thread : threads) {
thread.join();
}
if (exceptions.size() > 0) {
throw new Exception(exceptions.get(0).getMessage());
}
}
protected Answer execute(AttachIsoCommand cmd) { protected Answer execute(AttachIsoCommand cmd) {
if (s_logger.isInfoEnabled()) { if (s_logger.isInfoEnabled()) {
s_logger.info("Executing resource AttachIsoCommand: " + _gson.toJson(cmd)); s_logger.info("Executing resource AttachIsoCommand: " + _gson.toJson(cmd));

View File

@ -4172,11 +4172,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
for (VIF vif : vifs) { for (VIF vif : vifs) {
networks.add(vif.getNetwork(conn)); networks.add(vif.getNetwork(conn));
} }
List<VDI> vdis = getVdis(conn, vm);
vm.destroy(conn); vm.destroy(conn);
for( VDI vdi : vdis ){
umount(conn, vdi);
}
state = State.Stopped; state = State.Stopped;
SR sr = getISOSRbyVmName(conn, cmd.getVmName()); SR sr = getISOSRbyVmName(conn, cmd.getVmName());
removeSR(conn, sr); removeSR(conn, sr);
@ -4479,7 +4475,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
throw new CloudRuntimeException("Could not find available VIF slot in VM with name: " + vmName); throw new CloudRuntimeException("Could not find available VIF slot in VM with name: " + vmName);
} }
protected VDI mount(Connection conn, StoragePoolType pooltype, String volumeFolder, String volumePath) { protected VDI mount(Connection conn, StoragePoolType poolType, String volumeFolder, String volumePath) {
return getVDIbyUuid(conn, volumePath); return getVDIbyUuid(conn, volumePath);
} }
@ -5549,7 +5545,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
if (pool.getType() == StoragePoolType.NetworkFilesystem) { if (pool.getType() == StoragePoolType.NetworkFilesystem) {
getNfsSR(conn, pool); getNfsSR(conn, pool);
} else if (pool.getType() == StoragePoolType.IscsiLUN) { } else if (pool.getType() == StoragePoolType.IscsiLUN) {
getIscsiSR(conn, pool); getIscsiSR(conn, pool.getUuid(), pool.getHost(), pool.getPath(), null, null, new Boolean[1]);
} else if (pool.getType() == StoragePoolType.PreSetup) { } else if (pool.getType() == StoragePoolType.PreSetup) {
} else { } else {
return new Answer(cmd, false, "The pool type: " + pool.getType().name() + " is not supported."); return new Answer(cmd, false, "The pool type: " + pool.getType().name() + " is not supported.");
@ -6229,7 +6225,6 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
public Answer execute(ResizeVolumeCommand cmd) { public Answer execute(ResizeVolumeCommand cmd) {
Connection conn = getConnection(); Connection conn = getConnection();
StorageFilerTO pool = cmd.getPool();
String volid = cmd.getPath(); String volid = cmd.getPath();
long newSize = cmd.getNewSize(); long newSize = cmd.getNewSize();
@ -6367,19 +6362,18 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
} }
} }
protected SR getIscsiSR(Connection conn, StorageFilerTO pool) { protected SR getIscsiSR(Connection conn, String srNameLabel, String target, String path,
synchronized (pool.getUuid().intern()) { String chapInitiatorUsername, String chapInitiatorPassword, Boolean[] created) {
synchronized (srNameLabel.intern()) {
Map<String, String> deviceConfig = new HashMap<String, String>(); Map<String, String> deviceConfig = new HashMap<String, String>();
try { try {
String target = pool.getHost();
String path = pool.getPath();
if (path.endsWith("/")) { if (path.endsWith("/")) {
path = path.substring(0, path.length() - 1); path = path.substring(0, path.length() - 1);
} }
String tmp[] = path.split("/"); String tmp[] = path.split("/");
if (tmp.length != 3) { if (tmp.length != 3) {
String msg = "Wrong iscsi path " + pool.getPath() + " it should be /targetIQN/LUN"; String msg = "Wrong iscsi path " + path + " it should be /targetIQN/LUN";
s_logger.warn(msg); s_logger.warn(msg);
throw new CloudRuntimeException(msg); throw new CloudRuntimeException(msg);
} }
@ -6387,7 +6381,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
String lunid = tmp[2].trim(); String lunid = tmp[2].trim();
String scsiid = ""; String scsiid = "";
Set<SR> srs = SR.getByNameLabel(conn, pool.getUuid()); Set<SR> srs = SR.getByNameLabel(conn, srNameLabel);
for (SR sr : srs) { for (SR sr : srs) {
if (!SRType.LVMOISCSI.equals(sr.getType(conn))) { if (!SRType.LVMOISCSI.equals(sr.getType(conn))) {
continue; continue;
@ -6412,19 +6406,24 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
} }
if (target.equals(dc.get("target")) && targetiqn.equals(dc.get("targetIQN")) && lunid.equals(dc.get("lunid"))) { if (target.equals(dc.get("target")) && targetiqn.equals(dc.get("targetIQN")) && lunid.equals(dc.get("lunid"))) {
throw new CloudRuntimeException("There is a SR using the same configuration target:" + dc.get("target") + ", targetIQN:" throw new CloudRuntimeException("There is a SR using the same configuration target:" + dc.get("target") + ", targetIQN:"
+ dc.get("targetIQN") + ", lunid:" + dc.get("lunid") + " for pool " + pool.getUuid() + "on host:" + _host.uuid); + dc.get("targetIQN") + ", lunid:" + dc.get("lunid") + " for pool " + srNameLabel + "on host:" + _host.uuid);
} }
} }
deviceConfig.put("target", target); deviceConfig.put("target", target);
deviceConfig.put("targetIQN", targetiqn); deviceConfig.put("targetIQN", targetiqn);
if (StringUtils.isNotBlank(chapInitiatorUsername) &&
StringUtils.isNotBlank(chapInitiatorPassword)) {
deviceConfig.put("chapuser", chapInitiatorUsername);
deviceConfig.put("chappassword", chapInitiatorPassword);
}
Host host = Host.getByUuid(conn, _host.uuid); Host host = Host.getByUuid(conn, _host.uuid);
Map<String, String> smConfig = new HashMap<String, String>(); Map<String, String> smConfig = new HashMap<String, String>();
String type = SRType.LVMOISCSI.toString(); String type = SRType.LVMOISCSI.toString();
String poolId = Long.toString(pool.getId());
SR sr = null; SR sr = null;
try { try {
sr = SR.create(conn, host, deviceConfig, new Long(0), pool.getUuid(), poolId, type, "user", true, sr = SR.create(conn, host, deviceConfig, new Long(0), srNameLabel, srNameLabel, type, "user", true,
smConfig); smConfig);
} catch (XenAPIException e) { } catch (XenAPIException e) {
String errmsg = e.toString(); String errmsg = e.toString();
@ -6463,19 +6462,30 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
if( result.indexOf("<UUID>") != -1) { if( result.indexOf("<UUID>") != -1) {
pooluuid = result.substring(result.indexOf("<UUID>") + 6, result.indexOf("</UUID>")).trim(); pooluuid = result.substring(result.indexOf("<UUID>") + 6, result.indexOf("</UUID>")).trim();
} }
if( pooluuid == null || pooluuid.length() != 36) {
sr = SR.create(conn, host, deviceConfig, new Long(0), pool.getUuid(), poolId, type, "user", true, if (pooluuid == null || pooluuid.length() != 36)
{
sr = SR.create(conn, host, deviceConfig, new Long(0), srNameLabel, srNameLabel, type, "user", true,
smConfig); smConfig);
created[0] = true; // note that the SR was created (as opposed to introduced)
} else { } else {
sr = SR.introduce(conn, pooluuid, pool.getUuid(), poolId, sr = SR.introduce(conn, pooluuid, srNameLabel, srNameLabel,
type, "user", true, smConfig); type, "user", true, smConfig);
Pool.Record pRec = XenServerConnectionPool.getPoolRecord(conn);
PBD.Record rec = new PBD.Record(); Set<Host> setHosts = Host.getAll(conn);
rec.deviceConfig = deviceConfig;
rec.host = pRec.master; for (Host currentHost : setHosts) {
rec.SR = sr; PBD.Record rec = new PBD.Record();
PBD pbd = PBD.create(conn, rec);
pbd.plug(conn); rec.deviceConfig = deviceConfig;
rec.host = currentHost;
rec.SR = sr;
PBD pbd = PBD.create(conn, rec);
pbd.plug(conn);
}
} }
sr.scan(conn); sr.scan(conn);
return sr; return sr;
@ -6636,6 +6646,52 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
} }
} }
// for about 1 GiB of physical size, about 4 MiB seems to be used for metadata
private long getMetadata(long physicalSize) {
return (long)(physicalSize * 0.00390625); // 1 GiB / 4 MiB = 0.00390625
}
protected VDI handleSrAndVdiAttach(String iqn, String storageHostName,
String chapInitiatorName, String chapInitiatorPassword) throws Exception {
VDI vdi = null;
Connection conn = getConnection();
Boolean[] created = { false };
SR sr = getIscsiSR(conn, iqn,
storageHostName, iqn,
chapInitiatorName, chapInitiatorPassword, created);
// if created[0] is true, this means the SR was actually created...as opposed to introduced
if (created[0]) {
VDI.Record vdir = new VDI.Record();
vdir.nameLabel = iqn;
vdir.SR = sr;
vdir.type = Types.VdiType.USER;
vdir.virtualSize = sr.getPhysicalSize(conn) - sr.getPhysicalUtilisation(conn) - getMetadata(sr.getPhysicalSize(conn));
if (vdir.virtualSize < 0) {
throw new Exception("VDI virtual size cannot be less than 0.");
}
vdi = VDI.create(conn, vdir);
}
else {
vdi = sr.getVDIs(conn).iterator().next();
}
return vdi;
}
protected void handleSrAndVdiDetach(String iqn) throws Exception {
Connection conn = getConnection();
SR sr = getStorageRepository(conn, iqn);
removeSR(conn, sr);
}
protected AttachVolumeAnswer execute(final AttachVolumeCommand cmd) { protected AttachVolumeAnswer execute(final AttachVolumeCommand cmd) {
Connection conn = getConnection(); Connection conn = getConnection();
@ -6652,7 +6708,16 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
try { try {
// Look up the VDI // Look up the VDI
VDI vdi = mount(conn, cmd.getPooltype(), cmd.getVolumeFolder(),cmd.getVolumePath()); VDI vdi = null;
if (cmd.getAttach() && cmd.isManaged()) {
vdi = handleSrAndVdiAttach(cmd.get_iScsiName(), cmd.getStorageHost(),
cmd.getChapInitiatorUsername(), cmd.getChapInitiatorPassword());
}
else {
vdi = getVDIbyUuid(conn, cmd.getVolumePath());
}
// Look up the VM // Look up the VM
VM vm = getVM(conn, vmName); VM vm = getVM(conn, vmName);
/* For HVM guest, if no pv driver installed, no attach/detach */ /* For HVM guest, if no pv driver installed, no attach/detach */
@ -6704,7 +6769,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
// Update the VDI's label to include the VM name // Update the VDI's label to include the VM name
vdi.setNameLabel(conn, vmName + "-DATA"); vdi.setNameLabel(conn, vmName + "-DATA");
return new AttachVolumeAnswer(cmd, Long.parseLong(diskNumber)); return new AttachVolumeAnswer(cmd, Long.parseLong(diskNumber), vdi.getUuid(conn));
} else { } else {
// Look up all VBDs for this VDI // Look up all VBDs for this VDI
Set<VBD> vbds = vdi.getVBDs(conn); Set<VBD> vbds = vdi.getVBDs(conn);
@ -6723,7 +6788,9 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
// Update the VDI's label to be "detached" // Update the VDI's label to be "detached"
vdi.setNameLabel(conn, "detached"); vdi.setNameLabel(conn, "detached");
umount(conn, vdi); if (cmd.isManaged()) {
handleSrAndVdiDetach(cmd.get_iScsiName());
}
return new AttachVolumeAnswer(cmd); return new AttachVolumeAnswer(cmd);
} }
@ -7606,30 +7673,30 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
} }
} }
protected SR getStorageRepository(Connection conn, String uuid) { protected SR getStorageRepository(Connection conn, String srNameLabel) {
Set<SR> srs; Set<SR> srs;
try { try {
srs = SR.getByNameLabel(conn, uuid); srs = SR.getByNameLabel(conn, srNameLabel);
} catch (XenAPIException e) { } catch (XenAPIException e) {
throw new CloudRuntimeException("Unable to get SR " + uuid + " due to " + e.toString(), e); throw new CloudRuntimeException("Unable to get SR " + srNameLabel + " due to " + e.toString(), e);
} catch (Exception e) { } catch (Exception e) {
throw new CloudRuntimeException("Unable to get SR " + uuid + " due to " + e.getMessage(), e); throw new CloudRuntimeException("Unable to get SR " + srNameLabel + " due to " + e.getMessage(), e);
} }
if (srs.size() > 1) { if (srs.size() > 1) {
throw new CloudRuntimeException("More than one storage repository was found for pool with uuid: " + uuid); throw new CloudRuntimeException("More than one storage repository was found for pool with uuid: " + srNameLabel);
} else if (srs.size() == 1) { } else if (srs.size() == 1) {
SR sr = srs.iterator().next(); SR sr = srs.iterator().next();
if (s_logger.isDebugEnabled()) { if (s_logger.isDebugEnabled()) {
s_logger.debug("SR retrieved for " + uuid); s_logger.debug("SR retrieved for " + srNameLabel);
} }
if (checkSR(conn, sr)) { if (checkSR(conn, sr)) {
return sr; return sr;
} }
throw new CloudRuntimeException("SR check failed for storage pool: " + uuid + "on host:" + _host.uuid); throw new CloudRuntimeException("SR check failed for storage pool: " + srNameLabel + "on host:" + _host.uuid);
} else { } else {
throw new CloudRuntimeException("Can not see storage pool: " + uuid + " from on host:" + _host.uuid); throw new CloudRuntimeException("Can not see storage pool: " + srNameLabel + " from on host:" + _host.uuid);
} }
} }

View File

@ -55,7 +55,6 @@ import org.apache.xmlrpc.XmlRpcException;
import com.cloud.agent.api.Answer; import com.cloud.agent.api.Answer;
import com.cloud.agent.api.CreateStoragePoolCommand; import com.cloud.agent.api.CreateStoragePoolCommand;
import com.cloud.agent.api.CreateVolumeFromSnapshotAnswer;
import com.cloud.agent.api.to.DataObjectType; import com.cloud.agent.api.to.DataObjectType;
import com.cloud.agent.api.to.DataStoreTO; import com.cloud.agent.api.to.DataStoreTO;
import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.DataTO;
@ -171,7 +170,16 @@ public class XenServerStorageProcessor implements StorageProcessor {
try { try {
Connection conn = this.hypervisorResource.getConnection(); Connection conn = this.hypervisorResource.getConnection();
// Look up the VDI // Look up the VDI
VDI vdi = this.hypervisorResource.mount(conn, null, null, data.getPath()); VDI vdi = null;
if (cmd.isManaged()) {
vdi = this.hypervisorResource.handleSrAndVdiAttach(cmd.get_iScsiName(), cmd.getStorageHost(),
cmd.getChapInitiatorUsername(), cmd.getChapInitiatorPassword());
}
else {
vdi = this.hypervisorResource.mount(conn, null, null, data.getPath());
}
// Look up the VM // Look up the VM
VM vm = this.hypervisorResource.getVM(conn, vmName); VM vm = this.hypervisorResource.getVM(conn, vmName);
/* For HVM guest, if no pv driver installed, no attach/detach */ /* For HVM guest, if no pv driver installed, no attach/detach */
@ -223,7 +231,7 @@ public class XenServerStorageProcessor implements StorageProcessor {
// Update the VDI's label to include the VM name // Update the VDI's label to include the VM name
vdi.setNameLabel(conn, vmName + "-DATA"); vdi.setNameLabel(conn, vmName + "-DATA");
DiskTO newDisk = new DiskTO(disk.getData(), Long.parseLong(diskNumber), disk.getType()); DiskTO newDisk = new DiskTO(disk.getData(), Long.parseLong(diskNumber), vdi.getUuid(conn), disk.getType());
return new AttachAnswer(newDisk); return new AttachAnswer(newDisk);
} catch (XenAPIException e) { } catch (XenAPIException e) {
@ -350,6 +358,10 @@ public class XenServerStorageProcessor implements StorageProcessor {
this.hypervisorResource.umount(conn, vdi); this.hypervisorResource.umount(conn, vdi);
if (cmd.isManaged()) {
this.hypervisorResource.handleSrAndVdiDetach(cmd.get_iScsiName());
}
return new DettachAnswer(disk); return new DettachAnswer(disk);
} catch(Exception e) { } catch(Exception e) {
s_logger.warn("Failed dettach volume: " + data.getPath()); s_logger.warn("Failed dettach volume: " + data.getPath());

View File

@ -95,7 +95,12 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri
} }
@Override @Override
public void createAsync(DataObject data, AsyncCompletionCallback<CreateCmdResult> callback) { public ChapInfo getChapInfo(VolumeInfo volumeInfo) {
return null;
}
@Override
public void createAsync(DataStore dataStore, DataObject data, AsyncCompletionCallback<CreateCmdResult> callback) {
String errMsg = null; String errMsg = null;
Answer answer = null; Answer answer = null;
if (data.getType() == DataObjectType.VOLUME) { if (data.getType() == DataObjectType.VOLUME) {
@ -118,7 +123,7 @@ public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDri
} }
@Override @Override
public void deleteAsync(DataObject data, AsyncCompletionCallback<CommandResult> callback) { public void deleteAsync(DataStore dataStore, DataObject data, AsyncCompletionCallback<CommandResult> callback) {
DeleteCommand cmd = new DeleteCommand(data.getTO()); DeleteCommand cmd = new DeleteCommand(data.getTO());
CommandResult result = new CommandResult(); CommandResult result = new CommandResult();

View File

@ -54,6 +54,11 @@ public class SamplePrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
return null; return null;
} }
@Override
public ChapInfo getChapInfo(VolumeInfo volumeInfo) {
return null;
}
private class CreateVolumeContext<T> extends AsyncRpcConext<T> { private class CreateVolumeContext<T> extends AsyncRpcConext<T> {
private final DataObject volume; private final DataObject volume;
public CreateVolumeContext(AsyncCompletionCallback<T> callback, DataObject volume) { public CreateVolumeContext(AsyncCompletionCallback<T> callback, DataObject volume) {
@ -77,7 +82,7 @@ public class SamplePrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
} }
@Override @Override
public void deleteAsync(DataObject vo, AsyncCompletionCallback<CommandResult> callback) { public void deleteAsync(DataStore dataStore, DataObject vo, AsyncCompletionCallback<CommandResult> callback) {
/* /*
* DeleteCommand cmd = new DeleteCommand(vo.getUri()); * DeleteCommand cmd = new DeleteCommand(vo.getUri());
* *
@ -146,7 +151,7 @@ public class SamplePrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver
*/ */
@Override @Override
public void createAsync(DataObject vol, AsyncCompletionCallback<CreateCmdResult> callback) { public void createAsync(DataStore dataStore, DataObject vol, AsyncCompletionCallback<CreateCmdResult> callback) {
EndPoint ep = selector.select(vol); EndPoint ep = selector.select(vol);
CreateObjectCommand createCmd = new CreateObjectCommand(null); CreateObjectCommand createCmd = new CreateObjectCommand(null);

View File

@ -12,7 +12,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>cloud-plugin-storage-volume-solidfire</artifactId> <artifactId>cloud-plugin-storage-volume-solidfire</artifactId>
<name>Apache CloudStack Plugin - Storage Volume solidfire</name> <name>Apache CloudStack Plugin - Storage Volume SolidFire Provider</name>
<parent> <parent>
<groupId>org.apache.cloudstack</groupId> <groupId>org.apache.cloudstack</groupId>
<artifactId>cloudstack-plugins</artifactId> <artifactId>cloudstack-plugins</artifactId>
@ -31,6 +31,11 @@
<version>${cs.mysql.version}</version> <version>${cs.mysql.version}</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>${cs.gson.version}</version>
</dependency>
</dependencies> </dependencies>
<build> <build>
<defaultGoal>install</defaultGoal> <defaultGoal>install</defaultGoal>

View File

@ -16,13 +16,46 @@
// under the License. // under the License.
package org.apache.cloudstack.storage.datastore.driver; package org.apache.cloudstack.storage.datastore.driver;
import com.cloud.agent.api.to.DataStoreTO; import java.util.List;
import com.cloud.agent.api.to.DataTO; import java.util.Set;
import javax.inject.Inject;
import org.apache.cloudstack.engine.subsystem.api.storage.*; import org.apache.cloudstack.engine.subsystem.api.storage.*;
import org.apache.cloudstack.framework.async.AsyncCompletionCallback; import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
import org.apache.cloudstack.storage.command.CommandResult; import org.apache.cloudstack.storage.command.CommandResult;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.storage.datastore.util.SolidFireUtil;
import org.apache.log4j.Logger;
import com.cloud.agent.api.Answer;
import com.cloud.agent.api.to.DataObjectType;
import com.cloud.agent.api.to.DataStoreTO;
import com.cloud.agent.api.to.DataTO;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.exception.StorageUnavailableException;
import com.cloud.storage.Storage.StoragePoolType;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.VolumeDao;
import com.cloud.storage.dao.VolumeDetailsDao;
import com.cloud.user.AccountVO;
import com.cloud.user.AccountDetailsDao;
import com.cloud.user.AccountDetailVO;
import com.cloud.user.dao.AccountDao;
public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver { public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
private static final Logger s_logger = Logger.getLogger(SolidfirePrimaryDataStoreDriver.class);
@Inject private PrimaryDataStoreDao _storagePoolDao;
@Inject private StoragePoolDetailsDao _storagePoolDetailsDao;
@Inject private VolumeDao _volumeDao;
@Inject private VolumeDetailsDao _volumeDetailsDao;
@Inject private DataCenterDao _zoneDao;
@Inject private AccountDao _accountDao;
@Inject private AccountDetailsDao _accountDetailsDao;
@Override @Override
public DataTO getTO(DataObject data) { public DataTO getTO(DataObject data) {
@ -34,12 +67,450 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
return null; return null;
} }
@Override private static class SolidFireConnection {
public void createAsync(DataObject data, AsyncCompletionCallback<CreateCmdResult> callback) { private final String _managementVip;
private final int _managementPort;
private final String _clusterAdminUsername;
private final String _clusterAdminPassword;
public SolidFireConnection(String managementVip, int managementPort,
String clusterAdminUsername, String clusterAdminPassword) {
_managementVip = managementVip;
_managementPort = managementPort;
_clusterAdminUsername = clusterAdminUsername;
_clusterAdminPassword = clusterAdminPassword;
}
public String getManagementVip() {
return _managementVip;
}
public int getManagementPort() {
return _managementPort;
}
public String getClusterAdminUsername() {
return _clusterAdminUsername;
}
public String getClusterAdminPassword() {
return _clusterAdminPassword;
}
}
private SolidFireConnection getSolidFireConnection(long storagePoolId) {
StoragePoolDetailVO storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.MANAGEMENT_VIP);
String mVip = storagePoolDetail.getValue();
storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.MANAGEMENT_PORT);
int mPort = Integer.parseInt(storagePoolDetail.getValue());
storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_ADMIN_USERNAME);
String clusterAdminUsername = storagePoolDetail.getValue();
storagePoolDetail = _storagePoolDetailsDao.findDetail(storagePoolId, SolidFireUtil.CLUSTER_ADMIN_PASSWORD);
String clusterAdminPassword = storagePoolDetail.getValue();
return new SolidFireConnection(mVip, mPort, clusterAdminUsername, clusterAdminPassword);
}
private SolidFireUtil.SolidFireAccount createSolidFireAccount(String sfAccountName,
SolidFireConnection sfConnection) {
try {
String mVip = sfConnection.getManagementVip();
int mPort = sfConnection.getManagementPort();
String clusterAdminUsername = sfConnection.getClusterAdminUsername();
String clusterAdminPassword = sfConnection.getClusterAdminPassword();
long accountNumber = SolidFireUtil.createSolidFireAccount(mVip, mPort,
clusterAdminUsername, clusterAdminPassword, sfAccountName);
return SolidFireUtil.getSolidFireAccountById(mVip, mPort,
clusterAdminUsername, clusterAdminPassword, accountNumber);
}
catch (Exception ex) {
throw new IllegalArgumentException(ex.getMessage());
}
}
private void updateCsDbWithAccountInfo(long csAccountId, SolidFireUtil.SolidFireAccount sfAccount) {
AccountDetailVO accountDetails = new AccountDetailVO(csAccountId,
SolidFireUtil.ACCOUNT_ID,
String.valueOf(sfAccount.getId()));
_accountDetailsDao.persist(accountDetails);
accountDetails = new AccountDetailVO(csAccountId,
SolidFireUtil.CHAP_INITIATOR_USERNAME,
String.valueOf(sfAccount.getName()));
_accountDetailsDao.persist(accountDetails);
accountDetails = new AccountDetailVO(csAccountId,
SolidFireUtil.CHAP_INITIATOR_SECRET,
String.valueOf(sfAccount.getInitiatorSecret()));
_accountDetailsDao.persist(accountDetails);
accountDetails = new AccountDetailVO(csAccountId,
SolidFireUtil.CHAP_TARGET_USERNAME,
sfAccount.getName());
_accountDetailsDao.persist(accountDetails);
accountDetails = new AccountDetailVO(csAccountId,
SolidFireUtil.CHAP_TARGET_SECRET,
sfAccount.getTargetSecret());
_accountDetailsDao.persist(accountDetails);
}
private class ChapInfoImpl implements ChapInfo {
private final String _initiatorUsername;
private final String _initiatorSecret;
private final String _targetUsername;
private final String _targetSecret;
public ChapInfoImpl(String initiatorUsername, String initiatorSecret,
String targetUsername, String targetSecret) {
_initiatorUsername = initiatorUsername;
_initiatorSecret = initiatorSecret;
_targetUsername = targetUsername;
_targetSecret = targetSecret;
}
public String getInitiatorUsername() {
return _initiatorUsername;
}
public String getInitiatorSecret() {
return _initiatorSecret;
}
public String getTargetUsername() {
return _targetUsername;
}
public String getTargetSecret() {
return _targetSecret;
}
} }
@Override @Override
public void deleteAsync(DataObject data, AsyncCompletionCallback<CommandResult> callback) { public ChapInfo getChapInfo(VolumeInfo volumeInfo) {
long accountId = volumeInfo.getAccountId();
AccountDetailVO accountDetail = _accountDetailsDao.findDetail(accountId, SolidFireUtil.CHAP_INITIATOR_USERNAME);
String chapInitiatorUsername = accountDetail.getValue();
accountDetail = _accountDetailsDao.findDetail(accountId, SolidFireUtil.CHAP_INITIATOR_SECRET);
String chapInitiatorSecret = accountDetail.getValue();
accountDetail = _accountDetailsDao.findDetail(accountId, SolidFireUtil.CHAP_TARGET_USERNAME);
String chapTargetUsername = accountDetail.getValue();
accountDetail = _accountDetailsDao.findDetail(accountId, SolidFireUtil.CHAP_TARGET_SECRET);
String chapTargetSecret = accountDetail.getValue();
return new ChapInfoImpl(chapInitiatorUsername, chapInitiatorSecret,
chapTargetUsername, chapTargetSecret);
}
private SolidFireUtil.SolidFireVolume createSolidFireVolume(VolumeInfo volumeInfo, SolidFireConnection sfConnection)
throws StorageUnavailableException, Exception
{
String mVip = sfConnection.getManagementVip();
int mPort = sfConnection.getManagementPort();
String clusterAdminUsername = sfConnection.getClusterAdminUsername();
String clusterAdminPassword = sfConnection.getClusterAdminPassword();
AccountDetailVO accountDetail = _accountDetailsDao.findDetail(volumeInfo.getAccountId(), SolidFireUtil.ACCOUNT_ID);
long sfAccountId = Long.parseLong(accountDetail.getValue());
final Iops iops;
Long minIops = volumeInfo.getMinIops();
Long maxIops = volumeInfo.getMaxIops();
if (minIops == null || minIops <= 0 ||
maxIops == null || maxIops <= 0) {
iops = new Iops(100, 15000);
}
else {
iops = new Iops(volumeInfo.getMinIops(), volumeInfo.getMaxIops());
}
long sfVolumeId = SolidFireUtil.createSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword,
volumeInfo.getName(), sfAccountId, volumeInfo.getSize(), true,
iops.getMinIops(), iops.getMaxIops(), iops.getBurstIops());
return SolidFireUtil.getSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolumeId);
}
private static class Iops
{
private final long _minIops;
private final long _maxIops;
private final long _burstIops;
public Iops(long minIops, long maxIops) throws Exception
{
if (minIops <= 0 || maxIops <= 0) {
throw new Exception("The 'Min IOPS' and 'Max IOPS' values must be greater than 0.");
}
if (minIops > maxIops) {
throw new Exception("The 'Min IOPS' value cannot exceed the 'Max IOPS' value.");
}
_minIops = minIops;
_maxIops = maxIops;
_burstIops = getBurstIops(_maxIops);
}
public long getMinIops()
{
return _minIops;
}
public long getMaxIops()
{
return _maxIops;
}
public long getBurstIops()
{
return _burstIops;
}
private static long getBurstIops(long maxIops)
{
return (long)(maxIops * 1.5);
}
}
private void deleteSolidFireVolume(VolumeInfo volumeInfo, SolidFireConnection sfConnection)
throws StorageUnavailableException, Exception
{
Long storagePoolId = volumeInfo.getPoolId();
if (storagePoolId == null) {
return; // this volume was never assigned to a storage pool, so no SAN volume should exist for it
}
String mVip = sfConnection.getManagementVip();
int mPort = sfConnection.getManagementPort();
String clusterAdminUsername = sfConnection.getClusterAdminUsername();
String clusterAdminPassword = sfConnection.getClusterAdminPassword();
long sfVolumeId = Long.parseLong(volumeInfo.getFolder());
SolidFireUtil.deleteSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolumeId);
}
private String getSfAccountName(String csAccountUuid, long csAccountId) {
return "CloudStack_" + csAccountUuid + "_" + getRandomNumber() + "_" + csAccountId;
}
private static long getRandomNumber()
{
return Math.round(Math.random() * 1000000000);
}
private boolean sfAccountExists(String sfAccountName, SolidFireConnection sfConnection) throws Exception {
String mVip = sfConnection.getManagementVip();
int mPort = sfConnection.getManagementPort();
String clusterAdminUsername = sfConnection.getClusterAdminUsername();
String clusterAdminPassword = sfConnection.getClusterAdminPassword();
try {
SolidFireUtil.getSolidFireAccountByName(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfAccountName);
}
catch (Exception ex) {
return false;
}
return true;
}
@Override
public void createAsync(DataStore dataStore, DataObject dataObject,
AsyncCompletionCallback<CreateCmdResult> callback) {
String iqn = null;
String errMsg = null;
if (dataObject.getType() == DataObjectType.VOLUME) {
try {
VolumeInfo volumeInfo = (VolumeInfo)dataObject;
AccountVO account = _accountDao.findById(volumeInfo.getAccountId());
String sfAccountName = getSfAccountName(account.getUuid(), account.getAccountId());
long storagePoolId = dataStore.getId();
SolidFireConnection sfConnection = getSolidFireConnection(storagePoolId);
if (!sfAccountExists(sfAccountName, sfConnection)) {
SolidFireUtil.SolidFireAccount sfAccount = createSolidFireAccount(sfAccountName,
sfConnection);
updateCsDbWithAccountInfo(account.getId(), sfAccount);
}
SolidFireUtil.SolidFireVolume sfVolume = createSolidFireVolume(volumeInfo, sfConnection);
iqn = sfVolume.getIqn();
VolumeVO volume = this._volumeDao.findById(volumeInfo.getId());
volume.set_iScsiName(iqn);
volume.setFolder(String.valueOf(sfVolume.getId()));
volume.setPoolType(StoragePoolType.IscsiLUN);
volume.setPoolId(storagePoolId);
_volumeDao.update(volume.getId(), volume);
StoragePoolVO storagePool = _storagePoolDao.findById(dataStore.getId());
long capacityBytes = storagePool.getCapacityBytes();
long usedBytes = storagePool.getUsedBytes();
usedBytes += volumeInfo.getSize();
if (usedBytes > capacityBytes) {
usedBytes = capacityBytes;
}
storagePool.setUsedBytes(usedBytes);
_storagePoolDao.update(storagePoolId, storagePool);
} catch (StorageUnavailableException e) {
s_logger.error("Failed to create volume (StorageUnavailableException)", e);
errMsg = e.toString();
} catch (Exception e) {
s_logger.error("Failed to create volume (Exception)", e);
errMsg = e.toString();
}
}
else {
errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to createAsync";
}
// path = iqn
// size is pulled from DataObject instance, if errMsg is null
CreateCmdResult result = new CreateCmdResult(iqn, new Answer(null, errMsg == null, errMsg));
result.setResult(errMsg);
callback.complete(result);
}
private void deleteSolidFireAccount(long sfAccountId, SolidFireConnection sfConnection) throws Exception {
String mVip = sfConnection.getManagementVip();
int mPort = sfConnection.getManagementPort();
String clusterAdminUsername = sfConnection.getClusterAdminUsername();
String clusterAdminPassword = sfConnection.getClusterAdminPassword();
List<SolidFireUtil.SolidFireVolume> sfVolumes = SolidFireUtil.getDeletedVolumes(mVip, mPort,
clusterAdminUsername, clusterAdminPassword);
// if there are volumes for this account in the trash, delete them (so the account can be deleted)
if (sfVolumes != null) {
for (SolidFireUtil.SolidFireVolume sfVolume : sfVolumes) {
if (sfVolume.getAccountId() == sfAccountId) {
SolidFireUtil.purgeSolidFireVolume(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfVolume.getId());
}
}
}
SolidFireUtil.deleteSolidFireAccount(mVip, mPort, clusterAdminUsername, clusterAdminPassword, sfAccountId);
}
private boolean sfAccountHasVolume(long sfAccountId, SolidFireConnection sfConnection) throws Exception {
String mVip = sfConnection.getManagementVip();
int mPort = sfConnection.getManagementPort();
String clusterAdminUsername = sfConnection.getClusterAdminUsername();
String clusterAdminPassword = sfConnection.getClusterAdminPassword();
List<SolidFireUtil.SolidFireVolume> sfVolumes = SolidFireUtil.getSolidFireVolumesForAccountId(mVip, mPort,
clusterAdminUsername, clusterAdminPassword, sfAccountId);
if (sfVolumes != null) {
for (SolidFireUtil.SolidFireVolume sfVolume : sfVolumes) {
if (sfVolume.isActive()) {
return true;
}
}
}
return false;
}
@Override
public void deleteAsync(DataStore dataStore, DataObject dataObject,
AsyncCompletionCallback<CommandResult> callback) {
String errMsg = null;
if (dataObject.getType() == DataObjectType.VOLUME) {
try {
VolumeInfo volumeInfo = (VolumeInfo)dataObject;
AccountVO account = _accountDao.findById(volumeInfo.getAccountId());
AccountDetailVO accountDetails = _accountDetailsDao.findDetail(account.getAccountId(), SolidFireUtil.ACCOUNT_ID);
long sfAccountId = Long.parseLong(accountDetails.getValue());
long storagePoolId = dataStore.getId();
SolidFireConnection sfConnection = getSolidFireConnection(storagePoolId);
deleteSolidFireVolume(volumeInfo, sfConnection);
_volumeDao.deleteVolumesByInstance(volumeInfo.getId());
if (!sfAccountHasVolume(sfAccountId, sfConnection)) {
// delete the account from the SolidFire SAN
deleteSolidFireAccount(sfAccountId, sfConnection);
// delete the info in the account_details table
// that's related to the SolidFire account
_accountDetailsDao.deleteDetails(account.getAccountId());
}
StoragePoolVO storagePool = _storagePoolDao.findById(storagePoolId);
long usedBytes = storagePool.getUsedBytes();
usedBytes -= volumeInfo.getSize();
if (usedBytes < 0) {
usedBytes = 0;
}
storagePool.setUsedBytes(usedBytes);
_storagePoolDao.update(storagePoolId, storagePool);
} catch (StorageUnavailableException e) {
s_logger.error("Failed to create volume (StorageUnavailableException)", e);
errMsg = e.toString();
} catch (Exception e) {
s_logger.error("Failed to create volume (Exception)", e);
errMsg = e.toString();
}
}
else {
errMsg = "Invalid DataObjectType (" + dataObject.getType() + ") passed to deleteAsync";
}
CommandResult result = new CommandResult();
result.setResult(errMsg);
callback.complete(result);
} }
@Override @Override
@ -62,5 +533,4 @@ public class SolidfirePrimaryDataStoreDriver implements PrimaryDataStoreDriver {
@Override @Override
public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback) { public void takeSnapshot(SnapshotInfo snapshot, AsyncCompletionCallback<CreateCmdResult> callback) {
} }
} }

View File

@ -0,0 +1,274 @@
/*
* 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 org.apache.cloudstack.storage.datastore.lifecycle;
import java.util.Map;
import java.util.StringTokenizer;
import javax.inject.Inject;
import org.apache.cloudstack.engine.subsystem.api.storage.ClusterScope;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.HostScope;
import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreLifeCycle;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreParameters;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
import org.apache.cloudstack.storage.datastore.util.SolidFireUtil;
import org.apache.cloudstack.storage.volume.datastore.PrimaryDataStoreHelper;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.agent.api.StoragePoolInfo;
import com.cloud.storage.StoragePoolAutomation;
import com.cloud.storage.Storage.StoragePoolType;
import com.cloud.utils.exception.CloudRuntimeException;
public class SolidFirePrimaryDataStoreLifeCycle implements PrimaryDataStoreLifeCycle {
@Inject PrimaryDataStoreDao storagePoolDao;
@Inject PrimaryDataStoreHelper dataStoreHelper;
@Inject StoragePoolAutomation storagePoolAutomation;
@Inject StoragePoolDetailsDao storagePoolDetailsDao;
@Inject DataCenterDao zoneDao;
private static final int DEFAULT_MANAGEMENT_PORT = 443;
private static final int DEFAULT_STORAGE_PORT = 3260;
// invoked to add primary storage that is based on the SolidFire plug-in
@Override
public DataStore initialize(Map<String, Object> dsInfos) {
String url = (String)dsInfos.get("url");
Long zoneId = (Long)dsInfos.get("zoneId");
String storagePoolName = (String) dsInfos.get("name");
String providerName = (String)dsInfos.get("providerName");
Long capacityBytes = (Long)dsInfos.get("capacityBytes");
Long capacityIops = (Long)dsInfos.get("capacityIops");
String tags = (String)dsInfos.get("tags");
Map<String, String> details = (Map<String, String>)dsInfos.get("details");
String storageVip = getStorageVip(url);
int storagePort = getStoragePort(url);
DataCenterVO zone = zoneDao.findById(zoneId);
String uuid = SolidFireUtil.PROVIDER_NAME + "_" + zone.getUuid() + "_" + storageVip;
if (capacityBytes == null || capacityBytes <= 0) {
throw new IllegalArgumentException("'capacityBytes' must be present and greater than 0.");
}
if (capacityIops == null || capacityIops <= 0) {
throw new IllegalArgumentException("'capacityIops' must be present and greater than 0.");
}
PrimaryDataStoreParameters parameters = new PrimaryDataStoreParameters();
parameters.setHost(storageVip);
parameters.setPort(storagePort);
parameters.setPath(getModifiedUrl(url));
parameters.setType(StoragePoolType.Iscsi);
parameters.setUuid(uuid);
parameters.setZoneId(zoneId);
parameters.setName(storagePoolName);
parameters.setProviderName(providerName);
parameters.setManaged(true);
parameters.setCapacityBytes(capacityBytes);
parameters.setUsedBytes(0);
parameters.setCapacityIops(capacityIops);
parameters.setHypervisorType(HypervisorType.Any);
parameters.setTags(tags);
parameters.setDetails(details);
String managementVip = getManagementVip(url);
int managementPort = getManagementPort(url);
details.put(SolidFireUtil.MANAGEMENT_VIP, managementVip);
details.put(SolidFireUtil.MANAGEMENT_PORT, String.valueOf(managementPort));
String clusterAdminUsername = getValue(SolidFireUtil.CLUSTER_ADMIN_USERNAME, url);
String clusterAdminPassword = getValue(SolidFireUtil.CLUSTER_ADMIN_PASSWORD, url);
details.put(SolidFireUtil.CLUSTER_ADMIN_USERNAME, clusterAdminUsername);
details.put(SolidFireUtil.CLUSTER_ADMIN_PASSWORD, clusterAdminPassword);
// this adds a row in the cloud.storage_pool table for this SolidFire cluster
return dataStoreHelper.createPrimaryDataStore(parameters);
}
// remove the clusterAdmin and password key/value pairs
private String getModifiedUrl(String originalUrl)
{
StringBuilder sb = new StringBuilder();
String delimiter = ";";
StringTokenizer st = new StringTokenizer(originalUrl, delimiter);
while (st.hasMoreElements()) {
String token = st.nextElement().toString();
if (!token.startsWith(SolidFireUtil.CLUSTER_ADMIN_USERNAME) &&
!token.startsWith(SolidFireUtil.CLUSTER_ADMIN_PASSWORD)) {
sb.append(token).append(delimiter);
}
}
String modifiedUrl = sb.toString();
int lastIndexOf = modifiedUrl.lastIndexOf(delimiter);
if (lastIndexOf == (modifiedUrl.length() - delimiter.length())) {
return modifiedUrl.substring(0, lastIndexOf);
}
return modifiedUrl;
}
private String getManagementVip(String url)
{
return getVip(SolidFireUtil.MANAGEMENT_VIP, url);
}
private String getStorageVip(String url)
{
return getVip(SolidFireUtil.STORAGE_VIP, url);
}
private int getManagementPort(String url)
{
return getPort(SolidFireUtil.MANAGEMENT_VIP, url, DEFAULT_MANAGEMENT_PORT);
}
private int getStoragePort(String url)
{
return getPort(SolidFireUtil.STORAGE_VIP, url, DEFAULT_STORAGE_PORT);
}
private String getVip(String keyToMatch, String url)
{
String delimiter = ":";
String storageVip = getValue(keyToMatch, url);
int index = storageVip.indexOf(delimiter);
if (index != -1)
{
return storageVip.substring(0, index);
}
return storageVip;
}
private int getPort(String keyToMatch, String url, int defaultPortNumber)
{
String delimiter = ":";
String storageVip = getValue(keyToMatch, url);
int index = storageVip.indexOf(delimiter);
int portNumber = defaultPortNumber;
if (index != -1) {
String port = storageVip.substring(index + delimiter.length());
try {
portNumber = Integer.parseInt(port);
}
catch (NumberFormatException ex) {
throw new IllegalArgumentException("Invalid URL format (port is not an integer)");
}
}
return portNumber;
}
private String getValue(String keyToMatch, String url)
{
String delimiter1 = ";";
String delimiter2 = "=";
StringTokenizer st = new StringTokenizer(url, delimiter1);
while (st.hasMoreElements()) {
String token = st.nextElement().toString();
int index = token.indexOf(delimiter2);
if (index == -1)
{
throw new RuntimeException("Invalid URL format");
}
String key = token.substring(0, index);
if (key.equalsIgnoreCase(keyToMatch)) {
String valueToReturn = token.substring(index + delimiter2.length());
return valueToReturn;
}
}
throw new RuntimeException("Key not found in URL");
}
// do not implement this method for SolidFire's plug-in
@Override
public boolean attachHost(DataStore store, HostScope scope, StoragePoolInfo existingInfo) {
return true; // should be ignored for zone-wide-only plug-ins like SolidFire's
}
// do not implement this method for SolidFire's plug-in
@Override
public boolean attachCluster(DataStore store, ClusterScope scope) {
return true; // should be ignored for zone-wide-only plug-ins like SolidFire's
}
@Override
public boolean attachZone(DataStore dataStore, ZoneScope scope, HypervisorType hypervisorType) {
dataStoreHelper.attachZone(dataStore);
return true;
}
@Override
public boolean maintain(DataStore dataStore) {
storagePoolAutomation.maintain(dataStore);
dataStoreHelper.maintain(dataStore);
return true;
}
@Override
public boolean cancelMaintain(DataStore store) {
dataStoreHelper.cancelMaintain(store);
storagePoolAutomation.cancelMaintain(store);
return true;
}
// invoked to delete primary storage that is based on the SolidFire plug-in
@Override
public boolean deleteDataStore(DataStore store) {
return dataStoreHelper.deletePrimaryDataStore(store);
}
}

View File

@ -1,62 +1,91 @@
// Licensed to the Apache Software Foundation (ASF) under one /*
// or more contributor license agreements. See the NOTICE file * Licensed to the Apache Software Foundation (ASF) under one
// distributed with this work for additional information * or more contributor license agreements. See the NOTICE file
// regarding copyright ownership. The ASF licenses this file * distributed with this work for additional information
// to you under the Apache License, Version 2.0 (the * regarding copyright ownership. The ASF licenses this file
// "License"); you may not use this file except in compliance * to you under the Apache License, Version 2.0 (the
// with the License. You may obtain a copy of the License at * "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 *
// * 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 * Unless required by applicable law or agreed to in writing,
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * software distributed under the License is distributed on an
// KIND, either express or implied. See the License for the * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// specific language governing permissions and limitations * KIND, either express or implied. See the License for the
// under the License. * specific language governing permissions and limitations
* under the License.
*/
package org.apache.cloudstack.storage.datastore.provider; package org.apache.cloudstack.storage.datastore.provider;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.HashSet;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreDriver;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreLifeCycle; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreLifeCycle;
import org.apache.cloudstack.engine.subsystem.api.storage.HypervisorHostListener; import org.apache.cloudstack.engine.subsystem.api.storage.HypervisorHostListener;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreDriver;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreProvider; import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStoreProvider;
import org.apache.cloudstack.storage.datastore.driver.SolidfirePrimaryDataStoreDriver;
import org.apache.cloudstack.storage.datastore.lifecycle.SolidFirePrimaryDataStoreLifeCycle;
import org.apache.cloudstack.storage.datastore.util.SolidFireUtil;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import com.cloud.utils.component.ComponentContext;
@Component @Component
public class SolidfirePrimaryDataStoreProvider implements PrimaryDataStoreProvider { public class SolidfirePrimaryDataStoreProvider implements PrimaryDataStoreProvider {
private final String name = "Solidfire Primary Data Store Provider"; protected DataStoreLifeCycle lifecycle;
protected PrimaryDataStoreDriver driver;
protected HypervisorHostListener listener;
SolidfirePrimaryDataStoreProvider() {
}
@Override @Override
public String getName() { public String getName() {
return name; return SolidFireUtil.PROVIDER_NAME;
} }
@Override @Override
public DataStoreLifeCycle getDataStoreLifeCycle() { public DataStoreLifeCycle getDataStoreLifeCycle() {
return null; return lifecycle;
} }
@Override @Override
public DataStoreDriver getDataStoreDriver() { public PrimaryDataStoreDriver getDataStoreDriver() {
return null; return driver;
} }
@Override @Override
public HypervisorHostListener getHostListener() { public HypervisorHostListener getHostListener() {
return null; return listener;
} }
@Override @Override
public boolean configure(Map<String, Object> params) { public boolean configure(Map<String, Object> params) {
return false; lifecycle = ComponentContext.inject(SolidFirePrimaryDataStoreLifeCycle.class);
driver = ComponentContext.inject(SolidfirePrimaryDataStoreDriver.class);
listener = ComponentContext.inject(new HypervisorHostListener() {
public boolean hostConnect(long hostId, long poolId) {
return true;
}
public boolean hostDisconnected(long hostId, long poolId) {
return true;
}
});
return true;
} }
@Override @Override
public Set<DataStoreProviderType> getTypes() { public Set<DataStoreProviderType> getTypes() {
return null; Set<DataStoreProviderType> types = new HashSet<DataStoreProviderType>();
}
types.add(DataStoreProviderType.PRIMARY);
return types;
}
} }

View File

@ -0,0 +1,901 @@
package org.apache.cloudstack.storage.datastore.util;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URI;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.List;
import java.util.ArrayList;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.BasicClientConnectionManager;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
public class SolidFireUtil
{
public static final String PROVIDER_NAME = "SolidFire";
public static final String MANAGEMENT_VIP = "mVip";
public static final String STORAGE_VIP = "sVip";
public static final String MANAGEMENT_PORT = "mPort";
public static final String STORAGE_PORT = "sPort";
public static final String CLUSTER_ADMIN_USERNAME = "clusterAdminUsername";
public static final String CLUSTER_ADMIN_PASSWORD = "clusterAdminPassword";
public static final String ACCOUNT_ID = "accountId";
public static final String CHAP_INITIATOR_USERNAME = "chapInitiatorUsername";
public static final String CHAP_INITIATOR_SECRET = "chapInitiatorSecret";
public static final String CHAP_TARGET_USERNAME = "chapTargetUsername";
public static final String CHAP_TARGET_SECRET = "chapTargetSecret";
public static long createSolidFireVolume(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword,
String strSfVolumeName, long lSfAccountId, long lTotalSize, boolean bEnable512e,
long lMinIops, long lMaxIops, long lBurstIops) throws Exception
{
final Gson gson = new GsonBuilder().create();
VolumeToCreate volumeToCreate = new VolumeToCreate(strSfVolumeName, lSfAccountId, lTotalSize, bEnable512e,
lMinIops, lMaxIops, lBurstIops);
String strVolumeToCreateJson = gson.toJson(volumeToCreate);
String strVolumeCreateResultJson = executeJsonRpc(strVolumeToCreateJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
VolumeCreateResult volumeCreateResult = gson.fromJson(strVolumeCreateResultJson, VolumeCreateResult.class);
verifyResult(volumeCreateResult.result, strVolumeCreateResultJson, gson);
return volumeCreateResult.result.volumeID;
}
public static void deleteSolidFireVolume(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVolumeId) throws Exception
{
final Gson gson = new GsonBuilder().create();
VolumeToDelete volumeToDelete = new VolumeToDelete(lVolumeId);
String strVolumeToDeleteJson = gson.toJson(volumeToDelete);
executeJsonRpc(strVolumeToDeleteJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
}
public static void purgeSolidFireVolume(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVolumeId) throws Exception
{
final Gson gson = new GsonBuilder().create();
VolumeToPurge volumeToPurge = new VolumeToPurge(lVolumeId);
String strVolumeToPurgeJson = gson.toJson(volumeToPurge);
executeJsonRpc(strVolumeToPurgeJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
}
public static SolidFireVolume getSolidFireVolume(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVolumeId) throws Exception
{
final Gson gson = new GsonBuilder().create();
VolumeToGet volumeToGet = new VolumeToGet(lVolumeId);
String strVolumeToGetJson = gson.toJson(volumeToGet);
String strVolumeGetResultJson = executeJsonRpc(strVolumeToGetJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
VolumeGetResult volumeGetResult = gson.fromJson(strVolumeGetResultJson, VolumeGetResult.class);
verifyResult(volumeGetResult.result, strVolumeGetResultJson, gson);
String strVolumeName = getVolumeName(volumeGetResult, lVolumeId);
String strVolumeIqn = getVolumeIqn(volumeGetResult, lVolumeId);
long lAccountId = getVolumeAccountId(volumeGetResult, lVolumeId);
String strVolumeStatus = getVolumeStatus(volumeGetResult, lVolumeId);
return new SolidFireVolume(lVolumeId, strVolumeName, strVolumeIqn, lAccountId, strVolumeStatus);
}
public static List<SolidFireVolume> getSolidFireVolumesForAccountId(String strSfMvip, int iSfPort,
String strSfAdmin, String strSfPassword, long lAccountId) throws Exception
{
final Gson gson = new GsonBuilder().create();
VolumesToGetForAccount volumesToGetForAccount = new VolumesToGetForAccount(lAccountId);
String strVolumesToGetForAccountJson = gson.toJson(volumesToGetForAccount);
String strVolumesGetForAccountResultJson = executeJsonRpc(strVolumesToGetForAccountJson, strSfMvip, iSfPort,
strSfAdmin, strSfPassword);
VolumeGetResult volumeGetResult = gson.fromJson(strVolumesGetForAccountResultJson, VolumeGetResult.class);
verifyResult(volumeGetResult.result, strVolumesGetForAccountResultJson, gson);
List<SolidFireVolume> sfVolumes = new ArrayList<SolidFireVolume>();
for (VolumeGetResult.Result.Volume volume : volumeGetResult.result.volumes) {
sfVolumes.add(new SolidFireVolume(volume.volumeID, volume.name, volume.iqn, volume.accountID, volume.status));
}
return sfVolumes;
}
private static final String ACTIVE = "active";
public static class SolidFireVolume
{
private final long _id;
private final String _name;
private final String _iqn;
private final long _accountId;
private final String _status;
public SolidFireVolume(long id, String name, String iqn,
long accountId, String status)
{
_id = id;
_name = name;
_iqn = "/" + iqn + "/0";
_accountId = accountId;
_status = status;
}
public long getId()
{
return _id;
}
public String getName()
{
return _name;
}
public String getIqn()
{
return _iqn;
}
public long getAccountId()
{
return _accountId;
}
public boolean isActive()
{
return ACTIVE.equalsIgnoreCase(_status);
}
@Override
public int hashCode() {
return (int)_id;
}
@Override
public String toString() {
return _name;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof SolidFireVolume)) {
return false;
}
SolidFireVolume sfv = (SolidFireVolume)obj;
if (_id == sfv._id && _name.equals(sfv._name) &&
_iqn.equals(sfv._iqn) && isActive() == sfv.isActive()) {
return true;
}
return false;
}
}
public static long createSolidFireAccount(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword,
String strAccountName) throws Exception
{
final Gson gson = new GsonBuilder().create();
AccountToAdd accountToAdd = new AccountToAdd(strAccountName);
String strAccountAddJson = gson.toJson(accountToAdd);
String strAccountAddResultJson = executeJsonRpc(strAccountAddJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
AccountAddResult accountAddResult = gson.fromJson(strAccountAddResultJson, AccountAddResult.class);
verifyResult(accountAddResult.result, strAccountAddResultJson, gson);
return accountAddResult.result.accountID;
}
public static void deleteSolidFireAccount(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword,
long lAccountId) throws Exception
{
final Gson gson = new GsonBuilder().create();
AccountToRemove accountToRemove = new AccountToRemove(lAccountId);
String strAccountToRemoveJson = gson.toJson(accountToRemove);
executeJsonRpc(strAccountToRemoveJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
}
public static SolidFireAccount getSolidFireAccountById(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword,
long lSfAccountId) throws Exception
{
final Gson gson = new GsonBuilder().create();
AccountToGetById accountToGetById = new AccountToGetById(lSfAccountId);
String strAccountToGetByIdJson = gson.toJson(accountToGetById);
String strAccountGetByIdResultJson = executeJsonRpc(strAccountToGetByIdJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
AccountGetResult accountGetByIdResult = gson.fromJson(strAccountGetByIdResultJson, AccountGetResult.class);
verifyResult(accountGetByIdResult.result, strAccountGetByIdResultJson, gson);
String strSfAccountName = accountGetByIdResult.result.account.username;
String strSfAccountInitiatorSecret = accountGetByIdResult.result.account.initiatorSecret;
String strSfAccountTargetSecret = accountGetByIdResult.result.account.targetSecret;
return new SolidFireAccount(lSfAccountId, strSfAccountName, strSfAccountInitiatorSecret, strSfAccountTargetSecret);
}
public static SolidFireAccount getSolidFireAccountByName(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword,
String strSfAccountName) throws Exception
{
final Gson gson = new GsonBuilder().create();
AccountToGetByName accountToGetByName = new AccountToGetByName(strSfAccountName);
String strAccountToGetByNameJson = gson.toJson(accountToGetByName);
String strAccountGetByNameResultJson = executeJsonRpc(strAccountToGetByNameJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
AccountGetResult accountGetByNameResult = gson.fromJson(strAccountGetByNameResultJson, AccountGetResult.class);
verifyResult(accountGetByNameResult.result, strAccountGetByNameResultJson, gson);
long lSfAccountId = accountGetByNameResult.result.account.accountID;
String strSfAccountInitiatorSecret = accountGetByNameResult.result.account.initiatorSecret;
String strSfAccountTargetSecret = accountGetByNameResult.result.account.targetSecret;
return new SolidFireAccount(lSfAccountId, strSfAccountName, strSfAccountInitiatorSecret, strSfAccountTargetSecret);
}
public static class SolidFireAccount
{
private final long _id;
private final String _name;
private final String _initiatorSecret;
private final String _targetSecret;
public SolidFireAccount(long id, String name, String initiatorSecret, String targetSecret)
{
_id = id;
_name = name;
_initiatorSecret = initiatorSecret;
_targetSecret = targetSecret;
}
public long getId()
{
return _id;
}
public String getName()
{
return _name;
}
public String getInitiatorSecret()
{
return _initiatorSecret;
}
public String getTargetSecret()
{
return _targetSecret;
}
@Override
public int hashCode() {
return (int)_id;
}
@Override
public String toString() {
return _name;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof SolidFireAccount)) {
return false;
}
SolidFireAccount sfa = (SolidFireAccount)obj;
if (_id == sfa._id && _name.equals(sfa._name) &&
_initiatorSecret.equals(sfa._initiatorSecret) &&
_targetSecret.equals(sfa._targetSecret)) {
return true;
}
return false;
}
}
public static List<SolidFireVolume> getDeletedVolumes(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword) throws Exception
{
final Gson gson = new GsonBuilder().create();
ListDeletedVolumes listDeletedVolumes = new ListDeletedVolumes();
String strListDeletedVolumesJson = gson.toJson(listDeletedVolumes);
String strListDeletedVolumesResultJson = executeJsonRpc(strListDeletedVolumesJson, strSfMvip, iSfPort,
strSfAdmin, strSfPassword);
VolumeGetResult volumeGetResult = gson.fromJson(strListDeletedVolumesResultJson, VolumeGetResult.class);
verifyResult(volumeGetResult.result, strListDeletedVolumesResultJson, gson);
List<SolidFireVolume> deletedVolumes = new ArrayList<SolidFireVolume> ();
for (VolumeGetResult.Result.Volume volume : volumeGetResult.result.volumes) {
deletedVolumes.add(new SolidFireVolume(volume.volumeID, volume.name, volume.iqn, volume.accountID, volume.status));
}
return deletedVolumes;
}
public static long createSolidFireVag(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, String strVagName) throws Exception
{
final Gson gson = new GsonBuilder().create();
VagToCreate vagToCreate = new VagToCreate(strVagName);
String strVagCreateJson = gson.toJson(vagToCreate);
String strVagCreateResultJson = executeJsonRpc(strVagCreateJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
VagCreateResult vagCreateResult = gson.fromJson(strVagCreateResultJson, VagCreateResult.class);
verifyResult(vagCreateResult.result, strVagCreateResultJson, gson);
return vagCreateResult.result.volumeAccessGroupID;
}
public static void deleteSolidFireVag(String strSfMvip, int iSfPort, String strSfAdmin, String strSfPassword, long lVagId) throws Exception
{
final Gson gson = new GsonBuilder().create();
VagToDelete vagToDelete = new VagToDelete(lVagId);
String strVagToDeleteJson = gson.toJson(vagToDelete);
executeJsonRpc(strVagToDeleteJson, strSfMvip, iSfPort, strSfAdmin, strSfPassword);
}
@SuppressWarnings("unused")
private static final class VolumeToCreate
{
private final String method = "CreateVolume";
private final VolumeToCreateParams params;
private VolumeToCreate(final String strVolumeName, final long lAccountId, final long lTotalSize,
final boolean bEnable512e, final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS)
{
params = new VolumeToCreateParams(strVolumeName, lAccountId, lTotalSize, bEnable512e,
lMinIOPS, lMaxIOPS, lBurstIOPS);
}
private static final class VolumeToCreateParams
{
private final String name;
private final long accountID;
private final long totalSize;
private final boolean enable512e;
private final VolumeToCreateParamsQoS qos;
private VolumeToCreateParams(final String strVolumeName, final long lAccountId, final long lTotalSize,
final boolean bEnable512e, final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS)
{
name = strVolumeName;
accountID = lAccountId;
totalSize = lTotalSize;
enable512e = bEnable512e;
qos = new VolumeToCreateParamsQoS(lMinIOPS, lMaxIOPS, lBurstIOPS);
}
private static final class VolumeToCreateParamsQoS
{
private final long minIOPS;
private final long maxIOPS;
private final long burstIOPS;
private VolumeToCreateParamsQoS(final long lMinIOPS, final long lMaxIOPS, final long lBurstIOPS)
{
minIOPS = lMinIOPS;
maxIOPS = lMaxIOPS;
burstIOPS = lBurstIOPS;
}
}
}
}
@SuppressWarnings("unused")
private static final class VolumeToDelete
{
private final String method = "DeleteVolume";
private final VolumeToDeleteParams params;
private VolumeToDelete(final long lVolumeId)
{
params = new VolumeToDeleteParams(lVolumeId);
}
private static final class VolumeToDeleteParams
{
private long volumeID;
private VolumeToDeleteParams(final long lVolumeId)
{
volumeID = lVolumeId;
}
}
}
@SuppressWarnings("unused")
private static final class ListDeletedVolumes
{
private final String method = "ListDeletedVolumes";
}
@SuppressWarnings("unused")
private static final class VolumeToPurge
{
private final String method = "PurgeDeletedVolume";
private final VolumeToPurgeParams params;
private VolumeToPurge(final long lVolumeId)
{
params = new VolumeToPurgeParams(lVolumeId);
}
private static final class VolumeToPurgeParams
{
private long volumeID;
private VolumeToPurgeParams(final long lVolumeId)
{
volumeID = lVolumeId;
}
}
}
@SuppressWarnings("unused")
private static final class VolumeToGet
{
private final String method = "ListActiveVolumes";
private final VolumeToGetParams params;
private VolumeToGet(final long lVolumeId)
{
params = new VolumeToGetParams(lVolumeId);
}
private static final class VolumeToGetParams
{
private final long startVolumeID;
private final long limit = 1;
private VolumeToGetParams(final long lVolumeId)
{
startVolumeID = lVolumeId;
}
}
}
@SuppressWarnings("unused")
private static final class VolumesToGetForAccount
{
private final String method = "ListVolumesForAccount";
private final VolumesToGetForAccountParams params;
private VolumesToGetForAccount(final long lAccountId)
{
params = new VolumesToGetForAccountParams(lAccountId);
}
private static final class VolumesToGetForAccountParams
{
private final long accountID;
private VolumesToGetForAccountParams(final long lAccountId)
{
accountID = lAccountId;
}
}
}
@SuppressWarnings("unused")
private static final class AccountToAdd
{
private final String method = "AddAccount";
private final AccountToAddParams params;
private AccountToAdd(final String strAccountName)
{
params = new AccountToAddParams(strAccountName);
}
private static final class AccountToAddParams
{
private final String username;
private AccountToAddParams(final String strAccountName)
{
username = strAccountName;
}
}
}
@SuppressWarnings("unused")
private static final class AccountToRemove
{
private final String method = "RemoveAccount";
private final AccountToRemoveParams params;
private AccountToRemove(final long lAccountId)
{
params = new AccountToRemoveParams(lAccountId);
}
private static final class AccountToRemoveParams
{
private long accountID;
private AccountToRemoveParams(final long lAccountId)
{
accountID = lAccountId;
}
}
}
@SuppressWarnings("unused")
private static final class AccountToGetById
{
private final String method = "GetAccountByID";
private final AccountToGetByIdParams params;
private AccountToGetById(final long lAccountId)
{
params = new AccountToGetByIdParams(lAccountId);
}
private static final class AccountToGetByIdParams
{
private final long accountID;
private AccountToGetByIdParams(final long lAccountId)
{
accountID = lAccountId;
}
}
}
@SuppressWarnings("unused")
private static final class AccountToGetByName
{
private final String method = "GetAccountName";
private final AccountToGetByNameParams params;
private AccountToGetByName(final String strUsername)
{
params = new AccountToGetByNameParams(strUsername);
}
private static final class AccountToGetByNameParams
{
private final String username;
private AccountToGetByNameParams(final String strUsername)
{
username = strUsername;
}
}
}
@SuppressWarnings("unused")
private static final class VagToCreate
{
private final String method = "CreateVolumeAccessGroup";
private final VagToCreateParams params;
private VagToCreate(final String strVagName)
{
params = new VagToCreateParams(strVagName);
}
private static final class VagToCreateParams
{
private final String name;
private VagToCreateParams(final String strVagName)
{
name = strVagName;
}
}
}
@SuppressWarnings("unused")
private static final class VagToDelete
{
private final String method = "DeleteVolumeAccessGroup";
private final VagToDeleteParams params;
private VagToDelete(final long lVagId)
{
params = new VagToDeleteParams(lVagId);
}
private static final class VagToDeleteParams
{
private long volumeAccessGroupID;
private VagToDeleteParams(final long lVagId)
{
volumeAccessGroupID = lVagId;
}
}
}
private static final class VolumeCreateResult
{
private Result result;
private static final class Result
{
private long volumeID;
}
}
private static final class VolumeGetResult
{
private Result result;
private static final class Result
{
private Volume[] volumes;
private static final class Volume
{
private long volumeID;
private String name;
private String iqn;
private long accountID;
private String status;
}
}
}
private static final class AccountAddResult
{
private Result result;
private static final class Result
{
private long accountID;
}
}
private static final class AccountGetResult
{
private Result result;
private static final class Result
{
private Account account;
private static final class Account
{
private long accountID;
private String username;
private String initiatorSecret;
private String targetSecret;
}
}
}
private static final class VagCreateResult
{
private Result result;
private static final class Result
{
private long volumeAccessGroupID;
}
}
private static final class JsonError
{
private Error error;
private static final class Error
{
private String message;
}
}
private static DefaultHttpClient getHttpClient(int iPort) throws NoSuchAlgorithmException, KeyManagementException {
SSLContext sslContext = SSLContext.getInstance("SSL");
X509TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
sslContext.init(null, new TrustManager[] { tm }, new SecureRandom());
SSLSocketFactory socketFactory = new SSLSocketFactory(sslContext, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("https", iPort, socketFactory));
BasicClientConnectionManager mgr = new BasicClientConnectionManager(registry);
DefaultHttpClient client = new DefaultHttpClient();
return new DefaultHttpClient(mgr, client.getParams());
}
private static String executeJsonRpc(String strJsonToExecute, String strMvip, int iPort,
String strAdmin, String strPassword) throws Exception
{
DefaultHttpClient httpClient = null;
StringBuilder sb = new StringBuilder();
try
{
StringEntity input = new StringEntity(strJsonToExecute);
input.setContentType("application/json");
httpClient = getHttpClient(iPort);
URI uri = new URI("https://" + strMvip + ":" + iPort + "/json-rpc/1.0");
AuthScope authScope = new AuthScope(uri.getHost(), uri.getPort(), AuthScope.ANY_SCHEME);
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(strAdmin, strPassword);
httpClient.getCredentialsProvider().setCredentials(authScope, credentials);
HttpPost postRequest = new HttpPost(uri);
postRequest.setEntity(input);
HttpResponse response = httpClient.execute(postRequest);
if (!isSuccess(response.getStatusLine().getStatusCode()))
{
throw new RuntimeException("Failed on JSON-RPC API call. HTTP error code = " + response.getStatusLine().getStatusCode());
}
BufferedReader br = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
String strOutput;
while ((strOutput = br.readLine()) != null)
{
sb.append(strOutput);
}
} finally {
if (httpClient != null) {
try {
httpClient.getConnectionManager().shutdown();
} catch (Throwable t) {}
}
}
return sb.toString();
}
private static boolean isSuccess(int iCode) {
return iCode >= 200 && iCode < 300;
}
private static void verifyResult(Object obj, String strJson, Gson gson) throws IllegalStateException
{
if (obj != null)
{
return;
}
JsonError jsonError = gson.fromJson(strJson, JsonError.class);
if (jsonError != null)
{
throw new IllegalStateException(jsonError.error.message);
}
throw new IllegalStateException("Problem with the following JSON: " + strJson);
}
private static String getVolumeName(VolumeGetResult volumeGetResult, long lVolumeId) throws Exception
{
if (volumeGetResult.result.volumes != null && volumeGetResult.result.volumes.length == 1 &&
volumeGetResult.result.volumes[0].volumeID == lVolumeId)
{
return volumeGetResult.result.volumes[0].name;
}
throw new Exception("Could not determine the name of the volume, " +
"but the volume was created with an ID of " + lVolumeId + ".");
}
private static String getVolumeIqn(VolumeGetResult volumeGetResult, long lVolumeId) throws Exception
{
if (volumeGetResult.result.volumes != null && volumeGetResult.result.volumes.length == 1 &&
volumeGetResult.result.volumes[0].volumeID == lVolumeId)
{
return volumeGetResult.result.volumes[0].iqn;
}
throw new Exception("Could not determine the IQN of the volume, " +
"but the volume was created with an ID of " + lVolumeId + ".");
}
private static long getVolumeAccountId(VolumeGetResult volumeGetResult, long lVolumeId) throws Exception
{
if (volumeGetResult.result.volumes != null && volumeGetResult.result.volumes.length == 1 &&
volumeGetResult.result.volumes[0].volumeID == lVolumeId)
{
return volumeGetResult.result.volumes[0].accountID;
}
throw new Exception("Could not determine the volume's account ID, " +
"but the volume was created with an ID of " + lVolumeId + ".");
}
private static String getVolumeStatus(VolumeGetResult volumeGetResult, long lVolumeId) throws Exception
{
if (volumeGetResult.result.volumes != null && volumeGetResult.result.volumes.length == 1 &&
volumeGetResult.result.volumes[0].volumeID == lVolumeId)
{
return volumeGetResult.result.volumes[0].status;
}
throw new Exception("Could not determine the status of the volume, " +
"but the volume was created with an ID of " + lVolumeId + ".");
}
}

View File

@ -67,6 +67,8 @@ public class DiskOfferingJoinDaoImpl extends GenericDaoBase<DiskOfferingJoinVO,
diskOfferingResponse.setDisplayText(offering.getDisplayText()); diskOfferingResponse.setDisplayText(offering.getDisplayText());
diskOfferingResponse.setCreated(offering.getCreated()); diskOfferingResponse.setCreated(offering.getCreated());
diskOfferingResponse.setDiskSize(offering.getDiskSize() / (1024 * 1024 * 1024)); diskOfferingResponse.setDiskSize(offering.getDiskSize() / (1024 * 1024 * 1024));
diskOfferingResponse.setMinIops(offering.getMinIops());
diskOfferingResponse.setMaxIops(offering.getMaxIops());
diskOfferingResponse.setDomain(offering.getDomainName()); diskOfferingResponse.setDomain(offering.getDomainName());
diskOfferingResponse.setDomainId(offering.getDomainUuid()); diskOfferingResponse.setDomainId(offering.getDomainUuid());
@ -74,6 +76,7 @@ public class DiskOfferingJoinDaoImpl extends GenericDaoBase<DiskOfferingJoinVO,
diskOfferingResponse.setTags(offering.getTags()); diskOfferingResponse.setTags(offering.getTags());
diskOfferingResponse.setCustomized(offering.isCustomized()); diskOfferingResponse.setCustomized(offering.isCustomized());
diskOfferingResponse.setCustomizedIops(offering.isCustomizedIops());
diskOfferingResponse.setStorageType(offering.isUseLocalStorage() ? ServiceOffering.StorageType.local.toString() : ServiceOffering.StorageType.shared.toString()); diskOfferingResponse.setStorageType(offering.isUseLocalStorage() ? ServiceOffering.StorageType.local.toString() : ServiceOffering.StorageType.shared.toString());
diskOfferingResponse.setBytesReadRate(offering.getBytesReadRate()); diskOfferingResponse.setBytesReadRate(offering.getBytesReadRate());
diskOfferingResponse.setBytesWriteRate(offering.getBytesWriteRate()); diskOfferingResponse.setBytesWriteRate(offering.getBytesWriteRate());

View File

@ -83,6 +83,7 @@ public class StoragePoolJoinDaoImpl extends GenericDaoBase<StoragePoolJoinVO, Lo
long allocatedSize = pool.getUsedCapacity() + pool.getReservedCapacity(); long allocatedSize = pool.getUsedCapacity() + pool.getReservedCapacity();
poolResponse.setDiskSizeTotal(pool.getCapacityBytes()); poolResponse.setDiskSizeTotal(pool.getCapacityBytes());
poolResponse.setDiskSizeAllocated(allocatedSize); poolResponse.setDiskSizeAllocated(allocatedSize);
poolResponse.setCapacityIops(pool.getCapacityIops());
// TODO: StatsCollector does not persist data // TODO: StatsCollector does not persist data
StorageStats stats = ApiDBUtils.getStoragePoolStatistics(pool.getId()); StorageStats stats = ApiDBUtils.getStoragePoolStatistics(pool.getId());
@ -144,6 +145,7 @@ public class StoragePoolJoinDaoImpl extends GenericDaoBase<StoragePoolJoinVO, Lo
long allocatedSize = ApiDBUtils.getStorageCapacitybyPool(pool.getId(), capacityType); long allocatedSize = ApiDBUtils.getStorageCapacitybyPool(pool.getId(), capacityType);
poolResponse.setDiskSizeTotal(pool.getCapacityBytes()); poolResponse.setDiskSizeTotal(pool.getCapacityBytes());
poolResponse.setDiskSizeAllocated(allocatedSize); poolResponse.setDiskSizeAllocated(allocatedSize);
poolResponse.setCapacityIops(pool.getCapacityIops());
// TODO: StatsCollector does not persist data // TODO: StatsCollector does not persist data
StorageStats stats = ApiDBUtils.getStoragePoolStatistics(pool.getId()); StorageStats stats = ApiDBUtils.getStoragePoolStatistics(pool.getId());

View File

@ -101,6 +101,9 @@ public class VolumeJoinDaoImpl extends GenericDaoBase<VolumeJoinVO, Long> implem
// Show the virtual size of the volume // Show the virtual size of the volume
volResponse.setSize(volume.getSize()); volResponse.setSize(volume.getSize());
volResponse.setMinIops(volume.getMinIops());
volResponse.setMaxIops(volume.getMaxIops());
volResponse.setCreated(volume.getCreated()); volResponse.setCreated(volume.getCreated());
volResponse.setState(volume.getState().toString()); volResponse.setState(volume.getState().toString());
if (volume.getState() == Volume.State.UploadOp) { if (volume.getState() == Volume.State.UploadOp) {

View File

@ -61,6 +61,15 @@ public class DiskOfferingJoinVO extends BaseViewVO implements InternalIdentity,
@Column(name="customized") @Column(name="customized")
private boolean customized; private boolean customized;
@Column(name="customized_iops")
private Boolean customizedIops;
@Column(name="min_iops")
private Long minIops;
@Column(name="max_iops")
private Long maxIops;
@Column(name="sort_key") @Column(name="sort_key")
int sortKey; int sortKey;
@ -179,6 +188,30 @@ public class DiskOfferingJoinVO extends BaseViewVO implements InternalIdentity,
this.customized = customized; this.customized = customized;
} }
public Boolean isCustomizedIops() {
return customizedIops;
}
public void setCustomizedIops(Boolean customizedIops) {
this.customizedIops = customizedIops;
}
public Long getMinIops() {
return minIops;
}
public void setMinIops(Long minIops) {
this.minIops = minIops;
}
public Long getMaxIops() {
return maxIops;
}
public void setMaxIops(Long maxIops) {
this.maxIops = maxIops;
}
public boolean isDisplayOffering() { public boolean isDisplayOffering() {
return displayOffering; return displayOffering;
} }

View File

@ -60,7 +60,6 @@ public class StoragePoolJoinVO extends BaseViewVO implements InternalIdentity, I
@Column(name="host_address") @Column(name="host_address")
private String hostAddress; private String hostAddress;
@Column(name="status") @Column(name="status")
@Enumerated(value=EnumType.STRING) @Enumerated(value=EnumType.STRING)
private StoragePoolStatus status; private StoragePoolStatus status;
@ -109,7 +108,6 @@ public class StoragePoolJoinVO extends BaseViewVO implements InternalIdentity, I
@Column(name="pod_name") @Column(name="pod_name")
private String podName; private String podName;
@Column(name="tag") @Column(name="tag")
private String tag; private String tag;
@ -119,7 +117,6 @@ public class StoragePoolJoinVO extends BaseViewVO implements InternalIdentity, I
@Column(name="disk_reserved_capacity") @Column(name="disk_reserved_capacity")
private long reservedCapacity; private long reservedCapacity;
@Column(name="job_id") @Column(name="job_id")
private Long jobId; private Long jobId;
@ -133,6 +130,8 @@ public class StoragePoolJoinVO extends BaseViewVO implements InternalIdentity, I
@Enumerated(value = EnumType.STRING) @Enumerated(value = EnumType.STRING)
private ScopeType scope; private ScopeType scope;
@Column(name="capacity_iops")
private Long capacityIops;
@Column(name = "hypervisor") @Column(name = "hypervisor")
@Enumerated(value = EnumType.STRING) @Enumerated(value = EnumType.STRING)
@ -243,6 +242,14 @@ public class StoragePoolJoinVO extends BaseViewVO implements InternalIdentity, I
this.capacityBytes = capacityBytes; this.capacityBytes = capacityBytes;
} }
public Long getCapacityIops() {
return capacityIops;
}
public void setCapacityIops(Long capacityIops) {
this.capacityIops = capacityIops;
}
public long getClusterId() { public long getClusterId() {
return clusterId; return clusterId;
} }

View File

@ -58,6 +58,12 @@ public class VolumeJoinVO extends BaseViewVO implements ControlledViewEntity {
@Column(name = "size") @Column(name = "size")
long size; long size;
@Column(name = "min_iops")
Long minIops;
@Column(name = "max_iops")
Long maxIops;
@Column(name = "state") @Column(name = "state")
@Enumerated(value = EnumType.STRING) @Enumerated(value = EnumType.STRING)
private Volume.State state; private Volume.State state;
@ -337,14 +343,27 @@ public class VolumeJoinVO extends BaseViewVO implements ControlledViewEntity {
this.size = size; this.size = size;
} }
public Long getMinIops() {
return minIops;
}
public void setMinIops(Long minIops) {
this.minIops = minIops;
}
public Long getMaxIops() {
return maxIops;
}
public void setMaxIops(Long maxIops) {
this.maxIops = maxIops;
}
public Volume.State getState() { public Volume.State getState() {
return state; return state;
} }
public void setState(Volume.State state) { public void setState(Volume.State state) {
this.state = state; this.state = state;
} }

View File

@ -102,14 +102,18 @@ public interface ConfigurationManager extends ConfigurationService, Manager {
* @param isCustomized * @param isCustomized
* @param localStorageRequired * @param localStorageRequired
* @param isDisplayOfferingEnabled * @param isDisplayOfferingEnabled
* @param isCustomizedIops (is admin allowing users to set custom iops?)
* @param minIops
* @param maxIops
* @param bytesReadRate * @param bytesReadRate
* @param bytesWriteRate * @param bytesWriteRate
* @param iopsReadRate * @param iopsReadRate
* @param iopsWriteRate * @param iopsWriteRate
* @return newly created disk offering * @return newly created disk offering
*/ */
DiskOfferingVO createDiskOffering(Long domainId, String name, String description, Long numGibibytes, String tags, boolean isCustomized, boolean localStorageRequired, boolean isDisplayOfferingEnabled, DiskOfferingVO createDiskOffering(Long domainId, String name, String description, Long numGibibytes, String tags, boolean isCustomized,
Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate); boolean localStorageRequired, boolean isDisplayOfferingEnabled, Boolean isCustomizedIops, Long minIops, Long maxIops,
Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate);
/** /**
* Creates a new pod * Creates a new pod

View File

@ -2297,8 +2297,9 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
@Override @Override
@ActionEvent(eventType = EventTypes.EVENT_DISK_OFFERING_CREATE, eventDescription = "creating disk offering") @ActionEvent(eventType = EventTypes.EVENT_DISK_OFFERING_CREATE, eventDescription = "creating disk offering")
public DiskOfferingVO createDiskOffering(Long domainId, String name, String description, Long numGibibytes, String tags, boolean isCustomized, boolean localStorageRequired, boolean isDisplayOfferingEnabled, public DiskOfferingVO createDiskOffering(Long domainId, String name, String description, Long numGibibytes, String tags, boolean isCustomized,
Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate) { boolean localStorageRequired, boolean isDisplayOfferingEnabled, Boolean isCustomizedIops, Long minIops, Long maxIops,
Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate) {
long diskSize = 0;// special case for custom disk offerings long diskSize = 0;// special case for custom disk offerings
if (numGibibytes != null && (numGibibytes <= 0)) { if (numGibibytes != null && (numGibibytes <= 0)) {
throw new InvalidParameterValueException("Please specify a disk size of at least 1 Gb."); throw new InvalidParameterValueException("Please specify a disk size of at least 1 Gb.");
@ -2314,8 +2315,44 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
isCustomized = true; isCustomized = true;
} }
if (isCustomizedIops != null) {
bytesReadRate = null;
bytesWriteRate = null;
iopsReadRate = null;
iopsWriteRate = null;
if (isCustomizedIops) {
minIops = null;
maxIops = null;
}
else {
if (minIops == null && maxIops == null) {
minIops = 0L;
maxIops = 0L;
}
else {
if (minIops == null || minIops <= 0) {
throw new InvalidParameterValueException("The min IOPS must be greater than 0.");
}
if (maxIops == null) {
maxIops = 0L;
}
if (minIops > maxIops) {
throw new InvalidParameterValueException("The min IOPS must be less than or equal to the max IOPS.");
}
}
}
}
else {
minIops = null;
maxIops = null;
}
tags = cleanupTags(tags); tags = cleanupTags(tags);
DiskOfferingVO newDiskOffering = new DiskOfferingVO(domainId, name, description, diskSize, tags, isCustomized); DiskOfferingVO newDiskOffering = new DiskOfferingVO(domainId, name, description, diskSize, tags, isCustomized,
isCustomizedIops, minIops, maxIops);
newDiskOffering.setUseLocalStorage(localStorageRequired); newDiskOffering.setUseLocalStorage(localStorageRequired);
newDiskOffering.setDisplayOffering(isDisplayOfferingEnabled); newDiskOffering.setDisplayOffering(isDisplayOfferingEnabled);
@ -2355,7 +2392,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
Long domainId = cmd.getDomainId(); Long domainId = cmd.getDomainId();
if (!isCustomized && numGibibytes == null) { if (!isCustomized && numGibibytes == null) {
throw new InvalidParameterValueException("Disksize is required for non-customized disk offering"); throw new InvalidParameterValueException("Disksize is required for a non-customized disk offering");
} }
boolean localStorageRequired = false; boolean localStorageRequired = false;
@ -2369,11 +2406,17 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
} }
} }
Boolean isCustomizedIops = cmd.isCustomizedIops();
Long minIops = cmd.getMinIops();
Long maxIops = cmd.getMaxIops();
Long bytesReadRate = cmd.getBytesReadRate(); Long bytesReadRate = cmd.getBytesReadRate();
Long bytesWriteRate = cmd.getBytesWriteRate(); Long bytesWriteRate = cmd.getBytesWriteRate();
Long iopsReadRate = cmd.getIopsReadRate(); Long iopsReadRate = cmd.getIopsReadRate();
Long iopsWriteRate = cmd.getIopsWriteRate(); Long iopsWriteRate = cmd.getIopsWriteRate();
return createDiskOffering(domainId, name, description, numGibibytes, tags, isCustomized, localStorageRequired, isDisplayOfferingEnabled, bytesReadRate, bytesWriteRate, iopsReadRate, iopsWriteRate);
return createDiskOffering(domainId, name, description, numGibibytes, tags, isCustomized,
localStorageRequired, isDisplayOfferingEnabled, isCustomizedIops, minIops, maxIops,
bytesReadRate, bytesWriteRate, iopsReadRate, iopsWriteRate);
} }
@Override @Override

View File

@ -932,7 +932,7 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio
diskSize = diskSize * 1024 * 1024 * 1024; diskSize = diskSize * 1024 * 1024 * 1024;
tags = cleanupTags(tags); tags = cleanupTags(tags);
DiskOfferingVO newDiskOffering = new DiskOfferingVO(domainId, name, description, diskSize, tags, isCustomized); DiskOfferingVO newDiskOffering = new DiskOfferingVO(domainId, name, description, diskSize, tags, isCustomized, null, null, null);
newDiskOffering.setUniqueName("Cloud.Com-" + name); newDiskOffering.setUniqueName("Cloud.Com-" + name);
newDiskOffering.setSystemUse(isSystemUse); newDiskOffering.setSystemUse(isSystemUse);
newDiskOffering = _diskOfferingDao.persistDeafultDiskOffering(newDiskOffering); newDiskOffering = _diskOfferingDao.persistDeafultDiskOffering(newDiskOffering);

View File

@ -99,28 +99,23 @@ public interface StorageManager extends StorageService {
void cleanupSecondaryStorage(boolean recurring); void cleanupSecondaryStorage(boolean recurring);
HypervisorType getHypervisorTypeFromFormat(ImageFormat format); HypervisorType getHypervisorTypeFromFormat(ImageFormat format);
boolean storagePoolHasEnoughIops(List<Volume> volume, StoragePool pool);
boolean storagePoolHasEnoughSpace(List<Volume> volume, StoragePool pool); boolean storagePoolHasEnoughSpace(List<Volume> volume, StoragePool pool);
boolean registerHostListener(String providerUuid, HypervisorHostListener listener); boolean registerHostListener(String providerUuid, HypervisorHostListener listener);
StoragePool findStoragePool(DiskProfile dskCh, DataCenterVO dc, StoragePool findStoragePool(DiskProfile dskCh, DataCenterVO dc,
HostPodVO pod, Long clusterId, Long hostId, VMInstanceVO vm, HostPodVO pod, Long clusterId, Long hostId, VMInstanceVO vm,
Set<StoragePool> avoid); Set<StoragePool> avoid);
void connectHostToSharedPool(long hostId, long poolId) void connectHostToSharedPool(long hostId, long poolId)
throws StorageUnavailableException; throws StorageUnavailableException;
void createCapacityEntry(long poolId); void createCapacityEntry(long poolId);
DataStore createLocalStorage(Host host, StoragePoolInfo poolInfo) throws ConnectionException; DataStore createLocalStorage(Host host, StoragePoolInfo poolInfo) throws ConnectionException;
BigDecimal getStorageOverProvisioningFactor(Long dcId); BigDecimal getStorageOverProvisioningFactor(Long dcId);

View File

@ -694,9 +694,10 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
throw new InvalidParameterValueException( throw new InvalidParameterValueException(
"Missing parameter hypervisor. Hypervisor type is required to create zone wide primary storage."); "Missing parameter hypervisor. Hypervisor type is required to create zone wide primary storage.");
} }
if (hypervisorType != HypervisorType.KVM && hypervisorType != HypervisorType.VMware) { if (hypervisorType != HypervisorType.KVM && hypervisorType != HypervisorType.VMware &&
hypervisorType != HypervisorType.Any) {
throw new InvalidParameterValueException( throw new InvalidParameterValueException(
"zone wide storage pool is not suported for hypervisor type " + hypervisor); "zone wide storage pool is not supported for hypervisor type " + hypervisor);
} }
} }
@ -734,6 +735,9 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
params.put("name", cmd.getStoragePoolName()); params.put("name", cmd.getStoragePoolName());
params.put("details", details); params.put("details", details);
params.put("providerName", storeProvider.getName()); params.put("providerName", storeProvider.getName());
params.put("managed", cmd.isManaged());
params.put("capacityBytes", cmd.getCapacityBytes());
params.put("capacityIops", cmd.getCapacityIops());
DataStoreLifeCycle lifeCycle = storeProvider.getDataStoreLifeCycle(); DataStoreLifeCycle lifeCycle = storeProvider.getDataStoreLifeCycle();
DataStore store = null; DataStore store = null;
@ -1561,7 +1565,41 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
} }
@Override @Override
public boolean storagePoolHasEnoughSpace(List<Volume> volumes, StoragePool pool) { public boolean storagePoolHasEnoughIops(List<Volume> requestedVolumes,
StoragePool pool) {
if (requestedVolumes == null || requestedVolumes.isEmpty() || pool == null)
return false;
long currentIops = 0;
List<VolumeVO> volumesInPool = _volumeDao.findByPoolId(pool.getId(), null);
for (VolumeVO volumeInPool : volumesInPool) {
Long minIops = volumeInPool.getMinIops();
if (minIops != null && minIops > 0) {
currentIops += minIops;
}
}
long requestedIops = 0;
for (Volume requestedVolume : requestedVolumes) {
Long minIops = requestedVolume.getMinIops();
if (minIops != null && minIops > 0) {
requestedIops += minIops;
}
}
long futureIops = currentIops + requestedIops;
return futureIops <= pool.getCapacityIops();
}
@Override
public boolean storagePoolHasEnoughSpace(List<Volume> volumes,
StoragePool pool) {
if (volumes == null || volumes.isEmpty()) if (volumes == null || volumes.isEmpty())
return false; return false;

View File

@ -45,7 +45,6 @@ import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.VirtualMachineProfile;
public interface VolumeManager extends VolumeApiService { public interface VolumeManager extends VolumeApiService {
VolumeInfo moveVolume(VolumeInfo volume, long destPoolDcId, Long destPoolPodId, VolumeInfo moveVolume(VolumeInfo volume, long destPoolDcId, Long destPoolPodId,
Long destPoolClusterId, HypervisorType dataDiskHyperType) Long destPoolClusterId, HypervisorType dataDiskHyperType)
throws ConcurrentOperationException; throws ConcurrentOperationException;

View File

@ -55,6 +55,7 @@ import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator; import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator;
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateDataFactory;
import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo; import org.apache.cloudstack.engine.subsystem.api.storage.TemplateInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeDataFactory;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService; import org.apache.cloudstack.engine.subsystem.api.storage.VolumeService;
@ -65,6 +66,7 @@ import org.apache.cloudstack.storage.command.AttachCommand;
import org.apache.cloudstack.storage.command.CommandResult; import org.apache.cloudstack.storage.command.CommandResult;
import org.apache.cloudstack.storage.command.DettachCommand; import org.apache.cloudstack.storage.command.DettachCommand;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
@ -227,6 +229,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
@Inject @Inject
protected StoragePoolHostDao _storagePoolHostDao; protected StoragePoolHostDao _storagePoolHostDao;
@Inject @Inject
StoragePoolDetailsDao storagePoolDetailsDao;
@Inject
protected AlertManager _alertMgr; protected AlertManager _alertMgr;
@Inject @Inject
protected TemplateDataStoreDao _vmTemplateStoreDao = null; protected TemplateDataStoreDao _vmTemplateStoreDao = null;
@ -507,7 +511,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
VolumeVO newVol = new VolumeVO(oldVol.getVolumeType(), VolumeVO newVol = new VolumeVO(oldVol.getVolumeType(),
oldVol.getName(), oldVol.getDataCenterId(), oldVol.getName(), oldVol.getDataCenterId(),
oldVol.getDomainId(), oldVol.getAccountId(), oldVol.getDomainId(), oldVol.getAccountId(),
oldVol.getDiskOfferingId(), oldVol.getSize()); oldVol.getDiskOfferingId(), oldVol.getSize(),
oldVol.getMinIops(), oldVol.getMaxIops(), oldVol.get_iScsiName());
if (templateId != null) { if (templateId != null) {
newVol.setTemplateId(templateId); newVol.setTemplateId(templateId);
} else { } else {
@ -680,9 +685,9 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
pool = storageMgr.findStoragePool(dskCh, dc, pod, clusterId, vm.getHostId(), pool = storageMgr.findStoragePool(dskCh, dc, pod, clusterId, vm.getHostId(),
vm, avoidPools); vm, avoidPools);
if (pool == null) { if (pool == null) {
s_logger.warn("Unable to find storage poll when create volume " s_logger.warn("Unable to find storage pool when create volume "
+ volume.getName()); + volume.getName());
throw new CloudRuntimeException("Unable to find storage poll when create volume" + volume.getName()); throw new CloudRuntimeException("Unable to find storage pool when create volume" + volume.getName());
} }
if (s_logger.isDebugEnabled()) { if (s_logger.isDebugEnabled()) {
@ -731,8 +736,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
Transaction txn = Transaction.currentTxn(); Transaction txn = Transaction.currentTxn();
txn.start(); txn.start();
VolumeVO volume = new VolumeVO(volumeName, zoneId, -1L, -1L, -1, VolumeVO volume = new VolumeVO(volumeName, zoneId, -1, -1, -1,
new Long(-1), null, null, 0, Volume.Type.DATADISK); new Long(-1), null, null, 0, null, null, null, Volume.Type.DATADISK);
volume.setPoolId(null); volume.setPoolId(null);
volume.setDataCenterId(zoneId); volume.setDataCenterId(zoneId);
volume.setPodId(null); volume.setPodId(null);
@ -835,6 +840,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
Long diskOfferingId = null; Long diskOfferingId = null;
DiskOfferingVO diskOffering = null; DiskOfferingVO diskOffering = null;
Long size = null; Long size = null;
Long minIops = null;
Long maxIops = null;
// Volume VO used for extracting the source template id // Volume VO used for extracting the source template id
VolumeVO parentVolume = null; VolumeVO parentVolume = null;
@ -896,6 +903,37 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
size = diskOffering.getDiskSize(); size = diskOffering.getDiskSize();
} }
Boolean isCustomizedIops = diskOffering.isCustomizedIops();
if (isCustomizedIops != null) {
if (isCustomizedIops) {
minIops = cmd.getMinIops();
maxIops = cmd.getMaxIops();
if (minIops == null && maxIops == null) {
minIops = 0L;
maxIops = 0L;
}
else {
if (minIops == null || minIops <= 0) {
throw new InvalidParameterValueException("The min IOPS must be greater than 0.");
}
if (maxIops == null) {
maxIops = 0L;
}
if (minIops > maxIops) {
throw new InvalidParameterValueException("The min IOPS must be less than or equal to the max IOPS.");
}
}
}
else {
minIops = diskOffering.getMinIops();
maxIops = diskOffering.getMaxIops();
}
}
if (!validateVolumeSizeRange(size)) {// convert size from mb to gb if (!validateVolumeSizeRange(size)) {// convert size from mb to gb
// for validation // for validation
throw new InvalidParameterValueException( throw new InvalidParameterValueException(
@ -970,8 +1008,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
Transaction txn = Transaction.currentTxn(); Transaction txn = Transaction.currentTxn();
txn.start(); txn.start();
VolumeVO volume = new VolumeVO(userSpecifiedName, -1L, -1L, -1, -1, VolumeVO volume = new VolumeVO(userSpecifiedName, -1, -1, -1, -1,
new Long(-1), null, null, 0, Volume.Type.DATADISK); new Long(-1), null, null, 0, null, null, null, Volume.Type.DATADISK);
volume.setPoolId(null); volume.setPoolId(null);
volume.setDataCenterId(zoneId); volume.setDataCenterId(zoneId);
volume.setPodId(null); volume.setPodId(null);
@ -980,6 +1018,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
.getDomainId())); .getDomainId()));
volume.setDiskOfferingId(diskOfferingId); volume.setDiskOfferingId(diskOfferingId);
volume.setSize(size); volume.setSize(size);
volume.setMinIops(minIops);
volume.setMaxIops(maxIops);
volume.setInstanceId(null); volume.setInstanceId(null);
volume.setUpdated(new Date()); volume.setUpdated(new Date());
volume.setDomainId((caller == null) ? Domain.ROOT_DOMAIN : caller volume.setDomainId((caller == null) ? Domain.ROOT_DOMAIN : caller
@ -1171,7 +1211,6 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
UserVmVO userVm = _userVmDao.findById(volume.getInstanceId()); UserVmVO userVm = _userVmDao.findById(volume.getInstanceId());
PrimaryDataStoreInfo pool = (PrimaryDataStoreInfo)dataStoreMgr.getDataStore(volume.getPoolId(), DataStoreRole.Primary);
long currentSize = volume.getSize(); long currentSize = volume.getSize();
/* /*
@ -1358,7 +1397,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
size = (size * 1024 * 1024 * 1024); size = (size * 1024 * 1024 * 1024);
} }
VolumeVO vol = new VolumeVO(type, name, vm.getDataCenterId(), VolumeVO vol = new VolumeVO(type, name, vm.getDataCenterId(),
owner.getDomainId(), owner.getId(), offering.getId(), size); owner.getDomainId(), owner.getId(), offering.getId(), size,
offering.getMinIops(), offering.getMaxIops(), null);
if (vm != null) { if (vm != null) {
vol.setInstanceId(vm.getId()); vol.setInstanceId(vm.getId());
} }
@ -1398,7 +1438,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
Long size = _tmpltMgr.getTemplateSize(template.getId(), vm.getDataCenterId()); Long size = _tmpltMgr.getTemplateSize(template.getId(), vm.getDataCenterId());
VolumeVO vol = new VolumeVO(type, name, vm.getDataCenterId(), VolumeVO vol = new VolumeVO(type, name, vm.getDataCenterId(),
owner.getDomainId(), owner.getId(), offering.getId(), size); owner.getDomainId(), owner.getId(), offering.getId(), size,
offering.getMinIops(), offering.getMaxIops(), null);
vol.setFormat(this.getSupportedImageFormatForCluster(template.getHypervisorType())); vol.setFormat(this.getSupportedImageFormatForCluster(template.getHypervisorType()));
if (vm != null) { if (vm != null) {
vol.setInstanceId(vm.getId()); vol.setInstanceId(vm.getId());
@ -1542,8 +1583,8 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
return !storeForRootStoreScope.isSameScope(storeForDataStoreScope); return !storeForRootStoreScope.isSameScope(storeForDataStoreScope);
} }
private VolumeVO sendAttachVolumeCommand(UserVmVO vm, VolumeVO volume, Long deviceId) { private VolumeVO sendAttachVolumeCommand(UserVmVO vm, VolumeVO volumeToAttach, Long deviceId) {
String errorMsg = "Failed to attach volume: " + volume.getName() String errorMsg = "Failed to attach volume: " + volumeToAttach.getName()
+ " to VM: " + vm.getHostName(); + " to VM: " + vm.getHostName();
boolean sendCommand = (vm.getState() == State.Running); boolean sendCommand = (vm.getState() == State.Running);
AttachAnswer answer = null; AttachAnswer answer = null;
@ -1557,12 +1598,37 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
} }
} }
StoragePoolVO volumeToAttachStoragePool = null;
if (sendCommand) { if (sendCommand) {
DataTO volTO = volFactory.getVolume(volume.getId()).getTO(); volumeToAttachStoragePool = _storagePoolDao.findById(volumeToAttach.getPoolId());
DiskTO disk = new DiskTO(volTO, deviceId, volume.getVolumeType()); long storagePoolId = volumeToAttachStoragePool.getId();
DataTO volTO = volFactory.getVolume(volumeToAttach.getId()).getTO();
DiskTO disk = new DiskTO(volTO, deviceId, null, volumeToAttach.getVolumeType());
AttachCommand cmd = new AttachCommand(disk, vm.getInstanceName()); AttachCommand cmd = new AttachCommand(disk, vm.getInstanceName());
cmd.setManaged(volumeToAttachStoragePool.isManaged());
cmd.setStorageHost(volumeToAttachStoragePool.getHostAddress());
cmd.setStoragePort(volumeToAttachStoragePool.getPort());
cmd.set_iScsiName(volumeToAttach.get_iScsiName());
VolumeInfo volumeInfo = volFactory.getVolume(volumeToAttach.getId());
DataStore dataStore = dataStoreMgr.getDataStore(storagePoolId, DataStoreRole.Primary);
ChapInfo chapInfo = volService.getChapInfo(volumeInfo, dataStore);
if (chapInfo != null) {
cmd.setChapInitiatorUsername(chapInfo.getInitiatorUsername());
cmd.setChapInitiatorPassword(chapInfo.getInitiatorSecret());
cmd.setChapTargetUsername(chapInfo.getTargetUsername());
cmd.setChapTargetPassword(chapInfo.getTargetSecret());
}
try { try {
answer = (AttachAnswer) _agentMgr.send(hostId, cmd); answer = (AttachAnswer)_agentMgr.send(hostId, cmd);
} catch (Exception e) { } catch (Exception e) {
throw new CloudRuntimeException(errorMsg + " due to: " throw new CloudRuntimeException(errorMsg + " due to: "
+ e.getMessage()); + e.getMessage());
@ -1573,19 +1639,29 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
// Mark the volume as attached // Mark the volume as attached
if (sendCommand) { if (sendCommand) {
DiskTO disk = answer.getDisk(); DiskTO disk = answer.getDisk();
_volsDao.attachVolume(volume.getId(), vm.getId(), _volsDao.attachVolume(volumeToAttach.getId(), vm.getId(),
disk.getDiskSeq()); disk.getDiskSeq());
volumeToAttach = _volsDao.findById(volumeToAttach.getId());
if (volumeToAttachStoragePool.isManaged() &&
volumeToAttach.getPath() == null) {
volumeToAttach.setPath(answer.getDisk().getVdiUuid());
_volsDao.update(volumeToAttach.getId(), volumeToAttach);
}
} else { } else {
_volsDao.attachVolume(volume.getId(), vm.getId(), deviceId); _volsDao.attachVolume(volumeToAttach.getId(), vm.getId(), deviceId);
} }
// insert record for disk I/O statistics // insert record for disk I/O statistics
VmDiskStatisticsVO diskstats = _vmDiskStatsDao.findBy(vm.getAccountId(), vm.getDataCenterId(),vm.getId(), volume.getId()); VmDiskStatisticsVO diskstats = _vmDiskStatsDao.findBy(vm.getAccountId(), vm.getDataCenterId(),vm.getId(), volumeToAttach.getId());
if (diskstats == null) { if (diskstats == null) {
diskstats = new VmDiskStatisticsVO(vm.getAccountId(), vm.getDataCenterId(),vm.getId(), volume.getId()); diskstats = new VmDiskStatisticsVO(vm.getAccountId(), vm.getDataCenterId(),vm.getId(), volumeToAttach.getId());
_vmDiskStatsDao.persist(diskstats); _vmDiskStatsDao.persist(diskstats);
} }
return _volsDao.findById(volume.getId()); return _volsDao.findById(volumeToAttach.getId());
} else { } else {
if (answer != null) { if (answer != null) {
String details = answer.getDetails(); String details = answer.getDetails();
@ -1912,9 +1988,17 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
Answer answer = null; Answer answer = null;
if (sendCommand) { if (sendCommand) {
StoragePoolVO volumePool = _storagePoolDao.findById(volume.getPoolId());
DataTO volTO = volFactory.getVolume(volume.getId()).getTO(); DataTO volTO = volFactory.getVolume(volume.getId()).getTO();
DiskTO disk = new DiskTO(volTO, volume.getDeviceId(), volume.getVolumeType()); DiskTO disk = new DiskTO(volTO, volume.getDeviceId(), null, volume.getVolumeType());
DettachCommand cmd = new DettachCommand(disk, vm.getInstanceName()); DettachCommand cmd = new DettachCommand(disk, vm.getInstanceName());
cmd.setManaged(volumePool.isManaged());
cmd.set_iScsiName(volume.get_iScsiName());
try { try {
answer = _agentMgr.send(vm.getHostId(), cmd); answer = _agentMgr.send(vm.getHostId(), cmd);
} catch (Exception e) { } catch (Exception e) {
@ -1926,6 +2010,7 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
if (!sendCommand || (answer != null && answer.getResult())) { if (!sendCommand || (answer != null && answer.getResult())) {
// Mark the volume as detached // Mark the volume as detached
_volsDao.detachVolume(volume.getId()); _volsDao.detachVolume(volume.getId());
return _volsDao.findById(volumeId); return _volsDao.findById(volumeId);
} else { } else {
@ -1940,11 +2025,6 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
} }
} }
@DB @DB
protected VolumeVO switchVolume(VolumeVO existingVolume, protected VolumeVO switchVolume(VolumeVO existingVolume,
VirtualMachineProfile<? extends VirtualMachine> vm) VirtualMachineProfile<? extends VirtualMachine> vm)
@ -2232,7 +2312,7 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
for (VolumeVO vol : vols) { for (VolumeVO vol : vols) {
DataTO volTO = volFactory.getVolume(vol.getId()).getTO(); DataTO volTO = volFactory.getVolume(vol.getId()).getTO();
DiskTO disk = new DiskTO(volTO, vol.getDeviceId(), vol.getVolumeType()); DiskTO disk = new DiskTO(volTO, vol.getDeviceId(), null, vol.getVolumeType());
vm.addDisk(disk); vm.addDisk(disk);
} }
@ -2240,7 +2320,7 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
UserVmVO userVM = (UserVmVO) vm.getVirtualMachine(); UserVmVO userVM = (UserVmVO) vm.getVirtualMachine();
if (userVM.getIsoId() != null) { if (userVM.getIsoId() != null) {
DataTO dataTO = tmplFactory.getTemplate(userVM.getIsoId(), DataStoreRole.Image, userVM.getDataCenterId()).getTO(); DataTO dataTO = tmplFactory.getTemplate(userVM.getIsoId(), DataStoreRole.Image, userVM.getDataCenterId()).getTO();
DiskTO iso = new DiskTO(dataTO, 3L, Volume.Type.ISO); DiskTO iso = new DiskTO(dataTO, 3L, null, Volume.Type.ISO);
vm.addDisk(iso); vm.addDisk(iso);
} }
} }
@ -2458,7 +2538,7 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
vol = result.first(); vol = result.first();
} }
DataTO volumeTO = volFactory.getVolume(vol.getId()).getTO(); DataTO volumeTO = volFactory.getVolume(vol.getId()).getTO();
DiskTO disk = new DiskTO(volumeTO, vol.getDeviceId(), vol.getVolumeType()); DiskTO disk = new DiskTO(volumeTO, vol.getDeviceId(), null, vol.getVolumeType());
vm.addDisk(disk); vm.addDisk(disk);
} }
} }
@ -2745,7 +2825,6 @@ public class VolumeManagerImpl extends ManagerBase implements VolumeManager {
@Override @Override
public String getVmNameFromVolumeId(long volumeId) { public String getVmNameFromVolumeId(long volumeId) {
Long instanceId;
VolumeVO volume = _volsDao.findById(volumeId); VolumeVO volume = _volsDao.findById(volumeId);
return getVmNameOnVolume(volume); return getVmNameOnVolume(volume);
} }

View File

@ -1023,7 +1023,7 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
} }
DataTO isoTO = tmplt.getTO(); DataTO isoTO = tmplt.getTO();
DiskTO disk = new DiskTO(isoTO, null, Volume.Type.ISO); DiskTO disk = new DiskTO(isoTO, null, null, Volume.Type.ISO);
Command cmd = null; Command cmd = null;
if (attach) { if (attach) {
cmd = new AttachCommand(disk, vmName); cmd = new AttachCommand(disk, vmName);

View File

@ -979,7 +979,7 @@ public class DatabaseConfig {
newTags.delete(newTags.length() - 1, newTags.length()); newTags.delete(newTags.length() - 1, newTags.length());
tags = newTags.toString(); tags = newTags.toString();
} }
DiskOfferingVO diskOffering = new DiskOfferingVO(domainId, name, displayText, diskSpace , tags, false); DiskOfferingVO diskOffering = new DiskOfferingVO(domainId, name, displayText, diskSpace, tags, false, null, null, null);
diskOffering.setUseLocalStorage(local); diskOffering.setUseLocalStorage(local);
Long bytesReadRate = Long.parseLong(_currentObjectParams.get("bytesReadRate")); Long bytesReadRate = Long.parseLong(_currentObjectParams.get("bytesReadRate"));

View File

@ -2910,12 +2910,12 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
TemplateObjectTO iso = (TemplateObjectTO)template.getTO(); TemplateObjectTO iso = (TemplateObjectTO)template.getTO();
iso.setGuestOsType(displayName); iso.setGuestOsType(displayName);
DiskTO disk = new DiskTO(iso, 3L, Volume.Type.ISO); DiskTO disk = new DiskTO(iso, 3L, null, Volume.Type.ISO);
profile.addDisk(disk); profile.addDisk(disk);
} else { } else {
TemplateObjectTO iso = new TemplateObjectTO(); TemplateObjectTO iso = new TemplateObjectTO();
iso.setFormat(ImageFormat.ISO); iso.setFormat(ImageFormat.ISO);
DiskTO disk = new DiskTO(iso, 3L, Volume.Type.ISO); DiskTO disk = new DiskTO(iso, 3L, null, Volume.Type.ISO);
profile.addDisk(disk); profile.addDisk(disk);
} }

View File

@ -655,8 +655,9 @@ public class MockConfigurationManagerImpl extends ManagerBase implements Configu
* @see com.cloud.configuration.ConfigurationManager#createDiskOffering(java.lang.Long, java.lang.String, java.lang.String, java.lang.Long, java.lang.String, boolean, boolean, boolean) * @see com.cloud.configuration.ConfigurationManager#createDiskOffering(java.lang.Long, java.lang.String, java.lang.String, java.lang.Long, java.lang.String, boolean, boolean, boolean)
*/ */
@Override @Override
public DiskOfferingVO createDiskOffering(Long domainId, String name, String description, Long numGibibytes, String tags, boolean isCustomized, boolean localStorageRequired, boolean isDisplayOfferingEnabled, public DiskOfferingVO createDiskOffering(Long domainId, String name, String description, Long numGibibytes, String tags, boolean isCustomized,
Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate) { boolean localStorageRequired, boolean isDisplayOfferingEnabled, Boolean isCustomizedIops, Long minIops, Long maxIops,
Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
return null; return null;
} }

View File

@ -429,6 +429,20 @@ ALTER TABLE `cloud`.`nics` ADD COLUMN `display_nic` tinyint(1) NOT NULL DEFAULT
ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `display_offering` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Should disk offering be displayed to the end user'; ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `display_offering` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Should disk offering be displayed to the end user';
ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `customized_iops` tinyint(1) unsigned COMMENT 'Should customized IOPS be displayed to the end user';
ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `min_iops` bigint(20) unsigned COMMENT 'Minimum IOPS';
ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `max_iops` bigint(20) unsigned COMMENT 'Maximum IOPS';
ALTER TABLE `cloud`.`volumes` ADD COLUMN `min_iops` bigint(20) unsigned COMMENT 'Minimum IOPS';
ALTER TABLE `cloud`.`volumes` ADD COLUMN `max_iops` bigint(20) unsigned COMMENT 'Maximum IOPS';
ALTER TABLE `cloud`.`storage_pool` ADD COLUMN `managed` tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT 'Should CloudStack manage this storage';
ALTER TABLE `cloud`.`storage_pool` ADD COLUMN `capacity_iops` bigint(20) unsigned DEFAULT NULL COMMENT 'IOPS CloudStack can provision from this storage pool';
ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `bytes_read_rate` bigint(20); ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `bytes_read_rate` bigint(20);
ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `bytes_write_rate` bigint(20); ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `bytes_write_rate` bigint(20);
@ -871,6 +885,8 @@ CREATE VIEW `cloud`.`volume_view` AS
volumes.device_id, volumes.device_id,
volumes.volume_type, volumes.volume_type,
volumes.size, volumes.size,
volumes.min_iops,
volumes.max_iops,
volumes.created, volumes.created,
volumes.state, volumes.state,
volumes.attached, volumes.attached,
@ -981,6 +997,7 @@ CREATE VIEW `cloud`.`storage_pool_view` AS
storage_pool.created, storage_pool.created,
storage_pool.removed, storage_pool.removed,
storage_pool.capacity_bytes, storage_pool.capacity_bytes,
storage_pool.capacity_iops,
storage_pool.scope, storage_pool.scope,
storage_pool.hypervisor, storage_pool.hypervisor,
cluster.id cluster_id, cluster.id cluster_id,
@ -1521,9 +1538,12 @@ CREATE VIEW `cloud`.`disk_offering_view` AS
disk_offering.name, disk_offering.name,
disk_offering.display_text, disk_offering.display_text,
disk_offering.disk_size, disk_offering.disk_size,
disk_offering.min_iops,
disk_offering.max_iops,
disk_offering.created, disk_offering.created,
disk_offering.tags, disk_offering.tags,
disk_offering.customized, disk_offering.customized,
disk_offering.customized_iops,
disk_offering.removed, disk_offering.removed,
disk_offering.use_local_storage, disk_offering.use_local_storage,
disk_offering.system_use, disk_offering.system_use,
@ -1736,6 +1756,8 @@ CREATE VIEW `cloud`.`volume_view` AS
volumes.device_id, volumes.device_id,
volumes.volume_type, volumes.volume_type,
volumes.size, volumes.size,
volumes.min_iops,
volumes.max_iops,
volumes.created, volumes.created,
volumes.state, volumes.state,
volumes.attached, volumes.attached,

View File

@ -203,7 +203,7 @@ class cloudConnection(object):
i = i + 1 i = i + 1
return cmdname, isAsync, requests return cmdname, isAsync, requests
def marvin_request(self, cmd, response_type=None, method='GET'): def marvin_request(self, cmd, response_type=None, method='GET', data=''):
""" """
Requester for marvin command objects Requester for marvin command objects
@param cmd: marvin's command from cloudstackAPI @param cmd: marvin's command from cloudstackAPI

View File

@ -25,6 +25,9 @@ under the License.
<% long now = System.currentTimeMillis(); %> <% long now = System.currentTimeMillis(); %>
<script language="javascript"> <script language="javascript">
dictionary = { dictionary = {
'label.custom.disk.iops': '<fmt:message key="label.custom.disk.iops" />',
'label.disk.iops.min': '<fmt:message key="label.disk.iops.min" />',
'label.disk.iops.max': '<fmt:message key="label.disk.iops.max" />',
'label.acquire.new.secondary.ip': '<fmt:message key="label.acquire.new.secondary.ip" />', 'label.acquire.new.secondary.ip': '<fmt:message key="label.acquire.new.secondary.ip" />',
'label.view.secondary.ips': '<fmt:message key="label.view.secondary.ips" />', 'label.view.secondary.ips': '<fmt:message key="label.view.secondary.ips" />',
'message.acquire.ip.nic': '<fmt:message key="message.acquire.ip.nic" />', 'message.acquire.ip.nic': '<fmt:message key="message.acquire.ip.nic" />',
@ -472,6 +475,7 @@ dictionary = {
'label.disable.vpn': '<fmt:message key="label.disable.vpn" />', 'label.disable.vpn': '<fmt:message key="label.disable.vpn" />',
'label.disabling.vpn.access': '<fmt:message key="label.disabling.vpn.access" />', 'label.disabling.vpn.access': '<fmt:message key="label.disabling.vpn.access" />',
'label.disk.allocated': '<fmt:message key="label.disk.allocated" />', 'label.disk.allocated': '<fmt:message key="label.disk.allocated" />',
'label.disk.iops.total': '<fmt:message key="label.disk.iops.total" />',
'label.disk.bytes.read.rate': '<fmt:message key="label.disk.bytes.read.rate" />', 'label.disk.bytes.read.rate': '<fmt:message key="label.disk.bytes.read.rate" />',
'label.disk.bytes.write.rate': '<fmt:message key="label.disk.bytes.write.rate" />', 'label.disk.bytes.write.rate': '<fmt:message key="label.disk.bytes.write.rate" />',
'label.disk.iops.write.rate': '<fmt:message key="label.disk.iops.write.rate" />', 'label.disk.iops.write.rate': '<fmt:message key="label.disk.iops.write.rate" />',
@ -1029,6 +1033,7 @@ dictionary = {
'label.storage': '<fmt:message key="label.storage" />', 'label.storage': '<fmt:message key="label.storage" />',
'label.storage.tags': '<fmt:message key="label.storage.tags" />', 'label.storage.tags': '<fmt:message key="label.storage.tags" />',
'label.storage.type': '<fmt:message key="label.storage.type" />', 'label.storage.type': '<fmt:message key="label.storage.type" />',
'label.qos.type': '<fmt:message key="label.qos.type" />',
'label.subdomain.access': '<fmt:message key="label.subdomain.access" />', 'label.subdomain.access': '<fmt:message key="label.subdomain.access" />',
'label.submit': '<fmt:message key="label.submit" />', 'label.submit': '<fmt:message key="label.submit" />',
'label.submitted.by': '<fmt:message key="label.submitted.by" />', 'label.submitted.by': '<fmt:message key="label.submitted.by" />',

View File

@ -1015,6 +1015,86 @@
dependsOn: 'isCustomized', dependsOn: 'isCustomized',
validation: { required: true, number: true } validation: { required: true, number: true }
}, },
qosType: {
label: 'label.qos.type',
docID: 'helpDiskOfferingQoSType',
select: function(args) {
var items = [];
items.push({id: '', description: ''});
items.push({id: 'hypervisor', description: 'hypervisor'});
items.push({id: 'storage', description: 'storage'});
args.response.success({data: items});
args.$select.change(function() {
var $form = $(this).closest('form');
var $isCustomizedIops = $form.find('.form-item[rel=isCustomizedIops]');
var $minIops = $form.find('.form-item[rel=minIops]');
var $maxIops = $form.find('.form-item[rel=maxIops]');
var $diskBytesReadRate = $form.find('.form-item[rel=diskBytesReadRate]');
var $diskBytesWriteRate = $form.find('.form-item[rel=diskBytesWriteRate]');
var $diskIopsReadRate = $form.find('.form-item[rel=diskIopsReadRate]');
var $diskIopsWriteRate = $form.find('.form-item[rel=diskIopsWriteRate]');
var qosId = $(this).val();
if (qosId == 'storage') { // Storage QoS
$diskBytesReadRate.hide();
$diskBytesWriteRate.hide();
$diskIopsReadRate.hide();
$diskIopsWriteRate.hide();
$isCustomizedIops.css('display', 'inline-block');
if ($isCustomizedIops == true) {
$minIops.css('display', 'inline-block');
$maxIops.css('display', 'inline-block');
}
else {
$minIops.hide();
$maxIops.hide();
}
}
else if (qosId == 'hypervisor') { // Hypervisor Qos
$isCustomizedIops.hide();
$minIops.hide();
$maxIops.hide();
$diskBytesReadRate.css('display', 'inline-block');
$diskBytesWriteRate.css('display', 'inline-block');
$diskIopsReadRate.css('display', 'inline-block');
$diskIopsWriteRate.css('display', 'inline-block');
}
else { // No Qos
$diskBytesReadRate.hide();
$diskBytesWriteRate.hide();
$diskIopsReadRate.hide();
$diskIopsWriteRate.hide();
$isCustomizedIops.hide();
$minIops.hide();
$maxIops.hide();
}
});
}
},
isCustomizedIops: {
label: 'label.custom.disk.iops',
docID: 'helpDiskOfferingCustomDiskIops',
isBoolean: true,
isReverse: true,
isChecked: false
},
minIops: {
label: 'label.disk.iops.min',
docID: 'helpDiskOfferingDiskIopsMin',
dependsOn: 'isCustomizedIops',
validation: { required: false, number: true }
},
maxIops: {
label: 'label.disk.iops.max',
docID: 'helpDiskOfferingDiskIopsMax',
dependsOn: 'isCustomizedIops',
validation: { required: false, number: true }
},
diskBytesReadRate: { diskBytesReadRate: {
label: 'label.disk.bytes.read.rate', label: 'label.disk.bytes.read.rate',
validation: { validation: {
@ -1080,18 +1160,65 @@
action: function(args) { action: function(args) {
var data = { var data = {
isMirrored: false, isMirrored: false,
name: args.data.name, name: args.data.name,
displaytext: args.data.description, displaytext: args.data.description,
storageType: args.data.storageType, storageType: args.data.storageType,
customized: (args.data.isCustomized=="on") customized: (args.data.isCustomized=="on")
}; };
if(args.$form.find('.form-item[rel=disksize]').css("display") != "none") { if(args.$form.find('.form-item[rel=disksize]').css("display") != "none") {
$.extend(data, { $.extend(data, {
disksize: args.data.disksize disksize: args.data.disksize
}); });
} }
if (args.data.qosType == 'storage') {
var customIops = args.data.isCustomizedIops == "on";
$.extend(data, {
customizediops: customIops
});
if (!customIops) {
if (args.data.minIops != null && args.data.minIops.length > 0) {
$.extend(data, {
miniops: args.data.minIops
});
}
if(args.data.maxIops != null && args.data.maxIops.length > 0) {
$.extend(data, {
maxiops: args.data.maxIops
});
}
}
}
else if (args.data.qosType == 'hypervisor') {
if (args.data.diskBytesReadRate != null && args.data.diskBytesReadRate.length > 0) {
$.extend(data, {
bytesreadrate: args.data.diskBytesReadRate
});
}
if (args.data.diskBytesWriteRate != null && args.data.diskBytesWriteRate.length > 0) {
$.extend(data, {
byteswriterate: args.data.diskBytesWriteRate
});
}
if (args.data.diskIopsReadRate != null && args.data.diskIopsReadRate.length > 0) {
$.extend(data, {
iopsreadrate: args.data.diskIopsReadRate
});
}
if (args.data.diskIopsWriteRate != null && args.data.diskIopsWriteRate.length > 0) {
$.extend(data, {
iopswriterate: args.data.diskIopsWriteRate
});
}
}
if(args.data.tags != null && args.data.tags.length > 0) { if(args.data.tags != null && args.data.tags.length > 0) {
$.extend(data, { $.extend(data, {
@ -1104,26 +1231,6 @@
domainid: args.data.domainId domainid: args.data.domainId
}); });
} }
if(args.data.diskBytesReadRate != null && args.data.diskBytesReadRate.length > 0) {
$.extend(data, {
bytesreadrate: args.data.diskBytesReadRate
});
}
if(args.data.diskBytesWriteRate != null && args.data.diskBytesWriteRate.length > 0) {
$.extend(data, {
byteswriterate: args.data.diskBytesWriteRate
});
}
if(args.data.diskIopsReadRate != null && args.data.diskIopsReadRate.length > 0) {
$.extend(data, {
iopsreadrate: args.data.diskIopsReadRate
});
}
if(args.data.diskIopsWriteRate != null && args.data.diskIopsWriteRate.length > 0) {
$.extend(data, {
iopswriterate: args.data.diskIopsWriteRate
});
}
$.ajax({ $.ajax({
url: createURL('createDiskOffering'), url: createURL('createDiskOffering'),
@ -1236,6 +1343,28 @@
return "N/A"; return "N/A";
} }
}, },
iscustomizediops: {
label: 'label.custom.disk.iops',
converter: cloudStack.converters.toBooleanText
},
miniops: {
label: 'label.disk.iops.min',
converter: function(args) {
if(args > 0)
return args;
else
return "N/A";
}
},
maxiops: {
label: 'label.disk.iops.max',
converter: function(args) {
if(args > 0)
return args;
else
return "N/A";
}
},
diskBytesReadRate: { label: 'label.disk.bytes.write.rate' }, diskBytesReadRate: { label: 'label.disk.bytes.write.rate' },
diskBytesWriteRate: { label: 'label.disk.bytes.write.rate' }, diskBytesWriteRate: { label: 'label.disk.bytes.write.rate' },
diskIopsReadRate: { label: 'label.disk.iops.write.rate' }, diskIopsReadRate: { label: 'label.disk.iops.write.rate' },

View File

@ -270,6 +270,10 @@ cloudStack.docs = {
desc: 'Type of disk for the VM. Local is attached to the hypervisor host where the VM is running. Shared is storage accessible via NFS.', desc: 'Type of disk for the VM. Local is attached to the hypervisor host where the VM is running. Shared is storage accessible via NFS.',
externalLink: '' externalLink: ''
}, },
helpDiskOfferingQoSType: {
desc: 'Type of Quality of Service desired, if any.',
externalLink: ''
},
helpDiskOfferingCustomDiskSize: { helpDiskOfferingCustomDiskSize: {
desc: 'If checked, the user can set their own disk size. If not checked, the root administrator must define a value in Disk Size.', desc: 'If checked, the user can set their own disk size. If not checked, the root administrator must define a value in Disk Size.',
externalLink: '' externalLink: ''
@ -278,6 +282,18 @@ cloudStack.docs = {
desc: 'Appears only if Custom Disk Size is not selected. Define the volume size in GB.', desc: 'Appears only if Custom Disk Size is not selected. Define the volume size in GB.',
externalLink: '' externalLink: ''
}, },
helpDiskOfferingCustomDiskIops: {
desc: 'If checked, the user can set Min and Max IOPS. If not checked, the root administrator can define such values.',
externalLink: ''
},
helpDiskOfferingDiskIopsMin: {
desc: 'Appears only if Custom IOPS is not selected. Define the minimum volume IOPS.',
externalLink: ''
},
helpDiskOfferingDiskIopsMax: {
desc: 'Appears only if Custom IOPS is not selected. Define the maximum volume IOPS.',
externalLink: ''
},
helpDiskOfferingStorageTags: { helpDiskOfferingStorageTags: {
desc: 'Comma-separated list of attributes that should be associated with the primary storage for this disk. For example "ssd,blue".', desc: 'Comma-separated list of attributes that should be associated with the primary storage for this disk. For example "ssd,blue".',
externalLink: '' externalLink: ''

View File

@ -321,8 +321,8 @@ cloudStack.converters = {
toBooleanText: function(booleanValue) { toBooleanText: function(booleanValue) {
if(booleanValue == true) if(booleanValue == true)
return "Yes"; return "Yes";
else if(booleanValue == false)
return "No"; return "No";
}, },
convertHz: function(hz) { convertHz: function(hz) {
if (hz == null) if (hz == null)

View File

@ -132,6 +132,16 @@
else { else {
$diskSize.hide(); $diskSize.hide();
} }
var $minIops = $form.find('.form-item[rel=minIops]');
var $maxIops = $form.find('.form-item[rel=maxIops]');
if (selectedDiskOfferingObj.iscustomizediops == true) {
$minIops.css('display', 'inline-block');
$maxIops.css('display', 'inline-block');
}
else {
$minIops.hide();
$maxIops.hide();
}
}); });
} }
} }
@ -141,7 +151,19 @@
label: 'label.disk.size.gb', label: 'label.disk.size.gb',
validation: { required: true, number: true }, validation: { required: true, number: true },
isHidden: true isHidden: true
} },
minIops: {
label: 'label.disk.iops.min',
validation: { required: false, number: true },
isHidden: true
},
maxIops: {
label: 'label.disk.iops.max',
validation: { required: false, number: true },
isHidden: true
},
} }
}, },
@ -159,6 +181,20 @@
size: args.data.diskSize size: args.data.diskSize
}); });
} }
if (selectedDiskOfferingObj.iscustomizediops == true) {
if (args.data.minIops != "" && args.data.minIops > 0) {
$.extend(data, {
miniops: args.data.minIops
});
}
if (args.data.maxIops != "" && args.data.maxIops > 0) {
$.extend(data, {
maxiops: args.data.maxIops
});
}
}
$.ajax({ $.ajax({
url: createURL('createVolume'), url: createURL('createVolume'),
@ -1228,6 +1264,24 @@
return cloudStack.converters.convertBytes(args); return cloudStack.converters.convertBytes(args);
} }
}, },
miniops: {
label: 'label.disk.iops.min',
converter: function(args) {
if(args == null || args == 0)
return "";
else
return args;
}
},
maxiops: {
label: 'label.disk.iops.max',
converter: function(args) {
if(args == null || args == 0)
return "";
else
return args;
}
},
virtualmachineid: { virtualmachineid: {
label: 'VM ID', label: 'VM ID',
converter: function(args) { converter: function(args) {

View File

@ -12745,11 +12745,11 @@
{ {
id: { label: 'label.id' }, id: { label: 'label.id' },
state: { label: 'label.state' }, state: { label: 'label.state' },
tags: { tags: {
label: 'label.storage.tags', label: 'label.storage.tags',
isEditable: true isEditable: true
}, },
podname: { label: 'label.pod' }, podname: { label: 'label.pod' },
clustername: { label: 'label.cluster' }, clustername: { label: 'label.cluster' },
type: { label: 'label.type' }, type: { label: 'label.type' },
ipaddress: { label: 'label.ip.address' }, ipaddress: { label: 'label.ip.address' },
@ -12771,6 +12771,15 @@
else else
return cloudStack.converters.convertBytes(args); return cloudStack.converters.convertBytes(args);
} }
},
capacityiops: {
label: 'label.disk.iops.total',
converter: function(args) {
if (args == null || args == 0)
return "";
else
return args;
}
} }
} }
], ],

View File

@ -49,6 +49,14 @@ public class StringUtils {
return org.apache.commons.lang.StringUtils.join(components, delimiter); return org.apache.commons.lang.StringUtils.join(components, delimiter);
} }
public static boolean isNotBlank(String str) {
if (str != null && str.trim().length() > 0) {
return true;
}
return false;
}
/** /**
* @param tags * @param tags
* @return List of tags * @return List of tags

View File

@ -26,14 +26,16 @@ import com.vmware.vim25.CustomFieldStringValue;
import com.vmware.vim25.DatastoreInfo; import com.vmware.vim25.DatastoreInfo;
import com.vmware.vim25.DynamicProperty; import com.vmware.vim25.DynamicProperty;
import com.vmware.vim25.HostNasVolumeSpec; import com.vmware.vim25.HostNasVolumeSpec;
import com.vmware.vim25.HostScsiDisk;
import com.vmware.vim25.ManagedObjectReference; import com.vmware.vim25.ManagedObjectReference;
import com.vmware.vim25.NasDatastoreInfo; import com.vmware.vim25.NasDatastoreInfo;
import com.vmware.vim25.ObjectContent; import com.vmware.vim25.ObjectContent;
import com.vmware.vim25.ObjectSpec; import com.vmware.vim25.ObjectSpec;
import com.vmware.vim25.PropertyFilterSpec; import com.vmware.vim25.PropertyFilterSpec;
import com.vmware.vim25.PropertySpec; import com.vmware.vim25.PropertySpec;
import com.vmware.vim25.SelectionSpec;
import com.vmware.vim25.TraversalSpec; import com.vmware.vim25.TraversalSpec;
import com.vmware.vim25.VmfsDatastoreCreateSpec;
import com.vmware.vim25.VmfsDatastoreOption;
public class HostDatastoreSystemMO extends BaseMO { public class HostDatastoreSystemMO extends BaseMO {
@ -122,6 +124,22 @@ public class HostDatastoreSystemMO extends BaseMO {
return null; return null;
} }
public List<HostScsiDisk> queryAvailableDisksForVmfs() throws Exception {
return _context.getService().queryAvailableDisksForVmfs(_mor, null);
}
public ManagedObjectReference createVmfsDatastore(String datastoreName, HostScsiDisk hostScsiDisk) throws Exception {
// just grab the first instance of VmfsDatastoreOption
VmfsDatastoreOption vmfsDatastoreOption = _context.getService().queryVmfsDatastoreCreateOptions(_mor, hostScsiDisk.getDevicePath(), 4).get(0);
VmfsDatastoreCreateSpec vmfsDatastoreCreateSpec = (VmfsDatastoreCreateSpec)vmfsDatastoreOption.getSpec();
// set the name of the datastore to be created
vmfsDatastoreCreateSpec.getVmfs().setVolumeName(datastoreName);
return _context.getService().createVmfsDatastore(_mor, vmfsDatastoreCreateSpec);
}
public boolean deleteDatastore(String name) throws Exception { public boolean deleteDatastore(String name) throws Exception {
ManagedObjectReference morDatastore = findDatastore(name); ManagedObjectReference morDatastore = findDatastore(name);
if(morDatastore != null) { if(morDatastore != null) {

View File

@ -149,6 +149,13 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost {
throw new Exception("Could not find host default gateway, host is not properly configured?"); throw new Exception("Could not find host default gateway, host is not properly configured?");
} }
public HostStorageSystemMO getHostStorageSystemMO() throws Exception {
return new HostStorageSystemMO(_context,
(ManagedObjectReference)_context.getVimClient().getDynamicProperty(
_mor, "configManager.storageSystem")
);
}
public HostDatastoreSystemMO getHostDatastoreSystemMO() throws Exception { public HostDatastoreSystemMO getHostDatastoreSystemMO() throws Exception {
return new HostDatastoreSystemMO(_context, return new HostDatastoreSystemMO(_context,
(ManagedObjectReference)_context.getVimClient().getDynamicProperty( (ManagedObjectReference)_context.getVimClient().getDynamicProperty(
@ -797,14 +804,14 @@ public class HostMO extends BaseMO implements VmwareHypervisorHost {
} }
@Override @Override
public void unmountDatastore(String poolUuid) throws Exception { public void unmountDatastore(String uuid) throws Exception {
if(s_logger.isTraceEnabled()) if(s_logger.isTraceEnabled())
s_logger.trace("vCenter API trace - unmountDatastore(). target MOR: " + _mor.getValue() + ", poolUuid: " + poolUuid); s_logger.trace("vCenter API trace - unmountDatastore(). target MOR: " + _mor.getValue() + ", uuid: " + uuid);
HostDatastoreSystemMO hostDatastoreSystemMo = getHostDatastoreSystemMO(); HostDatastoreSystemMO hostDatastoreSystemMo = getHostDatastoreSystemMO();
if(!hostDatastoreSystemMo.deleteDatastore(poolUuid)) { if(!hostDatastoreSystemMo.deleteDatastore(uuid)) {
String msg = "Unable to unmount datastore. uuid: " + poolUuid; String msg = "Unable to unmount datastore. uuid: " + uuid;
s_logger.error(msg); s_logger.error(msg);
if(s_logger.isTraceEnabled()) if(s_logger.isTraceEnabled())

View File

@ -0,0 +1,51 @@
// 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.vmware.mo;
import java.util.List;
import com.cloud.hypervisor.vmware.util.VmwareContext;
import com.vmware.vim25.HostInternetScsiHbaStaticTarget;
import com.vmware.vim25.HostStorageDeviceInfo;
import com.vmware.vim25.ManagedObjectReference;
public class HostStorageSystemMO extends BaseMO {
public HostStorageSystemMO(VmwareContext context, ManagedObjectReference morHostDatastore) {
super(context, morHostDatastore);
}
public HostStorageSystemMO(VmwareContext context, String morType, String morValue) {
super(context, morType, morValue);
}
public HostStorageDeviceInfo getStorageDeviceInfo() throws Exception {
return (HostStorageDeviceInfo)_context.getVimClient().getDynamicProperty(_mor, "storageDeviceInfo");
}
public void addInternetScsiStaticTargets(String iScsiHbaDevice, List<HostInternetScsiHbaStaticTarget> lstTargets) throws Exception {
_context.getService().addInternetScsiStaticTargets(_mor, iScsiHbaDevice, lstTargets);
}
public void removeInternetScsiStaticTargets(String iScsiHbaDevice, List<HostInternetScsiHbaStaticTarget> lstTargets) throws Exception {
_context.getService().removeInternetScsiStaticTargets(_mor, iScsiHbaDevice, lstTargets);
}
public void rescanHba(String iScsiHbaDevice) throws Exception {
_context.getService().rescanHba(_mor, iScsiHbaDevice);
}
}