From 11f5bdd78de4121331b07995800f6e9e7c22f2c0 Mon Sep 17 00:00:00 2001 From: Marcus Sorensen Date: Thu, 10 Apr 2014 09:23:04 -0600 Subject: [PATCH] CLOUDSTACK-6191 Add support for specifying volume provisioning type (thin, sparse, fat) in disk/compute offerings. Submitted-by: Yoshikazu Nojima Reviewed-by: Marcus Sorensen, Mike Tutowski --- api/src/com/cloud/offering/DiskOffering.java | 4 + api/src/com/cloud/storage/Storage.java | 45 +- api/src/com/cloud/storage/Volume.java | 2 + api/src/com/cloud/vm/DiskProfile.java | 10 + .../apache/cloudstack/api/ApiConstants.java | 1 + .../admin/offering/CreateDiskOfferingCmd.java | 14 +- .../offering/CreateServiceOfferingCmd.java | 8 + .../api/response/DiskOfferingResponse.java | 11 + .../api/response/ServiceOfferingResponse.java | 11 + .../api/response/VolumeResponse.java | 8 + .../classes/resources/messages.properties | 1 + .../cloudstack/storage/to/VolumeObjectTO.java | 10 + .../orchestration/VolumeOrchestrator.java | 38 +- .../vm/VirtualMachineManagerImplTest.java | 15 +- .../com/cloud/service/ServiceOfferingVO.java | 17 +- .../src/com/cloud/storage/DiskOfferingVO.java | 29 +- .../src/com/cloud/storage/VolumeVO.java | 36 +- .../storage/volume/VolumeObject.java | 6 + .../resource/LibvirtComputingResource.java | 5 +- .../kvm/resource/LibvirtStorageVolumeDef.java | 18 + .../kvm/storage/IscsiAdmStorageAdaptor.java | 8 +- .../kvm/storage/IscsiAdmStoragePool.java | 5 +- .../kvm/storage/KVMStoragePool.java | 5 +- .../kvm/storage/KVMStoragePoolManager.java | 25 +- .../kvm/storage/KVMStorageProcessor.java | 5 +- .../kvm/storage/LibvirtStorageAdaptor.java | 501 +++++++++++------- .../kvm/storage/LibvirtStoragePool.java | 12 +- .../kvm/storage/StorageAdaptor.java | 8 +- .../apache/cloudstack/utils/qemu/QemuImg.java | 67 ++- .../cloudstack/utils/qemu/QemuImgTest.java | 46 +- .../lb/ElasticLoadBalancerManagerImpl.java | 6 +- .../lb/InternalLoadBalancerVMManagerImpl.java | 4 +- .../InternalLBVMManagerTest.java | 5 +- .../InternalLBVMServiceTest.java | 5 +- .../query/dao/DiskOfferingJoinDaoImpl.java | 1 + .../query/dao/ServiceOfferingJoinDaoImpl.java | 1 + .../api/query/dao/VolumeJoinDaoImpl.java | 2 + .../api/query/vo/DiskOfferingJoinVO.java | 8 + .../api/query/vo/ServiceOfferingJoinVO.java | 9 + .../com/cloud/api/query/vo/VolumeJoinVO.java | 8 + .../ConfigurationManagerImpl.java | 36 +- .../consoleproxy/ConsoleProxyManagerImpl.java | 5 +- .../VirtualNetworkApplianceManagerImpl.java | 6 +- .../cloud/server/ConfigurationServerImpl.java | 27 +- .../cloud/storage/VolumeApiServiceImpl.java | 82 +-- server/src/com/cloud/test/DatabaseConfig.java | 8 +- .../storage/VolumeApiServiceImplTest.java | 14 +- .../vm/DeploymentPlanningManagerImplTest.java | 10 +- .../test/com/cloud/vm/UserVmManagerTest.java | 4 +- .../service/ServiceOfferingVOTest.java | 5 +- .../SecondaryStorageManagerImpl.java | 6 +- setup/db/db/schema-440to450.sql | 201 +++++++ test/integration/smoke/test_disk_offerings.py | 92 ++++ test/integration/smoke/test_volumes.py | 22 + ui/dictionary.jsp | 1 + ui/scripts/configuration.js | 80 ++- ui/scripts/docs.js | 8 + ui/scripts/storage.js | 3 + 58 files changed, 1236 insertions(+), 394 deletions(-) diff --git a/api/src/com/cloud/offering/DiskOffering.java b/api/src/com/cloud/offering/DiskOffering.java index 4641b9a4ad8..928f7132d8e 100644 --- a/api/src/com/cloud/offering/DiskOffering.java +++ b/api/src/com/cloud/offering/DiskOffering.java @@ -22,6 +22,8 @@ import org.apache.cloudstack.acl.InfrastructureEntity; import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.InternalIdentity; +import com.cloud.storage.Storage.ProvisioningType; + /** * Represents a disk offering that specifies what the end user needs in * the disk offering. @@ -61,6 +63,8 @@ public interface DiskOffering extends InfrastructureEntity, Identity, InternalId String getDisplayText(); + public ProvisioningType getProvisioningType(); + public String getTags(); public String[] getTagsArray(); diff --git a/api/src/com/cloud/storage/Storage.java b/api/src/com/cloud/storage/Storage.java index 8ab2463a81b..cc8d0100f36 100755 --- a/api/src/com/cloud/storage/Storage.java +++ b/api/src/com/cloud/storage/Storage.java @@ -16,6 +16,8 @@ // under the License. package com.cloud.storage; +import org.apache.commons.lang.NotImplementedException; + import java.util.ArrayList; import java.util.List; @@ -32,27 +34,27 @@ public class Storage { VDI(true, true, false, "vdi"), TAR(false, false, false, "tar"); - private final boolean thinProvisioned; + private final boolean supportThinProvisioning; private final boolean supportSparse; private final boolean supportSnapshot; private final String fileExtension; - private ImageFormat(boolean thinProvisioned, boolean supportSparse, boolean supportSnapshot) { - this.thinProvisioned = thinProvisioned; + private ImageFormat(boolean supportThinProvisioning, boolean supportSparse, boolean supportSnapshot) { + this.supportThinProvisioning = supportThinProvisioning; this.supportSparse = supportSparse; this.supportSnapshot = supportSnapshot; fileExtension = null; } - private ImageFormat(boolean thinProvisioned, boolean supportSparse, boolean supportSnapshot, String fileExtension) { - this.thinProvisioned = thinProvisioned; + private ImageFormat(boolean supportThinProvisioning, boolean supportSparse, boolean supportSnapshot, String fileExtension) { + this.supportThinProvisioning = supportThinProvisioning; this.supportSparse = supportSparse; this.supportSnapshot = supportSnapshot; this.fileExtension = fileExtension; } - public boolean isThinProvisioned() { - return thinProvisioned; + public boolean supportThinProvisioning() { + return supportThinProvisioning; } public boolean supportsSparse() { @@ -72,6 +74,35 @@ public class Storage { } + public static enum ProvisioningType { + THIN("thin"), + SPARSE("sparse"), + FAT("fat"); + + private final String provisionType; + + private ProvisioningType(String provisionType){ + this.provisionType = provisionType; + } + + public String toString(){ + return this.provisionType; + } + + public static ProvisioningType getProvisioningType(String provisioningType){ + + if(provisioningType.equals(THIN.provisionType)){ + return ProvisioningType.THIN; + } else if(provisioningType.equals(SPARSE.provisionType)){ + return ProvisioningType.SPARSE; + } else if (provisioningType.equals(FAT.provisionType)){ + return ProvisioningType.FAT; + } else{ + throw new NotImplementedException(); + } + } + } + public static enum FileSystem { Unknown, ext3, ntfs, fat, fat32, ext2, ext4, cdfs, hpfs, ufs, hfs, hfsp } diff --git a/api/src/com/cloud/storage/Volume.java b/api/src/com/cloud/storage/Volume.java index 304dbcfe342..b9c27d71349 100755 --- a/api/src/com/cloud/storage/Volume.java +++ b/api/src/com/cloud/storage/Volume.java @@ -187,6 +187,8 @@ public interface Volume extends ControlledEntity, Identity, InternalIdentity, Ba Storage.ImageFormat getFormat(); + Storage.ProvisioningType getProvisioningType(); + Long getVmSnapshotChainSize(); Integer getHypervisorSnapshotReserve(); diff --git a/api/src/com/cloud/vm/DiskProfile.java b/api/src/com/cloud/vm/DiskProfile.java index 5fdacf2a755..a37f7aaf57b 100644 --- a/api/src/com/cloud/vm/DiskProfile.java +++ b/api/src/com/cloud/vm/DiskProfile.java @@ -18,6 +18,7 @@ package com.cloud.vm; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.offering.DiskOffering; +import com.cloud.storage.Storage.ProvisioningType; import com.cloud.storage.Volume; /** @@ -35,6 +36,7 @@ public class DiskProfile { private Long templateId; private long volumeId; private String path; + private ProvisioningType provisioningType; private Long bytesReadRate; private Long bytesWriteRate; private Long iopsReadRate; @@ -165,6 +167,14 @@ public class DiskProfile { return this.path; } + public void setProvisioningType(ProvisioningType provisioningType){ + this.provisioningType = provisioningType; + } + + public ProvisioningType getProvisioningType(){ + return this.provisioningType; + } + public void setSize(long size) { this.size = size; } diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java index 317177d02bc..a3faca7bd0a 100755 --- a/api/src/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/org/apache/cloudstack/api/ApiConstants.java @@ -195,6 +195,7 @@ public class ApiConstants { public static final String PRIVATE_END_PORT = "privateendport"; public static final String PRIVATE_ZONE = "privatezone"; public static final String PROTOCOL = "protocol"; + public static final String PROVISIONINGTYPE = "provisioningtype"; public static final String PUBLIC_INTERFACE = "publicinterface"; public static final String PUBLIC_IP_ID = "publicipid"; public static final String PUBLIC_IP = "publicip"; diff --git a/api/src/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java b/api/src/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java index 67c97923c06..7c28fd7e994 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java @@ -27,6 +27,7 @@ import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.response.DiskOfferingResponse; import org.apache.cloudstack.api.response.DomainResponse; +import com.cloud.storage.Storage.ProvisioningType; import com.cloud.offering.DiskOffering; import com.cloud.offering.ServiceOffering; import com.cloud.user.Account; @@ -66,9 +67,14 @@ public class CreateDiskOfferingCmd extends BaseCmd { @Parameter(name = ApiConstants.STORAGE_TYPE, type = CommandType.STRING, description = "the storage type of the disk offering. Values are local and shared.") private String storageType = ServiceOffering.StorageType.shared.toString(); + @Parameter(name = ApiConstants.PROVISIONINGTYPE, + type = CommandType.STRING, + description = "provisioning type used to create volumes. Valid values are thin, sparse, fat.") + private String provisioningType = ProvisioningType.THIN.toString(); + @Parameter(name = ApiConstants.DISPLAY_OFFERING, - type = CommandType.BOOLEAN, - description = "an optional field, whether to display the offering to the end user or not.") + 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") @@ -158,6 +164,10 @@ public class CreateDiskOfferingCmd extends BaseCmd { return storageType; } + public String getProvisioningType(){ + return provisioningType; + } + public Boolean getDisplayOffering() { return displayOffering; } diff --git a/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java b/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java index 6cd5d84a07c..36e1a25c582 100644 --- a/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java +++ b/api/src/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java @@ -21,6 +21,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import com.cloud.storage.Storage; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; @@ -53,6 +54,9 @@ public class CreateServiceOfferingCmd extends BaseCmd { @Parameter(name = ApiConstants.DISPLAY_TEXT, type = CommandType.STRING, required = true, description = "the display text of the service offering") private String displayText; + @Parameter(name = ApiConstants.PROVISIONINGTYPE, type = CommandType.STRING, description = "provisioning type used to create volumes. Valid values are thin, sparse, fat.") + private String provisioningType = Storage.ProvisioningType.THIN.toString(); + @Parameter(name = ApiConstants.MEMORY, type = CommandType.INTEGER, required = false, description = "the total memory of the service offering in MB") private Integer memory; @@ -150,6 +154,10 @@ public class CreateServiceOfferingCmd extends BaseCmd { return displayText; } + public String getProvisioningType(){ + return provisioningType; + } + public Integer getMemory() { return memory; } diff --git a/api/src/org/apache/cloudstack/api/response/DiskOfferingResponse.java b/api/src/org/apache/cloudstack/api/response/DiskOfferingResponse.java index 91bd4413539..f579119483b 100644 --- a/api/src/org/apache/cloudstack/api/response/DiskOfferingResponse.java +++ b/api/src/org/apache/cloudstack/api/response/DiskOfferingResponse.java @@ -85,6 +85,9 @@ public class DiskOfferingResponse extends BaseResponse { @Param(description = "the storage type for this disk offering") private String storageType; + @SerializedName("provisioningtype") @Param(description="provisioning type used to create volumes. Valid values are thin, sparse, fat.", since = "4.4.0") + private String provisioningType; + @SerializedName("diskBytesReadRate") @Param(description = "bytes read rate of the disk offering") private Long bytesReadRate; @@ -238,6 +241,14 @@ public class DiskOfferingResponse extends BaseResponse { this.storageType = storageType; } + public String getProvisioningType(){ + return provisioningType; + } + + public void setProvisioningType(String provisioningType){ + this.provisioningType = provisioningType; + } + public void setBytesReadRate(Long bytesReadRate) { this.bytesReadRate = bytesReadRate; } diff --git a/api/src/org/apache/cloudstack/api/response/ServiceOfferingResponse.java b/api/src/org/apache/cloudstack/api/response/ServiceOfferingResponse.java index 764ade510ad..d004e7ea071 100644 --- a/api/src/org/apache/cloudstack/api/response/ServiceOfferingResponse.java +++ b/api/src/org/apache/cloudstack/api/response/ServiceOfferingResponse.java @@ -62,6 +62,9 @@ public class ServiceOfferingResponse extends BaseResponse { @Param(description = "the storage type for this service offering") private String storageType; + @SerializedName("provisioningtype") @Param(description="provisioning type used to create volumes. Valid values are thin, sparse, fat.", since = "4.4.0") + private String provisioningType; + @SerializedName("offerha") @Param(description = "the ha support in the service offering") private Boolean offerHa; @@ -241,6 +244,14 @@ public class ServiceOfferingResponse extends BaseResponse { this.storageType = storageType; } + public String getProvisioningType(){ + return provisioningType; + } + + public void setProvisioningType(String provisioningType){ + this.provisioningType = provisioningType; + } + public Boolean getOfferHa() { return offerHa; } diff --git a/api/src/org/apache/cloudstack/api/response/VolumeResponse.java b/api/src/org/apache/cloudstack/api/response/VolumeResponse.java index 45c15daa6a4..e79c4c33570 100644 --- a/api/src/org/apache/cloudstack/api/response/VolumeResponse.java +++ b/api/src/org/apache/cloudstack/api/response/VolumeResponse.java @@ -72,6 +72,10 @@ public class VolumeResponse extends BaseResponse implements ControlledViewEntity @Param(description = "state of the virtual machine") private String virtualMachineState; + @SerializedName(ApiConstants.PROVISIONINGTYPE) + @Param(description = "provisioning type used to create volumes.") + private String provisioningType; + @SerializedName(ApiConstants.SIZE) @Param(description = "size of the disk volume") private Long size; @@ -278,6 +282,10 @@ public class VolumeResponse extends BaseResponse implements ControlledViewEntity this.virtualMachineState = virtualMachineState; } + public void setProvisioningType(String provisioningType){ + this.provisioningType = provisioningType; + } + public void setSize(Long size) { this.size = size; } diff --git a/client/WEB-INF/classes/resources/messages.properties b/client/WEB-INF/classes/resources/messages.properties index 8abe874a299..f40ac609d7e 100644 --- a/client/WEB-INF/classes/resources/messages.properties +++ b/client/WEB-INF/classes/resources/messages.properties @@ -529,6 +529,7 @@ label.disk.bytes.write.rate=Disk Write Rate (BPS) label.disk.iops.read.rate=Disk Read Rate (IOPS) label.disk.iops.write.rate=Disk Write Rate (IOPS) label.disk.offering=Disk Offering +label.disk.provisioningtype=Provisioning Type label.disk.read.bytes=Disk Read (Bytes) label.disk.read.io=Disk Read (IO) label.disk.size.gb=Disk Size (in GB) diff --git a/core/src/org/apache/cloudstack/storage/to/VolumeObjectTO.java b/core/src/org/apache/cloudstack/storage/to/VolumeObjectTO.java index 11a33245773..f700854e5c6 100644 --- a/core/src/org/apache/cloudstack/storage/to/VolumeObjectTO.java +++ b/core/src/org/apache/cloudstack/storage/to/VolumeObjectTO.java @@ -38,6 +38,7 @@ public class VolumeObjectTO implements DataTO { private long accountId; private String chainInfo; private Storage.ImageFormat format; + private Storage.ProvisioningType provisioningType; private long id; private Long deviceId; @@ -69,6 +70,7 @@ public class VolumeObjectTO implements DataTO { name = volume.getName(); setId(volume.getId()); format = volume.getFormat(); + provisioningType = volume.getProvisioningType(); bytesReadRate = volume.getBytesReadRate(); bytesWriteRate = volume.getBytesWriteRate(); iopsReadRate = volume.getIopsReadRate(); @@ -187,6 +189,14 @@ public class VolumeObjectTO implements DataTO { this.format = format; } + public Storage.ProvisioningType getProvisioningType(){ + return provisioningType; + } + + public void setProvisioningType(Storage.ProvisioningType provisioningType){ + this.provisioningType = provisioningType; + } + @Override public String toString() { return new StringBuilder("volumeTO[uuid=").append(uuid).append("|path=").append(path).append("|datastore=").append(dataStore).append("]").toString(); diff --git a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java index c5e09832227..6256e2526ef 100644 --- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java @@ -209,8 +209,17 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati } public VolumeVO allocateDuplicateVolumeVO(Volume oldVol, Long templateId) { - VolumeVO newVol = new VolumeVO(oldVol.getVolumeType(), oldVol.getName(), oldVol.getDataCenterId(), oldVol.getDomainId(), oldVol.getAccountId(), oldVol.getDiskOfferingId(), - oldVol.getSize(), oldVol.getMinIops(), oldVol.getMaxIops(), oldVol.get_iScsiName()); + VolumeVO newVol = new VolumeVO(oldVol.getVolumeType(), + oldVol.getName(), + oldVol.getDataCenterId(), + oldVol.getDomainId(), + oldVol.getAccountId(), + oldVol.getDiskOfferingId(), + oldVol.getProvisioningType(), + oldVol.getSize(), + oldVol.getMinIops(), + oldVol.getMaxIops(), + oldVol.get_iScsiName()); if (templateId != null) { newVol.setTemplateId(templateId); } else { @@ -584,7 +593,17 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati minIops = minIops != null ? minIops : offering.getMinIops(); maxIops = maxIops != null ? maxIops : offering.getMaxIops(); - VolumeVO vol = new VolumeVO(type, name, vm.getDataCenterId(), owner.getDomainId(), owner.getId(), offering.getId(), size, minIops, maxIops, null); + VolumeVO vol = new VolumeVO(type, + name, + vm.getDataCenterId(), + owner.getDomainId(), + owner.getId(), + offering.getId(), + offering.getProvisioningType(), + size, + minIops, + maxIops, + null); if (vm != null) { vol.setInstanceId(vm.getId()); } @@ -635,8 +654,17 @@ public class VolumeOrchestrator extends ManagerBase implements VolumeOrchestrati minIops = minIops != null ? minIops : offering.getMinIops(); maxIops = maxIops != null ? maxIops : offering.getMaxIops(); - VolumeVO vol = new VolumeVO(type, name, vm.getDataCenterId(), owner.getDomainId(), owner.getId(), offering.getId(), size, minIops, maxIops, null); - + VolumeVO vol = new VolumeVO(type, + name, + vm.getDataCenterId(), + owner.getDomainId(), + owner.getId(), + offering.getId(), + offering.getProvisioningType(), + size, + minIops, + maxIops, + null); vol.setFormat(getSupportedImageFormatForCluster(template.getHypervisorType())); if (vm != null) { vol.setInstanceId(vm.getId()); diff --git a/engine/orchestration/test/com/cloud/vm/VirtualMachineManagerImplTest.java b/engine/orchestration/test/com/cloud/vm/VirtualMachineManagerImplTest.java index 49b2fc5fb11..06a2983883b 100644 --- a/engine/orchestration/test/com/cloud/vm/VirtualMachineManagerImplTest.java +++ b/engine/orchestration/test/com/cloud/vm/VirtualMachineManagerImplTest.java @@ -83,16 +83,17 @@ import com.cloud.hypervisor.HypervisorGuru; import com.cloud.hypervisor.HypervisorGuruManager; import com.cloud.offering.ServiceOffering; import com.cloud.service.ServiceOfferingVO; -import com.cloud.storage.DiskOfferingVO; -import com.cloud.storage.StoragePool; -import com.cloud.storage.StoragePoolHostVO; -import com.cloud.storage.VMTemplateVO; -import com.cloud.storage.Volume; -import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VolumeDao; +import com.cloud.storage.Storage.ProvisioningType; +import com.cloud.storage.DiskOfferingVO; +import com.cloud.storage.StoragePool; +import com.cloud.storage.StoragePoolHostVO; +import com.cloud.storage.Volume; +import com.cloud.storage.VolumeVO; +import com.cloud.storage.VMTemplateVO; import com.cloud.user.Account; import com.cloud.user.AccountVO; import com.cloud.user.UserVO; @@ -310,7 +311,7 @@ public class VirtualMachineManagerImplTest { boolean useLocalStorage = false; ServiceOfferingVO serviceOffering = - new ServiceOfferingVO(name, cpu, ramSize, speed, null, null, ha, displayText, useLocalStorage, false, null, false, null, false); + new ServiceOfferingVO(name, cpu, ramSize, speed, null, null, ha, displayText, ProvisioningType.THIN, useLocalStorage, false, null, false, null, false); return serviceOffering; } diff --git a/engine/schema/src/com/cloud/service/ServiceOfferingVO.java b/engine/schema/src/com/cloud/service/ServiceOfferingVO.java index 3be0aaa7745..8d03bf40d60 100755 --- a/engine/schema/src/com/cloud/service/ServiceOfferingVO.java +++ b/engine/schema/src/com/cloud/service/ServiceOfferingVO.java @@ -27,6 +27,7 @@ import javax.persistence.Transient; import com.cloud.offering.ServiceOffering; import com.cloud.storage.DiskOfferingVO; +import com.cloud.storage.Storage.ProvisioningType; import com.cloud.vm.VirtualMachine; @Entity @@ -89,8 +90,8 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering } public ServiceOfferingVO(String name, Integer cpu, Integer ramSize, Integer speed, Integer rateMbps, Integer multicastRateMbps, boolean offerHA, String displayText, - boolean useLocalStorage, boolean recreatable, String tags, boolean systemUse, VirtualMachine.Type vmType, boolean defaultUse) { - super(name, displayText, false, tags, recreatable, useLocalStorage, systemUse, true); + ProvisioningType provisioningType, boolean useLocalStorage, boolean recreatable, String tags, boolean systemUse, VirtualMachine.Type vmType, boolean defaultUse) { + super(name, displayText, provisioningType, false, tags, recreatable, useLocalStorage, systemUse, true); this.cpu = cpu; this.ramSize = ramSize; this.speed = speed; @@ -104,9 +105,8 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering } public ServiceOfferingVO(String name, Integer cpu, Integer ramSize, Integer speed, Integer rateMbps, Integer multicastRateMbps, boolean offerHA, boolean limitCpuUse, - boolean volatileVm, String displayText, boolean useLocalStorage, boolean recreatable, String tags, boolean systemUse, VirtualMachine.Type vmType, - Long domainId) { - super(name, displayText, false, tags, recreatable, useLocalStorage, systemUse, true, domainId); + boolean volatileVm, String displayText, ProvisioningType provisioningType, boolean useLocalStorage, boolean recreatable, String tags, boolean systemUse, VirtualMachine.Type vmType, Long domainId) { + super(name, displayText, provisioningType, false, tags, recreatable, useLocalStorage, systemUse, true, domainId); this.cpu = cpu; this.ramSize = ramSize; this.speed = speed; @@ -119,7 +119,7 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering } public ServiceOfferingVO(String name, Integer cpu, Integer ramSize, Integer speed, Integer rateMbps, Integer multicastRateMbps, boolean offerHA, - boolean limitResourceUse, boolean volatileVm, String displayText, boolean useLocalStorage, boolean recreatable, String tags, boolean systemUse, + boolean limitResourceUse, boolean volatileVm, String displayText, ProvisioningType provisioningType, boolean useLocalStorage, boolean recreatable, String tags, boolean systemUse, VirtualMachine.Type vmType, Long domainId, String hostTag) { this(name, cpu, @@ -131,6 +131,7 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering limitResourceUse, volatileVm, displayText, + provisioningType, useLocalStorage, recreatable, tags, @@ -141,7 +142,7 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering } public ServiceOfferingVO(String name, Integer cpu, Integer ramSize, Integer speed, Integer rateMbps, Integer multicastRateMbps, boolean offerHA, - boolean limitResourceUse, boolean volatileVm, String displayText, boolean useLocalStorage, boolean recreatable, String tags, boolean systemUse, + boolean limitResourceUse, boolean volatileVm, String displayText, ProvisioningType provisioningType, boolean useLocalStorage, boolean recreatable, String tags, boolean systemUse, VirtualMachine.Type vmType, Long domainId, String hostTag, String deploymentPlanner) { this(name, cpu, @@ -153,6 +154,7 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering limitResourceUse, volatileVm, displayText, + provisioningType, useLocalStorage, recreatable, tags, @@ -167,6 +169,7 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering super(offering.getId(), offering.getName(), offering.getDisplayText(), + offering.getProvisioningType(), false, offering.getTags(), offering.isRecreatable(), diff --git a/engine/schema/src/com/cloud/storage/DiskOfferingVO.java b/engine/schema/src/com/cloud/storage/DiskOfferingVO.java index 99214b21905..152fa2c797c 100755 --- a/engine/schema/src/com/cloud/storage/DiskOfferingVO.java +++ b/engine/schema/src/com/cloud/storage/DiskOfferingVO.java @@ -124,7 +124,10 @@ public class DiskOfferingVO implements DiskOffering { @Enumerated(value = EnumType.STRING) private DiskCacheMode cacheMode; - @Column(name = "display_offering") + @Column(name="provisioning_type") + Storage.ProvisioningType provisioningType; + + @Column(name="display_offering") boolean displayOffering = true; @Enumerated(EnumType.STRING) @@ -138,11 +141,12 @@ public class DiskOfferingVO implements DiskOffering { uuid = UUID.randomUUID().toString(); } - public DiskOfferingVO(Long domainId, String name, String displayText, long diskSize, String tags, boolean isCustomized, + public DiskOfferingVO(Long domainId, String name, String displayText, Storage.ProvisioningType provisioningType, long diskSize, String tags, boolean isCustomized, Boolean isCustomizedIops, Long minIops, Long maxIops, DiskCacheMode cacheMode) { this.domainId = domainId; this.name = name; this.displayText = displayText; + this.provisioningType = provisioningType; this.diskSize = diskSize; this.tags = tags; recreatable = false; @@ -156,11 +160,12 @@ public class DiskOfferingVO implements DiskOffering { this.cacheMode = cacheMode; } - public DiskOfferingVO(Long domainId, String name, String displayText, long diskSize, String tags, boolean isCustomized, + public DiskOfferingVO(Long domainId, String name, String displayText, Storage.ProvisioningType provisioningType, long diskSize, String tags, boolean isCustomized, Boolean isCustomizedIops, Long minIops, Long maxIops) { this.domainId = domainId; this.name = name; this.displayText = displayText; + this.provisioningType = provisioningType; this.diskSize = diskSize; this.tags = tags; recreatable = false; @@ -174,12 +179,13 @@ public class DiskOfferingVO implements DiskOffering { state = State.Active; } - public DiskOfferingVO(String name, String displayText, boolean mirrored, String tags, boolean recreatable, boolean useLocalStorage, boolean systemUse, - boolean customized) { + public DiskOfferingVO(String name, String displayText, Storage.ProvisioningType provisioningType, boolean mirrored, String tags, boolean recreatable, + boolean useLocalStorage, boolean systemUse, boolean customized) { domainId = null; type = Type.Service; this.name = name; this.displayText = displayText; + this.provisioningType = provisioningType; this.tags = tags; this.recreatable = recreatable; this.useLocalStorage = useLocalStorage; @@ -191,11 +197,12 @@ public class DiskOfferingVO implements DiskOffering { // domain specific offerings constructor (null domainId implies public // offering) - public DiskOfferingVO(String name, String displayText, boolean mirrored, String tags, boolean recreatable, boolean useLocalStorage, boolean systemUse, - boolean customized, Long domainId) { + public DiskOfferingVO(String name, String displayText, Storage.ProvisioningType provisioningType, boolean mirrored, String tags, boolean recreatable, + boolean useLocalStorage, boolean systemUse, boolean customized, Long domainId) { type = Type.Service; this.name = name; this.displayText = displayText; + this.provisioningType = provisioningType; this.tags = tags; this.recreatable = recreatable; this.useLocalStorage = useLocalStorage; @@ -206,12 +213,13 @@ public class DiskOfferingVO implements DiskOffering { state = State.Active; } - public DiskOfferingVO(long id, String name, String displayText, boolean mirrored, String tags, boolean recreatable, + public DiskOfferingVO(long id, String name, String displayText, Storage.ProvisioningType provisioningType, boolean mirrored, String tags, boolean recreatable, boolean useLocalStorage, boolean systemUse, boolean customized, boolean customizedIops, Long domainId) { this.id = id; type = Type.Service; this.name = name; this.displayText = displayText; + this.provisioningType = provisioningType; this.tags = tags; this.recreatable = recreatable; this.useLocalStorage = useLocalStorage; @@ -341,6 +349,11 @@ public class DiskOfferingVO implements DiskOffering { this.displayText = displayText; } + @Override + public Storage.ProvisioningType getProvisioningType(){ + return provisioningType; + } + @Override public long getDiskSize() { return diskSize; diff --git a/engine/schema/src/com/cloud/storage/VolumeVO.java b/engine/schema/src/com/cloud/storage/VolumeVO.java index 9f27c231f0d..ec9afbeb1c6 100755 --- a/engine/schema/src/com/cloud/storage/VolumeVO.java +++ b/engine/schema/src/com/cloud/storage/VolumeVO.java @@ -32,6 +32,7 @@ import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.Transient; +import com.cloud.storage.Storage.ProvisioningType; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.utils.NumbersUtil; import com.cloud.utils.db.GenericDao; @@ -143,6 +144,9 @@ public class VolumeVO implements Volume { @Column(name = "format") private Storage.ImageFormat format; + @Column(name = "provisioning_type") + private Storage.ProvisioningType provisioningType; + @Column(name = "display_volume", updatable = true, nullable = false) protected boolean displayVolume = true; @@ -163,12 +167,15 @@ public class VolumeVO implements Volume { Integer hypervisorSnapshotReserve; // Real Constructor - public VolumeVO(Type type, String name, long dcId, long domainId, long accountId, long diskOfferingId, long size, Long minIops, Long maxIops, String iScsiName) { - volumeType = type; + public VolumeVO(Type type, String name, long dcId, long domainId, + long accountId, long diskOfferingId, Storage.ProvisioningType provisioningType, long size, + Long minIops, Long maxIops, String iScsiName) { + this.volumeType = type; this.name = name; dataCenterId = dcId; this.accountId = accountId; this.domainId = domainId; + this.provisioningType = provisioningType; this.size = size; this.minIops = minIops; this.maxIops = maxIops; @@ -178,14 +185,17 @@ public class VolumeVO implements Volume { uuid = UUID.randomUUID().toString(); } - public VolumeVO(String name, long dcId, Long podId, long accountId, long domainId, Long instanceId, String folder, String path, long size, Long minIops, - Long maxIops, String iScsiName, Volume.Type vType) { + public VolumeVO(String name, long dcId, Long podId, long accountId, + long domainId, Long instanceId, String folder, String path, Storage.ProvisioningType provisioningType, + long size, Long minIops, Long maxIops, String iScsiName, + Volume.Type vType) { this.name = name; this.accountId = accountId; this.domainId = domainId; this.instanceId = instanceId; this.folder = folder; this.path = path; + this.provisioningType = provisioningType; this.size = size; this.minIops = minIops; this.maxIops = maxIops; @@ -198,13 +208,16 @@ public class VolumeVO implements Volume { uuid = UUID.randomUUID().toString(); } - public VolumeVO(String name, long dcId, long podId, long accountId, long domainId, Long instanceId, String folder, String path, long size, Volume.Type vType) { + public VolumeVO(String name, long dcId, long podId, long accountId, + long domainId, Long instanceId, String folder, String path, Storage.ProvisioningType provisioningType, + long size, Volume.Type vType) { this.name = name; this.accountId = accountId; this.domainId = domainId; this.instanceId = instanceId; this.folder = folder; this.path = path; + this.provisioningType = provisioningType; this.size = size; minIops = null; maxIops = null; @@ -227,6 +240,7 @@ public class VolumeVO implements Volume { that.getInstanceId(), that.getFolder(), that.getPath(), + that.getProvisioningType(), that.getSize(), that.getMinIops(), that.getMaxIops(), @@ -245,6 +259,7 @@ public class VolumeVO implements Volume { templateId = that.getTemplateId(); deviceId = that.getDeviceId(); format = that.getFormat(); + provisioningType = that.getProvisioningType(); uuid = UUID.randomUUID().toString(); } @@ -565,7 +580,16 @@ public class VolumeVO implements Volume { this.format = format; } - public void setVmSnapshotChainSize(Long vmSnapshotChainSize) { + @Override + public ProvisioningType getProvisioningType(){ + return provisioningType; + } + + public void setProvisioningType(ProvisioningType provisioningType){ + this.provisioningType = provisioningType; + } + + public void setVmSnapshotChainSize(Long vmSnapshotChainSize){ this.vmSnapshotChainSize = vmSnapshotChainSize; } diff --git a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java index 385f8e63e96..08925d0d463 100644 --- a/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java +++ b/engine/storage/volume/src/org/apache/cloudstack/storage/volume/VolumeObject.java @@ -42,6 +42,7 @@ import com.cloud.offering.DiskOffering.DiskCacheMode; import com.cloud.storage.DataStoreRole; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.Storage.ImageFormat; +import com.cloud.storage.Storage.ProvisioningType; import com.cloud.storage.Volume; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.DiskOfferingDao; @@ -655,6 +656,11 @@ public class VolumeObject implements VolumeInfo { return volumeVO.getFormat(); } + @Override + public ProvisioningType getProvisioningType(){ + return this.volumeVO.getProvisioningType(); + } + @Override public boolean delete() { if (dataStore != null) { diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index beb5e10fd34..e3a805db7dc 100755 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -1735,13 +1735,14 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv vol = templateToPrimaryDownload(cmd.getTemplateUrl(), primaryPool, dskch.getPath()); } else { BaseVol = primaryPool.getPhysicalDisk(cmd.getTemplateUrl()); - vol = _storagePoolMgr.createDiskFromTemplate(BaseVol, dskch.getPath(), primaryPool, 0); + vol = _storagePoolMgr.createDiskFromTemplate(BaseVol, + dskch.getPath(), dskch.getProvisioningType(), primaryPool, 0); } if (vol == null) { return new Answer(cmd, false, " Can't create storage volume on storage pool"); } } else { - vol = primaryPool.createPhysicalDisk(dskch.getPath(), dskch.getSize()); + vol = primaryPool.createPhysicalDisk(dskch.getPath(), dskch.getProvisioningType(), dskch.getSize()); } VolumeTO volume = new VolumeTO(cmd.getVolumeId(), dskch.getType(), pool.getType(), pool.getUuid(), pool.getPath(), vol.getName(), vol.getName(), disksize, null); diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtStorageVolumeDef.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtStorageVolumeDef.java index 35777cfebe4..61123110275 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtStorageVolumeDef.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtStorageVolumeDef.java @@ -16,6 +16,9 @@ // under the License. package com.cloud.hypervisor.kvm.resource; +import org.apache.cloudstack.utils.qemu.QemuImg; +import org.apache.commons.lang.NotImplementedException; + public class LibvirtStorageVolumeDef { public enum volFormat { RAW("raw"), QCOW2("qcow2"), DIR("dir"), TAR("tar"); @@ -45,6 +48,21 @@ public class LibvirtStorageVolumeDef { } return null; } + + public static volFormat getFormat(QemuImg.PhysicalDiskFormat format){ + switch (format){ + case RAW: + return RAW; + case QCOW2: + return QCOW2; + case DIR: + return DIR; + case TAR: + return TAR; + default: + throw new NotImplementedException(); + } + } } private String _volName; diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/IscsiAdmStorageAdaptor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/IscsiAdmStorageAdaptor.java index 761bb377ee3..fbbdd7afa74 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/IscsiAdmStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/IscsiAdmStorageAdaptor.java @@ -20,11 +20,13 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import com.cloud.storage.Storage; import org.apache.log4j.Logger; import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; import com.cloud.agent.api.to.DiskTO; +import com.cloud.storage.Storage.ProvisioningType; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.utils.StringUtils; import com.cloud.utils.exception.CloudRuntimeException; @@ -63,7 +65,7 @@ public class IscsiAdmStorageAdaptor implements StorageAdaptor { // called from LibvirtComputingResource.execute(CreateCommand) // does not apply for iScsiAdmStorageAdaptor @Override - public KVMPhysicalDisk createPhysicalDisk(String volumeUuid, KVMStoragePool pool, PhysicalDiskFormat format, long size) { + public KVMPhysicalDisk createPhysicalDisk(String volumeUuid, KVMStoragePool pool, PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size) { throw new UnsupportedOperationException("Creating a physical disk is not supported."); } @@ -336,7 +338,9 @@ public class IscsiAdmStorageAdaptor implements StorageAdaptor { } @Override - public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, PhysicalDiskFormat format, long size, KVMStoragePool destPool, int timeout) { + public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, PhysicalDiskFormat format, + ProvisioningType provisioningType, long size, + KVMStoragePool destPool, int timeout) { throw new UnsupportedOperationException("Creating a disk from a template is not yet supported for this configuration."); } diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/IscsiAdmStoragePool.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/IscsiAdmStoragePool.java index 2cecfa3892b..81cd5bb7f81 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/IscsiAdmStoragePool.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/IscsiAdmStoragePool.java @@ -19,6 +19,7 @@ package com.cloud.hypervisor.kvm.storage; import java.util.List; import java.util.Map; +import com.cloud.storage.Storage; import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; import com.cloud.storage.Storage.StoragePoolType; @@ -86,7 +87,7 @@ public class IscsiAdmStoragePool implements KVMStoragePool { // from LibvirtComputingResource.createDiskFromTemplate(KVMPhysicalDisk, String, PhysicalDiskFormat, long, KVMStoragePool) // does not apply for iScsiAdmStoragePool @Override - public KVMPhysicalDisk createPhysicalDisk(String name, PhysicalDiskFormat format, long size) { + public KVMPhysicalDisk createPhysicalDisk(String name, PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size) { throw new UnsupportedOperationException("Creating a physical disk is not supported."); } @@ -94,7 +95,7 @@ public class IscsiAdmStoragePool implements KVMStoragePool { // from KVMStorageProcessor.createVolume(CreateObjectCommand) // does not apply for iScsiAdmStoragePool @Override - public KVMPhysicalDisk createPhysicalDisk(String name, long size) { + public KVMPhysicalDisk createPhysicalDisk(String name, Storage.ProvisioningType provisioningType, long size) { throw new UnsupportedOperationException("Creating a physical disk is not supported."); } diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePool.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePool.java index ca3d32f08f9..83370f8549d 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePool.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePool.java @@ -19,14 +19,15 @@ package com.cloud.hypervisor.kvm.storage; import java.util.List; import java.util.Map; +import com.cloud.storage.Storage; import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; import com.cloud.storage.Storage.StoragePoolType; public interface KVMStoragePool { - public KVMPhysicalDisk createPhysicalDisk(String volumeUuid, PhysicalDiskFormat format, long size); + public KVMPhysicalDisk createPhysicalDisk(String volumeUuid, PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size); - public KVMPhysicalDisk createPhysicalDisk(String volumeUuid, long size); + public KVMPhysicalDisk createPhysicalDisk(String volumeUuid, Storage.ProvisioningType provisioningType, long size); public boolean connectPhysicalDisk(String volumeUuid, Map details); diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java index 583d48af524..1d288042fe5 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java @@ -36,6 +36,7 @@ import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.hypervisor.kvm.resource.KVMHABase; import com.cloud.hypervisor.kvm.resource.KVMHABase.PoolType; import com.cloud.hypervisor.kvm.resource.KVMHAMonitor; +import com.cloud.storage.Storage; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StorageLayer; import com.cloud.storage.Volume; @@ -301,22 +302,32 @@ public class KVMStoragePoolManager { return true; } - public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, KVMStoragePool destPool, int timeout) { - return createDiskFromTemplate(template, name, destPool, template.getSize(), timeout); + public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, Storage.ProvisioningType provisioningType, + KVMStoragePool destPool, int timeout) { + return createDiskFromTemplate(template, name, provisioningType, destPool, template.getSize(), timeout); } - public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, KVMStoragePool destPool, long size, int timeout) { + public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, Storage.ProvisioningType provisioningType, + KVMStoragePool destPool, long size, int timeout) { StorageAdaptor adaptor = getStorageAdaptor(destPool.getType()); // LibvirtStorageAdaptor-specific statement if (destPool.getType() == StoragePoolType.RBD) { - return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.RAW, size, destPool, timeout); + return adaptor.createDiskFromTemplate(template, name, + PhysicalDiskFormat.RAW, provisioningType, + size, destPool, timeout); } else if (destPool.getType() == StoragePoolType.CLVM) { - return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.RAW, size, destPool, timeout); + return adaptor.createDiskFromTemplate(template, name, + PhysicalDiskFormat.RAW, provisioningType, + size, destPool, timeout); } else if (template.getFormat() == PhysicalDiskFormat.DIR) { - return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.DIR, size, destPool, timeout); + return adaptor.createDiskFromTemplate(template, name, + PhysicalDiskFormat.DIR, provisioningType, + size, destPool, timeout); } else { - return adaptor.createDiskFromTemplate(template, name, PhysicalDiskFormat.QCOW2, size, destPool, timeout); + return adaptor.createDiskFromTemplate(template, name, + PhysicalDiskFormat.QCOW2, provisioningType, + size, destPool, timeout); } } diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java index a294ced734e..df82bc5e8b5 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java @@ -335,7 +335,8 @@ public class KVMStorageProcessor implements StorageProcessor { templatePath = templatePath.substring(templatePath.lastIndexOf(File.separator) + 1); } BaseVol = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), templatePath); - vol = storagePoolMgr.createDiskFromTemplate(BaseVol, volume.getUuid(), BaseVol.getPool(), volume.getSize(), cmd.getWaitInMillSeconds()); + vol = storagePoolMgr.createDiskFromTemplate(BaseVol, volume.getUuid(), volume.getProvisioningType(), + BaseVol.getPool(), volume.getSize(), cmd.getWaitInMillSeconds()); } if (vol == null) { return new CopyCmdAnswer(" Can't create storage volume on storage pool"); @@ -1074,7 +1075,7 @@ public class KVMStorageProcessor implements StorageProcessor { primaryPool = storagePoolMgr.getStoragePool(primaryStore.getPoolType(), primaryStore.getUuid()); disksize = volume.getSize(); - vol = primaryPool.createPhysicalDisk(volume.getUuid(), disksize); + vol = primaryPool.createPhysicalDisk(volume.getUuid(), volume.getProvisioningType(), disksize); VolumeObjectTO newVol = new VolumeObjectTO(); newVol.setPath(vol.getName()); diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java index b13fe65f42f..fb35d45cb0f 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java @@ -21,6 +21,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.HashMap; + import org.apache.commons.codec.binary.Base64; import org.apache.log4j.Logger; @@ -55,6 +57,7 @@ import com.cloud.hypervisor.kvm.resource.LibvirtStoragePoolXMLParser; import com.cloud.hypervisor.kvm.resource.LibvirtStorageVolumeDef; import com.cloud.hypervisor.kvm.resource.LibvirtStorageVolumeDef.volFormat; import com.cloud.hypervisor.kvm.resource.LibvirtStorageVolumeXMLParser; +import com.cloud.storage.Storage; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.storage.StorageLayer; import com.cloud.utils.exception.CloudRuntimeException; @@ -614,16 +617,103 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { } @Override - public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, PhysicalDiskFormat format, long size) { - LibvirtStoragePool libvirtPool = (LibvirtStoragePool)pool; + public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, + PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size) { + + switch (pool.getType()){ + case RBD: + return createPhysicalDiskOnRBD(name, pool, format, provisioningType, size); + case NetworkFilesystem: + case Filesystem: + switch (format){ + case QCOW2: + return createPhysicalDiskByQemuImg(name, pool, format, provisioningType, size); + case RAW: + return createPhysicalDiskByQemuImg(name, pool, format, provisioningType, size); + case DIR: + return createPhysicalDiskByLibVirt(name, pool, format, provisioningType, size); + case TAR: + return createPhysicalDiskByLibVirt(name, pool, format, provisioningType, size); + default: + throw new CloudRuntimeException("Unexpected disk format is specified."); + } + default: + return createPhysicalDiskByLibVirt(name, pool, format, provisioningType, size); + } + } + + private KVMPhysicalDisk createPhysicalDiskByLibVirt(String name, KVMStoragePool pool, + PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size) { + LibvirtStoragePool libvirtPool = (LibvirtStoragePool) pool; StoragePool virtPool = libvirtPool.getPool(); - LibvirtStorageVolumeDef.volFormat libvirtformat = null; + LibvirtStorageVolumeDef.volFormat libvirtformat = LibvirtStorageVolumeDef.volFormat.getFormat(format); String volPath = null; String volName = null; long volAllocation = 0; long volCapacity = 0; + LibvirtStorageVolumeDef volDef = new LibvirtStorageVolumeDef(name, + size, libvirtformat, null, null); + s_logger.debug(volDef.toString()); + try { + StorageVol vol = virtPool.storageVolCreateXML(volDef.toString(), 0); + volPath = vol.getPath(); + volName = vol.getName(); + volAllocation = vol.getInfo().allocation; + volCapacity = vol.getInfo().capacity; + } catch (LibvirtException e) { + throw new CloudRuntimeException(e.toString()); + } + + KVMPhysicalDisk disk = new KVMPhysicalDisk(volPath, volName, pool); + disk.setFormat(format); + disk.setSize(volAllocation); + disk.setVirtualSize(volCapacity); + return disk; + } + + + private KVMPhysicalDisk createPhysicalDiskByQemuImg(String name, KVMStoragePool pool, + PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size) { + String volPath = pool.getLocalPath() + "/" + name; + String volName = name; + long volAllocation = 0; + long volCapacity = 0; + + final int timeout = 0; + + QemuImgFile destFile = new QemuImgFile(volPath); + destFile.setFormat(format); + destFile.setSize(size); + QemuImg qemu = new QemuImg(timeout); + Map options = new HashMap(); + if (pool.getType() == StoragePoolType.NetworkFilesystem){ + options.put("preallocation", QemuImg.PreallocationType.getPreallocationType(provisioningType).toString()); + } + + try{ + qemu.create(destFile, options); + Map info = qemu.info(destFile); + volAllocation = Long.parseLong(info.get(new String("virtual-size"))); + volCapacity = Long.parseLong(info.get(new String("actual-size"))); + } catch (QemuImgException e) { + s_logger.error("Failed to create " + volPath + + " due to a failed executing of qemu-img: " + e.getMessage()); + } + + KVMPhysicalDisk disk = new KVMPhysicalDisk(volPath, volName, pool); + disk.setFormat(format); + disk.setSize(volAllocation); + disk.setVirtualSize(volCapacity); + return disk; + } + + private KVMPhysicalDisk createPhysicalDiskOnRBD(String name, KVMStoragePool pool, + PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size) { + LibvirtStoragePool libvirtPool = (LibvirtStoragePool) pool; + String volPath = null; + /** * To have RBD function properly we want RBD images of format 2 * libvirt currently defaults to format 1 @@ -631,63 +721,34 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { * For that reason we use the native RBD bindings to create the * RBD image until libvirt creates RBD format 2 by default */ - if (pool.getType() == StoragePoolType.RBD) { - format = PhysicalDiskFormat.RAW; + format = PhysicalDiskFormat.RAW; - try { - s_logger.info("Creating RBD image " + pool.getSourceDir() + "/" + name + " with size " + size); + try { + s_logger.info("Creating RBD image " + pool.getSourceDir() + "/" + name + " with size " + size); - Rados r = new Rados(pool.getAuthUserName()); - r.confSet("mon_host", pool.getSourceHost() + ":" + pool.getSourcePort()); - r.confSet("key", pool.getAuthSecret()); - r.confSet("client_mount_timeout", "30"); - r.connect(); - s_logger.debug("Succesfully connected to Ceph cluster at " + r.confGet("mon_host")); + Rados r = new Rados(pool.getAuthUserName()); + r.confSet("mon_host", pool.getSourceHost() + ":" + pool.getSourcePort()); + r.confSet("key", pool.getAuthSecret()); + r.confSet("client_mount_timeout", "30"); + r.connect(); + s_logger.debug("Succesfully connected to Ceph cluster at " + r.confGet("mon_host")); - IoCTX io = r.ioCtxCreate(pool.getSourceDir()); - Rbd rbd = new Rbd(io); - rbd.create(name, size, rbdFeatures, rbdOrder); + IoCTX io = r.ioCtxCreate(pool.getSourceDir()); + Rbd rbd = new Rbd(io); + rbd.create(name, size, this.rbdFeatures, this.rbdOrder); - r.ioCtxDestroy(io); - } catch (RadosException e) { - throw new CloudRuntimeException(e.toString()); - } catch (RbdException e) { - throw new CloudRuntimeException(e.toString()); - } - - volPath = pool.getSourceDir() + "/" + name; - volName = name; - volCapacity = size; - volAllocation = size; - } else { - - if (format == PhysicalDiskFormat.QCOW2) { - libvirtformat = LibvirtStorageVolumeDef.volFormat.QCOW2; - } else if (format == PhysicalDiskFormat.RAW) { - libvirtformat = LibvirtStorageVolumeDef.volFormat.RAW; - } else if (format == PhysicalDiskFormat.DIR) { - libvirtformat = LibvirtStorageVolumeDef.volFormat.DIR; - } else if (format == PhysicalDiskFormat.TAR) { - libvirtformat = LibvirtStorageVolumeDef.volFormat.TAR; - } - - LibvirtStorageVolumeDef volDef = new LibvirtStorageVolumeDef(name, size, libvirtformat, null, null); - s_logger.debug(volDef.toString()); - try { - StorageVol vol = virtPool.storageVolCreateXML(volDef.toString(), 0); - volPath = vol.getPath(); - volName = vol.getName(); - volAllocation = vol.getInfo().allocation; - volCapacity = vol.getInfo().capacity; - } catch (LibvirtException e) { - throw new CloudRuntimeException(e.toString()); - } + r.ioCtxDestroy(io); + } catch (RadosException e) { + throw new CloudRuntimeException(e.toString()); + } catch (RbdException e) { + throw new CloudRuntimeException(e.toString()); } - KVMPhysicalDisk disk = new KVMPhysicalDisk(volPath, volName, pool); + volPath = pool.getSourceDir() + "/" + name; + KVMPhysicalDisk disk = new KVMPhysicalDisk(volPath, name, pool); disk.setFormat(format); - disk.setSize(volAllocation); - disk.setVirtualSize(volCapacity); + disk.setSize(size); + disk.setVirtualSize(size); return disk; } @@ -806,38 +867,44 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { * If it has been created on Primary Storage, it will be copied on the Primary Storage */ @Override - public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, PhysicalDiskFormat format, long size, KVMStoragePool destPool, int timeout) { + public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, + String name, PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, KVMStoragePool destPool, int timeout) { - String newUuid = name; - KVMStoragePool srcPool = template.getPool(); KVMPhysicalDisk disk = null; - /* - With RBD you can't run qemu-img convert with an existing RBD image as destination - qemu-img will exit with the error that the destination already exists. - So for RBD we don't create the image, but let qemu-img do that for us. - - We then create a KVMPhysicalDisk object that we can return - */ - try { - if (destPool.getType() != StoragePoolType.RBD) { - disk = destPool.createPhysicalDisk(newUuid, format, template.getVirtualSize()); + if (destPool.getType() == StoragePoolType.RBD) { + disk = createDiskFromTemplateOnRBD(template, name, format, provisioningType, size, destPool, timeout); + } else { + try { + String newUuid = name; + disk = destPool.createPhysicalDisk(newUuid, format, provisioningType, template.getVirtualSize()); if (template.getFormat() == PhysicalDiskFormat.TAR) { - Script.runSimpleBashScript("tar -x -f " + template.getPath() + " -C " + disk.getPath(), timeout); + Script.runSimpleBashScript("tar -x -f " + template.getPath() + " -C " + disk.getPath(), timeout); // TO BE FIXED to aware provisioningType } else if (template.getFormat() == PhysicalDiskFormat.DIR) { Script.runSimpleBashScript("mkdir -p " + disk.getPath()); Script.runSimpleBashScript("chmod 755 " + disk.getPath()); - Script.runSimpleBashScript("cp -p -r " + template.getPath() + "/* " + disk.getPath(), timeout); + Script.runSimpleBashScript("cp -p -r " + template.getPath() + "/* " + disk.getPath(), timeout); // TO BE FIXED to aware provisioningType } else if (format == PhysicalDiskFormat.QCOW2) { - QemuImgFile backingFile = new QemuImgFile(template.getPath(), template.getFormat()); - QemuImgFile destFile = new QemuImgFile(disk.getPath()); + QemuImg qemu = new QemuImg(timeout); + QemuImgFile destFile = new QemuImgFile(disk.getPath(), format); if (size > template.getVirtualSize()) { destFile.setSize(size); } else { destFile.setSize(template.getVirtualSize()); } - QemuImg qemu = new QemuImg(timeout); - qemu.create(destFile, backingFile); + Map options = new HashMap(); + options.put("preallocation", QemuImg.PreallocationType.getPreallocationType(provisioningType).toString()); + switch(provisioningType){ + case THIN: + QemuImgFile backingFile = new QemuImgFile(template.getPath(), template.getFormat()); + qemu.create(destFile, backingFile, options); + break; + case SPARSE: + case FAT: + QemuImgFile srcFile = new QemuImgFile(template.getPath(), template.getFormat()); + qemu.convert(srcFile, destFile, options); + break; + } } else if (format == PhysicalDiskFormat.RAW) { QemuImgFile sourceFile = new QemuImgFile(template.getPath(), template.getFormat()); QemuImgFile destFile = new QemuImgFile(disk.getPath(), PhysicalDiskFormat.RAW); @@ -847,137 +914,13 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { destFile.setSize(template.getVirtualSize()); } QemuImg qemu = new QemuImg(timeout); - qemu.convert(sourceFile, destFile); - } - } else { - format = PhysicalDiskFormat.RAW; - disk = new KVMPhysicalDisk(destPool.getSourceDir() + "/" + newUuid, newUuid, destPool); - disk.setFormat(format); - if (size > template.getVirtualSize()) { - disk.setSize(size); - disk.setVirtualSize(size); - } else { - // leave these as they were if size isn't applicable - disk.setSize(template.getVirtualSize()); - disk.setVirtualSize(disk.getSize()); - } - - QemuImg qemu = new QemuImg(timeout); - QemuImgFile srcFile; - QemuImgFile destFile = - new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(destPool.getSourceHost(), destPool.getSourcePort(), destPool.getAuthUserName(), - destPool.getAuthSecret(), disk.getPath())); - destFile.setFormat(format); - if (size > template.getVirtualSize()) { - destFile.setSize(size); - } else { - destFile.setSize(template.getVirtualSize()); - } - - if (srcPool.getType() != StoragePoolType.RBD) { - srcFile = new QemuImgFile(template.getPath(), template.getFormat()); - qemu.convert(srcFile, destFile); - } else { - - /** - * We have to find out if the source file is in the same RBD pool and has - * RBD format 2 before we can do a layering/clone operation on the RBD image - * - * This will be the case when the template is already on Primary Storage and - * we want to copy it - */ - - try { - if ((srcPool.getSourceHost().equals(destPool.getSourceHost())) && (srcPool.getSourceDir().equals(destPool.getSourceDir()))) { - /* We are on the same Ceph cluster, but we require RBD format 2 on the source image */ - s_logger.debug("Trying to perform a RBD clone (layering) since we are operating in the same storage pool"); - - Rados r = new Rados(srcPool.getAuthUserName()); - r.confSet("mon_host", srcPool.getSourceHost() + ":" + srcPool.getSourcePort()); - r.confSet("key", srcPool.getAuthSecret()); - r.confSet("client_mount_timeout", "30"); - r.connect(); - s_logger.debug("Succesfully connected to Ceph cluster at " + r.confGet("mon_host")); - - IoCTX io = r.ioCtxCreate(srcPool.getSourceDir()); - Rbd rbd = new Rbd(io); - RbdImage srcImage = rbd.open(template.getName()); - - if (srcImage.isOldFormat()) { - /* The source image is RBD format 1, we have to do a regular copy */ - s_logger.debug("The source image " + srcPool.getSourceDir() + "/" + template.getName() + - " is RBD format 1. We have to perform a regular copy (" + disk.getVirtualSize() + " bytes)"); - - rbd.create(disk.getName(), disk.getVirtualSize(), rbdFeatures, rbdOrder); - RbdImage destImage = rbd.open(disk.getName()); - - s_logger.debug("Starting to copy " + srcImage.getName() + " to " + destImage.getName() + " in Ceph pool " + srcPool.getSourceDir()); - rbd.copy(srcImage, destImage); - - s_logger.debug("Finished copying " + srcImage.getName() + " to " + destImage.getName() + " in Ceph pool " + srcPool.getSourceDir()); - rbd.close(destImage); - } else { - s_logger.debug("The source image " + srcPool.getSourceDir() + "/" + template.getName() + - " is RBD format 2. We will perform a RBD clone using snapshot " + rbdTemplateSnapName); - /* The source image is format 2, we can do a RBD snapshot+clone (layering) */ - rbd.clone(template.getName(), rbdTemplateSnapName, io, disk.getName(), rbdFeatures, rbdOrder); - s_logger.debug("Succesfully cloned " + template.getName() + "@" + rbdTemplateSnapName + " to " + disk.getName()); - } - - rbd.close(srcImage); - r.ioCtxDestroy(io); - } else { - /* The source pool or host is not the same Ceph cluster, we do a simple copy with Qemu-Img */ - s_logger.debug("Both the source and destination are RBD, but not the same Ceph cluster. Performing a copy"); - - Rados rSrc = new Rados(srcPool.getAuthUserName()); - rSrc.confSet("mon_host", srcPool.getSourceHost() + ":" + srcPool.getSourcePort()); - rSrc.confSet("key", srcPool.getAuthSecret()); - rSrc.confSet("client_mount_timeout", "30"); - rSrc.connect(); - s_logger.debug("Succesfully connected to source Ceph cluster at " + rSrc.confGet("mon_host")); - - Rados rDest = new Rados(destPool.getAuthUserName()); - rDest.confSet("mon_host", destPool.getSourceHost() + ":" + destPool.getSourcePort()); - rDest.confSet("key", destPool.getAuthSecret()); - rDest.confSet("client_mount_timeout", "30"); - rDest.connect(); - s_logger.debug("Succesfully connected to source Ceph cluster at " + rDest.confGet("mon_host")); - - IoCTX sIO = rSrc.ioCtxCreate(srcPool.getSourceDir()); - Rbd sRbd = new Rbd(sIO); - - IoCTX dIO = rDest.ioCtxCreate(destPool.getSourceDir()); - Rbd dRbd = new Rbd(dIO); - - s_logger.debug("Creating " + disk.getName() + " on the destination cluster " + rDest.confGet("mon_host") + " in pool " + - destPool.getSourceDir()); - dRbd.create(disk.getName(), disk.getVirtualSize(), rbdFeatures, rbdOrder); - - RbdImage srcImage = sRbd.open(template.getName()); - RbdImage destImage = dRbd.open(disk.getName()); - - s_logger.debug("Copying " + template.getName() + " from Ceph cluster " + rSrc.confGet("mon_host") + " to " + disk.getName() + " on cluster " + - rDest.confGet("mon_host")); - sRbd.copy(srcImage, destImage); - - sRbd.close(srcImage); - dRbd.close(destImage); - - rSrc.ioCtxDestroy(sIO); - rDest.ioCtxDestroy(dIO); - } - } catch (RadosException e) { - s_logger.error("Failed to perform a RADOS action on the Ceph cluster, the error was: " + e.getMessage()); - disk = null; - } catch (RbdException e) { - s_logger.error("Failed to perform a RBD action on the Ceph cluster, the error was: " + e.getMessage()); - disk = null; - } + Map options = new HashMap(); + qemu.convert(sourceFile, destFile, options); } + } catch (QemuImgException e) { + s_logger.error("Failed to create " + disk.getPath() + + " due to a failed executing of qemu-img: " + e.getMessage()); } - } catch (QemuImgException e) { - s_logger.error("Failed to create " + disk.getPath() + " due to a failed executing of qemu-img: " + e.getMessage()); } if (disk == null) { @@ -987,6 +930,154 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { return disk; } + private KVMPhysicalDisk createDiskFromTemplateOnRBD(KVMPhysicalDisk template, + String name, PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, KVMStoragePool destPool, int timeout){ + + /* + With RBD you can't run qemu-img convert with an existing RBD image as destination + qemu-img will exit with the error that the destination already exists. + So for RBD we don't create the image, but let qemu-img do that for us. + + We then create a KVMPhysicalDisk object that we can return + */ + + KVMStoragePool srcPool = template.getPool(); + KVMPhysicalDisk disk = null; + String newUuid = name; + + format = PhysicalDiskFormat.RAW; + disk = new KVMPhysicalDisk(destPool.getSourceDir() + "/" + newUuid, newUuid, destPool); + disk.setFormat(format); + if (size > template.getVirtualSize()) { + disk.setSize(size); + disk.setVirtualSize(size); + } else { + // leave these as they were if size isn't applicable + disk.setSize(template.getVirtualSize()); + disk.setVirtualSize(disk.getSize()); + } + + + QemuImg qemu = new QemuImg(timeout); + QemuImgFile srcFile; + QemuImgFile destFile = new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(destPool.getSourceHost(), + destPool.getSourcePort(), + destPool.getAuthUserName(), + destPool.getAuthSecret(), + disk.getPath())); + destFile.setFormat(format); + + + if (srcPool.getType() != StoragePoolType.RBD) { + srcFile = new QemuImgFile(template.getPath(), template.getFormat()); + try{ + qemu.convert(srcFile, destFile); + } catch (QemuImgException e) { + s_logger.error("Failed to create " + disk.getPath() + + " due to a failed executing of qemu-img: " + e.getMessage()); + } + } else { + + /** + * We have to find out if the source file is in the same RBD pool and has + * RBD format 2 before we can do a layering/clone operation on the RBD image + * + * This will be the case when the template is already on Primary Storage and + * we want to copy it + */ + + try { + if ((srcPool.getSourceHost().equals(destPool.getSourceHost())) && (srcPool.getSourceDir().equals(destPool.getSourceDir()))) { + /* We are on the same Ceph cluster, but we require RBD format 2 on the source image */ + s_logger.debug("Trying to perform a RBD clone (layering) since we are operating in the same storage pool"); + + Rados r = new Rados(srcPool.getAuthUserName()); + r.confSet("mon_host", srcPool.getSourceHost() + ":" + srcPool.getSourcePort()); + r.confSet("key", srcPool.getAuthSecret()); + r.confSet("client_mount_timeout", "30"); + r.connect(); + s_logger.debug("Succesfully connected to Ceph cluster at " + r.confGet("mon_host")); + + IoCTX io = r.ioCtxCreate(srcPool.getSourceDir()); + Rbd rbd = new Rbd(io); + RbdImage srcImage = rbd.open(template.getName()); + + if (srcImage.isOldFormat()) { + /* The source image is RBD format 1, we have to do a regular copy */ + s_logger.debug("The source image " + srcPool.getSourceDir() + "/" + template.getName() + + " is RBD format 1. We have to perform a regular copy (" + disk.getVirtualSize() + " bytes)"); + + rbd.create(disk.getName(), disk.getVirtualSize(), rbdFeatures, rbdOrder); + RbdImage destImage = rbd.open(disk.getName()); + + s_logger.debug("Starting to copy " + srcImage.getName() + " to " + destImage.getName() + " in Ceph pool " + srcPool.getSourceDir()); + rbd.copy(srcImage, destImage); + + s_logger.debug("Finished copying " + srcImage.getName() + " to " + destImage.getName() + " in Ceph pool " + srcPool.getSourceDir()); + rbd.close(destImage); + } else { + s_logger.debug("The source image " + srcPool.getSourceDir() + "/" + template.getName() + + " is RBD format 2. We will perform a RBD clone using snapshot " + + this.rbdTemplateSnapName); + /* The source image is format 2, we can do a RBD snapshot+clone (layering) */ + rbd.clone(template.getName(), this.rbdTemplateSnapName, io, disk.getName(), this.rbdFeatures, this.rbdOrder); + s_logger.debug("Succesfully cloned " + template.getName() + "@" + this.rbdTemplateSnapName + " to " + disk.getName()); + } + + rbd.close(srcImage); + r.ioCtxDestroy(io); + } else { + /* The source pool or host is not the same Ceph cluster, we do a simple copy with Qemu-Img */ + s_logger.debug("Both the source and destination are RBD, but not the same Ceph cluster. Performing a copy"); + + Rados rSrc = new Rados(srcPool.getAuthUserName()); + rSrc.confSet("mon_host", srcPool.getSourceHost() + ":" + srcPool.getSourcePort()); + rSrc.confSet("key", srcPool.getAuthSecret()); + rSrc.confSet("client_mount_timeout", "30"); + rSrc.connect(); + s_logger.debug("Succesfully connected to source Ceph cluster at " + rSrc.confGet("mon_host")); + + Rados rDest = new Rados(destPool.getAuthUserName()); + rDest.confSet("mon_host", destPool.getSourceHost() + ":" + destPool.getSourcePort()); + rDest.confSet("key", destPool.getAuthSecret()); + rDest.confSet("client_mount_timeout", "30"); + rDest.connect(); + s_logger.debug("Succesfully connected to source Ceph cluster at " + rDest.confGet("mon_host")); + + IoCTX sIO = rSrc.ioCtxCreate(srcPool.getSourceDir()); + Rbd sRbd = new Rbd(sIO); + + IoCTX dIO = rDest.ioCtxCreate(destPool.getSourceDir()); + Rbd dRbd = new Rbd(dIO); + + s_logger.debug("Creating " + disk.getName() + " on the destination cluster " + rDest.confGet("mon_host") + " in pool " + + destPool.getSourceDir()); + dRbd.create(disk.getName(), disk.getVirtualSize(), rbdFeatures, rbdOrder); + + RbdImage srcImage = sRbd.open(template.getName()); + RbdImage destImage = dRbd.open(disk.getName()); + + s_logger.debug("Copying " + template.getName() + " from Ceph cluster " + rSrc.confGet("mon_host") + " to " + disk.getName() + + " on cluster " + rDest.confGet("mon_host")); + sRbd.copy(srcImage, destImage); + + sRbd.close(srcImage); + dRbd.close(destImage); + + rSrc.ioCtxDestroy(sIO); + rDest.ioCtxDestroy(dIO); + } + } catch (RadosException e) { + s_logger.error("Failed to perform a RADOS action on the Ceph cluster, the error was: " + e.getMessage()); + disk = null; + } catch (RbdException e) { + s_logger.error("Failed to perform a RBD action on the Ceph cluster, the error was: " + e.getMessage()); + disk = null; + } + } + return disk; + } + @Override public KVMPhysicalDisk createTemplateFromDisk(KVMPhysicalDisk disk, String name, PhysicalDiskFormat format, long size, KVMStoragePool destPool) { return null; @@ -1037,13 +1128,13 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { s_logger.debug("copyPhysicalDisk: disk size:" + disk.getSize() + ", virtualsize:" + disk.getVirtualSize()+" format:"+disk.getFormat()); if (destPool.getType() != StoragePoolType.RBD) { if (disk.getFormat() == PhysicalDiskFormat.TAR) { - newDisk = destPool.createPhysicalDisk(name, PhysicalDiskFormat.DIR, disk.getVirtualSize()); + newDisk = destPool.createPhysicalDisk(name, PhysicalDiskFormat.DIR, Storage.ProvisioningType.THIN, disk.getVirtualSize()); } else { /* If the source device is on a RBD storage pool force the new disk to the same format (RAW) */ if (srcPool.getType() != StoragePoolType.RBD) { - newDisk = destPool.createPhysicalDisk(name, disk.getVirtualSize()); + newDisk = destPool.createPhysicalDisk(name, Storage.ProvisioningType.THIN, disk.getVirtualSize()); } else { - newDisk = destPool.createPhysicalDisk(name, sourceFormat, disk.getVirtualSize()); + newDisk = destPool.createPhysicalDisk(name, sourceFormat, Storage.ProvisioningType.THIN, disk.getVirtualSize()); } } } else { @@ -1071,7 +1162,7 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { srcFile = new QemuImgFile(sourcePath, sourceFormat); try { Map info = qemu.info(srcFile); - String backingFile = info.get(new String("backing_file")); + String backingFile = info.get(new String("backing-file")); // qcow2 templates can just be copied into place if (sourceFormat.equals(destFormat) && backingFile == null && sourcePath.endsWith(".qcow2")) { String result = Script.runSimpleBashScript("cp -f " + sourcePath + " " + destPath, timeout); diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java index 605d154f69e..7678f1dd237 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java @@ -25,6 +25,7 @@ import org.libvirt.StoragePool; import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; +import com.cloud.storage.Storage; import com.cloud.storage.Storage.StoragePoolType; import com.cloud.utils.exception.CloudRuntimeException; @@ -113,13 +114,16 @@ public class LibvirtStoragePool implements KVMStoragePool { } @Override - public KVMPhysicalDisk createPhysicalDisk(String name, PhysicalDiskFormat format, long size) { - return this._storageAdaptor.createPhysicalDisk(name, this, format, size); + public KVMPhysicalDisk createPhysicalDisk(String name, + PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size) { + return this._storageAdaptor + .createPhysicalDisk(name, this, format, provisioningType, size); } @Override - public KVMPhysicalDisk createPhysicalDisk(String name, long size) { - return this._storageAdaptor.createPhysicalDisk(name, this, this.getDefaultFormat(), size); + public KVMPhysicalDisk createPhysicalDisk(String name, Storage.ProvisioningType provisioningType, long size) { + return this._storageAdaptor.createPhysicalDisk(name, this, + this.getDefaultFormat(), provisioningType, size); } @Override diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java index c751aabdde9..ff14148b049 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/StorageAdaptor.java @@ -21,6 +21,7 @@ import java.util.Map; import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; +import com.cloud.storage.Storage; import com.cloud.storage.Storage.StoragePoolType; public interface StorageAdaptor { @@ -35,7 +36,8 @@ public interface StorageAdaptor { public boolean deleteStoragePool(String uuid); - public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, PhysicalDiskFormat format, long size); + public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, + PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size); // given disk path (per database) and pool, prepare disk on host public boolean connectPhysicalDisk(String volumePath, KVMStoragePool pool, Map details); @@ -49,7 +51,9 @@ public interface StorageAdaptor { public boolean deletePhysicalDisk(String uuid, KVMStoragePool pool); - public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, String name, PhysicalDiskFormat format, long size, KVMStoragePool destPool, int timeout); + public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template, + String name, PhysicalDiskFormat format, Storage.ProvisioningType provisioningType, long size, + KVMStoragePool destPool, int timeout); public KVMPhysicalDisk createTemplateFromDisk(KVMPhysicalDisk disk, String name, PhysicalDiskFormat format, long size, KVMStoragePool destPool); diff --git a/plugins/hypervisors/kvm/src/org/apache/cloudstack/utils/qemu/QemuImg.java b/plugins/hypervisors/kvm/src/org/apache/cloudstack/utils/qemu/QemuImg.java index 4bec37535e2..56ed6078063 100644 --- a/plugins/hypervisors/kvm/src/org/apache/cloudstack/utils/qemu/QemuImg.java +++ b/plugins/hypervisors/kvm/src/org/apache/cloudstack/utils/qemu/QemuImg.java @@ -16,11 +16,16 @@ // under the License. package org.apache.cloudstack.utils.qemu; -import java.util.HashMap; import java.util.Map; +import com.cloud.storage.Storage; -import com.cloud.utils.script.OutputInterpreter; import com.cloud.utils.script.Script; +import com.cloud.utils.script.OutputInterpreter; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import org.apache.commons.lang.NotImplementedException; + +import java.lang.reflect.Type; public class QemuImg { @@ -43,6 +48,35 @@ public class QemuImg { } } + public static enum PreallocationType { + Off("off"), + Metadata("metadata"), + Full("full"); + + private final String preallocationType; + + private PreallocationType(String preallocationType){ + this.preallocationType = preallocationType; + } + + public String toString(){ + return this.preallocationType; + } + + public static PreallocationType getPreallocationType(Storage.ProvisioningType provisioningType){ + switch (provisioningType){ + case THIN: + return PreallocationType.Off; + case SPARSE: + return PreallocationType.Metadata; + case FAT: + return PreallocationType.Full; + default: + throw new NotImplementedException(); + } + } + } + public QemuImg(int timeout) { this.timeout = timeout; } @@ -251,9 +285,9 @@ public class QemuImg { * Qemu-img returns human readable output, but this method does it's best * to turn that into machine readeable data. * - * Spaces in keys are replaced by underscores (_). - * Sizes (virtual_size and disk_size) are returned in bytes - * Paths (image and backing_file) are the absolute path to the file + * Spaces in keys are replaced by hyphen-minus (-). + * Sizes (virtual-size and disk-size) are returned in bytes + * Paths (image and backing-file) are the absolute path to the file * * @param file * A QemuImgFile object containing the file to get the information from @@ -262,6 +296,8 @@ public class QemuImg { public Map info(QemuImgFile file) throws QemuImgException { Script s = new Script(_qemuImgPath); s.add("info"); + s.add("--output"); + s.add("json"); s.add(file.getFileName()); OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser(); String result = s.execute(parser); @@ -269,24 +305,9 @@ public class QemuImg { throw new QemuImgException(result); } - HashMap info = new HashMap(); - String[] outputBuffer = parser.getLines().trim().split("\n"); - for (int i = 0; i < outputBuffer.length; i++) { - String[] lineBuffer = outputBuffer[i].split(":", 2); - if (lineBuffer.length == 2) { - String key = lineBuffer[0].trim().replace(" ", "_"); - String value = null; - - if (key.equals("virtual_size")) { - value = lineBuffer[1].trim().replaceAll("^.*\\(([0-9]+).*$", "$1"); - } else { - value = lineBuffer[1].trim(); - } - - info.put(key, value); - } - } - return info; + Type stringStringMap = new TypeToken>(){}.getType(); + Gson gson = new Gson(); + return gson.fromJson(parser.getLines(), stringStringMap); } /* List, apply, create or delete snapshots in image */ diff --git a/plugins/hypervisors/kvm/test/org/apache/cloudstack/utils/qemu/QemuImgTest.java b/plugins/hypervisors/kvm/test/org/apache/cloudstack/utils/qemu/QemuImgTest.java index 8bdff4d7875..9059f8cb48b 100644 --- a/plugins/hypervisors/kvm/test/org/apache/cloudstack/utils/qemu/QemuImgTest.java +++ b/plugins/hypervisors/kvm/test/org/apache/cloudstack/utils/qemu/QemuImgTest.java @@ -17,9 +17,11 @@ package org.apache.cloudstack.utils.qemu; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.fail; import java.io.File; +import com.cloud.utils.script.Script; import java.util.HashMap; import java.util.Map; import java.util.UUID; @@ -29,6 +31,7 @@ import org.junit.Test; import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat; + @Ignore public class QemuImgTest { @@ -48,7 +51,7 @@ public class QemuImgTest { fail("We didn't get any information back from qemu-img"); } - Long infoSize = Long.parseLong(info.get(new String("virtual_size"))); + Long infoSize = Long.parseLong(info.get(new String("virtual-size"))); assertEquals(Long.valueOf(size), Long.valueOf(infoSize)); String infoPath = info.get(new String("image")); @@ -75,13 +78,13 @@ public class QemuImgTest { qemu.create(file, options); Map info = qemu.info(file); - Long infoSize = Long.parseLong(info.get(new String("virtual_size"))); + Long infoSize = Long.parseLong(info.get(new String("virtual-size"))); assertEquals(Long.valueOf(size), Long.valueOf(infoSize)); String infoPath = info.get(new String("image")); assertEquals(filename, infoPath); - String infoClusterSize = info.get(new String("cluster_size")); + String infoClusterSize = info.get(new String("cluster-size")); assertEquals(clusterSize, infoClusterSize); File f = new File(filename); @@ -89,6 +92,31 @@ public class QemuImgTest { } + @Test + public void testCreateSparseVolume() throws QemuImgException { + String filename = "/tmp/" + UUID.randomUUID() + ".qcow2"; + + /* 10TB virtual_size */ + long size = 10995116277760l; + QemuImgFile file = new QemuImgFile(filename, size, PhysicalDiskFormat.QCOW2); + String preallocation = "metadata"; + Map options = new HashMap(); + + options.put("preallocation", preallocation); + + QemuImg qemu = new QemuImg(0); + qemu.create(file, options); + + String allocatedSize = Script.runSimpleBashScript(String.format("ls -alhs %s | awk '{print $1}'", file)); + String declaredSize = Script.runSimpleBashScript(String.format("ls -alhs %s | awk '{print $6}'", file)); + + assertFalse(allocatedSize.equals(declaredSize)); + + File f = new File(filename); + f.delete(); + + } + @Test public void testCreateAndResize() throws QemuImgException { String filename = "/tmp/" + UUID.randomUUID() + ".qcow2"; @@ -107,7 +135,7 @@ public class QemuImgTest { fail("We didn't get any information back from qemu-img"); } - Long infoSize = Long.parseLong(info.get(new String("virtual_size"))); + Long infoSize = Long.parseLong(info.get(new String("virtual-size"))); assertEquals(Long.valueOf(endSize), Long.valueOf(infoSize)); } catch (QemuImgException e) { fail(e.getMessage()); @@ -136,7 +164,7 @@ public class QemuImgTest { fail("We didn't get any information back from qemu-img"); } - Long infoSize = Long.parseLong(info.get(new String("virtual_size"))); + Long infoSize = Long.parseLong(info.get(new String("virtual-size"))); assertEquals(Long.valueOf(startSize + increment), Long.valueOf(infoSize)); } catch (QemuImgException e) { fail(e.getMessage()); @@ -164,7 +192,7 @@ public class QemuImgTest { fail("We didn't get any information back from qemu-img"); } - Long infoSize = Long.parseLong(info.get(new String("virtual_size"))); + Long infoSize = Long.parseLong(info.get(new String("virtual-size"))); assertEquals(Long.valueOf(startSize + increment), Long.valueOf(infoSize)); } catch (QemuImgException e) { fail(e.getMessage()); @@ -227,7 +255,7 @@ public class QemuImgTest { fail("We didn't get any information back from qemu-img"); } - String backingFile = info.get(new String("backing_file")); + String backingFile = info.get(new String("backing-file")); if (backingFile == null) { fail("The second file does not have a property backing_file! Create failed?"); } @@ -275,10 +303,10 @@ public class QemuImgTest { Map info = qemu.info(destFile); - PhysicalDiskFormat infoFormat = PhysicalDiskFormat.valueOf(info.get(new String("file_format")).toUpperCase()); + PhysicalDiskFormat infoFormat = PhysicalDiskFormat.valueOf(info.get(new String("format")).toUpperCase()); assertEquals(destFormat, infoFormat); - Long infoSize = Long.parseLong(info.get(new String("virtual_size"))); + Long infoSize = Long.parseLong(info.get(new String("virtual-size"))); assertEquals(Long.valueOf(srcSize), Long.valueOf(infoSize)); File sf = new File(srcFileName); diff --git a/plugins/network-elements/elastic-loadbalancer/src/com/cloud/network/lb/ElasticLoadBalancerManagerImpl.java b/plugins/network-elements/elastic-loadbalancer/src/com/cloud/network/lb/ElasticLoadBalancerManagerImpl.java index 2a7bcac7f0c..0415baed5ed 100644 --- a/plugins/network-elements/elastic-loadbalancer/src/com/cloud/network/lb/ElasticLoadBalancerManagerImpl.java +++ b/plugins/network-elements/elastic-loadbalancer/src/com/cloud/network/lb/ElasticLoadBalancerManagerImpl.java @@ -83,6 +83,7 @@ import com.cloud.offering.ServiceOffering; import com.cloud.offerings.dao.NetworkOfferingDao; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.storage.Storage; import com.cloud.user.Account; import com.cloud.user.AccountService; import com.cloud.utils.NumbersUtil; @@ -294,8 +295,9 @@ public class ElasticLoadBalancerManagerImpl extends ManagerBase implements Elast _elasticLbVmRamSize = NumbersUtil.parseInt(configs.get(Config.ElasticLoadBalancerVmMemory.key()), DEFAULT_ELB_VM_RAMSIZE); _elasticLbvmCpuMHz = NumbersUtil.parseInt(configs.get(Config.ElasticLoadBalancerVmCpuMhz.key()), DEFAULT_ELB_VM_CPU_MHZ); _elasticLbvmNumCpu = NumbersUtil.parseInt(configs.get(Config.ElasticLoadBalancerVmNumVcpu.key()), 1); - _elasticLbVmOffering = new ServiceOfferingVO("System Offering For Elastic LB VM", _elasticLbvmNumCpu, _elasticLbVmRamSize, _elasticLbvmCpuMHz, 0, 0, true, null, - useLocalStorage, true, null, true, VirtualMachine.Type.ElasticLoadBalancerVm, true); + _elasticLbVmOffering = new ServiceOfferingVO("System Offering For Elastic LB VM", _elasticLbvmNumCpu, + _elasticLbVmRamSize, _elasticLbvmCpuMHz, 0, 0, true, null, Storage.ProvisioningType.THIN, useLocalStorage, + true, null, true, VirtualMachine.Type.ElasticLoadBalancerVm, true); _elasticLbVmOffering.setUniqueName(ServiceOffering.elbVmDefaultOffUniqueName); _elasticLbVmOffering = _serviceOfferingDao.persistSystemServiceOffering(_elasticLbVmOffering); diff --git a/plugins/network-elements/internal-loadbalancer/src/org/apache/cloudstack/network/lb/InternalLoadBalancerVMManagerImpl.java b/plugins/network-elements/internal-loadbalancer/src/org/apache/cloudstack/network/lb/InternalLoadBalancerVMManagerImpl.java index 89707c9dd23..a7943905c04 100644 --- a/plugins/network-elements/internal-loadbalancer/src/org/apache/cloudstack/network/lb/InternalLoadBalancerVMManagerImpl.java +++ b/plugins/network-elements/internal-loadbalancer/src/org/apache/cloudstack/network/lb/InternalLoadBalancerVMManagerImpl.java @@ -92,6 +92,7 @@ import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.dao.VMTemplateDao; +import com.cloud.storage.Storage; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.User; @@ -379,7 +380,8 @@ public class InternalLoadBalancerVMManagerImpl extends ManagerBase implements In boolean useLocalStorage = Boolean.parseBoolean(configs.get(Config.SystemVMUseLocalStorage.key())); ServiceOfferingVO newOff = new ServiceOfferingVO("System Offering For Internal LB VM", 1, InternalLoadBalancerVMManager.DEFAULT_INTERNALLB_VM_RAMSIZE, - InternalLoadBalancerVMManager.DEFAULT_INTERNALLB_VM_CPU_MHZ, null, null, true, null, useLocalStorage, true, null, true, + InternalLoadBalancerVMManager.DEFAULT_INTERNALLB_VM_CPU_MHZ, null, null, true, null, + Storage.ProvisioningType.THIN, useLocalStorage, true, null, true, VirtualMachine.Type.InternalLoadBalancerVm, true); newOff.setUniqueName(ServiceOffering.internalLbVmDefaultOffUniqueName); newOff = _serviceOfferingDao.persistSystemServiceOffering(newOff); diff --git a/plugins/network-elements/internal-loadbalancer/test/org/apache/cloudstack/internallbvmmgr/InternalLBVMManagerTest.java b/plugins/network-elements/internal-loadbalancer/test/org/apache/cloudstack/internallbvmmgr/InternalLBVMManagerTest.java index f4185860dfc..375ba5e593e 100644 --- a/plugins/network-elements/internal-loadbalancer/test/org/apache/cloudstack/internallbvmmgr/InternalLBVMManagerTest.java +++ b/plugins/network-elements/internal-loadbalancer/test/org/apache/cloudstack/internallbvmmgr/InternalLBVMManagerTest.java @@ -24,6 +24,7 @@ import java.util.List; import javax.inject.Inject; +import com.cloud.storage.Storage; import junit.framework.TestCase; import org.junit.Before; @@ -117,8 +118,8 @@ public class InternalLBVMManagerTest extends TestCase { public void setUp() { //mock system offering creation as it's used by configure() method called by initComponentsLifeCycle Mockito.when(_accountMgr.getAccount(1L)).thenReturn(new AccountVO()); - ServiceOfferingVO off = - new ServiceOfferingVO("alena", 1, 1, 1, 1, 1, false, "alena", false, false, null, false, VirtualMachine.Type.InternalLoadBalancerVm, false); + ServiceOfferingVO off = new ServiceOfferingVO("alena", 1, 1, + 1, 1, 1, false, "alena", Storage.ProvisioningType.THIN, false, false, null, false, VirtualMachine.Type.InternalLoadBalancerVm, false); off = setId(off, 1); Mockito.when(_svcOffDao.persistSystemServiceOffering(Matchers.any(ServiceOfferingVO.class))).thenReturn(off); diff --git a/plugins/network-elements/internal-loadbalancer/test/org/apache/cloudstack/internallbvmmgr/InternalLBVMServiceTest.java b/plugins/network-elements/internal-loadbalancer/test/org/apache/cloudstack/internallbvmmgr/InternalLBVMServiceTest.java index 62ca09b9c76..11a0bed8e81 100644 --- a/plugins/network-elements/internal-loadbalancer/test/org/apache/cloudstack/internallbvmmgr/InternalLBVMServiceTest.java +++ b/plugins/network-elements/internal-loadbalancer/test/org/apache/cloudstack/internallbvmmgr/InternalLBVMServiceTest.java @@ -20,6 +20,7 @@ import java.lang.reflect.Field; import javax.inject.Inject; +import com.cloud.storage.Storage; import junit.framework.TestCase; import org.junit.After; @@ -87,8 +88,8 @@ public class InternalLBVMServiceTest extends TestCase { public void setUp() { //mock system offering creation as it's used by configure() method called by initComponentsLifeCycle Mockito.when(_accountMgr.getAccount(1L)).thenReturn(new AccountVO()); - ServiceOfferingVO off = - new ServiceOfferingVO("alena", 1, 1, 1, 1, 1, false, "alena", false, false, null, false, VirtualMachine.Type.InternalLoadBalancerVm, false); + ServiceOfferingVO off = new ServiceOfferingVO("alena", 1, 1, + 1, 1, 1, false, "alena", Storage.ProvisioningType.THIN, false, false, null, false, VirtualMachine.Type.InternalLoadBalancerVm, false); off = setId(off, 1); Mockito.when(_svcOffDao.persistSystemServiceOffering(Matchers.any(ServiceOfferingVO.class))).thenReturn(off); diff --git a/server/src/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java b/server/src/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java index bf28c03e46f..a99f19dd0dd 100644 --- a/server/src/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java +++ b/server/src/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java @@ -59,6 +59,7 @@ public class DiskOfferingJoinDaoImpl extends GenericDaoBase implem volResponse.setVirtualMachineDisplayName(volume.getVmDisplayName()); } + volResponse.setProvisioningType(volume.getProvisioningType().toString()); + // Show the virtual size of the volume volResponse.setSize(volume.getSize()); diff --git a/server/src/com/cloud/api/query/vo/DiskOfferingJoinVO.java b/server/src/com/cloud/api/query/vo/DiskOfferingJoinVO.java index e3e0a9e2186..1a1c787f687 100644 --- a/server/src/com/cloud/api/query/vo/DiskOfferingJoinVO.java +++ b/server/src/com/cloud/api/query/vo/DiskOfferingJoinVO.java @@ -23,6 +23,7 @@ import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; +import com.cloud.storage.Storage; import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.InternalIdentity; @@ -46,6 +47,9 @@ public class DiskOfferingJoinVO extends BaseViewVO implements InternalIdentity, @Column(name = "display_text") private String displayText; + @Column(name = "provisioning_type") + Storage.ProvisioningType provisioningType; + @Column(name = "disk_size") long diskSize; @@ -136,6 +140,10 @@ public class DiskOfferingJoinVO extends BaseViewVO implements InternalIdentity, return displayText; } + public Storage.ProvisioningType getProvisioningType(){ + return provisioningType; + } + public long getDiskSize() { return diskSize; } diff --git a/server/src/com/cloud/api/query/vo/ServiceOfferingJoinVO.java b/server/src/com/cloud/api/query/vo/ServiceOfferingJoinVO.java index dbeb53062f3..f16ba8333bd 100644 --- a/server/src/com/cloud/api/query/vo/ServiceOfferingJoinVO.java +++ b/server/src/com/cloud/api/query/vo/ServiceOfferingJoinVO.java @@ -23,6 +23,8 @@ import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.Table; +import com.cloud.storage.Storage; + import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.InternalIdentity; @@ -45,6 +47,9 @@ public class ServiceOfferingJoinVO extends BaseViewVO implements InternalIdentit @Column(name = "display_text") private String displayText; + @Column(name = "provisioning_type") + Storage.ProvisioningType provisioningType; + @Column(name = "tags", length = 4096) String tags; @@ -156,6 +161,10 @@ public class ServiceOfferingJoinVO extends BaseViewVO implements InternalIdentit return displayText; } + public Storage.ProvisioningType getProvisioningType(){ + return provisioningType; + } + public String getTags() { return tags; } diff --git a/server/src/com/cloud/api/query/vo/VolumeJoinVO.java b/server/src/com/cloud/api/query/vo/VolumeJoinVO.java index 8165d68f800..cbc9efe4c44 100644 --- a/server/src/com/cloud/api/query/vo/VolumeJoinVO.java +++ b/server/src/com/cloud/api/query/vo/VolumeJoinVO.java @@ -56,6 +56,10 @@ public class VolumeJoinVO extends BaseViewVO implements ControlledViewEntity { @Enumerated(EnumType.STRING) Volume.Type volumeType; + @Column(name = "provisioning_type") + @Enumerated(EnumType.STRING) + Storage.ProvisioningType provisioningType; + @Column(name = "size") long size; @@ -292,6 +296,10 @@ public class VolumeJoinVO extends BaseViewVO implements ControlledViewEntity { return volumeType; } + public Storage.ProvisioningType getProvisioningType(){ + return provisioningType; + } + public long getSize() { return size; } diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index 30afd7bab64..6aca4a8d044 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -175,6 +175,7 @@ import com.cloud.service.ServiceOfferingDetailsVO; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.service.dao.ServiceOfferingDetailsDao; +import com.cloud.storage.Storage.ProvisioningType; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.test.IPRangeConfig; @@ -2018,19 +2019,24 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } } - return createServiceOffering(userId, cmd.getIsSystem(), vmType, cmd.getServiceOfferingName(), cpuNumber, memory, cpuSpeed, cmd.getDisplayText(), localStorageRequired, - offerHA, limitCpuUse, volatileVm, cmd.getTags(), cmd.getDomainId(), cmd.getHostTag(), cmd.getNetworkRate(), cmd.getDeploymentPlanner(), cmd.getDetails(), - cmd.isCustomizedIops(), cmd.getMinIops(), cmd.getMaxIops(), cmd.getBytesReadRate(), cmd.getBytesWriteRate(), cmd.getIopsReadRate(), cmd.getIopsWriteRate(), - cmd.getHypervisorSnapshotReserve()); + return createServiceOffering(userId, cmd.getIsSystem(), vmType, cmd.getServiceOfferingName(), cpuNumber, memory, cpuSpeed, cmd.getDisplayText(), + cmd.getProvisioningType(), localStorageRequired, offerHA, limitCpuUse, volatileVm, cmd.getTags(), cmd.getDomainId(), cmd.getHostTag(), + cmd.getNetworkRate(), cmd.getDeploymentPlanner(), cmd.getDetails(), cmd.isCustomizedIops(), cmd.getMinIops(), cmd.getMaxIops(), + cmd.getBytesReadRate(), cmd.getBytesWriteRate(), cmd.getIopsReadRate(), cmd.getIopsWriteRate(), cmd.getHypervisorSnapshotReserve()); } - protected ServiceOfferingVO createServiceOffering(long userId, boolean isSystem, VirtualMachine.Type vmType, String name, Integer cpu, Integer ramSize, Integer speed, - String displayText, boolean localStorageRequired, boolean offerHA, boolean limitResourceUse, boolean volatileVm, String tags, Long domainId, String hostTag, + protected ServiceOfferingVO createServiceOffering(long userId, boolean isSystem, VirtualMachine.Type vmType, + String name, Integer cpu, Integer ramSize, Integer speed, String displayText, String provisioningType, boolean localStorageRequired, + boolean offerHA, boolean limitResourceUse, boolean volatileVm, String tags, Long domainId, String hostTag, Integer networkRate, String deploymentPlanner, Map details, Boolean isCustomizedIops, Long minIops, Long maxIops, Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate, Integer hypervisorSnapshotReserve) { + + ProvisioningType typedProvisioningType = ProvisioningType.getProvisioningType(provisioningType); + tags = StringUtils.cleanupTags(tags); - ServiceOfferingVO offering = new ServiceOfferingVO(name, cpu, ramSize, speed, networkRate, null, offerHA, limitResourceUse, volatileVm, displayText, localStorageRequired, - false, tags, isSystem, vmType, domainId, hostTag, deploymentPlanner); + ServiceOfferingVO offering = new ServiceOfferingVO(name, cpu, ramSize, speed, networkRate, null, offerHA, + limitResourceUse, volatileVm, displayText, typedProvisioningType, localStorageRequired, false, tags, isSystem, vmType, + domainId, hostTag, deploymentPlanner); if (isCustomizedIops != null) { bytesReadRate = null; @@ -2200,8 +2206,10 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } } - protected DiskOfferingVO createDiskOffering(Long domainId, String name, String description, Long numGibibytes, String tags, boolean isCustomized, boolean localStorageRequired, - boolean isDisplayOfferingEnabled, Boolean isCustomizedIops, Long minIops, Long maxIops, Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate, + protected DiskOfferingVO createDiskOffering(Long domainId, String name, String description, String provisioningType, + Long numGibibytes, String tags, boolean isCustomized, boolean localStorageRequired, + boolean isDisplayOfferingEnabled, Boolean isCustomizedIops, Long minIops, Long maxIops, + Long bytesReadRate, Long bytesWriteRate, Long iopsReadRate, Long iopsWriteRate, Integer hypervisorSnapshotReserve) { long diskSize = 0;// special case for custom disk offerings if (numGibibytes != null && (numGibibytes <= 0)) { @@ -2209,6 +2217,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } else if (numGibibytes != null && (numGibibytes > _maxVolumeSizeInGb)) { throw new InvalidParameterValueException("The maximum size for a disk is " + _maxVolumeSizeInGb + " Gb."); } + ProvisioningType typedProvisioningType = ProvisioningType.getProvisioningType(provisioningType); if (numGibibytes != null) { diskSize = numGibibytes * 1024 * 1024 * 1024; @@ -2251,7 +2260,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati } tags = StringUtils.cleanupTags(tags); - DiskOfferingVO newDiskOffering = new DiskOfferingVO(domainId, name, description, diskSize, tags, isCustomized, isCustomizedIops, minIops, maxIops); + DiskOfferingVO newDiskOffering = new DiskOfferingVO(domainId, name, description, typedProvisioningType, diskSize, tags, isCustomized, + isCustomizedIops, minIops, maxIops); newDiskOffering.setUseLocalStorage(localStorageRequired); newDiskOffering.setDisplayOffering(isDisplayOfferingEnabled); @@ -2285,6 +2295,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati public DiskOffering createDiskOffering(CreateDiskOfferingCmd cmd) { String name = cmd.getOfferingName(); String description = cmd.getDisplayText(); + String provisioningType = cmd.getProvisioningType(); Long numGibibytes = cmd.getDiskSize(); boolean isDisplayOfferingEnabled = cmd.getDisplayOffering() != null ? cmd.getDisplayOffering() : true; boolean isCustomized = cmd.isCustomized() != null ? cmd.isCustomized() : false; // false @@ -2320,7 +2331,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati Long iopsWriteRate = cmd.getIopsWriteRate(); Integer hypervisorSnapshotReserve = cmd.getHypervisorSnapshotReserve(); - return createDiskOffering(domainId, name, description, numGibibytes, tags, isCustomized, localStorageRequired, isDisplayOfferingEnabled, isCustomizedIops, minIops, + return createDiskOffering(domainId, name, description, provisioningType, numGibibytes, tags, isCustomized, + localStorageRequired, isDisplayOfferingEnabled, isCustomizedIops, minIops, maxIops, bytesReadRate, bytesWriteRate, iopsReadRate, iopsWriteRate, hypervisorSnapshotReserve); } diff --git a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java index 05120963961..14d782a47ef 100755 --- a/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java +++ b/server/src/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java @@ -31,6 +31,7 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.cloudstack.config.ApiServiceConfiguration; + import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; @@ -100,6 +101,7 @@ import com.cloud.resource.ServerResource; import com.cloud.resource.UnableDeleteHostException; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.storage.Storage; import com.cloud.storage.StoragePoolStatus; import com.cloud.storage.VMTemplateStorageResourceAssoc.Status; import com.cloud.storage.VMTemplateVO; @@ -1267,7 +1269,8 @@ public class ConsoleProxyManagerImpl extends ManagerBase implements ConsoleProxy int ramSize = NumbersUtil.parseInt(_configDao.getValue("console.ram.size"), DEFAULT_PROXY_VM_RAMSIZE); int cpuFreq = NumbersUtil.parseInt(_configDao.getValue("console.cpu.mhz"), DEFAULT_PROXY_VM_CPUMHZ); _serviceOffering = - new ServiceOfferingVO("System Offering For Console Proxy", 1, ramSize, cpuFreq, 0, 0, false, null, useLocalStorage, true, null, true, + new ServiceOfferingVO("System Offering For Console Proxy", 1, ramSize, cpuFreq, 0, 0, false, null, + Storage.ProvisioningType.THIN, useLocalStorage, true, null, true, VirtualMachine.Type.ConsoleProxy, true); _serviceOffering.setUniqueName(ServiceOffering.consoleProxyDefaultOffUniqueName); _serviceOffering = _offeringDao.persistSystemServiceOffering(_serviceOffering); diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index 3cd3e807f62..bbec5f7c8e5 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -226,6 +226,7 @@ import com.cloud.resource.ResourceManager; import com.cloud.server.ConfigurationServer; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.storage.Storage.ProvisioningType; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.Volume; import com.cloud.storage.VolumeVO; @@ -741,9 +742,8 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V _agentMgr.registerForHostEvents(new SshKeysDistriMonitor(_agentMgr, _hostDao, _configDao), true, false, false); final boolean useLocalStorage = Boolean.parseBoolean(configs.get(Config.SystemVMUseLocalStorage.key())); - _offering = - new ServiceOfferingVO("System Offering For Software Router", 1, _routerRamSize, _routerCpuMHz, null, null, true, null, useLocalStorage, true, null, true, - VirtualMachine.Type.DomainRouter, true); + _offering = new ServiceOfferingVO("System Offering For Software Router", 1, _routerRamSize, _routerCpuMHz, null, + null, true, null, ProvisioningType.THIN, useLocalStorage, true, null, true, VirtualMachine.Type.DomainRouter, true); _offering.setUniqueName(ServiceOffering.routerDefaultOffUniqueName); _offering = _serviceOfferingDao.persistSystemServiceOffering(_offering); diff --git a/server/src/com/cloud/server/ConfigurationServerImpl.java b/server/src/com/cloud/server/ConfigurationServerImpl.java index 13135b0e192..40f69aaccbc 100755 --- a/server/src/com/cloud/server/ConfigurationServerImpl.java +++ b/server/src/com/cloud/server/ConfigurationServerImpl.java @@ -96,6 +96,7 @@ import com.cloud.offerings.dao.NetworkOfferingServiceMapDao; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.DiskOfferingVO; +import com.cloud.storage.Storage.ProvisioningType; import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.test.IPRangeConfig; import com.cloud.user.Account; @@ -218,14 +219,14 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio s_logger.debug("ConfigurationServer made secondary storage copy use realhostip."); // Save default service offerings - createServiceOffering(User.UID_SYSTEM, "Small Instance", 1, 512, 500, "Small Instance", false, false, null); - createServiceOffering(User.UID_SYSTEM, "Medium Instance", 1, 1024, 1000, "Medium Instance", false, false, null); + createServiceOffering(User.UID_SYSTEM, "Small Instance", 1, 512, 500, "Small Instance", ProvisioningType.THIN, false, false, null); + createServiceOffering(User.UID_SYSTEM, "Medium Instance", 1, 1024, 1000, "Medium Instance", ProvisioningType.THIN, false, false, null); // Save default disk offerings - createdefaultDiskOffering(null, "Small", "Small Disk, 5 GB", 5, null, false, false); - createdefaultDiskOffering(null, "Medium", "Medium Disk, 20 GB", 20, null, false, false); - createdefaultDiskOffering(null, "Large", "Large Disk, 100 GB", 100, null, false, false); - createdefaultDiskOffering(null, "Large", "Large Disk, 100 GB", 100, null, false, false); - createdefaultDiskOffering(null, "Custom", "Custom Disk", 0, null, true, false); + createdefaultDiskOffering(null, "Small", "Small Disk, 5 GB", ProvisioningType.THIN, 5, null, false, false); + createdefaultDiskOffering(null, "Medium", "Medium Disk, 20 GB", ProvisioningType.THIN, 20, null, false, false); + createdefaultDiskOffering(null, "Large", "Large Disk, 100 GB", ProvisioningType.THIN, 100, null, false, false); + createdefaultDiskOffering(null, "Large", "Large Disk, 100 GB", ProvisioningType.THIN, 100, null, false, false); + createdefaultDiskOffering(null, "Custom", "Custom Disk", ProvisioningType.THIN, 0, null, true, false); // Save the mount parent to the configuration table String mountParent = getMountParent(); @@ -1026,24 +1027,24 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio return pod; } - private DiskOfferingVO createdefaultDiskOffering(Long domainId, String name, String description, int numGibibytes, String tags, boolean isCustomized, - boolean isSystemUse) { + private DiskOfferingVO createdefaultDiskOffering(Long domainId, String name, String description, ProvisioningType provisioningType, + int numGibibytes, String tags, boolean isCustomized, boolean isSystemUse) { long diskSize = numGibibytes; diskSize = diskSize * 1024 * 1024 * 1024; tags = cleanupTags(tags); - DiskOfferingVO newDiskOffering = new DiskOfferingVO(domainId, name, description, diskSize, tags, isCustomized, null, null, null); + DiskOfferingVO newDiskOffering = new DiskOfferingVO(domainId, name, description, provisioningType, diskSize, tags, isCustomized, null, null, null); newDiskOffering.setUniqueName("Cloud.Com-" + name); newDiskOffering.setSystemUse(isSystemUse); newDiskOffering = _diskOfferingDao.persistDeafultDiskOffering(newDiskOffering); return newDiskOffering; } - private ServiceOfferingVO createServiceOffering(long userId, String name, int cpu, int ramSize, int speed, String displayText, boolean localStorageRequired, - boolean offerHA, String tags) { + private ServiceOfferingVO createServiceOffering(long userId, String name, int cpu, int ramSize, int speed, String displayText, + ProvisioningType provisioningType, boolean localStorageRequired, boolean offerHA, String tags) { tags = cleanupTags(tags); ServiceOfferingVO offering = - new ServiceOfferingVO(name, cpu, ramSize, speed, null, null, offerHA, displayText, localStorageRequired, false, tags, false, null, false); + new ServiceOfferingVO(name, cpu, ramSize, speed, null, null, offerHA, displayText, provisioningType, localStorageRequired, false, tags, false, null, false); offering.setUniqueName("Cloud.Com-" + name); offering = _serviceOfferingDao.persistSystemServiceOffering(offering); return offering; diff --git a/server/src/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/com/cloud/storage/VolumeApiServiceImpl.java index c0741c58637..7a0517fd290 100644 --- a/server/src/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/com/cloud/storage/VolumeApiServiceImpl.java @@ -330,10 +330,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic return Transaction.execute(new TransactionCallback() { @Override public VolumeVO doInTransaction(TransactionStatus status) { - VolumeVO volume = new VolumeVO(volumeName, zoneId, -1, -1, -1, new Long(-1), null, null, 0, Volume.Type.DATADISK); - volume.setPoolId(null); - volume.setDataCenterId(zoneId); - volume.setPodId(null); + VolumeVO volume = new VolumeVO(volumeName, zoneId, -1, -1, -1, new Long(-1), null, null, Storage.ProvisioningType.THIN, 0, Volume.Type.DATADISK); + volume.setPoolId(null); + volume.setDataCenterId(zoneId); + volume.setPodId(null); // to prevent a nullpointer deref I put the system account id here when no owner is given. // TODO Decide if this is valid or whether throwing a CloudRuntimeException is more appropriate volume.setAccountId((owner == null) ? Account.ACCOUNT_ID_SYSTEM : owner.getAccountId()); @@ -391,6 +391,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic Long zoneId = cmd.getZoneId(); Long diskOfferingId = null; DiskOfferingVO diskOffering = null; + Storage.ProvisioningType provisioningType; Long size = null; Long minIops = null; Long maxIops = null; @@ -477,6 +478,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic } } + provisioningType = diskOffering.getProvisioningType(); + if (!validateVolumeSizeRange(size)) {// convert size from mb to gb // for validation throw new InvalidParameterValueException("Invalid size for custom volume creation: " + size + " ,max volume size is:" + _maxVolumeSizeInGb); @@ -502,6 +505,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic size = snapshotCheck.getSize(); // ; disk offering is used for tags // purposes + provisioningType = diskOffering.getProvisioningType(); + // one step operation - create volume in VM's cluster and attach it // to the VM Long vmId = cmd.getVirtualMachineId(); @@ -545,54 +550,55 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic userSpecifiedName = getRandomVolumeName(); } - VolumeVO volume = commitVolume(cmd, caller, ownerId, displayVolume, zoneId, diskOfferingId, size, minIops, maxIops, parentVolume, userSpecifiedName, - _uuidMgr.generateUuid(Volume.class, cmd.getCustomId())); + VolumeVO volume = commitVolume(cmd, caller, ownerId, displayVolume, zoneId, diskOfferingId, provisioningType, size, + minIops, maxIops, parentVolume, userSpecifiedName, _uuidMgr.generateUuid(Volume.class, cmd.getCustomId())); return volume; } - private VolumeVO commitVolume(final CreateVolumeCmd cmd, final Account caller, final long ownerId, final Boolean displayVolume, final Long zoneId, - final Long diskOfferingId, final Long size, final Long minIops, final Long maxIops, final VolumeVO parentVolume, final String userSpecifiedName, final String uuid) { + private VolumeVO commitVolume(final CreateVolumeCmd cmd, final Account caller, final long ownerId, final Boolean displayVolume, + final Long zoneId, final Long diskOfferingId, final Storage.ProvisioningType provisioningType, final Long size, final Long minIops, final Long maxIops, final VolumeVO parentVolume, + final String userSpecifiedName, final String uuid) { return Transaction.execute(new TransactionCallback() { @Override public VolumeVO doInTransaction(TransactionStatus status) { - VolumeVO volume = new VolumeVO(userSpecifiedName, -1, -1, -1, -1, new Long(-1), null, null, 0, Volume.Type.DATADISK); - volume.setPoolId(null); + VolumeVO volume = new VolumeVO(userSpecifiedName, -1, -1, -1, -1, new Long(-1), null, null, provisioningType, 0, Volume.Type.DATADISK); + volume.setPoolId(null); volume.setUuid(uuid); - volume.setDataCenterId(zoneId); - volume.setPodId(null); - volume.setAccountId(ownerId); - volume.setDomainId(((caller == null) ? Domain.ROOT_DOMAIN : caller.getDomainId())); - volume.setDiskOfferingId(diskOfferingId); - volume.setSize(size); - volume.setMinIops(minIops); - volume.setMaxIops(maxIops); - volume.setInstanceId(null); - volume.setUpdated(new Date()); - volume.setDomainId((caller == null) ? Domain.ROOT_DOMAIN : caller.getDomainId()); + volume.setDataCenterId(zoneId); + volume.setPodId(null); + volume.setAccountId(ownerId); + volume.setDomainId(((caller == null) ? Domain.ROOT_DOMAIN : caller.getDomainId())); + volume.setDiskOfferingId(diskOfferingId); + volume.setSize(size); + volume.setMinIops(minIops); + volume.setMaxIops(maxIops); + volume.setInstanceId(null); + volume.setUpdated(new Date()); + volume.setDomainId((caller == null) ? Domain.ROOT_DOMAIN : caller.getDomainId()); volume.setDisplayVolume(displayVolume); - if (parentVolume != null) { - volume.setTemplateId(parentVolume.getTemplateId()); - volume.setFormat(parentVolume.getFormat()); - } else { - volume.setTemplateId(null); - } + if (parentVolume != null) { + volume.setTemplateId(parentVolume.getTemplateId()); + volume.setFormat(parentVolume.getFormat()); + } else { + volume.setTemplateId(null); + } - volume = _volsDao.persist(volume); - if (cmd.getSnapshotId() == null && displayVolume) { - // for volume created from snapshot, create usage event after volume creation - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), - diskOfferingId, null, size, Volume.class.getName(), volume.getUuid(), displayVolume); - } + volume = _volsDao.persist(volume); + if (cmd.getSnapshotId() == null && displayVolume) { + // for volume created from snapshot, create usage event after volume creation + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), + diskOfferingId, null, size, Volume.class.getName(), volume.getUuid(), displayVolume); + } - CallContext.current().setEventDetails("Volume Id: " + volume.getId()); + CallContext.current().setEventDetails("Volume Id: " + volume.getId()); - // Increment resource count during allocation; if actual creation fails, - // decrement it + // Increment resource count during allocation; if actual creation fails, + // decrement it _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.volume, displayVolume); _resourceLimitMgr.incrementResourceCount(volume.getAccountId(), ResourceType.primary_storage, displayVolume, new Long(volume.getSize())); - return volume; - } + return volume; + } }); } diff --git a/server/src/com/cloud/test/DatabaseConfig.java b/server/src/com/cloud/test/DatabaseConfig.java index 8d7b42a8e00..07e4348f3fd 100755 --- a/server/src/com/cloud/test/DatabaseConfig.java +++ b/server/src/com/cloud/test/DatabaseConfig.java @@ -53,6 +53,7 @@ import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDaoImpl; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.dao.DiskOfferingDaoImpl; +import com.cloud.storage.Storage.ProvisioningType; import com.cloud.utils.PropertiesUtil; import com.cloud.utils.component.ComponentContext; import com.cloud.utils.db.DB; @@ -906,6 +907,7 @@ public class DatabaseConfig { long id = Long.parseLong(_currentObjectParams.get("id")); String name = _currentObjectParams.get("name"); String displayText = _currentObjectParams.get("displayText"); + ProvisioningType provisioningType = ProvisioningType.valueOf(_currentObjectParams.get("provisioningType")); int cpu = Integer.parseInt(_currentObjectParams.get("cpu")); int ramSize = Integer.parseInt(_currentObjectParams.get("ramSize")); int speed = Integer.parseInt(_currentObjectParams.get("speed")); @@ -928,7 +930,8 @@ public class DatabaseConfig { } ServiceOfferingVO serviceOffering = - new ServiceOfferingVO(name, cpu, ramSize, speed, null, null, ha, displayText, useLocalStorage, false, null, false, null, false); + new ServiceOfferingVO(name, cpu, ramSize, speed, null, null, ha, displayText, + provisioningType, useLocalStorage, false, null, false, null, false); Long bytesReadRate = Long.parseLong(_currentObjectParams.get("bytesReadRate")); if ((bytesReadRate != null) && (bytesReadRate > 0)) @@ -971,6 +974,7 @@ public class DatabaseConfig { long domainId = Long.parseLong(_currentObjectParams.get("domainId")); String name = _currentObjectParams.get("name"); String displayText = _currentObjectParams.get("displayText"); + ProvisioningType provisioningType = ProvisioningType.valueOf(_currentObjectParams.get("provisioningtype")); long diskSpace = Long.parseLong(_currentObjectParams.get("diskSpace")); diskSpace = diskSpace * 1024 * 1024; // boolean mirroring = Boolean.parseBoolean(_currentObjectParams.get("mirrored")); @@ -990,7 +994,7 @@ public class DatabaseConfig { newTags.delete(newTags.length() - 1, newTags.length()); tags = newTags.toString(); } - DiskOfferingVO diskOffering = new DiskOfferingVO(domainId, name, displayText, diskSpace, tags, false, null, null, null); + DiskOfferingVO diskOffering = new DiskOfferingVO(domainId, name, displayText, provisioningType, diskSpace, tags, false, null, null, null); diskOffering.setUseLocalStorage(local); Long bytesReadRate = Long.parseLong(_currentObjectParams.get("bytesReadRate")); diff --git a/server/test/com/cloud/storage/VolumeApiServiceImplTest.java b/server/test/com/cloud/storage/VolumeApiServiceImplTest.java index 0be6f3ab238..b7d041da0f0 100644 --- a/server/test/com/cloud/storage/VolumeApiServiceImplTest.java +++ b/server/test/com/cloud/storage/VolumeApiServiceImplTest.java @@ -116,7 +116,7 @@ public class VolumeApiServiceImplTest { TransactionLegacy txn = TransactionLegacy.open("runVolumeDaoImplTest"); try { // volume of running vm id=1 - VolumeVO volumeOfRunningVm = new VolumeVO("root", 1L, 1L, 1L, 1L, 1L, "root", "root", 1, null, + VolumeVO volumeOfRunningVm = new VolumeVO("root", 1L, 1L, 1L, 1L, 1L, "root", "root", Storage.ProvisioningType.THIN, 1, null, null, "root", Volume.Type.ROOT); when(_svc._volsDao.findById(1L)).thenReturn(volumeOfRunningVm); @@ -127,7 +127,7 @@ public class VolumeApiServiceImplTest { when(_svc._userVmDao.findById(1L)).thenReturn(runningVm); // volume of stopped vm id=2 - VolumeVO volumeOfStoppedVm = new VolumeVO("root", 1L, 1L, 1L, 1L, 2L, "root", "root", 1, null, + VolumeVO volumeOfStoppedVm = new VolumeVO("root", 1L, 1L, 1L, 1L, 2L, "root", "root", Storage.ProvisioningType.THIN, 1, null, null, "root", Volume.Type.ROOT); volumeOfStoppedVm.setPoolId(1L); when(_svc._volsDao.findById(2L)).thenReturn(volumeOfStoppedVm); @@ -146,7 +146,7 @@ public class VolumeApiServiceImplTest { hyperVVm.setDataCenterId(1L); when(_svc._userVmDao.findById(3L)).thenReturn(hyperVVm); - VolumeVO volumeOfStoppeHyperVVm = new VolumeVO("root", 1L, 1L, 1L, 1L, 3L, "root", "root", 1, null, + VolumeVO volumeOfStoppeHyperVVm = new VolumeVO("root", 1L, 1L, 1L, 1L, 3L, "root", "root", Storage.ProvisioningType.THIN, 1, null, null, "root", Volume.Type.ROOT); volumeOfStoppeHyperVVm.setPoolId(1L); when(_svc._volsDao.findById(3L)).thenReturn(volumeOfStoppeHyperVVm); @@ -158,7 +158,7 @@ public class VolumeApiServiceImplTest { StoragePoolVO managedPool = new StoragePoolVO(); managedPool.setManaged(true); when(_svc._storagePoolDao.findById(2L)).thenReturn(managedPool); - VolumeVO managedPoolVolume = new VolumeVO("root", 1L, 1L, 1L, 1L, 2L, "root", "root", 1, null, + VolumeVO managedPoolVolume = new VolumeVO("root", 1L, 1L, 1L, 1L, 2L, "root", "root", Storage.ProvisioningType.THIN, 1, null, null, "root", Volume.Type.ROOT); managedPoolVolume.setPoolId(2L); when(_svc._volsDao.findById(4L)).thenReturn(managedPoolVolume); @@ -177,7 +177,7 @@ public class VolumeApiServiceImplTest { when(correctRootVolume.getInstanceId()).thenReturn(null); when(_svc.volFactory.getVolume(6L)).thenReturn(correctRootVolume); - VolumeVO correctRootVolumeVO = new VolumeVO("root", 1L, 1L, 1L, 1L, 2L, "root", "root", 1, null, + VolumeVO correctRootVolumeVO = new VolumeVO("root", 1L, 1L, 1L, 1L, 2L, "root", "root", Storage.ProvisioningType.THIN, 1, null, null, "root", Volume.Type.ROOT); when(_svc._volsDao.findById(6L)).thenReturn(correctRootVolumeVO); @@ -190,7 +190,7 @@ public class VolumeApiServiceImplTest { when(managedVolume.getPoolId()).thenReturn(2L); when(_svc.volFactory.getVolume(7L)).thenReturn(managedVolume); - VolumeVO managedVolume1 = new VolumeVO("root", 1L, 1L, 1L, 1L, 2L, "root", "root", 1, null, + VolumeVO managedVolume1 = new VolumeVO("root", 1L, 1L, 1L, 1L, 2L, "root", "root", Storage.ProvisioningType.THIN, 1, null, null, "root", Volume.Type.ROOT); managedVolume1.setPoolId(2L); managedVolume1.setDataCenterId(1L); @@ -216,7 +216,7 @@ public class VolumeApiServiceImplTest { when(uploadedVolume.getState()).thenReturn(Volume.State.Uploaded); when(_svc.volFactory.getVolume(8L)).thenReturn(uploadedVolume); - VolumeVO upVolume = new VolumeVO("root", 1L, 1L, 1L, 1L, 2L, "root", "root", 1, null, + VolumeVO upVolume = new VolumeVO("root", 1L, 1L, 1L, 1L, 2L, "root", "root", Storage.ProvisioningType.THIN, 1, null, null, "root", Volume.Type.ROOT); upVolume.setPoolId(1L); upVolume.setDataCenterId(1L); diff --git a/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java b/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java index fb63766e378..94c1d186e4a 100644 --- a/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java +++ b/server/test/com/cloud/vm/DeploymentPlanningManagerImplTest.java @@ -81,6 +81,7 @@ import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.resource.ResourceManager; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDetailsDao; +import com.cloud.storage.Storage.ProvisioningType; import com.cloud.storage.StorageManager; import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.dao.GuestOSCategoryDao; @@ -163,7 +164,8 @@ public class DeploymentPlanningManagerImplTest { @Test public void dataCenterAvoidTest() throws InsufficientServerCapacityException, AffinityConflictException { ServiceOfferingVO svcOffering = - new ServiceOfferingVO("testOffering", 1, 512, 500, 1, 1, false, false, false, "test dpm", false, false, null, false, VirtualMachine.Type.User, domainId, + new ServiceOfferingVO("testOffering", 1, 512, 500, 1, 1, false, false, false, "test dpm", + ProvisioningType.THIN, false, false, null, false, VirtualMachine.Type.User, domainId, null, "FirstFitPlanner"); Mockito.when(vmProfile.getServiceOffering()).thenReturn(svcOffering); @@ -177,7 +179,8 @@ public class DeploymentPlanningManagerImplTest { @Test public void plannerCannotHandleTest() throws InsufficientServerCapacityException, AffinityConflictException { ServiceOfferingVO svcOffering = - new ServiceOfferingVO("testOffering", 1, 512, 500, 1, 1, false, false, false, "test dpm", false, false, null, false, VirtualMachine.Type.User, domainId, + new ServiceOfferingVO("testOffering", 1, 512, 500, 1, 1, false, false, false, "test dpm", + ProvisioningType.THIN, false, false, null, false, VirtualMachine.Type.User, domainId, null, "UserDispersingPlanner"); Mockito.when(vmProfile.getServiceOffering()).thenReturn(svcOffering); @@ -192,7 +195,8 @@ public class DeploymentPlanningManagerImplTest { @Test public void emptyClusterListTest() throws InsufficientServerCapacityException, AffinityConflictException { ServiceOfferingVO svcOffering = - new ServiceOfferingVO("testOffering", 1, 512, 500, 1, 1, false, false, false, "test dpm", false, false, null, false, VirtualMachine.Type.User, domainId, + new ServiceOfferingVO("testOffering", 1, 512, 500, 1, 1, false, false, false, "test dpm", + ProvisioningType.THIN, false, false, null, false, VirtualMachine.Type.User, domainId, null, "FirstFitPlanner"); Mockito.when(vmProfile.getServiceOffering()).thenReturn(svcOffering); diff --git a/server/test/com/cloud/vm/UserVmManagerTest.java b/server/test/com/cloud/vm/UserVmManagerTest.java index 927d5e33b5f..b07f7257591 100755 --- a/server/test/com/cloud/vm/UserVmManagerTest.java +++ b/server/test/com/cloud/vm/UserVmManagerTest.java @@ -35,6 +35,7 @@ import java.lang.reflect.Field; import java.util.List; import java.util.UUID; +import com.cloud.storage.Storage; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -538,7 +539,8 @@ public class UserVmManagerTest { boolean useLocalStorage = false; ServiceOfferingVO serviceOffering = - new ServiceOfferingVO(name, cpu, ramSize, speed, null, null, ha, displayText, useLocalStorage, false, null, false, null, false); + new ServiceOfferingVO(name, cpu, ramSize, speed, null, null, ha, displayText, Storage.ProvisioningType.THIN, + useLocalStorage, false, null, false, null, false); return serviceOffering; } diff --git a/server/test/org/apache/cloudstack/service/ServiceOfferingVOTest.java b/server/test/org/apache/cloudstack/service/ServiceOfferingVOTest.java index a3f8afbd4d6..0d4630a38d3 100644 --- a/server/test/org/apache/cloudstack/service/ServiceOfferingVOTest.java +++ b/server/test/org/apache/cloudstack/service/ServiceOfferingVOTest.java @@ -21,6 +21,7 @@ import org.junit.Before; import org.junit.Test; import org.mockito.MockitoAnnotations; +import com.cloud.storage.Storage; import com.cloud.service.ServiceOfferingVO; import com.cloud.vm.VirtualMachine; @@ -31,8 +32,8 @@ public class ServiceOfferingVOTest { @Before public void setup() { MockitoAnnotations.initMocks(this); - offeringCustom = new ServiceOfferingVO("custom", null, null, 500, 10, 10, false, "custom", false, false, "", false, VirtualMachine.Type.User, false); - offering = new ServiceOfferingVO("normal", 1, 1000, 500, 10, 10, false, "normal", false, false, "", false, VirtualMachine.Type.User, false); + offeringCustom = new ServiceOfferingVO("custom", null, null, 500, 10, 10, false, "custom", Storage.ProvisioningType.THIN, false, false, "", false, VirtualMachine.Type.User, false); + offering = new ServiceOfferingVO("normal", 1, 1000, 500, 10, 10, false, "normal", Storage.ProvisioningType.THIN, false, false, "", false, VirtualMachine.Type.User, false); } // Test restoreVm when VM state not in running/stopped case diff --git a/services/secondary-storage/controller/src/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java b/services/secondary-storage/controller/src/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java index f8edefa39a1..1ddc8f5e950 100755 --- a/services/secondary-storage/controller/src/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java +++ b/services/secondary-storage/controller/src/org/apache/cloudstack/secondarystorage/SecondaryStorageManagerImpl.java @@ -32,6 +32,7 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import org.apache.cloudstack.config.ApiServiceConfiguration; + import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; @@ -109,6 +110,7 @@ import com.cloud.storage.secondary.SecStorageVmAlertEventArgs; import com.cloud.storage.secondary.SecondaryStorageListener; import com.cloud.storage.secondary.SecondaryStorageVmAllocator; import com.cloud.storage.secondary.SecondaryStorageVmManager; +import com.cloud.storage.Storage; import com.cloud.storage.template.TemplateConstants; import com.cloud.template.TemplateManager; import com.cloud.user.Account; @@ -849,8 +851,8 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar int cpuFreq = NumbersUtil.parseInt(_configDao.getValue("ssvm.cpu.mhz"), DEFAULT_SS_VM_CPUMHZ); _useLocalStorage = Boolean.parseBoolean(configs.get(Config.SystemVMUseLocalStorage.key())); _serviceOffering = - new ServiceOfferingVO("System Offering For Secondary Storage VM", 1, ramSize, cpuFreq, null, null, false, null, _useLocalStorage, true, null, true, - VirtualMachine.Type.SecondaryStorageVm, true); + new ServiceOfferingVO("System Offering For Secondary Storage VM", 1, ramSize, cpuFreq, null, null, false, null, + Storage.ProvisioningType.THIN, _useLocalStorage, true, null, true, VirtualMachine.Type.SecondaryStorageVm, true); _serviceOffering.setUniqueName(ServiceOffering.ssvmDefaultOffUniqueName); _serviceOffering = _offeringDao.persistSystemServiceOffering(_serviceOffering); diff --git a/setup/db/db/schema-440to450.sql b/setup/db/db/schema-440to450.sql index 2bd5386a3e6..4cc4879540f 100644 --- a/setup/db/db/schema-440to450.sql +++ b/setup/db/db/schema-440to450.sql @@ -22,3 +22,204 @@ -- Disable foreign key checking -- SET foreign_key_checks = 0; +ALTER TABLE `cloud`.`volumes` ADD COLUMN `provisioning_type` VARCHAR(32) NOT NULL DEFAULT 'Off' COMMENT 'pre allocation setting of the volume'; +ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `provisioning_type` VARCHAR(32) NOT NULL DEFAULT 'Off' COMMENT 'pre allocation setting of the volume'; + +DROP VIEW IF EXISTS `cloud`.`disk_offering_view`; +CREATE VIEW `cloud`.`disk_offering_view` AS + select + disk_offering.id, + disk_offering.uuid, + disk_offering.name, + disk_offering.display_text, + disk_offering.provisioning_type, + disk_offering.disk_size, + disk_offering.min_iops, + disk_offering.max_iops, + disk_offering.created, + disk_offering.tags, + disk_offering.customized, + disk_offering.customized_iops, + disk_offering.removed, + disk_offering.use_local_storage, + disk_offering.system_use, + disk_offering.hv_ss_reserve, + disk_offering.bytes_read_rate, + disk_offering.bytes_write_rate, + disk_offering.iops_read_rate, + disk_offering.iops_write_rate, + disk_offering.cache_mode, + disk_offering.sort_key, + disk_offering.type, + disk_offering.display_offering, + domain.id domain_id, + domain.uuid domain_uuid, + domain.name domain_name, + domain.path domain_path + from + `cloud`.`disk_offering` + left join + `cloud`.`domain` ON disk_offering.domain_id = domain.id + where + disk_offering.state='ACTIVE'; + + +DROP VIEW IF EXISTS `cloud`.`service_offering_view`; +CREATE VIEW `cloud`.`service_offering_view` AS + select + service_offering.id, + disk_offering.uuid, + disk_offering.name, + disk_offering.display_text, + disk_offering.provisioning_type, + disk_offering.created, + disk_offering.tags, + disk_offering.removed, + disk_offering.use_local_storage, + disk_offering.system_use, + disk_offering.customized_iops, + disk_offering.min_iops, + disk_offering.max_iops, + disk_offering.hv_ss_reserve, + disk_offering.bytes_read_rate, + disk_offering.bytes_write_rate, + disk_offering.iops_read_rate, + disk_offering.iops_write_rate, + disk_offering.cache_mode, + service_offering.cpu, + service_offering.speed, + service_offering.ram_size, + service_offering.nw_rate, + service_offering.mc_rate, + service_offering.ha_enabled, + service_offering.limit_cpu_use, + service_offering.host_tag, + service_offering.default_use, + service_offering.vm_type, + service_offering.sort_key, + service_offering.is_volatile, + service_offering.deployment_planner, + domain.id domain_id, + domain.uuid domain_uuid, + domain.name domain_name, + domain.path domain_path + from + `cloud`.`service_offering` + inner join + `cloud`.`disk_offering` ON service_offering.id = disk_offering.id + left join + `cloud`.`domain` ON disk_offering.domain_id = domain.id + where + disk_offering.state='Active'; + +DROP VIEW IF EXISTS `cloud`.`volume_view`; +CREATE VIEW `cloud`.`volume_view` AS + select + volumes.id, + volumes.uuid, + volumes.name, + volumes.device_id, + volumes.volume_type, + volumes.provisioning_type, + volumes.size, + volumes.min_iops, + volumes.max_iops, + volumes.created, + volumes.state, + volumes.attached, + volumes.removed, + volumes.pod_id, + volumes.display_volume, + volumes.format, + volumes.path, + volumes.chain_info, + account.id account_id, + account.uuid account_uuid, + account.account_name account_name, + account.type account_type, + domain.id domain_id, + domain.uuid domain_uuid, + domain.name domain_name, + domain.path domain_path, + projects.id project_id, + projects.uuid project_uuid, + projects.name project_name, + data_center.id data_center_id, + data_center.uuid data_center_uuid, + data_center.name data_center_name, + data_center.networktype data_center_type, + vm_instance.id vm_id, + vm_instance.uuid vm_uuid, + vm_instance.name vm_name, + vm_instance.state vm_state, + vm_instance.vm_type, + user_vm.display_name vm_display_name, + volume_store_ref.size volume_store_size, + volume_store_ref.download_pct, + volume_store_ref.download_state, + volume_store_ref.error_str, + volume_store_ref.created created_on_store, + disk_offering.id disk_offering_id, + disk_offering.uuid disk_offering_uuid, + disk_offering.name disk_offering_name, + disk_offering.display_text disk_offering_display_text, + disk_offering.use_local_storage, + disk_offering.system_use, + disk_offering.bytes_read_rate, + disk_offering.bytes_write_rate, + disk_offering.iops_read_rate, + disk_offering.iops_write_rate, + disk_offering.cache_mode, + storage_pool.id pool_id, + storage_pool.uuid pool_uuid, + storage_pool.name pool_name, + cluster.hypervisor_type, + vm_template.id template_id, + vm_template.uuid template_uuid, + vm_template.extractable, + vm_template.type template_type, + resource_tags.id tag_id, + resource_tags.uuid tag_uuid, + resource_tags.key tag_key, + resource_tags.value tag_value, + resource_tags.domain_id tag_domain_id, + resource_tags.account_id tag_account_id, + resource_tags.resource_id tag_resource_id, + resource_tags.resource_uuid tag_resource_uuid, + resource_tags.resource_type tag_resource_type, + resource_tags.customer tag_customer, + async_job.id job_id, + async_job.uuid job_uuid, + async_job.job_status job_status, + async_job.account_id job_account_id + from + `cloud`.`volumes` + inner join + `cloud`.`account` ON volumes.account_id = account.id + inner join + `cloud`.`domain` ON volumes.domain_id = domain.id + left join + `cloud`.`projects` ON projects.project_account_id = account.id + left join + `cloud`.`data_center` ON volumes.data_center_id = data_center.id + left join + `cloud`.`vm_instance` ON volumes.instance_id = vm_instance.id + left join + `cloud`.`user_vm` ON user_vm.id = vm_instance.id + left join + `cloud`.`volume_store_ref` ON volumes.id = volume_store_ref.volume_id + left join + `cloud`.`disk_offering` ON volumes.disk_offering_id = disk_offering.id + left join + `cloud`.`storage_pool` ON volumes.pool_id = storage_pool.id + left join + `cloud`.`cluster` ON storage_pool.cluster_id = cluster.id + left join + `cloud`.`vm_template` ON volumes.template_id = vm_template.id OR volumes.iso_id = vm_template.id + left join + `cloud`.`resource_tags` ON resource_tags.resource_id = volumes.id + and resource_tags.resource_type = 'Volume' + left join + `cloud`.`async_job` ON async_job.instance_id = volumes.id + and async_job.instance_type = 'Volume' + and async_job.job_status = 0; diff --git a/test/integration/smoke/test_disk_offerings.py b/test/integration/smoke/test_disk_offerings.py index 4c8a34c953e..8c52d887e96 100644 --- a/test/integration/smoke/test_disk_offerings.py +++ b/test/integration/smoke/test_disk_offerings.py @@ -38,6 +38,18 @@ class Services: "displaytext": "Disk offering", "disksize": 1 # in GB }, + "sparse": { + "name": "Sparse Type Disk offering", + "displaytext": "Sparse Type Disk offering", + "disksize": 1, # in GB + "provisioningtype" : "sparse" + }, + "fat": { + "name": "Fat Type Disk offering", + "displaytext": "Fat Type Disk offering", + "disksize": 1, # in GB + "provisioningtype" : "fat" + } } class TestCreateDiskOffering(cloudstackTestCase): @@ -102,6 +114,86 @@ class TestCreateDiskOffering(cloudstackTestCase): ) return + @attr(hypervisor="kvm") + @attr(tags = ["advanced", "basic", "eip", "sg", "advancedns", "simulator", "smoke"]) + def test_02_create_sparse_type_disk_offering(self): + """Test to create a sparse type disk offering""" + + # Validate the following: + # 1. createDiskOfferings should return valid info for new offering + # 2. The Cloud Database contains the valid information + + disk_offering = DiskOffering.create( + self.apiclient, + self.services["sparse"] + ) + self.cleanup.append(disk_offering) + + self.debug("Created Disk offering with ID: %s" % disk_offering.id) + + list_disk_response = list_disk_offering( + self.apiclient, + id=disk_offering.id + ) + self.assertEqual( + isinstance(list_disk_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(list_disk_response), + 0, + "Check Disk offering is created" + ) + disk_response = list_disk_response[0] + + self.assertEqual( + disk_response.provisioningtype, + self.services["sparse"]["provisioningtype"], + "Check provisionig type in createServiceOffering" + ) + return + + + @attr(hypervisor="kvm") + @attr(tags = ["advanced", "basic", "eip", "sg", "advancedns", "simulator", "smoke"]) + def test_04_create_fat_type_disk_offering(self): + """Test to create a sparse type disk offering""" + + # Validate the following: + # 1. createDiskOfferings should return valid info for new offering + # 2. The Cloud Database contains the valid information + + disk_offering = DiskOffering.create( + self.apiclient, + self.services["fat"] + ) + self.cleanup.append(disk_offering) + + self.debug("Created Disk offering with ID: %s" % disk_offering.id) + + list_disk_response = list_disk_offering( + self.apiclient, + id=disk_offering.id + ) + self.assertEqual( + isinstance(list_disk_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(list_disk_response), + 0, + "Check Disk offering is created" + ) + disk_response = list_disk_response[0] + + self.assertEqual( + disk_response.provisioningtype, + self.services["fat"]["provisioningtype"], + "Check provisionig type in createServiceOffering" + ) + return class TestDiskOfferings(cloudstackTestCase): diff --git a/test/integration/smoke/test_volumes.py b/test/integration/smoke/test_volumes.py index 411ecf11a65..aed31fb729d 100644 --- a/test/integration/smoke/test_volumes.py +++ b/test/integration/smoke/test_volumes.py @@ -62,6 +62,12 @@ class Services: "name": "Small", "disksize": 1 }, + "sparse_disk_offering": { + "displaytext": "Sparse", + "name": "Sparse", + "provisioningtype": "sparse", + "disksize": 1 + }, 'resized_disk_offering': { "displaytext": "Resized", "name": "Resized", @@ -102,6 +108,10 @@ class TestCreateVolume(cloudstackTestCase): cls.api_client, cls.services["disk_offering"] ) + cls.sparse_disk_offering = DiskOffering.create( + cls.api_client, + cls.services["sparse_disk_offering"] + ) cls.custom_disk_offering = DiskOffering.create( cls.api_client, cls.services["disk_offering"], @@ -172,6 +182,18 @@ class TestCreateVolume(cloudstackTestCase): self.debug("Created a volume with ID: %s" % volume.id) self.volumes.append(volume) + if self.virtual_machine.hypervisor == "KVM": + sparse_volume = Volume.create( + self.apiClient, + self.services, + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid, + diskofferingid=self.sparse_disk_offering.id + ) + self.debug("Created a sparse volume: %s" % sparse_volume.id) + self.volumes.append(sparse_volume) + volume = Volume.create_custom_disk( self.apiClient, self.services, diff --git a/ui/dictionary.jsp b/ui/dictionary.jsp index ebe71b6a879..c2fa55b9ad1 100644 --- a/ui/dictionary.jsp +++ b/ui/dictionary.jsp @@ -536,6 +536,7 @@ dictionary = { 'label.disk.read.bytes': '', 'label.disk.read.io': '', 'label.disk.offering': '', +'label.disk.provisioningtype': '', 'label.disk.size': '', 'label.disk.size.gb': '', 'label.disk.total': '', diff --git a/ui/scripts/configuration.js b/ui/scripts/configuration.js index ba6bdce070f..e6503d7947b 100644 --- a/ui/scripts/configuration.js +++ b/ui/scripts/configuration.js @@ -95,6 +95,28 @@ }); } }, + provisioningType: { + label: 'label.disk.provisioningtype', + docID: 'helpComputeOfferingProvisioningType', + select: function(args) { + var items = []; + items.push({ + id: 'thin', + description: 'thin' + }); + items.push({ + id: 'sparse', + description: 'sparse' + }); + items.push({ + id: 'fat', + description: 'fat' + }); + args.response.success({ + data: items + }); + } + }, isCustomized: { label: 'label.custom', isBoolean: true, @@ -456,7 +478,8 @@ name: args.data.name, displaytext: args.data.description, storageType: args.data.storageType, - customized: (args.data.isCustomized == "on") + provisioningType :args.data.provisioningType, + customized: (args.data.isCustomized == "on") }; //custom fields (begin) @@ -721,6 +744,9 @@ storagetype: { label: 'label.storage.type' }, + provisioningtype: { + label: 'label.disk.provisioningtype' + }, cpunumber: { label: 'label.num.cpu.cores' }, @@ -937,6 +963,28 @@ }); } }, + provisioningType: { + label: 'label.disk.provisioningtype', + docID: 'helpDiskOfferingProvisioningType', + select: function(args) { + var items = []; + items.push({ + id: 'thin', + description: 'thin' + }); + items.push({ + id: 'sparse', + description: 'sparse' + }); + items.push({ + id: 'fat', + description: 'fat' + }); + args.response.success({ + data: items + }); + } + }, cpuNumber: { label: 'label.num.cpu.cores', docID: 'helpSystemOfferingCPUCores', @@ -1064,6 +1112,7 @@ displaytext: args.data.description, systemvmtype: args.data.systemvmtype, storageType: args.data.storageType, + provisioningType: args.data.provisioningType, cpuNumber: args.data.cpuNumber, cpuSpeed: args.data.cpuSpeed, memory: args.data.memory @@ -1271,6 +1320,9 @@ storagetype: { label: 'label.storage.type' }, + provisioningtype: { + label: 'label.disk.provisioningtype' + }, cpunumber: { label: 'label.num.cpu.cores' }, @@ -1447,6 +1499,28 @@ }); } }, + provisioningType: { + label: 'label.disk.provisioningtype', + docID: 'helpDiskOfferingProvisioningType', + select: function(args) { + var items = []; + items.push({ + id: 'thin', + description: 'thin' + }); + items.push({ + id: 'sparse', + description: 'sparse' + }); + items.push({ + id: 'fat', + description: 'fat' + }); + args.response.success({ + data: items + }); + } + }, isCustomized: { label: 'label.custom.disk.size', docID: 'helpDiskOfferingCustomDiskSize', @@ -1671,6 +1745,7 @@ displaytext: args.data.description, storageType: args.data.storageType, cacheMode: args.data.cacheMode, + provisioningType: args.data.provisioningType, customized: (args.data.isCustomized == "on") }; @@ -1917,6 +1992,9 @@ }, storagetype: { label: 'label.storage.type' + }, + provisioningtype: { + label: 'label.disk.provisioningtype' } }], diff --git a/ui/scripts/docs.js b/ui/scripts/docs.js index df4bcf0144d..8c9f514bf5b 100755 --- a/ui/scripts/docs.js +++ b/ui/scripts/docs.js @@ -218,6 +218,10 @@ cloudStack.docs = { desc: 'Type of disk for the VM. Local storage is attached to the hypervisor host where the VM is running. Shared storage is accessible via NFS.', externalLink: '' }, + helpComputeOfferingProvisioningType: { + desc: 'Provisioning type to create a volume. Thin and sparse is lazy allocation. fat is eager allocation.', + externalLink: '' + }, helpComputeOfferingCPUCores: { desc: 'The number of cores which should be allocated to a VM with this offering', externalLink: '' @@ -282,6 +286,10 @@ cloudStack.docs = { desc: 'A short description of the offering that can be displayed to users', externalLink: '' }, + helpDiskOfferingProvisioningType: { + desc: 'Provisioning type to create a volume. Thin and sparse is lazy allocation. fat is eager allocation.', + externalLink: '' + }, helpDiskOfferingStorageType: { 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: '' diff --git a/ui/scripts/storage.js b/ui/scripts/storage.js index e69a07e115c..528a3c5dbad 100644 --- a/ui/scripts/storage.js +++ b/ui/scripts/storage.js @@ -1402,6 +1402,9 @@ storagetype: { label: 'label.storage.type' }, + provisioningtype: { + label: 'label.disk.provisioningtype' + }, hypervisor: { label: 'label.hypervisor' },