diff --git a/api/src/main/java/com/cloud/event/EventTypes.java b/api/src/main/java/com/cloud/event/EventTypes.java index 7533e58d4f34..84514493e941 100644 --- a/api/src/main/java/com/cloud/event/EventTypes.java +++ b/api/src/main/java/com/cloud/event/EventTypes.java @@ -258,6 +258,7 @@ public class EventTypes { public static final String EVENT_VOLUME_UPDATE = "VOLUME.UPDATE"; public static final String EVENT_VOLUME_DESTROY = "VOLUME.DESTROY"; public static final String EVENT_VOLUME_RECOVER = "VOLUME.RECOVER"; + public static final String EVENT_VOLUME_CHANGE_DISK_OFFERING = "VOLUME.CHANGE.DISK.OFFERING"; // Domains public static final String EVENT_DOMAIN_CREATE = "DOMAIN.CREATE"; @@ -768,6 +769,7 @@ public class EventTypes { entityEventDetails.put(EVENT_VOLUME_RESIZE, Volume.class); entityEventDetails.put(EVENT_VOLUME_DESTROY, Volume.class); entityEventDetails.put(EVENT_VOLUME_RECOVER, Volume.class); + entityEventDetails.put(EVENT_VOLUME_CHANGE_DISK_OFFERING, Volume.class); // Domains entityEventDetails.put(EVENT_DOMAIN_CREATE, Domain.class); diff --git a/api/src/main/java/com/cloud/offering/DiskOffering.java b/api/src/main/java/com/cloud/offering/DiskOffering.java index fd21118e4a20..8f2a0c9f761c 100644 --- a/api/src/main/java/com/cloud/offering/DiskOffering.java +++ b/api/src/main/java/com/cloud/offering/DiskOffering.java @@ -34,10 +34,6 @@ enum State { Inactive, Active, } - enum Type { - Disk, Service - }; - State getState(); enum DiskCacheMode { @@ -61,8 +57,6 @@ public String toString() { String getName(); - boolean isSystemUse(); - String getDisplayText(); ProvisioningType getProvisioningType(); @@ -152,5 +146,7 @@ public String toString() { void setCacheMode(DiskCacheMode cacheMode); - Type getType(); + boolean isComputeOnly(); + + boolean getDiskSizeStrictness(); } diff --git a/api/src/main/java/com/cloud/offering/ServiceOffering.java b/api/src/main/java/com/cloud/offering/ServiceOffering.java index 7bad77fef30e..278acbe231ef 100644 --- a/api/src/main/java/com/cloud/offering/ServiceOffering.java +++ b/api/src/main/java/com/cloud/offering/ServiceOffering.java @@ -25,37 +25,34 @@ /** * offered. */ -public interface ServiceOffering extends DiskOffering, InfrastructureEntity, InternalIdentity, Identity { - public static final String consoleProxyDefaultOffUniqueName = "Cloud.com-ConsoleProxy"; - public static final String ssvmDefaultOffUniqueName = "Cloud.com-SecondaryStorage"; - public static final String routerDefaultOffUniqueName = "Cloud.Com-SoftwareRouter"; - public static final String elbVmDefaultOffUniqueName = "Cloud.Com-ElasticLBVm"; - public static final String internalLbVmDefaultOffUniqueName = "Cloud.Com-InternalLBVm"; +public interface ServiceOffering extends InfrastructureEntity, InternalIdentity, Identity { + static final String consoleProxyDefaultOffUniqueName = "Cloud.com-ConsoleProxy"; + static final String ssvmDefaultOffUniqueName = "Cloud.com-SecondaryStorage"; + static final String routerDefaultOffUniqueName = "Cloud.Com-SoftwareRouter"; + static final String elbVmDefaultOffUniqueName = "Cloud.Com-ElasticLBVm"; + static final String internalLbVmDefaultOffUniqueName = "Cloud.Com-InternalLBVm"; // leaving cloud.com references as these are identifyers and no real world addresses (check against DB) - public enum StorageType { + enum State { + Inactive, Active, + } + + enum StorageType { local, shared } - @Override String getDisplayText(); - @Override Date getCreated(); - @Override - String getTags(); - /** * @return user readable description */ - @Override String getName(); /** * @return is this a system service offering */ - @Override boolean isSystemUse(); /** @@ -98,12 +95,6 @@ public enum StorageType { */ Integer getMulticastRateMbps(); - /** - * @return whether or not the service offering requires local storage - */ - @Override - boolean isUseLocalStorage(); - /** * @return tag that should be present on the host needed, optional parameter */ @@ -117,5 +108,35 @@ public enum StorageType { boolean isDynamic(); + void setState(ServiceOffering.State state); + + ServiceOffering.State getState(); + + void setName(String name); + + String getUniqueName(); + + void setUniqueName(String uniqueName); + + void setDisplayText(String displayText); + + boolean isCustomized(); + + void setCustomized(boolean customized); + + Date getRemoved(); + + void setRemoved(Date removed); + + void setSortKey(int key); + + int getSortKey(); + + Long getDiskOfferingId(); + boolean isDynamicScalingEnabled(); + + Boolean getDiskOfferingStrictness(); + + void setDiskOfferingStrictness(boolean diskOfferingStrictness); } diff --git a/api/src/main/java/com/cloud/server/ManagementService.java b/api/src/main/java/com/cloud/server/ManagementService.java index 56f36a8d90ca..a11123139619 100644 --- a/api/src/main/java/com/cloud/server/ManagementService.java +++ b/api/src/main/java/com/cloud/server/ManagementService.java @@ -394,7 +394,8 @@ public interface ManagementService { /** * List storage pools for live migrating of a volume. The API returns list of all pools in the cluster to which the - * volume can be migrated. Current pool is not included in the list. + * volume can be migrated. Current pool is not included in the list. In case of vSphere datastore cluster storage pools, + * this method removes the child storage pools and adds the corresponding parent datastore cluster for API response listing * * @param Long volumeId * @return Pair, List> List of storage pools in cluster and list @@ -402,6 +403,8 @@ public interface ManagementService { */ Pair, List> listStoragePoolsForMigrationOfVolume(Long volumeId); + Pair, List> listStoragePoolsForMigrationOfVolumeInternal(Long volumeId, Long newDiskOfferingId, Long newSize, Long newMinIops, Long newMaxIops, boolean keepSourceStoragePool); + String[] listEventTypes(); Pair, Integer> listHypervisorCapabilities(Long id, HypervisorType hypervisorType, String keyword, Long startIndex, diff --git a/api/src/main/java/com/cloud/storage/VolumeApiService.java b/api/src/main/java/com/cloud/storage/VolumeApiService.java index 6087ece36102..516fe7722e9b 100644 --- a/api/src/main/java/com/cloud/storage/VolumeApiService.java +++ b/api/src/main/java/com/cloud/storage/VolumeApiService.java @@ -22,6 +22,7 @@ import java.util.Map; import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd; +import org.apache.cloudstack.api.command.user.volume.ChangeOfferingForVolumeCmd; import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd; import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd; import org.apache.cloudstack.api.command.user.volume.ExtractVolumeCmd; @@ -152,4 +153,6 @@ Snapshot takeSnapshot(Long volumeId, Long policyId, Long snapshotId, Account acc Volume destroyVolume(long volumeId, Account caller, boolean expunge, boolean forceExpunge); Volume recoverVolume(long volumeId); + + Volume changeDiskOfferingForVolume(ChangeOfferingForVolumeCmd cmd) throws ResourceAllocationException; } diff --git a/api/src/main/java/com/cloud/vm/DiskProfile.java b/api/src/main/java/com/cloud/vm/DiskProfile.java index 175a92afaf9d..9de5ce6fefd0 100644 --- a/api/src/main/java/com/cloud/vm/DiskProfile.java +++ b/api/src/main/java/com/cloud/vm/DiskProfile.java @@ -42,6 +42,8 @@ public class DiskProfile { private Long iopsReadRate; private Long iopsWriteRate; private String cacheMode; + private Long minIops; + private Long maxIops; private HypervisorType hyperType; @@ -227,4 +229,22 @@ public void setCacheMode(String cacheMode) { public String getCacheMode() { return cacheMode; } + + + public Long getMinIops() { + return minIops; + } + + public void setMinIops(Long minIops) { + this.minIops = minIops; + } + + public Long getMaxIops() { + return maxIops; + } + + public void setMaxIops(Long maxIops) { + this.maxIops = maxIops; + } + } diff --git a/api/src/main/java/com/cloud/vm/UserVmService.java b/api/src/main/java/com/cloud/vm/UserVmService.java index d6252c084751..8a1d6155e982 100644 --- a/api/src/main/java/com/cloud/vm/UserVmService.java +++ b/api/src/main/java/com/cloud/vm/UserVmService.java @@ -218,7 +218,7 @@ UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering s String userData, String sshKeyPair, Map requestedIps, IpAddresses defaultIp, Boolean displayVm, String keyboard, List affinityGroupIdList, Map customParameter, String customId, Map> dhcpOptionMap, Map dataDiskTemplateToDiskOfferingMap, - Map userVmOVFProperties, boolean dynamicScalingEnabled) throws InsufficientCapacityException, + Map userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; /** @@ -300,7 +300,7 @@ UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, ServiceOfferin HTTPMethod httpmethod, String userData, String sshKeyPair, Map requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, List affinityGroupIdList, Map customParameters, String customId, Map> dhcpOptionMap, Map dataDiskTemplateToDiskOfferingMap, - Map userVmOVFProperties, boolean dynamicScalingEnabled) throws InsufficientCapacityException, + Map userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; /** @@ -379,7 +379,7 @@ UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serviceOffe String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData, String sshKeyPair, Map requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, List affinityGroupIdList, Map customParameters, String customId, Map> dhcpOptionMap, Map dataDiskTemplateToDiskOfferingMap, - Map templateOvfPropertiesMap, boolean dynamicScalingEnabled, String type) + Map templateOvfPropertiesMap, boolean dynamicScalingEnabled, String type, Long overrideDiskOfferingId) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; diff --git a/api/src/main/java/com/cloud/vm/VirtualMachine.java b/api/src/main/java/com/cloud/vm/VirtualMachine.java index 79154cbee196..7a0715f1ec93 100644 --- a/api/src/main/java/com/cloud/vm/VirtualMachine.java +++ b/api/src/main/java/com/cloud/vm/VirtualMachine.java @@ -323,8 +323,6 @@ public boolean isUsedBySystem() { long getServiceOfferingId(); - Long getDiskOfferingId(); - Long getBackupOfferingId(); String getBackupExternalId(); diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index b9a809f99567..94e6a7deeb0f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -112,12 +112,15 @@ public class ApiConstants { public static final String DISK = "disk"; public static final String DISK_OFFERING_ID = "diskofferingid"; public static final String NEW_DISK_OFFERING_ID = "newdiskofferingid"; + public static final String OVERRIDE_DISK_OFFERING_ID = "overridediskofferingid"; public static final String DISK_KBS_READ = "diskkbsread"; public static final String DISK_KBS_WRITE = "diskkbswrite"; public static final String DISK_IO_READ = "diskioread"; public static final String DISK_IO_WRITE = "diskiowrite"; public static final String DISK_IO_PSTOTAL = "diskiopstotal"; public static final String DISK_SIZE = "disksize"; + public static final String DISK_SIZE_STRICTNESS = "disksizestrictness"; + public static final String DISK_OFFERING_STRICTNESS = "diskofferingstrictness"; public static final String DOWNLOAD_DETAILS = "downloaddetails"; public static final String UTILIZATION = "utilization"; public static final String DRIVER = "driver"; @@ -589,6 +592,7 @@ public class ApiConstants { public static final String LIVE_MIGRATE = "livemigrate"; public static final String MIGRATE_ALLOWED = "migrateallowed"; public static final String MIGRATE_TO = "migrateto"; + public static final String AUTO_MIGRATE = "automigrate"; public static final String GUID = "guid"; public static final String VSWITCH_TYPE_GUEST_TRAFFIC = "guestvswitchtype"; public static final String VSWITCH_TYPE_PUBLIC_TRAFFIC = "publicvswitchtype"; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java index e7b46be0040f..1992ed8caa1f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateDiskOfferingCmd.java @@ -159,6 +159,9 @@ public class CreateDiskOfferingCmd extends BaseCmd { @Parameter(name = ApiConstants.STORAGE_POLICY, type = CommandType.UUID, entityType = VsphereStoragePoliciesResponse.class,required = false, description = "Name of the storage policy defined at vCenter, this is applicable only for VMware", since = "4.15") private Long storagePolicy; + @Parameter(name = ApiConstants.DISK_SIZE_STRICTNESS, type = CommandType.BOOLEAN, description = "To allow or disallow the resize operation on the disks created from this disk offering, if the flag is true then resize is not allowed", since = "4.17") + private Boolean diskSizeStrictness; + @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "details to specify disk offering parameters", since = "4.16") private Map details; @@ -301,6 +304,11 @@ public Map getDetails() { public Long getStoragePolicy() { return storagePolicy; } + + public boolean getDiskSizeStrictness() { + return diskSizeStrictness != null ? diskSizeStrictness : false; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java index 5976aa813018..4eadfcdff256 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java @@ -33,6 +33,7 @@ import org.apache.cloudstack.api.response.ServiceOfferingResponse; import org.apache.cloudstack.api.response.VsphereStoragePoliciesResponse; import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.api.response.DiskOfferingResponse; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.collections.CollectionUtils; @@ -210,13 +211,13 @@ public class CreateServiceOfferingCmd extends BaseCmd { @Parameter(name = ApiConstants.MAX_MEMORY, type = CommandType.INTEGER, - description = "The maximum memroy size of the custom service offering in MB", + description = "The maximum memory size of the custom service offering in MB", since = "4.13") private Integer maxMemory; @Parameter(name = ApiConstants.MIN_MEMORY, type = CommandType.INTEGER, - description = "The minimum memroy size of the custom service offering in MB", + description = "The minimum memory size of the custom service offering in MB", since = "4.13") private Integer minMemory; @@ -227,6 +228,20 @@ public class CreateServiceOfferingCmd extends BaseCmd { description = "true if virtual machine needs to be dynamically scalable of cpu or memory") protected Boolean isDynamicScalingEnabled; + @Parameter(name = ApiConstants.DISK_OFFERING_ID, + required = false, + type = CommandType.UUID, + entityType = DiskOfferingResponse.class, + description = "the ID of the disk offering to which service offering should be mapped", + since = "4.17") + private Long diskOfferingId; + + @Parameter(name = ApiConstants.DISK_OFFERING_STRICTNESS, + type = CommandType.BOOLEAN, + description = "True/False to indicate the strictness of the disk offering association with the compute offering. When set to true, override of disk offering is not allowed when VM is deployed and change disk offering is not allowed for the ROOT disk after the VM is deployed", + since = "4.17") + private Boolean diskOfferingStrictness; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -449,6 +464,14 @@ public boolean getDynamicScalingEnabled() { return isDynamicScalingEnabled == null ? true : isDynamicScalingEnabled; } + public Long getDiskOfferingId() { + return diskOfferingId; + } + + public boolean getDiskOfferingStrictness() { + return diskOfferingStrictness == null ? false : diskOfferingStrictness; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/MigrateVolumeCmdByAdmin.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/MigrateVolumeCmdByAdmin.java index 135c8fc8bd12..9dd544cae581 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/MigrateVolumeCmdByAdmin.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/volume/MigrateVolumeCmdByAdmin.java @@ -26,4 +26,5 @@ @APICommand(name = "migrateVolume", description = "Migrate volume", responseObject = VolumeResponse.class, since = "3.0.0", responseView = ResponseView.Full, entityType = { Volume.class}, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) -public class MigrateVolumeCmdByAdmin extends MigrateVolumeCmd implements AdminCmd {} +public class MigrateVolumeCmdByAdmin extends MigrateVolumeCmd implements AdminCmd { +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListDiskOfferingsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListDiskOfferingsCmd.java index 92b8676b4693..91fa1f864dc1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListDiskOfferingsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/offering/ListDiskOfferingsCmd.java @@ -16,6 +16,8 @@ // under the License. package org.apache.cloudstack.api.command.user.offering; +import org.apache.cloudstack.api.response.StoragePoolResponse; +import org.apache.cloudstack.api.response.VolumeResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.log4j.Logger; @@ -50,6 +52,12 @@ public class ListDiskOfferingsCmd extends BaseListDomainResourcesCmd { since = "4.13") private Long zoneId; + @Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.UUID, entityType = VolumeResponse.class, description = "The ID of the volume, tags of the volume are used to filter the offerings", since = "4.17") + private Long volumeId; + + @Parameter(name = ApiConstants.STORAGE_ID, type = CommandType.UUID, entityType = StoragePoolResponse.class, description = "The ID of the storage pool, tags of the storage pool are used to filter the offerings", since = "4.17") + private Long storagePoolId; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -66,6 +74,14 @@ public Long getZoneId() { return zoneId; } + public Long getVolumeId() { + return volumeId; + } + + public Long getStoragePoolId() { + return storagePoolId; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java index dab181e7c1af..4a9862c3caa5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java @@ -239,6 +239,10 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityG description = "true if virtual machine needs to be dynamically scalable") protected Boolean dynamicScalingEnabled; + @Parameter(name = ApiConstants.OVERRIDE_DISK_OFFERING_ID, type = CommandType.UUID, since = "4.17", entityType = DiskOfferingResponse.class, description = "the ID of the disk offering for the virtual machine to be used for root volume instead of the disk offering mapped in service offering." + + "In case of virtual machine deploying from ISO, then the diskofferingid specified for root volume is ignored and uses this override disk offering id") + private Long overrideDiskOfferingId; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -637,6 +641,10 @@ public boolean isDynamicScalingEnabled() { return dynamicScalingEnabled == null ? true : dynamicScalingEnabled; } + public Long getOverrideDiskOfferingId() { + return overrideDiskOfferingId; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ScaleVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ScaleVMCmd.java index 4d05ce24979a..8243e1cda7d8 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ScaleVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/ScaleVMCmd.java @@ -70,6 +70,19 @@ public class ScaleVMCmd extends BaseAsyncCmd implements UserCmd { @Parameter(name = ApiConstants.DETAILS, type = BaseCmd.CommandType.MAP, description = "name value pairs of custom parameters for cpu,memory and cpunumber. example details[i].name=value") private Map details; + @Parameter(name = ApiConstants.MIN_IOPS, type = CommandType.LONG, required = false, description = "New minimum number of IOPS for the custom disk offering", since = "4.17") + private Long minIops; + + @Parameter(name = ApiConstants.MAX_IOPS, type = CommandType.LONG, required = false, description = "New maximum number of IOPS for the custom disk offering", since = "4.17") + private Long maxIops; + + @Parameter(name = ApiConstants.AUTO_MIGRATE, type = CommandType.BOOLEAN, required = false, description = "Flag for automatic migration of the root volume " + + "with new compute offering whenever migration is required to apply the offering", since = "4.17") + private Boolean autoMigrate; + + @Parameter(name = ApiConstants.SHRINK_OK, type = CommandType.BOOLEAN, required = false, description = "Verify OK to Shrink", since = "4.17") + private Boolean shrinkOk; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -97,9 +110,29 @@ public Map getDetails() { } } } + + if (shrinkOk != null) customparameterMap.put(ApiConstants.SHRINK_OK, String.valueOf(isShrinkOk())); + if (autoMigrate != null) customparameterMap.put(ApiConstants.AUTO_MIGRATE, String.valueOf(getAutoMigrate())); + if (getMinIops() != null) customparameterMap.put(ApiConstants.MIN_IOPS, String.valueOf(getMinIops())); + if (getMaxIops() != null) customparameterMap.put(ApiConstants.MAX_IOPS, String.valueOf(getMaxIops())); + return customparameterMap; } + public Long getMinIops() { + return minIops; + } + + public Long getMaxIops() { + return maxIops; + } + + public boolean getAutoMigrate() { + return autoMigrate == null ? true : autoMigrate; + } + + public boolean isShrinkOk() { return shrinkOk == null ? true : shrinkOk; } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpgradeVMCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpgradeVMCmd.java index 8dc8f445cd48..13185d34c507 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpgradeVMCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vm/UpgradeVMCmd.java @@ -68,6 +68,19 @@ public class UpgradeVMCmd extends BaseCmd implements UserCmd { @Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "name value pairs of custom parameters for cpu, memory and cpunumber. example details[i].name=value") private Map details; + @Parameter(name = ApiConstants.MIN_IOPS, type = CommandType.LONG, required = false, description = "New minimum number of IOPS for the custom disk offering", since = "4.17") + private Long minIops; + + @Parameter(name = ApiConstants.MAX_IOPS, type = CommandType.LONG, required = false, description = "New maximum number of IOPS for the custom disk offering", since = "4.17") + private Long maxIops; + + @Parameter(name = ApiConstants.AUTO_MIGRATE, type = CommandType.BOOLEAN, required = false, description = "Flag for automatic migration of the root volume " + + "with new compute offering whenever migration is required to apply the offering", since = "4.17") + private Boolean autoMigrate; + + @Parameter(name = ApiConstants.SHRINK_OK, type = CommandType.BOOLEAN, required = false, description = "Verify OK to Shrink", since = "4.17") + private Boolean shrinkOk; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -92,9 +105,29 @@ public Map getDetails() { } } } + + if (shrinkOk != null) customparameterMap.put(ApiConstants.SHRINK_OK, String.valueOf(isShrinkOk())); + if (autoMigrate != null) customparameterMap.put(ApiConstants.AUTO_MIGRATE, String.valueOf(getAutoMigrate())); + if (getMinIops() != null) customparameterMap.put(ApiConstants.MIN_IOPS, String.valueOf(getMinIops())); + if (getMaxIops() != null) customparameterMap.put(ApiConstants.MAX_IOPS, String.valueOf(getMaxIops())); + return customparameterMap; } + public Long getMinIops() { + return minIops; + } + + public Long getMaxIops() { + return maxIops; + } + + public boolean getAutoMigrate() { + return autoMigrate == null ? true : autoMigrate; + } + + public boolean isShrinkOk() { return shrinkOk == null ? true : shrinkOk; } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ChangeOfferingForVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ChangeOfferingForVolumeCmd.java new file mode 100644 index 000000000000..5a1d07ef2be5 --- /dev/null +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ChangeOfferingForVolumeCmd.java @@ -0,0 +1,155 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command.user.volume; + +import com.cloud.event.EventTypes; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.offering.DiskOffering; +import com.cloud.storage.Volume; +import org.apache.cloudstack.acl.RoleType; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ResponseObject; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.DiskOfferingResponse; +import org.apache.cloudstack.api.response.VolumeResponse; +import org.apache.cloudstack.context.CallContext; + +@APICommand(name = ChangeOfferingForVolumeCmd.APINAME, + description = "Change disk offering of the volume and also an option to auto migrate if required to apply the new disk offering", + responseObject = VolumeResponse.class, + requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, + authorized = { RoleType.Admin, RoleType.ResourceAdmin, RoleType.DomainAdmin, RoleType.User}, + since = "4.17") +public class ChangeOfferingForVolumeCmd extends BaseAsyncCmd implements UserCmd { + public static final String APINAME = "changeOfferingForVolume"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.ID, entityType = VolumeResponse.class, required = true, type = CommandType.UUID, description = "the ID of the volume") + private Long id; + + @Parameter(name = ApiConstants.DISK_OFFERING_ID, + entityType = DiskOfferingResponse.class, + type = CommandType.UUID, + required = true, + description = "new disk offering id") + private Long newDiskOfferingId; + + @Parameter(name = ApiConstants.SIZE, type = CommandType.LONG, required = false, description = "New volume size in GB for the custom disk offering") + private Long size; + + @Parameter(name = ApiConstants.MIN_IOPS, type = CommandType.LONG, required = false, description = "New minimum number of IOPS for the custom disk offering") + private Long minIops; + + @Parameter(name = ApiConstants.MAX_IOPS, type = CommandType.LONG, required = false, description = "New maximum number of IOPS for the custom disk offering") + private Long maxIops; + + @Parameter(name = ApiConstants.AUTO_MIGRATE, type = CommandType.BOOLEAN, required = false, description = "Flag for automatic migration of the volume " + + "with new disk offering whenever migration is required to apply the offering") + private Boolean autoMigrate; + + @Parameter(name = ApiConstants.SHRINK_OK, type = CommandType.BOOLEAN, required = false, description = "Verify OK to Shrink") + private Boolean shrinkOk; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public ChangeOfferingForVolumeCmd() {} + + public ChangeOfferingForVolumeCmd(Long volumeId, long newDiskOfferingId, Long minIops, Long maxIops, boolean autoMigrate, boolean shrinkOk) { + this.id = volumeId; + this.minIops = minIops; + this.maxIops = maxIops; + this.newDiskOfferingId = newDiskOfferingId; + this.autoMigrate = autoMigrate; + this.shrinkOk = shrinkOk; + } + + public Long getId() { + return id; + } + + public Long getNewDiskOfferingId() { + return newDiskOfferingId; + } + + public Long getSize() { + return size; + } + + public Long getMinIops() { + return minIops; + } + + public Long getMaxIops() { + return maxIops; + } + + public boolean getAutoMigrate() { + return autoMigrate == null ? true : autoMigrate; + } + + public boolean isShrinkOk() { + return shrinkOk == null ? false: shrinkOk; + } + + @Override + public String getEventType() { + return EventTypes.EVENT_VOLUME_CHANGE_DISK_OFFERING; + } + + @Override + public String getEventDescription() { + return "Changing Disk offering of Volume Id: " + this._uuidMgr.getUuid(Volume.class, getId()) + " to " + this._uuidMgr.getUuid(DiskOffering.class, getNewDiskOfferingId()); + } + + @Override + public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException { + CallContext.current().setEventDetails("Volume Id: " + getId()); + Volume result = _volumeService.changeDiskOfferingForVolume(this); + if (result != null) { + VolumeResponse response = _responseGenerator.createVolumeResponse(ResponseObject.ResponseView.Restricted, result); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to change disk offering of volume"); + } + } + + @Override + public String getCommandName() { + return APINAME.toLowerCase() + BaseAsyncCmd.RESPONSE_SUFFIX; + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccountId(); + } +} diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/MigrateVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/MigrateVolumeCmd.java index 44dd4bf8c391..d3183dbf3182 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/MigrateVolumeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/MigrateVolumeCmd.java @@ -25,6 +25,7 @@ import org.apache.cloudstack.api.ResponseObject.ResponseView; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.command.user.UserCmd; +import org.apache.cloudstack.api.response.DiskOfferingResponse; import org.apache.cloudstack.api.response.StoragePoolResponse; import org.apache.cloudstack.api.response.VolumeResponse; @@ -50,8 +51,8 @@ public class MigrateVolumeCmd extends BaseAsyncCmd implements UserCmd { @Parameter(name = ApiConstants.LIVE_MIGRATE, type = CommandType.BOOLEAN, required = false, description = "if the volume should be live migrated when it is attached to a running vm") private Boolean liveMigrate; - @Parameter(name = ApiConstants.NEW_DISK_OFFERING_ID, type = CommandType.STRING, description = "The new disk offering ID that replaces the current one used by the volume. This new disk offering is used to better reflect the new storage where the volume is going to be migrated to.") - private String newDiskOfferingUuid; + @Parameter(name = ApiConstants.NEW_DISK_OFFERING_ID, type = CommandType.UUID, entityType = DiskOfferingResponse.class, description = "The new disk offering ID that replaces the current one used by the volume. This new disk offering is used to better reflect the new storage where the volume is going to be migrated to.") + private Long newDiskOfferingId; ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -74,6 +75,14 @@ public boolean isLiveMigrate() { return (liveMigrate != null) ? liveMigrate : false; } + public MigrateVolumeCmd() { + } + public MigrateVolumeCmd(Long volumeId, Long storageId, Long newDiskOfferingId, Boolean liveMigrate) { + this.volumeId = volumeId; + this.storageId = storageId; + this.newDiskOfferingId = newDiskOfferingId; + this.liveMigrate = liveMigrate; + } ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -103,8 +112,8 @@ public String getEventDescription() { return "Attempting to migrate volume Id: " + this._uuidMgr.getUuid(Volume.class, getVolumeId()) + " to storage pool Id: " + this._uuidMgr.getUuid(StoragePool.class, getStoragePoolId()); } - public String getNewDiskOfferingUuid() { - return newDiskOfferingUuid; + public Long getNewDiskOfferingId() { + return newDiskOfferingId; } @Override diff --git a/api/src/main/java/org/apache/cloudstack/api/response/DiskOfferingResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/DiskOfferingResponse.java index 004a70272620..7c220f627c02 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/DiskOfferingResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/DiskOfferingResponse.java @@ -155,6 +155,10 @@ public class DiskOfferingResponse extends BaseResponseWithAnnotations { @Param(description = "the vsphere storage policy tagged to the disk offering in case of VMware", since = "4.15") private String vsphereStoragePolicy; + @SerializedName(ApiConstants.DISK_SIZE_STRICTNESS) + @Param(description = "To allow or disallow the resize operation on the disks created from this disk offering, if the flag is true then resize is not allowed", since = "4.17") + private Boolean diskSizeStrictness; + public Boolean getDisplayOffering() { return displayOffering; } @@ -363,4 +367,12 @@ public String getVsphereStoragePolicy() { public void setVsphereStoragePolicy(String vsphereStoragePolicy) { this.vsphereStoragePolicy = vsphereStoragePolicy; } + + public Boolean getDiskSizeStrictness() { + return diskSizeStrictness; + } + + public void setDiskSizeStrictness(Boolean diskSizeStrictness) { + this.diskSizeStrictness = diskSizeStrictness; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ServiceOfferingResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ServiceOfferingResponse.java index ea9d8eef7a05..b65911e572c2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ServiceOfferingResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ServiceOfferingResponse.java @@ -208,6 +208,24 @@ public class ServiceOfferingResponse extends BaseResponseWithAnnotations { @Param(description = "true if virtual machine needs to be dynamically scalable of cpu or memory", since = "4.16") private Boolean dynamicScalingEnabled; + @SerializedName(ApiConstants.DISK_OFFERING_STRICTNESS) + @Param(description = "True/False to indicate the strictness of the disk offering association with the compute offering. " + + "When set to true, override of disk offering is not allowed when VM is deployed and " + + "change disk offering is not allowed for the ROOT disk after the VM is deployed", since = "4.17") + private Boolean diskOfferingStrictness; + + @SerializedName(ApiConstants.DISK_OFFERING_ID) + @Param(description = "the ID of the disk offering to which service offering is linked", since = "4.17") + private String diskOfferingId; + + @SerializedName("diskofferingname") + @Param(description = "name of the disk offering", since = "4.17") + private String diskOfferingName; + + @SerializedName("diskofferingdisplaytext") + @Param(description = "the display text of the disk offering", since = "4.17") + private String diskOfferingDisplayText; + public ServiceOfferingResponse() { } @@ -486,4 +504,36 @@ public Boolean getDynamicScalingEnabled() { public void setDynamicScalingEnabled(Boolean dynamicScalingEnabled) { this.dynamicScalingEnabled = dynamicScalingEnabled; } + + public Boolean getDiskOfferingStrictness() { + return diskOfferingStrictness; + } + + public void setDiskOfferingStrictness(Boolean diskOfferingStrictness) { + this.diskOfferingStrictness = diskOfferingStrictness; + } + + public void setDiskOfferingId(String diskOfferingId) { + this.diskOfferingId = diskOfferingId; + } + + public void setDiskOfferingName(String diskOfferingName) { + this.diskOfferingName = diskOfferingName; + } + + public void setDiskOfferingDisplayText(String diskOfferingDisplayText) { + this.diskOfferingDisplayText = diskOfferingDisplayText; + } + + public String getDiskOfferingId() { + return diskOfferingId; + } + + public String getDiskOfferingName() { + return diskOfferingName; + } + + public String getDiskOfferingDisplayText() { + return diskOfferingDisplayText; + } } diff --git a/core/src/test/java/org/apache/cloudstack/api/agent/test/CheckNetworkAnswerTest.java b/core/src/test/java/org/apache/cloudstack/api/agent/test/CheckNetworkAnswerTest.java index 8b2550151f38..f554cb5b58ab 100644 --- a/core/src/test/java/org/apache/cloudstack/api/agent/test/CheckNetworkAnswerTest.java +++ b/core/src/test/java/org/apache/cloudstack/api/agent/test/CheckNetworkAnswerTest.java @@ -209,7 +209,7 @@ public Hypervisor.HypervisorType getHypervisor() { Long newSize = 4194304L; Long currentSize = 1048576L; - ResizeVolumeCommand rv = new ResizeVolumeCommand("dummydiskpath", new StorageFilerTO(dummypool), currentSize, newSize, false, "vmName"); + ResizeVolumeCommand rv = new ResizeVolumeCommand("dummydiskpath", new StorageFilerTO(dummypool), currentSize, newSize, false, "vmName", null); @Test public void testExecuteInSequence() { diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java index c6b96bca3e66..733302f75e68 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java @@ -137,6 +137,8 @@ List allocateTemplatedVolumes(Type type, String name, DiskOffering StoragePool findStoragePool(DiskProfile dskCh, DataCenter dc, Pod pod, Long clusterId, Long hostId, VirtualMachine vm, Set avoid); + List findStoragePoolsForVolumeWithNewDiskOffering(DiskProfile dskCh, DataCenter dc, Pod pod, Long clusterId, Long hostId, VirtualMachine vm, Set avoid); + void updateVolumeDiskChain(long volumeId, String path, String chainInfo, String updatedDataStoreUUID); /** diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/service/api/OrchestrationService.java b/engine/api/src/main/java/org/apache/cloudstack/engine/service/api/OrchestrationService.java index 82c3dd14cf3c..3ffa496b5445 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/service/api/OrchestrationService.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/service/api/OrchestrationService.java @@ -67,14 +67,15 @@ VirtualMachineEntity createVirtualMachine(@QueryParam("id") String id, @QueryPar @QueryParam("compute-tags") List computeTags, @QueryParam("root-disk-tags") List rootDiskTags, @QueryParam("network-nic-map") Map> networkNicMap, @QueryParam("deploymentplan") DeploymentPlan plan, @QueryParam("root-disk-size") Long rootDiskSize, @QueryParam("extra-dhcp-option-map") Map> extraDhcpOptionMap, - @QueryParam("datadisktemplate-diskoffering-map") Map datadiskTemplateToDiskOfferingMap) throws InsufficientCapacityException; + @QueryParam("datadisktemplate-diskoffering-map") Map datadiskTemplateToDiskOfferingMap, @QueryParam("disk-offering-id") Long diskOfferingId, @QueryParam("root-disk-offering-id") Long rootDiskOfferingId) throws InsufficientCapacityException; @POST VirtualMachineEntity createVirtualMachineFromScratch(@QueryParam("id") String id, @QueryParam("owner") String owner, @QueryParam("iso-id") String isoId, @QueryParam("host-name") String hostName, @QueryParam("display-name") String displayName, @QueryParam("hypervisor") String hypervisor, @QueryParam("os") String os, @QueryParam("cpu") int cpu, @QueryParam("speed") int speed, @QueryParam("ram") long memory, @QueryParam("disk-size") Long diskSize, @QueryParam("compute-tags") List computeTags, @QueryParam("root-disk-tags") List rootDiskTags, - @QueryParam("network-nic-map") Map> networkNicMap, @QueryParam("deploymentplan") DeploymentPlan plan, @QueryParam("extra-dhcp-option-map") Map> extraDhcpOptionMap) throws InsufficientCapacityException; + @QueryParam("network-nic-map") Map> networkNicMap, @QueryParam("deploymentplan") DeploymentPlan plan, + @QueryParam("extra-dhcp-option-map") Map> extraDhcpOptionMap, @QueryParam("disk-offering-id") Long diskOfferingId) throws InsufficientCapacityException; @POST NetworkEntity createNetwork(String id, String name, String domainName, String cidr, String gateway); diff --git a/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java b/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java index a62876aeff0a..23abb67dfa00 100644 --- a/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java +++ b/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java @@ -228,9 +228,9 @@ public interface StorageManager extends StorageService { HypervisorType getHypervisorTypeFromFormat(ImageFormat format); - boolean storagePoolHasEnoughIops(List volume, StoragePool pool); + boolean storagePoolHasEnoughIops(List> volumeDiskProfilePairs, StoragePool pool); - boolean storagePoolHasEnoughSpace(List volume, StoragePool pool); + boolean storagePoolHasEnoughSpace(List> volumeDiskProfilePairs, StoragePool pool); /** * This comment is relevant to managed storage only. @@ -254,13 +254,13 @@ public interface StorageManager extends StorageService { * * Cloning volumes on the back-end instead of copying down a new template for each new volume helps to alleviate load on the hypervisors. */ - boolean storagePoolHasEnoughSpace(List volume, StoragePool pool, Long clusterId); + boolean storagePoolHasEnoughSpace(List> volume, StoragePool pool, Long clusterId); boolean storagePoolHasEnoughSpaceForResize(StoragePool pool, long currentSize, long newSize); boolean storagePoolCompatibleWithVolumePool(StoragePool pool, Volume volume); - boolean isStoragePoolCompliantWithStoragePolicy(List volumes, StoragePool pool) throws StorageUnavailableException; + boolean isStoragePoolCompliantWithStoragePolicy(List> volumes, StoragePool pool) throws StorageUnavailableException; boolean registerHostListener(String providerUuid, HypervisorHostListener listener); diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java index aeb4a2e2ba5d..2cbcf6fb9d1f 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -523,7 +523,8 @@ public void doInTransactionWithoutResult(final TransactionStatus status) throws @Override public void allocate(final String vmInstanceName, final VirtualMachineTemplate template, final ServiceOffering serviceOffering, final LinkedHashMap> networks, final DeploymentPlan plan, final HypervisorType hyperType) throws InsufficientCapacityException { - allocate(vmInstanceName, template, serviceOffering, new DiskOfferingInfo(serviceOffering), new ArrayList<>(), networks, plan, hyperType, null, null); + DiskOffering diskOffering = _diskOfferingDao.findById(serviceOffering.getId()); + allocate(vmInstanceName, template, serviceOffering, new DiskOfferingInfo(diskOffering), new ArrayList<>(), networks, plan, hyperType, null, null); } private VirtualMachineGuru getVmGuru(final VirtualMachine vm) { @@ -3738,8 +3739,10 @@ public void checkIfCanUpgrade(final VirtualMachine vmInstance, final ServiceOffe } final ServiceOfferingVO currentServiceOffering = _offeringDao.findByIdIncludingRemoved(vmInstance.getId(), vmInstance.getServiceOfferingId()); + final DiskOfferingVO currentDiskOffering = _diskOfferingDao.findByIdIncludingRemoved(currentServiceOffering.getDiskOfferingId()); + final DiskOfferingVO newDiskOffering = _diskOfferingDao.findById(newServiceOffering.getDiskOfferingId()); - checkIfNewOfferingStorageScopeMatchesStoragePool(vmInstance, newServiceOffering); + checkIfNewOfferingStorageScopeMatchesStoragePool(vmInstance, newDiskOffering); if (currentServiceOffering.isSystemUse() != newServiceOffering.isSystemUse()) { throw new InvalidParameterValueException("isSystem property is different for current service offering and new service offering"); @@ -3750,8 +3753,8 @@ public void checkIfCanUpgrade(final VirtualMachine vmInstance, final ServiceOffe newServiceOffering.getCpu() + " cpu(s) at " + newServiceOffering.getSpeed() + " Mhz, and " + newServiceOffering.getRamSize() + " MB of memory"); } - final List currentTags = StringUtils.csvTagsToList(currentServiceOffering.getTags()); - final List newTags = StringUtils.csvTagsToList(newServiceOffering.getTags()); + final List currentTags = StringUtils.csvTagsToList(currentDiskOffering.getTags()); + final List newTags = StringUtils.csvTagsToList(newDiskOffering.getTags()); if (!newTags.containsAll(currentTags)) { throw new InvalidParameterValueException("Unable to upgrade virtual machine; the current service offering " + " should have tags as subset of " + "the new service offering tags. Current service offering tags: " + currentTags + "; " + "new service " + "offering tags: " + newTags); @@ -3761,16 +3764,16 @@ public void checkIfCanUpgrade(final VirtualMachine vmInstance, final ServiceOffe /** * Throws an InvalidParameterValueException in case the new service offerings does not match the storage scope (e.g. local or shared). */ - protected void checkIfNewOfferingStorageScopeMatchesStoragePool(VirtualMachine vmInstance, ServiceOffering newServiceOffering) { + protected void checkIfNewOfferingStorageScopeMatchesStoragePool(VirtualMachine vmInstance, DiskOffering newDiskOffering) { boolean isRootVolumeOnLocalStorage = isRootVolumeOnLocalStorage(vmInstance.getId()); - if (newServiceOffering.isUseLocalStorage() && !isRootVolumeOnLocalStorage) { + if (newDiskOffering.isUseLocalStorage() && !isRootVolumeOnLocalStorage) { String message = String .format("Unable to upgrade virtual machine %s, target offering use local storage but the storage pool where " + "the volume is allocated is a shared storage.", vmInstance.toString()); throw new InvalidParameterValueException(message); } - if (!newServiceOffering.isUseLocalStorage() && isRootVolumeOnLocalStorage) { + if (!newDiskOffering.isUseLocalStorage() && isRootVolumeOnLocalStorage) { String message = String.format("Unable to upgrade virtual machine %s, target offering use shared storage but the storage pool where " + "the volume is allocated is a local storage.", vmInstance.toString()); throw new InvalidParameterValueException(message); diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java index c6f8e418642c..d639b4513e4a 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/CloudOrchestrator.java @@ -156,7 +156,7 @@ public void destroyVolume(String volumeEntity) { @Override public VirtualMachineEntity createVirtualMachine(String id, String owner, String templateId, String hostName, String displayName, String hypervisor, int cpu, int speed, long memory, Long diskSize, List computeTags, List rootDiskTags, Map> networkNicMap, DeploymentPlan plan, - Long rootDiskSize, Map> extraDhcpOptionMap, Map dataDiskTemplateToDiskOfferingMap) throws InsufficientCapacityException { + Long rootDiskSize, Map> extraDhcpOptionMap, Map dataDiskTemplateToDiskOfferingMap, Long dataDiskOfferingId, Long rootDiskOfferingId) throws InsufficientCapacityException { // VirtualMachineEntityImpl vmEntity = new VirtualMachineEntityImpl(id, owner, hostName, displayName, cpu, speed, memory, computeTags, rootDiskTags, networks, // vmEntityManager); @@ -185,10 +185,14 @@ public VirtualMachineEntity createVirtualMachine(String id, String owner, String ServiceOfferingVO computeOffering = _serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); - rootDiskOfferingInfo.setDiskOffering(computeOffering); + DiskOfferingVO rootDiskOffering = _diskOfferingDao.findById(rootDiskOfferingId); + if (rootDiskOffering == null) { + throw new InvalidParameterValueException("Unable to find root disk offering " + rootDiskOfferingId); + } + rootDiskOfferingInfo.setDiskOffering(rootDiskOffering); rootDiskOfferingInfo.setSize(rootDiskSize); - if (computeOffering.isCustomizedIops() != null && computeOffering.isCustomizedIops()) { + if (rootDiskOffering.isCustomizedIops() != null && rootDiskOffering.isCustomizedIops()) { Map userVmDetails = _userVmDetailsDao.listDetailsKeyPairs(vm.getId()); if (userVmDetails != null) { @@ -200,48 +204,50 @@ public VirtualMachineEntity createVirtualMachine(String id, String owner, String } } - if (vm.getDiskOfferingId() != null) { - DiskOfferingVO diskOffering = _diskOfferingDao.findById(vm.getDiskOfferingId()); + if (dataDiskOfferingId != null) { + DiskOfferingVO diskOffering = _diskOfferingDao.findById(dataDiskOfferingId); if (diskOffering == null) { - throw new InvalidParameterValueException("Unable to find disk offering " + vm.getDiskOfferingId()); + throw new InvalidParameterValueException("Unable to find data disk offering " + dataDiskOfferingId); } - Long size = null; - if (diskOffering.getDiskSize() == 0) { - size = diskSize; - if (size == null) { - throw new InvalidParameterValueException("Disk offering " + diskOffering + " requires size parameter."); + if (!diskOffering.isComputeOnly()) { + Long size = null; + if (diskOffering.getDiskSize() == 0) { + size = diskSize; + if (size == null) { + throw new InvalidParameterValueException("Disk offering " + diskOffering + " requires size parameter."); + } + _volumeMgr.validateVolumeSizeRange(size * 1024 * 1024 * 1024); } - _volumeMgr.validateVolumeSizeRange(size * 1024 * 1024 * 1024); - } - DiskOfferingInfo dataDiskOfferingInfo = new DiskOfferingInfo(); + DiskOfferingInfo dataDiskOfferingInfo = new DiskOfferingInfo(); - dataDiskOfferingInfo.setDiskOffering(diskOffering); - dataDiskOfferingInfo.setSize(size); + dataDiskOfferingInfo.setDiskOffering(diskOffering); + dataDiskOfferingInfo.setSize(size); - if (diskOffering.isCustomizedIops() != null && diskOffering.isCustomizedIops()) { - Map userVmDetails = _userVmDetailsDao.listDetailsKeyPairs(vm.getId()); + if (diskOffering.isCustomizedIops() != null && diskOffering.isCustomizedIops()) { + Map userVmDetails = _userVmDetailsDao.listDetailsKeyPairs(vm.getId()); - if (userVmDetails != null) { - String minIops = userVmDetails.get("minIopsDo"); - String maxIops = userVmDetails.get("maxIopsDo"); + if (userVmDetails != null) { + String minIops = userVmDetails.get("minIopsDo"); + String maxIops = userVmDetails.get("maxIopsDo"); - dataDiskOfferingInfo.setMinIops(minIops != null && minIops.trim().length() > 0 ? Long.parseLong(minIops) : null); - dataDiskOfferingInfo.setMaxIops(maxIops != null && maxIops.trim().length() > 0 ? Long.parseLong(maxIops) : null); + dataDiskOfferingInfo.setMinIops(minIops != null && minIops.trim().length() > 0 ? Long.parseLong(minIops) : null); + dataDiskOfferingInfo.setMaxIops(maxIops != null && maxIops.trim().length() > 0 ? Long.parseLong(maxIops) : null); + } } - } - dataDiskOfferings.add(dataDiskOfferingInfo); + dataDiskOfferings.add(dataDiskOfferingInfo); + } } if (dataDiskTemplateToDiskOfferingMap != null && !dataDiskTemplateToDiskOfferingMap.isEmpty()) { for (Entry datadiskTemplateToDiskOffering : dataDiskTemplateToDiskOfferingMap.entrySet()) { - DiskOffering diskOffering = datadiskTemplateToDiskOffering.getValue(); - if (diskOffering == null) { - throw new InvalidParameterValueException("Unable to find disk offering " + vm.getDiskOfferingId()); + DiskOffering dataDiskOffering = datadiskTemplateToDiskOffering.getValue(); + if (dataDiskOffering == null) { + throw new InvalidParameterValueException("Unable to find disk offering " + dataDiskOfferingId); } - if (diskOffering.getDiskSize() == 0) { // Custom disk offering is not supported for volumes created from datadisk templates - throw new InvalidParameterValueException("Disk offering " + diskOffering + " requires size parameter."); + if (dataDiskOffering.getDiskSize() == 0) { // Custom disk offering is not supported for volumes created from datadisk templates + throw new InvalidParameterValueException("Disk offering " + dataDiskOffering + " requires size parameter."); } } } @@ -254,7 +260,8 @@ public VirtualMachineEntity createVirtualMachine(String id, String owner, String @Override public VirtualMachineEntity createVirtualMachineFromScratch(String id, String owner, String isoId, String hostName, String displayName, String hypervisor, String os, - int cpu, int speed, long memory, Long diskSize, List computeTags, List rootDiskTags, Map> networkNicMap, DeploymentPlan plan, Map> extraDhcpOptionMap) + int cpu, int speed, long memory, Long diskSize, List computeTags, List rootDiskTags, Map> networkNicMap, DeploymentPlan plan, + Map> extraDhcpOptionMap, Long diskOfferingId) throws InsufficientCapacityException { // VirtualMachineEntityImpl vmEntity = new VirtualMachineEntityImpl(id, owner, hostName, displayName, cpu, speed, memory, computeTags, rootDiskTags, networks, vmEntityManager); @@ -268,9 +275,6 @@ public VirtualMachineEntity createVirtualMachineFromScratch(String id, String ow DiskOfferingInfo rootDiskOfferingInfo = new DiskOfferingInfo(); - rootDiskOfferingInfo.setDiskOffering(computeOffering); - - Long diskOfferingId = vm.getDiskOfferingId(); if (diskOfferingId == null) { throw new InvalidParameterValueException("Installing from ISO requires a disk offering to be specified for the root disk."); } @@ -278,8 +282,10 @@ public VirtualMachineEntity createVirtualMachineFromScratch(String id, String ow if (diskOffering == null) { throw new InvalidParameterValueException("Unable to find disk offering " + diskOfferingId); } + rootDiskOfferingInfo.setDiskOffering(diskOffering); + Long size = null; - if (diskOffering.getDiskSize() == 0) { + if (!diskOffering.isComputeOnly() && diskOffering.getDiskSize() == 0) { size = diskSize; if (size == null) { throw new InvalidParameterValueException("Disk offering " + diskOffering + " requires size parameter."); diff --git a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java index 4a30ee4043bb..339153268fcd 100644 --- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java +++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java @@ -365,6 +365,36 @@ public StoragePool findStoragePool(DiskProfile dskCh, DataCenter dc, Pod pod, Lo return null; } + public List findStoragePoolsForVolumeWithNewDiskOffering(DiskProfile dskCh, DataCenter dc, Pod pod, Long clusterId, Long hostId, VirtualMachine vm, final Set avoid) { + Long podId = null; + if (pod != null) { + podId = pod.getId(); + } else if (clusterId != null) { + Cluster cluster = _entityMgr.findById(Cluster.class, clusterId); + if (cluster != null) { + podId = cluster.getPodId(); + } + } + + VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm); + List suitablePools = new ArrayList<>(); + for (StoragePoolAllocator allocator : _storagePoolAllocators) { + + ExcludeList avoidList = new ExcludeList(); + for (StoragePool pool : avoid) { + avoidList.addPool(pool.getId()); + } + DataCenterDeployment plan = new DataCenterDeployment(dc.getId(), podId, clusterId, hostId, null, null); + + final List poolList = allocator.allocateToPool(dskCh, profile, plan, avoidList, StoragePoolAllocator.RETURN_UPTO_ALL); + if (CollectionUtils.isEmpty(poolList)) { + continue; + } + suitablePools.addAll(poolList); + } + return suitablePools; + } + @Override public StoragePool findChildDataStoreInDataStoreCluster(DataCenter dc, Pod pod, Long clusterId, Long hostId, VirtualMachine vm, Long datastoreClusterId) { Long podId = null; @@ -634,7 +664,7 @@ public VolumeInfo createVolume(VolumeInfo volume, VirtualMachine vm, VirtualMach DiskProfile dskCh = null; if (volume.getVolumeType() == Type.ROOT && Storage.ImageFormat.ISO != template.getFormat()) { - dskCh = createDiskCharacteristics(volume, template, dc, offering); + dskCh = createDiskCharacteristics(volume, template, dc, diskOffering); storageMgr.setDiskProfileThrottling(dskCh, offering, diskOffering); } else { dskCh = createDiskCharacteristics(volume, template, dc, diskOffering); @@ -893,7 +923,7 @@ private DiskProfile allocateTemplatedVolume(Type type, String name, DiskOffering Long offeringId = null; - if (offering.getType() == DiskOffering.Type.Disk) { + if (!offering.isComputeOnly()) { offeringId = offering.getId(); } diff --git a/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java b/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java index e478290d486f..1655bc4634eb 100644 --- a/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java +++ b/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java @@ -95,6 +95,9 @@ public class VirtualMachineManagerImplTest { @Mock private ServiceOfferingVO serviceOfferingMock; + @Mock + private DiskOfferingVO diskOfferingMock; + private long hostMockId = 1L; @Mock private HostVO hostMock; @@ -218,14 +221,15 @@ public void testCheckIfCanUpgrade() throws Exception { when(serviceOfferingMock.getId()).thenReturn(2l); ServiceOfferingVO mockCurrentServiceOffering = mock(ServiceOfferingVO.class); + DiskOfferingVO mockCurrentDiskOffering = mock(DiskOfferingVO.class); when(serviceOfferingDaoMock.findByIdIncludingRemoved(anyLong(), anyLong())).thenReturn(mockCurrentServiceOffering); - when(mockCurrentServiceOffering.isUseLocalStorage()).thenReturn(true); - when(serviceOfferingMock.isUseLocalStorage()).thenReturn(true); + when(mockCurrentDiskOffering.isUseLocalStorage()).thenReturn(true); + when(diskOfferingMock.isUseLocalStorage()).thenReturn(true); when(mockCurrentServiceOffering.isSystemUse()).thenReturn(true); when(serviceOfferingMock.isSystemUse()).thenReturn(true); - when(mockCurrentServiceOffering.getTags()).thenReturn("x,y"); - when(serviceOfferingMock.getTags()).thenReturn("z,x,y"); + when(mockCurrentDiskOffering.getTags()).thenReturn("x,y"); + when(diskOfferingMock.getTags()).thenReturn("z,x,y"); virtualMachineManagerImpl.checkIfCanUpgrade(vmInstanceMock, serviceOfferingMock); } @@ -677,7 +681,7 @@ public void checkIfNewOfferingStorageScopeMatchesStoragePoolTestSharedLocal() { private void prepareAndRunCheckIfNewOfferingStorageScopeMatchesStoragePool(boolean isRootOnLocal, boolean isOfferingUsingLocal) { Mockito.doReturn(isRootOnLocal).when(virtualMachineManagerImpl).isRootVolumeOnLocalStorage(Mockito.anyLong()); Mockito.doReturn("vmInstanceMockedToString").when(vmInstanceMock).toString(); - Mockito.doReturn(isOfferingUsingLocal).when(serviceOfferingMock).isUseLocalStorage(); - virtualMachineManagerImpl.checkIfNewOfferingStorageScopeMatchesStoragePool(vmInstanceMock, serviceOfferingMock); + Mockito.doReturn(isOfferingUsingLocal).when(diskOfferingMock).isUseLocalStorage(); + virtualMachineManagerImpl.checkIfNewOfferingStorageScopeMatchesStoragePool(vmInstanceMock, diskOfferingMock); } } diff --git a/engine/schema/src/main/java/com/cloud/service/ServiceOfferingVO.java b/engine/schema/src/main/java/com/cloud/service/ServiceOfferingVO.java index a23623164208..6e3093769248 100644 --- a/engine/schema/src/main/java/com/cloud/service/ServiceOfferingVO.java +++ b/engine/schema/src/main/java/com/cloud/service/ServiceOfferingVO.java @@ -16,25 +16,66 @@ // under the License. package com.cloud.service; +import java.util.Date; import java.util.Map; +import java.util.UUID; import javax.persistence.Column; -import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; -import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.Table; import javax.persistence.Transient; +import javax.persistence.Id; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Temporal; +import javax.persistence.TemporalType; +import javax.persistence.Enumerated; +import javax.persistence.EnumType; import com.cloud.offering.ServiceOffering; -import com.cloud.storage.DiskOfferingVO; -import com.cloud.storage.Storage.ProvisioningType; +import com.cloud.utils.db.GenericDao; import com.cloud.vm.VirtualMachine; @Entity @Table(name = "service_offering") -@DiscriminatorValue(value = "Service") -@PrimaryKeyJoinColumn(name = "id") -public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering { +public class ServiceOfferingVO implements ServiceOffering { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "name") + private String name = null; + + @Column(name = "unique_name") + private String uniqueName; + + @Column(name = "display_text", length = 4096) + private String displayText = null; + + @Column(name = "customized") + private boolean customized; + + @Column(name = GenericDao.REMOVED_COLUMN) + @Temporal(TemporalType.TIMESTAMP) + private Date removed; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + + @Enumerated(EnumType.STRING) + @Column(name = "state") + ServiceOffering.State state = ServiceOffering.State.Active; + + @Column(name = "disk_offering_id") + private Long diskOfferingId; + + @Column(name = "disk_offering_strictness") + private boolean diskOfferingStrictness = false; @Column(name = "cpu") private Integer cpu; @@ -75,6 +116,9 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering @Column(name = "deployment_planner") private String deploymentPlanner = null; + @Column(name = "system_use") + private boolean systemUse; + @Column(name = "dynamic_scaling_enabled") private boolean dynamicScalingEnabled = true; @@ -94,8 +138,7 @@ protected ServiceOfferingVO() { } public ServiceOfferingVO(String name, Integer cpu, Integer ramSize, Integer speed, Integer rateMbps, Integer multicastRateMbps, boolean offerHA, String displayText, - 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); + boolean systemUse, VirtualMachine.Type vmType, boolean defaultUse) { this.cpu = cpu; this.ramSize = ramSize; this.speed = speed; @@ -106,12 +149,15 @@ public ServiceOfferingVO(String name, Integer cpu, Integer ramSize, Integer spee volatileVm = false; this.defaultUse = defaultUse; this.vmType = vmType == null ? null : vmType.toString().toLowerCase(); + uuid = UUID.randomUUID().toString(); + this.systemUse = systemUse; + this.name = name; + this.displayText = displayText; } public ServiceOfferingVO(String name, Integer cpu, Integer ramSize, Integer speed, Integer rateMbps, Integer multicastRateMbps, boolean offerHA, - boolean limitResourceUse, boolean volatileVm, String displayText, ProvisioningType provisioningType, boolean useLocalStorage, boolean recreatable, String tags, boolean systemUse, - VirtualMachine.Type vmType, String hostTag, String deploymentPlanner, boolean dynamicScalingEnabled) { - super(name, displayText, provisioningType, false, tags, recreatable, useLocalStorage, systemUse, true); + boolean limitResourceUse, boolean volatileVm, String displayText, boolean systemUse, + VirtualMachine.Type vmType, String hostTag, String deploymentPlanner, boolean dynamicScalingEnabled, boolean isCustomized) { this.cpu = cpu; this.ramSize = ramSize; this.speed = speed; @@ -123,23 +169,20 @@ public ServiceOfferingVO(String name, Integer cpu, Integer ramSize, Integer spee this.vmType = vmType == null ? null : vmType.toString().toLowerCase(); this.hostTag = hostTag; this.deploymentPlanner = deploymentPlanner; + uuid = UUID.randomUUID().toString(); + this.systemUse = systemUse; + this.name = name; + this.displayText = displayText; this.dynamicScalingEnabled = dynamicScalingEnabled; + this.customized = isCustomized; } public ServiceOfferingVO(ServiceOfferingVO offering) { - super(offering.getId(), - offering.getName(), - offering.getDisplayText(), - offering.getProvisioningType(), - false, - offering.getTags(), - offering.isRecreatable(), - offering.isUseLocalStorage(), - offering.isSystemUse(), - true, - offering.isCustomizedIops()== null ? false:offering.isCustomizedIops(), - offering.getMinIops(), - offering.getMaxIops()); + id = offering.getId(); + diskOfferingId = offering.getDiskOfferingId(); + name = offering.getName(); + displayText = offering.getDisplayText(); + customized = true; cpu = offering.getCpu(); ramSize = offering.getRamSize(); speed = offering.getSpeed(); @@ -150,6 +193,7 @@ public ServiceOfferingVO(ServiceOfferingVO offering) { volatileVm = offering.isVolatileVm(); hostTag = offering.getHostTag(); vmType = offering.getSystemVmType(); + systemUse = offering.isSystemUse(); dynamicScalingEnabled = offering.isDynamicScalingEnabled(); } @@ -176,17 +220,6 @@ public boolean getDefaultUse() { return defaultUse; } - @Override - @Transient - public String[] getTagsArray() { - String tags = getTags(); - if (tags == null || tags.length() == 0) { - return new String[0]; - } - - return tags.split(","); - } - @Override public Integer getCpu() { return cpu; @@ -297,6 +330,98 @@ public boolean isCustomCpuSpeedSupported() { return isCustomized() && getDetail("minCPU") != null; } + @Override + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean isSystemUse() { + return systemUse; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public String getUniqueName() { + return uniqueName; + } + + @Override + public void setUniqueName(String uniqueName) { + this.uniqueName = uniqueName; + } + + @Override + public String getDisplayText() { + return displayText; + } + + @Override + public void setDisplayText(String displayText) { + this.displayText = displayText; + } + + @Override + public boolean isCustomized() { + return customized; + } + + @Override + public void setCustomized(boolean customized) { + this.customized = customized; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } + + @Override + public Date getRemoved() { + return removed; + } + + @Override + public Date getCreated() { + return created; + } + + @Override + public ServiceOffering.State getState() { + return state; + } + + @Override + public void setState(ServiceOffering.State state) { + this.state = state; + } + + @Override + public Long getDiskOfferingId() { + return diskOfferingId; + } + + public void setDiskOfferingId(Long diskOfferingId) { + this.diskOfferingId = diskOfferingId; + } + + @Override + public String getUuid() { + return uuid; + } + @Override public String toString() { return String.format("Service offering {\"id\": %s, \"name\": \"%s\", \"uuid\": \"%s\"}", getId(), getName(), getUuid()); @@ -309,4 +434,12 @@ public boolean isDynamicScalingEnabled() { public void setDynamicScalingEnabled(boolean dynamicScalingEnabled) { this.dynamicScalingEnabled = dynamicScalingEnabled; } + + public Boolean getDiskOfferingStrictness() { + return diskOfferingStrictness; + } + + public void setDiskOfferingStrictness(boolean diskOfferingStrictness) { + this.diskOfferingStrictness = diskOfferingStrictness; + } } diff --git a/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDao.java b/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDao.java index 623179c2d8e1..e2fc5b49ae84 100644 --- a/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDao.java +++ b/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDao.java @@ -53,4 +53,6 @@ List createSystemServiceOfferings(String name, String uniqueN ServiceOfferingVO findDefaultSystemOffering(String offeringName, Boolean useLocalStorage); List listPublicByCpuAndMemory(Integer cpus, Integer memory); + + ServiceOfferingVO findServiceOfferingByComputeOnlyDiskOffering(long diskOfferingId); } diff --git a/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDaoImpl.java b/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDaoImpl.java index 14400516d542..5c8e49938295 100644 --- a/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/service/dao/ServiceOfferingDaoImpl.java @@ -24,6 +24,8 @@ import javax.inject.Inject; import javax.persistence.EntityExistsException; +import com.cloud.storage.DiskOfferingVO; +import com.cloud.storage.dao.DiskOfferingDao; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -48,17 +50,20 @@ public class ServiceOfferingDaoImpl extends GenericDaoBase UniqueNameSearch; protected final SearchBuilder ServiceOfferingsByKeywordSearch; protected final SearchBuilder PublicCpuRamSearch; + protected final SearchBuilder SearchComputeOfferingByComputeOnlyDiskOffering; public ServiceOfferingDaoImpl() { super(); UniqueNameSearch = createSearchBuilder(); UniqueNameSearch.and("name", UniqueNameSearch.entity().getUniqueName(), SearchCriteria.Op.EQ); - UniqueNameSearch.and("system", UniqueNameSearch.entity().isSystemUse(), SearchCriteria.Op.EQ); + UniqueNameSearch.and("system_use", UniqueNameSearch.entity().isSystemUse(), SearchCriteria.Op.EQ); UniqueNameSearch.done(); ServiceOfferingsByKeywordSearch = createSearchBuilder(); @@ -71,13 +76,17 @@ public ServiceOfferingDaoImpl() { PublicCpuRamSearch.and("ram", PublicCpuRamSearch.entity().getRamSize(), SearchCriteria.Op.EQ); PublicCpuRamSearch.and("system_use", PublicCpuRamSearch.entity().isSystemUse(), SearchCriteria.Op.EQ); PublicCpuRamSearch.done(); + + SearchComputeOfferingByComputeOnlyDiskOffering = createSearchBuilder(); + SearchComputeOfferingByComputeOnlyDiskOffering.and("disk_offering_id", SearchComputeOfferingByComputeOnlyDiskOffering.entity().getDiskOfferingId(), SearchCriteria.Op.EQ); + SearchComputeOfferingByComputeOnlyDiskOffering.done(); } @Override public ServiceOfferingVO findByName(String name) { SearchCriteria sc = UniqueNameSearch.create(); sc.setParameters("name", name); - sc.setParameters("system", true); + sc.setParameters("system_use", true); List vos = search(sc, null, null, false); if (vos.size() == 0) { return null; @@ -92,13 +101,14 @@ public ServiceOfferingVO persistSystemServiceOffering(ServiceOfferingVO offering assert offering.getUniqueName() != null : "how are you going to find this later if you don't set it?"; ServiceOfferingVO vo = findByName(offering.getUniqueName()); if (vo != null) { + DiskOfferingVO diskOfferingVO = diskOfferingDao.findById(vo.getDiskOfferingId()); // check invalid CPU speed in system service offering, set it to default value of 500 Mhz if 0 CPU speed is found if (vo.getSpeed() <= 0) { vo.setSpeed(500); update(vo.getId(), vo); } if (!vo.getUniqueName().endsWith("-Local")) { - if (vo.isUseLocalStorage()) { + if (diskOfferingVO.isUseLocalStorage()) { vo.setUniqueName(vo.getUniqueName() + "-Local"); vo.setName(vo.getName() + " - Local Storage"); update(vo.getId(), vo); @@ -214,23 +224,33 @@ public ServiceOfferingVO getComputeOffering(ServiceOfferingVO serviceOffering, M public List createSystemServiceOfferings(String name, String uniqueName, int cpuCount, int ramSize, int cpuSpeed, Integer rateMbps, Integer multicastRateMbps, boolean offerHA, String displayText, ProvisioningType provisioningType, boolean recreatable, String tags, boolean systemUse, VirtualMachine.Type vmType, boolean defaultUse) { + DiskOfferingVO diskOfferingVO = new DiskOfferingVO(name, displayText, provisioningType, false, tags, recreatable, false, true); + diskOfferingVO.setUniqueName(uniqueName); + diskOfferingVO = diskOfferingDao.persistDefaultDiskOffering(diskOfferingVO); + List list = new ArrayList(); ServiceOfferingVO offering = new ServiceOfferingVO(name, cpuCount, ramSize, cpuSpeed, rateMbps, multicastRateMbps, offerHA, displayText, - provisioningType, false, recreatable, tags, systemUse, vmType, defaultUse); + systemUse, vmType, defaultUse); offering.setUniqueName(uniqueName); + offering.setDiskOfferingId(diskOfferingVO.getId()); offering = persistSystemServiceOffering(offering); if (offering != null) { list.add(offering); } boolean useLocal = true; - if (offering.isUseLocalStorage()) { // if 1st one is already local then 2nd needs to be shared + if (diskOfferingVO.isUseLocalStorage()) { // if 1st one is already local then 2nd needs to be shared useLocal = false; } + diskOfferingVO = new DiskOfferingVO(name + (useLocal ? " - Local Storage" : ""), displayText, provisioningType, false, tags, recreatable, useLocal, true); + diskOfferingVO.setUniqueName(uniqueName + (useLocal ? "-Local" : "")); + diskOfferingVO = diskOfferingDao.persistDefaultDiskOffering(diskOfferingVO); + offering = new ServiceOfferingVO(name + (useLocal ? " - Local Storage" : ""), cpuCount, ramSize, cpuSpeed, rateMbps, multicastRateMbps, offerHA, displayText, - provisioningType, useLocal, recreatable, tags, systemUse, vmType, defaultUse); + systemUse, vmType, defaultUse); offering.setUniqueName(uniqueName + (useLocal ? "-Local" : "")); + offering.setDiskOfferingId(diskOfferingVO.getId()); offering = persistSystemServiceOffering(offering); if (offering != null) { list.add(offering); @@ -262,4 +282,15 @@ public List listPublicByCpuAndMemory(Integer cpus, Integer me sc.setParameters("system_use", false); return listBy(sc); } + + @Override + public ServiceOfferingVO findServiceOfferingByComputeOnlyDiskOffering(long diskOfferingId) { + SearchCriteria sc = SearchComputeOfferingByComputeOnlyDiskOffering.create(); + sc.setParameters("disk_offering_id", diskOfferingId); + List vos = listBy(sc); + if (vos.size() == 0) { + return null; + } + return vos.get(0); + } } diff --git a/engine/schema/src/main/java/com/cloud/storage/DiskOfferingVO.java b/engine/schema/src/main/java/com/cloud/storage/DiskOfferingVO.java index 952d3014209c..bfdbba2a6d3b 100644 --- a/engine/schema/src/main/java/com/cloud/storage/DiskOfferingVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/DiskOfferingVO.java @@ -21,16 +21,12 @@ import java.util.UUID; import javax.persistence.Column; -import javax.persistence.DiscriminatorColumn; -import javax.persistence.DiscriminatorType; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; -import javax.persistence.Inheritance; -import javax.persistence.InheritanceType; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; @@ -41,8 +37,6 @@ @Entity @Table(name = "disk_offering") -@Inheritance(strategy = InheritanceType.JOINED) -@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING, length = 32) public class DiskOfferingVO implements DiskOffering { @Id @@ -65,8 +59,8 @@ public class DiskOfferingVO implements DiskOffering { @Column(name = "tags", length = 4096) String tags; - @Column(name = "type") - Type type; + @Column(name = "compute_only") + boolean computeOnly; @Column(name = GenericDao.REMOVED_COLUMN) @Temporal(TemporalType.TIMESTAMP) @@ -81,9 +75,6 @@ public class DiskOfferingVO implements DiskOffering { @Column(name = "use_local_storage") private boolean useLocalStorage; - @Column(name = "system_use") - private boolean systemUse; - @Column(name = "customized") private boolean customized; @@ -156,6 +147,9 @@ public class DiskOfferingVO implements DiskOffering { @Column(name = "hv_ss_reserve") Integer hypervisorSnapshotReserve; + @Column(name = "disk_size_strictness") + boolean diskSizeStrictness = false; + public DiskOfferingVO() { uuid = UUID.randomUUID().toString(); } @@ -168,7 +162,7 @@ public DiskOfferingVO(String name, String displayText, Storage.ProvisioningType this.diskSize = diskSize; this.tags = tags; recreatable = false; - type = Type.Disk; + computeOnly = false; useLocalStorage = false; customized = isCustomized; uuid = UUID.randomUUID().toString(); @@ -186,7 +180,7 @@ public DiskOfferingVO(String name, String displayText, Storage.ProvisioningType this.diskSize = diskSize; this.tags = tags; recreatable = false; - type = Type.Disk; + computeOnly = false; useLocalStorage = false; customized = isCustomized; uuid = UUID.randomUUID().toString(); @@ -196,32 +190,30 @@ public DiskOfferingVO(String name, String displayText, Storage.ProvisioningType state = State.Active; } - public DiskOfferingVO(String name, String displayText, Storage.ProvisioningType provisioningType, boolean mirrored, String tags, boolean recreatable, boolean useLocalStorage, boolean systemUse, + public DiskOfferingVO(String name, String displayText, Storage.ProvisioningType provisioningType, boolean mirrored, String tags, boolean recreatable, boolean useLocalStorage, boolean customized) { - type = Type.Service; + computeOnly = true; this.name = name; this.displayText = displayText; this.provisioningType = provisioningType; this.tags = tags; this.recreatable = recreatable; this.useLocalStorage = useLocalStorage; - this.systemUse = systemUse; this.customized = customized; uuid = UUID.randomUUID().toString(); state = State.Active; } 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 minIops, Long maxIops) { + boolean customized, boolean customizedIops, Long minIops, Long maxIops) { this.id = id; - type = Type.Service; + computeOnly = true; this.name = name; this.displayText = displayText; this.provisioningType = provisioningType; this.tags = tags; this.recreatable = recreatable; this.useLocalStorage = useLocalStorage; - this.systemUse = systemUse; this.customized = customized; this.customizedIops = customizedIops; uuid = UUID.randomUUID().toString(); @@ -304,8 +296,8 @@ public boolean isUseLocalStorage() { } @Override - public Type getType() { - return type; + public boolean isComputeOnly() { + return computeOnly; } @Override @@ -322,15 +314,6 @@ public void setName(String name) { this.name = name; } - @Override - public boolean isSystemUse() { - return systemUse; - } - - public void setSystemUse(boolean systemUse) { - this.systemUse = systemUse; - } - @Override public String getDisplayText() { return displayText; @@ -588,4 +571,12 @@ public Integer getHypervisorSnapshotReserve() { public boolean isShared() { return !useLocalStorage; } + + public boolean getDiskSizeStrictness() { + return diskSizeStrictness; + } + + public void setDiskSizeStrictness(boolean diskSizeStrictness) { + this.diskSizeStrictness = diskSizeStrictness; + } } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java index 3305752459d9..b08892571211 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDao.java @@ -24,13 +24,9 @@ public interface DiskOfferingDao extends GenericDao { - List findPrivateDiskOffering(); - - List findPublicDiskOfferings(); - DiskOfferingVO findByUniqueName(String uniqueName); - DiskOfferingVO persistDeafultDiskOffering(DiskOfferingVO offering); + DiskOfferingVO persistDefaultDiskOffering(DiskOfferingVO offering); List listAllBySizeAndProvisioningType(long size, Storage.ProvisioningType provisioningType); diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDaoImpl.java index b9fa10c12366..435474ed942f 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/DiskOfferingDaoImpl.java @@ -29,7 +29,6 @@ import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao; import org.springframework.stereotype.Component; -import com.cloud.offering.DiskOffering.Type; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.Storage; import com.cloud.utils.db.Attribute; @@ -37,7 +36,6 @@ import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; -import com.cloud.utils.db.SearchCriteria.Op; import com.cloud.utils.db.TransactionLegacy; import com.cloud.utils.exception.CloudRuntimeException; @@ -47,48 +45,28 @@ public class DiskOfferingDaoImpl extends GenericDaoBase im @Inject protected DiskOfferingDetailsDao detailsDao; - private final SearchBuilder PrivateDiskOfferingSearch; - private final SearchBuilder PublicDiskOfferingSearch; protected final SearchBuilder UniqueNameSearch; private final String SizeDiskOfferingSearch = "SELECT * FROM disk_offering WHERE " + "disk_size = ? AND provisioning_type = ? AND removed IS NULL"; - private final Attribute _typeAttr; + private final Attribute _computeOnlyAttr; protected final static long GB_UNIT_BYTES = 1024 * 1024 * 1024; protected DiskOfferingDaoImpl() { - PrivateDiskOfferingSearch = createSearchBuilder(); - PrivateDiskOfferingSearch.and("diskSize", PrivateDiskOfferingSearch.entity().getDiskSize(), SearchCriteria.Op.EQ); - PrivateDiskOfferingSearch.done(); - - PublicDiskOfferingSearch = createSearchBuilder(); - PublicDiskOfferingSearch.and("system", PublicDiskOfferingSearch.entity().isSystemUse(), SearchCriteria.Op.EQ); - PublicDiskOfferingSearch.and("removed", PublicDiskOfferingSearch.entity().getRemoved(), SearchCriteria.Op.NULL); - PublicDiskOfferingSearch.done(); - UniqueNameSearch = createSearchBuilder(); UniqueNameSearch.and("name", UniqueNameSearch.entity().getUniqueName(), SearchCriteria.Op.EQ); UniqueNameSearch.done(); - _typeAttr = _allAttributes.get("type"); - } - - @Override - public List findPrivateDiskOffering() { - SearchCriteria sc = PrivateDiskOfferingSearch.create(); - sc.setParameters("diskSize", 0); - return listBy(sc); + _computeOnlyAttr = _allAttributes.get("computeOnly"); } @Override public List searchIncludingRemoved(SearchCriteria sc, final Filter filter, final Boolean lock, final boolean cache) { - sc.addAnd(_typeAttr, Op.EQ, Type.Disk); return super.searchIncludingRemoved(sc, filter, lock, cache); } @Override public List customSearchIncludingRemoved(SearchCriteria sc, final Filter filter) { - sc.addAnd(_typeAttr, Op.EQ, Type.Disk); return super.customSearchIncludingRemoved(sc, filter); } @@ -97,23 +75,12 @@ protected List executeList(final String sql, final Object... par StringBuilder builder = new StringBuilder(sql); int index = builder.indexOf("WHERE"); if (index == -1) { - builder.append(" WHERE type=?"); + builder.append(" WHERE compute_only=?"); } else { - builder.insert(index + 6, "type=? "); + builder.insert(index + 6, "compute_only=? "); } - return super.executeList(sql, Type.Disk, params); - } - - @Override - public List findPublicDiskOfferings() { - SearchCriteria sc = PublicDiskOfferingSearch.create(); - sc.setParameters("system", false); - List offerings = listBy(sc); - if(offerings!=null) { - offerings.removeIf(o -> (!detailsDao.findDomainIds(o.getId()).isEmpty())); - } - return offerings; + return super.executeList(sql, false, params); } @Override @@ -129,7 +96,7 @@ public DiskOfferingVO findByUniqueName(String uniqueName) { } @Override - public DiskOfferingVO persistDeafultDiskOffering(DiskOfferingVO offering) { + public DiskOfferingVO persistDefaultDiskOffering(DiskOfferingVO offering) { assert offering.getUniqueName() != null : "unique name shouldn't be null for the disk offering"; DiskOfferingVO vo = findByUniqueName(offering.getUniqueName()); if (vo != null) { diff --git a/engine/schema/src/main/java/com/cloud/vm/UserVmVO.java b/engine/schema/src/main/java/com/cloud/vm/UserVmVO.java index e62162e25960..dad6d10e3765 100644 --- a/engine/schema/src/main/java/com/cloud/vm/UserVmVO.java +++ b/engine/schema/src/main/java/com/cloud/vm/UserVmVO.java @@ -73,8 +73,8 @@ public long getServiceOfferingId() { } public UserVmVO(long id, String instanceName, String displayName, long templateId, HypervisorType hypervisorType, long guestOsId, boolean haEnabled, - boolean limitCpuUse, long domainId, long accountId, long userId, long serviceOfferingId, String userData, String name, Long diskOfferingId) { - super(id, serviceOfferingId, name, instanceName, Type.User, templateId, hypervisorType, guestOsId, domainId, accountId, userId, haEnabled, limitCpuUse, diskOfferingId); + boolean limitCpuUse, long domainId, long accountId, long userId, long serviceOfferingId, String userData, String name) { + super(id, serviceOfferingId, name, instanceName, Type.User, templateId, hypervisorType, guestOsId, domainId, accountId, userId, haEnabled, limitCpuUse); this.userData = userData; this.displayName = displayName; this.details = new HashMap(); diff --git a/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java b/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java index b1debd0b8942..e0b37e476287 100644 --- a/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java +++ b/engine/schema/src/main/java/com/cloud/vm/VMInstanceVO.java @@ -174,9 +174,6 @@ public class VMInstanceVO implements VirtualMachine, FiniteStateObject networkIds; - @Column(name = "disk_offering_id") - protected Long diskOfferingId; - @Column(name = "display_vm", updatable = true, nullable = false) protected boolean display = true; @@ -196,7 +193,7 @@ public class VMEntityVO implements VirtualMachine, FiniteStateObject networkIds) { this.networkIds = networkIds; } - @Override - public Long getDiskOfferingId() { - return diskOfferingId; - } - public VMReservationVO getVmReservation() { return vmReservation; } diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41610to41700.sql b/engine/schema/src/main/resources/META-INF/db/schema-41610to41700.sql index e28fe4329ddf..6cd11a4c8111 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41610to41700.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41610to41700.sql @@ -21,3 +21,621 @@ -- PR#5668 Change the type of the 'ipsec_psk' field to allow large PSK. ALTER TABLE cloud.remote_access_vpn MODIFY ipsec_psk text NOT NULL; + + + +ALTER TABLE `cloud`.`service_offering` ADD COLUMN `uuid` varchar(40) UNIQUE DEFAULT NULL; +ALTER TABLE `cloud`.`service_offering` ADD COLUMN `name` varchar(255) NOT NULL; +ALTER TABLE `cloud`.`service_offering` ADD COLUMN `display_text` varchar(4096) DEFAULT NULL ; +ALTER TABLE `cloud`.`service_offering` ADD COLUMN `unique_name` varchar(32) DEFAULT NULL COMMENT 'unique name for system offerings'; +ALTER TABLE `cloud`.`service_offering` ADD COLUMN `customized` tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT '0 implies not customized by default'; +ALTER TABLE `cloud`.`service_offering` ADD COLUMN `created` datetime DEFAULT NULL COMMENT 'date when service offering was created'; +ALTER TABLE `cloud`.`service_offering` ADD COLUMN `removed` datetime DEFAULT NULL COMMENT 'date when service offering was removed'; +ALTER TABLE `cloud`.`service_offering` ADD COLUMN `state` CHAR(40) NOT NULL DEFAULT 'Active' COMMENT 'state of service offering either Active or Inactive'; +ALTER TABLE `cloud`.`service_offering` ADD COLUMN `disk_offering_id` bigint unsigned; +ALTER TABLE `cloud`.`service_offering` ADD COLUMN `system_use` tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT 'is this offering for system used only'; +ALTER TABLE `cloud`.`service_offering` ADD COLUMN `disk_offering_strictness` tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT 'strict binding with disk offering or not'; +ALTER TABLE `cloud`.`service_offering` ADD CONSTRAINT `fk_service_offering__disk_offering_id` FOREIGN KEY `fk_service_offering__disk_offering_id`(`disk_offering_id`) REFERENCES `disk_offering`(`id`) ON DELETE CASCADE; +ALTER TABLE `cloud`.`service_offering` DROP FOREIGN KEY `fk_service_offering__id`; + +ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `disk_size_strictness` tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT 'To allow or disallow the resize operation on the disks created from this offering'; +ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `compute_only` tinyint(1) unsigned NOT NULL DEFAULT 0 COMMENT 'when set to 1, disk offering has one to one binding with service offering'; + +ALTER TABLE `cloud`.`vm_instance` DROP COLUMN `disk_offering_id`; + +UPDATE `cloud`.`service_offering` so, `cloud`.`disk_offering` do SET so.`uuid` = do.`uuid`, + so.`name` = do.`name`, + so.`display_text` = do.`display_text`, + so.`unique_name` = do.`unique_name`, + so.`customized` = do.`customized`, + so.`created` = do.`created`, + so.`removed` = do.`removed`, + so.`state` = do.`state`, + so.`disk_offering_id` = do.`id`, + so.`system_use` = do.`system_use` WHERE so.`id` = do.`id`; + +UPDATE `cloud`.`disk_offering` SET `compute_only` = 1 where `type` = 'Service'; +UPDATE `cloud`.`disk_offering` SET `disk_size_strictness` = 1 WHERE `compute_only` = 1 AND `disk_size` != 0; + +ALTER TABLE `cloud`.`disk_offering` DROP COLUMN `type`; +ALTER TABLE `cloud`.`disk_offering` DROP COLUMN `system_use`; + +DROP VIEW IF EXISTS `cloud`.`disk_offering_view`; +CREATE VIEW `cloud`.`disk_offering_view` AS + SELECT + `disk_offering`.`id` AS `id`, + `disk_offering`.`uuid` AS `uuid`, + `disk_offering`.`name` AS `name`, + `disk_offering`.`display_text` AS `display_text`, + `disk_offering`.`provisioning_type` AS `provisioning_type`, + `disk_offering`.`disk_size` AS `disk_size`, + `disk_offering`.`min_iops` AS `min_iops`, + `disk_offering`.`max_iops` AS `max_iops`, + `disk_offering`.`created` AS `created`, + `disk_offering`.`tags` AS `tags`, + `disk_offering`.`customized` AS `customized`, + `disk_offering`.`customized_iops` AS `customized_iops`, + `disk_offering`.`removed` AS `removed`, + `disk_offering`.`use_local_storage` AS `use_local_storage`, + `disk_offering`.`hv_ss_reserve` AS `hv_ss_reserve`, + `disk_offering`.`bytes_read_rate` AS `bytes_read_rate`, + `disk_offering`.`bytes_read_rate_max` AS `bytes_read_rate_max`, + `disk_offering`.`bytes_read_rate_max_length` AS `bytes_read_rate_max_length`, + `disk_offering`.`bytes_write_rate` AS `bytes_write_rate`, + `disk_offering`.`bytes_write_rate_max` AS `bytes_write_rate_max`, + `disk_offering`.`bytes_write_rate_max_length` AS `bytes_write_rate_max_length`, + `disk_offering`.`iops_read_rate` AS `iops_read_rate`, + `disk_offering`.`iops_read_rate_max` AS `iops_read_rate_max`, + `disk_offering`.`iops_read_rate_max_length` AS `iops_read_rate_max_length`, + `disk_offering`.`iops_write_rate` AS `iops_write_rate`, + `disk_offering`.`iops_write_rate_max` AS `iops_write_rate_max`, + `disk_offering`.`iops_write_rate_max_length` AS `iops_write_rate_max_length`, + `disk_offering`.`cache_mode` AS `cache_mode`, + `disk_offering`.`sort_key` AS `sort_key`, + `disk_offering`.`compute_only` AS `compute_only`, + `disk_offering`.`display_offering` AS `display_offering`, + `disk_offering`.`state` AS `state`, + `disk_offering`.`disk_size_strictness` AS `disk_size_strictness`, + `vsphere_storage_policy`.`value` AS `vsphere_storage_policy`, + GROUP_CONCAT(DISTINCT(domain.id)) AS domain_id, + GROUP_CONCAT(DISTINCT(domain.uuid)) AS domain_uuid, + GROUP_CONCAT(DISTINCT(domain.name)) AS domain_name, + GROUP_CONCAT(DISTINCT(domain.path)) AS domain_path, + GROUP_CONCAT(DISTINCT(zone.id)) AS zone_id, + GROUP_CONCAT(DISTINCT(zone.uuid)) AS zone_uuid, + GROUP_CONCAT(DISTINCT(zone.name)) AS zone_name + FROM + `cloud`.`disk_offering` + LEFT JOIN + `cloud`.`disk_offering_details` AS `domain_details` ON `domain_details`.`offering_id` = `disk_offering`.`id` AND `domain_details`.`name`='domainid' + LEFT JOIN + `cloud`.`domain` AS `domain` ON FIND_IN_SET(`domain`.`id`, `domain_details`.`value`) + LEFT JOIN + `cloud`.`disk_offering_details` AS `zone_details` ON `zone_details`.`offering_id` = `disk_offering`.`id` AND `zone_details`.`name`='zoneid' + LEFT JOIN + `cloud`.`data_center` AS `zone` ON FIND_IN_SET(`zone`.`id`, `zone_details`.`value`) + LEFT JOIN + `cloud`.`disk_offering_details` AS `vsphere_storage_policy` ON `vsphere_storage_policy`.`offering_id` = `disk_offering`.`id` + AND `vsphere_storage_policy`.`name` = 'storagepolicy' + WHERE + `disk_offering`.`state`='Active' + GROUP BY + `disk_offering`.`id`; + +DROP VIEW IF EXISTS `cloud`.`service_offering_view`; +CREATE VIEW `cloud`.`service_offering_view` AS + SELECT + `service_offering`.`id` AS `id`, + `service_offering`.`uuid` AS `uuid`, + `service_offering`.`name` AS `name`, + `service_offering`.`display_text` AS `display_text`, + `disk_offering`.`provisioning_type` AS `provisioning_type`, + `service_offering`.`created` AS `created`, + `disk_offering`.`tags` AS `tags`, + `service_offering`.`removed` AS `removed`, + `disk_offering`.`use_local_storage` AS `use_local_storage`, + `service_offering`.`system_use` AS `system_use`, + `disk_offering`.`id` AS `disk_offering_id`, + `disk_offering`.`name` AS `disk_offering_name`, + `disk_offering`.`uuid` AS `disk_offering_uuid`, + `disk_offering`.`display_text` AS `disk_offering_display_text`, + `disk_offering`.`customized_iops` AS `customized_iops`, + `disk_offering`.`min_iops` AS `min_iops`, + `disk_offering`.`max_iops` AS `max_iops`, + `disk_offering`.`hv_ss_reserve` AS `hv_ss_reserve`, + `disk_offering`.`bytes_read_rate` AS `bytes_read_rate`, + `disk_offering`.`bytes_read_rate_max` AS `bytes_read_rate_max`, + `disk_offering`.`bytes_read_rate_max_length` AS `bytes_read_rate_max_length`, + `disk_offering`.`bytes_write_rate` AS `bytes_write_rate`, + `disk_offering`.`bytes_write_rate_max` AS `bytes_write_rate_max`, + `disk_offering`.`bytes_write_rate_max_length` AS `bytes_write_rate_max_length`, + `disk_offering`.`iops_read_rate` AS `iops_read_rate`, + `disk_offering`.`iops_read_rate_max` AS `iops_read_rate_max`, + `disk_offering`.`iops_read_rate_max_length` AS `iops_read_rate_max_length`, + `disk_offering`.`iops_write_rate` AS `iops_write_rate`, + `disk_offering`.`iops_write_rate_max` AS `iops_write_rate_max`, + `disk_offering`.`iops_write_rate_max_length` AS `iops_write_rate_max_length`, + `disk_offering`.`cache_mode` AS `cache_mode`, + `disk_offering`.`disk_size` AS `root_disk_size`, + `service_offering`.`cpu` AS `cpu`, + `service_offering`.`speed` AS `speed`, + `service_offering`.`ram_size` AS `ram_size`, + `service_offering`.`nw_rate` AS `nw_rate`, + `service_offering`.`mc_rate` AS `mc_rate`, + `service_offering`.`ha_enabled` AS `ha_enabled`, + `service_offering`.`limit_cpu_use` AS `limit_cpu_use`, + `service_offering`.`host_tag` AS `host_tag`, + `service_offering`.`default_use` AS `default_use`, + `service_offering`.`vm_type` AS `vm_type`, + `service_offering`.`sort_key` AS `sort_key`, + `service_offering`.`is_volatile` AS `is_volatile`, + `service_offering`.`deployment_planner` AS `deployment_planner`, + `service_offering`.`dynamic_scaling_enabled` AS `dynamic_scaling_enabled`, + `service_offering`.`disk_offering_strictness` AS `disk_offering_strictness`, + `vsphere_storage_policy`.`value` AS `vsphere_storage_policy`, + GROUP_CONCAT(DISTINCT(domain.id)) AS domain_id, + GROUP_CONCAT(DISTINCT(domain.uuid)) AS domain_uuid, + GROUP_CONCAT(DISTINCT(domain.name)) AS domain_name, + GROUP_CONCAT(DISTINCT(domain.path)) AS domain_path, + GROUP_CONCAT(DISTINCT(zone.id)) AS zone_id, + GROUP_CONCAT(DISTINCT(zone.uuid)) AS zone_uuid, + GROUP_CONCAT(DISTINCT(zone.name)) AS zone_name, + IFNULL(`min_compute_details`.`value`, `cpu`) AS min_cpu, + IFNULL(`max_compute_details`.`value`, `cpu`) AS max_cpu, + IFNULL(`min_memory_details`.`value`, `ram_size`) AS min_memory, + IFNULL(`max_memory_details`.`value`, `ram_size`) AS max_memory + FROM + `cloud`.`service_offering` + INNER JOIN + `cloud`.`disk_offering_view` AS `disk_offering` ON service_offering.disk_offering_id = disk_offering.id + LEFT JOIN + `cloud`.`service_offering_details` AS `domain_details` ON `domain_details`.`service_offering_id` = `service_offering`.`id` AND `domain_details`.`name`='domainid' + LEFT JOIN + `cloud`.`domain` AS `domain` ON FIND_IN_SET(`domain`.`id`, `domain_details`.`value`) + LEFT JOIN + `cloud`.`service_offering_details` AS `zone_details` ON `zone_details`.`service_offering_id` = `service_offering`.`id` AND `zone_details`.`name`='zoneid' + LEFT JOIN + `cloud`.`data_center` AS `zone` ON FIND_IN_SET(`zone`.`id`, `zone_details`.`value`) + LEFT JOIN + `cloud`.`service_offering_details` AS `min_compute_details` ON `min_compute_details`.`service_offering_id` = `service_offering`.`id` + AND `min_compute_details`.`name` = 'mincpunumber' + LEFT JOIN + `cloud`.`service_offering_details` AS `max_compute_details` ON `max_compute_details`.`service_offering_id` = `service_offering`.`id` + AND `max_compute_details`.`name` = 'maxcpunumber' + LEFT JOIN + `cloud`.`service_offering_details` AS `min_memory_details` ON `min_memory_details`.`service_offering_id` = `service_offering`.`id` + AND `min_memory_details`.`name` = 'minmemory' + LEFT JOIN + `cloud`.`service_offering_details` AS `max_memory_details` ON `max_memory_details`.`service_offering_id` = `service_offering`.`id` + AND `max_memory_details`.`name` = 'maxmemory' + LEFT JOIN + `cloud`.`service_offering_details` AS `vsphere_storage_policy` ON `vsphere_storage_policy`.`service_offering_id` = `service_offering`.`id` + AND `vsphere_storage_policy`.`name` = 'storagepolicy' + WHERE + `service_offering`.`state`='Active' + GROUP BY + `service_offering`.`id`; + +DROP VIEW IF EXISTS `cloud`.`volume_view`; +CREATE VIEW `cloud`.`volume_view` AS + SELECT + volumes.id, + volumes.uuid, + volumes.name, + volumes.device_id, + volumes.volume_type, + volumes.provisioning_type, + volumes.size, + volumes.min_iops, + volumes.max_iops, + volumes.created, + volumes.state, + volumes.attached, + volumes.removed, + volumes.display_volume, + volumes.format, + volumes.path, + volumes.chain_info, + account.id account_id, + account.uuid account_uuid, + account.account_name account_name, + account.type account_type, + domain.id domain_id, + domain.uuid domain_uuid, + domain.name domain_name, + domain.path domain_path, + projects.id project_id, + projects.uuid project_uuid, + projects.name project_name, + data_center.id data_center_id, + data_center.uuid data_center_uuid, + data_center.name data_center_name, + data_center.networktype data_center_type, + vm_instance.id vm_id, + vm_instance.uuid vm_uuid, + vm_instance.name vm_name, + vm_instance.state vm_state, + vm_instance.vm_type, + user_vm.display_name vm_display_name, + volume_store_ref.size volume_store_size, + volume_store_ref.download_pct, + volume_store_ref.download_state, + volume_store_ref.error_str, + volume_store_ref.created created_on_store, + disk_offering.id disk_offering_id, + disk_offering.uuid disk_offering_uuid, + disk_offering.name disk_offering_name, + disk_offering.display_text disk_offering_display_text, + disk_offering.use_local_storage, + service_offering.system_use, + disk_offering.bytes_read_rate, + disk_offering.bytes_write_rate, + disk_offering.iops_read_rate, + disk_offering.iops_write_rate, + disk_offering.cache_mode, + storage_pool.id pool_id, + storage_pool.uuid pool_uuid, + storage_pool.name pool_name, + cluster.id cluster_id, + cluster.name cluster_name, + cluster.uuid cluster_uuid, + cluster.hypervisor_type, + vm_template.id template_id, + vm_template.uuid template_uuid, + vm_template.extractable, + vm_template.type template_type, + vm_template.name template_name, + vm_template.display_text template_display_text, + iso.id iso_id, + iso.uuid iso_uuid, + iso.name iso_name, + iso.display_text iso_display_text, + resource_tags.id tag_id, + resource_tags.uuid tag_uuid, + resource_tags.key tag_key, + resource_tags.value tag_value, + resource_tags.domain_id tag_domain_id, + resource_tags.account_id tag_account_id, + resource_tags.resource_id tag_resource_id, + resource_tags.resource_uuid tag_resource_uuid, + resource_tags.resource_type tag_resource_type, + resource_tags.customer tag_customer, + async_job.id job_id, + async_job.uuid job_uuid, + async_job.job_status job_status, + async_job.account_id job_account_id, + host_pod_ref.id pod_id, + host_pod_ref.uuid pod_uuid, + host_pod_ref.name pod_name, + resource_tag_account.account_name tag_account_name, + resource_tag_domain.uuid tag_domain_uuid, + resource_tag_domain.name tag_domain_name + from + `cloud`.`volumes` + inner join + `cloud`.`account` ON volumes.account_id = account.id + inner join + `cloud`.`domain` ON volumes.domain_id = domain.id + left join + `cloud`.`projects` ON projects.project_account_id = account.id + left join + `cloud`.`data_center` ON volumes.data_center_id = data_center.id + left join + `cloud`.`vm_instance` ON volumes.instance_id = vm_instance.id + left join + `cloud`.`user_vm` ON user_vm.id = vm_instance.id + left join + `cloud`.`volume_store_ref` ON volumes.id = volume_store_ref.volume_id + left join + `cloud`.`service_offering` ON vm_instance.service_offering_id = service_offering.id + left join + `cloud`.`disk_offering` ON volumes.disk_offering_id = disk_offering.id + left join + `cloud`.`storage_pool` ON volumes.pool_id = storage_pool.id + left join + `cloud`.`host_pod_ref` ON storage_pool.pod_id = host_pod_ref.id + left join + `cloud`.`cluster` ON storage_pool.cluster_id = cluster.id + left join + `cloud`.`vm_template` ON volumes.template_id = vm_template.id + left join + `cloud`.`vm_template` iso ON iso.id = volumes.iso_id + left join + `cloud`.`resource_tags` ON resource_tags.resource_id = volumes.id + and resource_tags.resource_type = 'Volume' + left join + `cloud`.`async_job` ON async_job.instance_id = volumes.id + and async_job.instance_type = 'Volume' + and async_job.job_status = 0 + left join + `cloud`.`account` resource_tag_account ON resource_tag_account.id = resource_tags.account_id + left join + `cloud`.`domain` resource_tag_domain ON resource_tag_domain.id = resource_tags.domain_id; + +DROP VIEW IF EXISTS `cloud`.`user_vm_view`; +CREATE + VIEW `user_vm_view` AS +SELECT + `vm_instance`.`id` AS `id`, + `vm_instance`.`name` AS `name`, + `user_vm`.`display_name` AS `display_name`, + `user_vm`.`user_data` AS `user_data`, + `account`.`id` AS `account_id`, + `account`.`uuid` AS `account_uuid`, + `account`.`account_name` AS `account_name`, + `account`.`type` AS `account_type`, + `domain`.`id` AS `domain_id`, + `domain`.`uuid` AS `domain_uuid`, + `domain`.`name` AS `domain_name`, + `domain`.`path` AS `domain_path`, + `projects`.`id` AS `project_id`, + `projects`.`uuid` AS `project_uuid`, + `projects`.`name` AS `project_name`, + `instance_group`.`id` AS `instance_group_id`, + `instance_group`.`uuid` AS `instance_group_uuid`, + `instance_group`.`name` AS `instance_group_name`, + `vm_instance`.`uuid` AS `uuid`, + `vm_instance`.`user_id` AS `user_id`, + `vm_instance`.`last_host_id` AS `last_host_id`, + `vm_instance`.`vm_type` AS `type`, + `vm_instance`.`limit_cpu_use` AS `limit_cpu_use`, + `vm_instance`.`created` AS `created`, + `vm_instance`.`state` AS `state`, + `vm_instance`.`update_time` AS `update_time`, + `vm_instance`.`removed` AS `removed`, + `vm_instance`.`ha_enabled` AS `ha_enabled`, + `vm_instance`.`hypervisor_type` AS `hypervisor_type`, + `vm_instance`.`instance_name` AS `instance_name`, + `vm_instance`.`guest_os_id` AS `guest_os_id`, + `vm_instance`.`display_vm` AS `display_vm`, + `guest_os`.`uuid` AS `guest_os_uuid`, + `vm_instance`.`pod_id` AS `pod_id`, + `host_pod_ref`.`uuid` AS `pod_uuid`, + `vm_instance`.`private_ip_address` AS `private_ip_address`, + `vm_instance`.`private_mac_address` AS `private_mac_address`, + `vm_instance`.`vm_type` AS `vm_type`, + `data_center`.`id` AS `data_center_id`, + `data_center`.`uuid` AS `data_center_uuid`, + `data_center`.`name` AS `data_center_name`, + `data_center`.`is_security_group_enabled` AS `security_group_enabled`, + `data_center`.`networktype` AS `data_center_type`, + `host`.`id` AS `host_id`, + `host`.`uuid` AS `host_uuid`, + `host`.`name` AS `host_name`, + `host`.`cluster_id` AS `cluster_id`, + `vm_template`.`id` AS `template_id`, + `vm_template`.`uuid` AS `template_uuid`, + `vm_template`.`name` AS `template_name`, + `vm_template`.`display_text` AS `template_display_text`, + `vm_template`.`enable_password` AS `password_enabled`, + `iso`.`id` AS `iso_id`, + `iso`.`uuid` AS `iso_uuid`, + `iso`.`name` AS `iso_name`, + `iso`.`display_text` AS `iso_display_text`, + `service_offering`.`id` AS `service_offering_id`, + `service_offering`.`uuid` AS `service_offering_uuid`, + `disk_offering`.`uuid` AS `disk_offering_uuid`, + `disk_offering`.`id` AS `disk_offering_id`, + (CASE + WHEN ISNULL(`service_offering`.`cpu`) THEN `custom_cpu`.`value` + ELSE `service_offering`.`cpu` + END) AS `cpu`, + (CASE + WHEN ISNULL(`service_offering`.`speed`) THEN `custom_speed`.`value` + ELSE `service_offering`.`speed` + END) AS `speed`, + (CASE + WHEN ISNULL(`service_offering`.`ram_size`) THEN `custom_ram_size`.`value` + ELSE `service_offering`.`ram_size` + END) AS `ram_size`, + `backup_offering`.`uuid` AS `backup_offering_uuid`, + `backup_offering`.`id` AS `backup_offering_id`, + `service_offering`.`name` AS `service_offering_name`, + `disk_offering`.`name` AS `disk_offering_name`, + `backup_offering`.`name` AS `backup_offering_name`, + `storage_pool`.`id` AS `pool_id`, + `storage_pool`.`uuid` AS `pool_uuid`, + `storage_pool`.`pool_type` AS `pool_type`, + `volumes`.`id` AS `volume_id`, + `volumes`.`uuid` AS `volume_uuid`, + `volumes`.`device_id` AS `volume_device_id`, + `volumes`.`volume_type` AS `volume_type`, + `security_group`.`id` AS `security_group_id`, + `security_group`.`uuid` AS `security_group_uuid`, + `security_group`.`name` AS `security_group_name`, + `security_group`.`description` AS `security_group_description`, + `nics`.`id` AS `nic_id`, + `nics`.`uuid` AS `nic_uuid`, + `nics`.`device_id` AS `nic_device_id`, + `nics`.`network_id` AS `network_id`, + `nics`.`ip4_address` AS `ip_address`, + `nics`.`ip6_address` AS `ip6_address`, + `nics`.`ip6_gateway` AS `ip6_gateway`, + `nics`.`ip6_cidr` AS `ip6_cidr`, + `nics`.`default_nic` AS `is_default_nic`, + `nics`.`gateway` AS `gateway`, + `nics`.`netmask` AS `netmask`, + `nics`.`mac_address` AS `mac_address`, + `nics`.`broadcast_uri` AS `broadcast_uri`, + `nics`.`isolation_uri` AS `isolation_uri`, + `vpc`.`id` AS `vpc_id`, + `vpc`.`uuid` AS `vpc_uuid`, + `networks`.`uuid` AS `network_uuid`, + `networks`.`name` AS `network_name`, + `networks`.`traffic_type` AS `traffic_type`, + `networks`.`guest_type` AS `guest_type`, + `user_ip_address`.`id` AS `public_ip_id`, + `user_ip_address`.`uuid` AS `public_ip_uuid`, + `user_ip_address`.`public_ip_address` AS `public_ip_address`, + `ssh_keypairs`.`keypair_name` AS `keypair_name`, + `resource_tags`.`id` AS `tag_id`, + `resource_tags`.`uuid` AS `tag_uuid`, + `resource_tags`.`key` AS `tag_key`, + `resource_tags`.`value` AS `tag_value`, + `resource_tags`.`domain_id` AS `tag_domain_id`, + `domain`.`uuid` AS `tag_domain_uuid`, + `domain`.`name` AS `tag_domain_name`, + `resource_tags`.`account_id` AS `tag_account_id`, + `account`.`account_name` AS `tag_account_name`, + `resource_tags`.`resource_id` AS `tag_resource_id`, + `resource_tags`.`resource_uuid` AS `tag_resource_uuid`, + `resource_tags`.`resource_type` AS `tag_resource_type`, + `resource_tags`.`customer` AS `tag_customer`, + `async_job`.`id` AS `job_id`, + `async_job`.`uuid` AS `job_uuid`, + `async_job`.`job_status` AS `job_status`, + `async_job`.`account_id` AS `job_account_id`, + `affinity_group`.`id` AS `affinity_group_id`, + `affinity_group`.`uuid` AS `affinity_group_uuid`, + `affinity_group`.`name` AS `affinity_group_name`, + `affinity_group`.`description` AS `affinity_group_description`, + `vm_instance`.`dynamically_scalable` AS `dynamically_scalable` +FROM + (((((((((((((((((((((((((((((((((`user_vm` + JOIN `vm_instance` ON (((`vm_instance`.`id` = `user_vm`.`id`) + AND ISNULL(`vm_instance`.`removed`)))) + JOIN `account` ON ((`vm_instance`.`account_id` = `account`.`id`))) + JOIN `domain` ON ((`vm_instance`.`domain_id` = `domain`.`id`))) + LEFT JOIN `guest_os` ON ((`vm_instance`.`guest_os_id` = `guest_os`.`id`))) + LEFT JOIN `host_pod_ref` ON ((`vm_instance`.`pod_id` = `host_pod_ref`.`id`))) + LEFT JOIN `projects` ON ((`projects`.`project_account_id` = `account`.`id`))) + LEFT JOIN `instance_group_vm_map` ON ((`vm_instance`.`id` = `instance_group_vm_map`.`instance_id`))) + LEFT JOIN `instance_group` ON ((`instance_group_vm_map`.`group_id` = `instance_group`.`id`))) + LEFT JOIN `data_center` ON ((`vm_instance`.`data_center_id` = `data_center`.`id`))) + LEFT JOIN `host` ON ((`vm_instance`.`host_id` = `host`.`id`))) + LEFT JOIN `vm_template` ON ((`vm_instance`.`vm_template_id` = `vm_template`.`id`))) + LEFT JOIN `vm_template` `iso` ON ((`iso`.`id` = `user_vm`.`iso_id`))) + LEFT JOIN `volumes` ON ((`vm_instance`.`id` = `volumes`.`instance_id`))) + LEFT JOIN `service_offering` ON ((`vm_instance`.`service_offering_id` = `service_offering`.`id`))) + LEFT JOIN `disk_offering` `svc_disk_offering` ON ((`volumes`.`disk_offering_id` = `svc_disk_offering`.`id`))) + LEFT JOIN `disk_offering` ON ((`volumes`.`disk_offering_id` = `disk_offering`.`id`))) + LEFT JOIN `backup_offering` ON ((`vm_instance`.`backup_offering_id` = `backup_offering`.`id`))) + LEFT JOIN `storage_pool` ON ((`volumes`.`pool_id` = `storage_pool`.`id`))) + LEFT JOIN `security_group_vm_map` ON ((`vm_instance`.`id` = `security_group_vm_map`.`instance_id`))) + LEFT JOIN `security_group` ON ((`security_group_vm_map`.`security_group_id` = `security_group`.`id`))) + LEFT JOIN `nics` ON (((`vm_instance`.`id` = `nics`.`instance_id`) + AND ISNULL(`nics`.`removed`)))) + LEFT JOIN `networks` ON ((`nics`.`network_id` = `networks`.`id`))) + LEFT JOIN `vpc` ON (((`networks`.`vpc_id` = `vpc`.`id`) + AND ISNULL(`vpc`.`removed`)))) + LEFT JOIN `user_ip_address` ON ((`user_ip_address`.`vm_id` = `vm_instance`.`id`))) + LEFT JOIN `user_vm_details` `ssh_details` ON (((`ssh_details`.`vm_id` = `vm_instance`.`id`) + AND (`ssh_details`.`name` = 'SSH.PublicKey')))) + LEFT JOIN `ssh_keypairs` ON (((`ssh_keypairs`.`public_key` = `ssh_details`.`value`) + AND (`ssh_keypairs`.`account_id` = `account`.`id`)))) + LEFT JOIN `resource_tags` ON (((`resource_tags`.`resource_id` = `vm_instance`.`id`) + AND (`resource_tags`.`resource_type` = 'UserVm')))) + LEFT JOIN `async_job` ON (((`async_job`.`instance_id` = `vm_instance`.`id`) + AND (`async_job`.`instance_type` = 'VirtualMachine') + AND (`async_job`.`job_status` = 0)))) + LEFT JOIN `affinity_group_vm_map` ON ((`vm_instance`.`id` = `affinity_group_vm_map`.`instance_id`))) + LEFT JOIN `affinity_group` ON ((`affinity_group_vm_map`.`affinity_group_id` = `affinity_group`.`id`))) + LEFT JOIN `user_vm_details` `custom_cpu` ON (((`custom_cpu`.`vm_id` = `vm_instance`.`id`) + AND (`custom_cpu`.`name` = 'CpuNumber')))) + LEFT JOIN `user_vm_details` `custom_speed` ON (((`custom_speed`.`vm_id` = `vm_instance`.`id`) + AND (`custom_speed`.`name` = 'CpuSpeed')))) + LEFT JOIN `user_vm_details` `custom_ram_size` ON (((`custom_ram_size`.`vm_id` = `vm_instance`.`id`) + AND (`custom_ram_size`.`name` = 'memory')))); + +DROP VIEW IF EXISTS `cloud`.`domain_router_view`; +CREATE VIEW `cloud`.`domain_router_view` AS + select + vm_instance.id id, + vm_instance.name name, + 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, + vm_instance.uuid uuid, + vm_instance.created created, + vm_instance.state state, + vm_instance.removed removed, + vm_instance.pod_id pod_id, + vm_instance.instance_name instance_name, + host_pod_ref.uuid pod_uuid, + data_center.id data_center_id, + data_center.uuid data_center_uuid, + data_center.name data_center_name, + data_center.networktype data_center_type, + data_center.dns1 dns1, + data_center.dns2 dns2, + data_center.ip6_dns1 ip6_dns1, + data_center.ip6_dns2 ip6_dns2, + host.id host_id, + host.uuid host_uuid, + host.name host_name, + host.hypervisor_type, + host.cluster_id cluster_id, + vm_template.id template_id, + vm_template.uuid template_uuid, + service_offering.id service_offering_id, + service_offering.uuid service_offering_uuid, + service_offering.name service_offering_name, + nics.id nic_id, + nics.uuid nic_uuid, + nics.network_id network_id, + nics.ip4_address ip_address, + nics.ip6_address ip6_address, + nics.ip6_gateway ip6_gateway, + nics.ip6_cidr ip6_cidr, + nics.default_nic is_default_nic, + nics.gateway gateway, + nics.netmask netmask, + nics.mac_address mac_address, + nics.broadcast_uri broadcast_uri, + nics.isolation_uri isolation_uri, + vpc.id vpc_id, + vpc.uuid vpc_uuid, + vpc.name vpc_name, + networks.uuid network_uuid, + networks.name network_name, + networks.network_domain network_domain, + networks.traffic_type traffic_type, + networks.guest_type guest_type, + async_job.id job_id, + async_job.uuid job_uuid, + async_job.job_status job_status, + async_job.account_id job_account_id, + domain_router.template_version template_version, + domain_router.scripts_version scripts_version, + domain_router.is_redundant_router is_redundant_router, + domain_router.redundant_state redundant_state, + domain_router.stop_pending stop_pending, + domain_router.role role + from + `cloud`.`domain_router` + inner join + `cloud`.`vm_instance` ON vm_instance.id = domain_router.id + inner join + `cloud`.`account` ON vm_instance.account_id = account.id + inner join + `cloud`.`domain` ON vm_instance.domain_id = domain.id + left join + `cloud`.`host_pod_ref` ON vm_instance.pod_id = host_pod_ref.id + left join + `cloud`.`projects` ON projects.project_account_id = account.id + left join + `cloud`.`data_center` ON vm_instance.data_center_id = data_center.id + left join + `cloud`.`host` ON vm_instance.host_id = host.id + left join + `cloud`.`vm_template` ON vm_instance.vm_template_id = vm_template.id + left join + `cloud`.`service_offering` ON vm_instance.service_offering_id = service_offering.id + left join + `cloud`.`nics` ON vm_instance.id = nics.instance_id and nics.removed is null + left join + `cloud`.`networks` ON nics.network_id = networks.id + left join + `cloud`.`vpc` ON domain_router.vpc_id = vpc.id and vpc.removed is null + left join + `cloud`.`async_job` ON async_job.instance_id = vm_instance.id + and async_job.instance_type = 'DomainRouter' + and async_job.job_status = 0; \ No newline at end of file diff --git a/engine/schema/src/test/java/com/cloud/host/HostVOTest.java b/engine/schema/src/test/java/com/cloud/host/HostVOTest.java index 7b70abaf32eb..291fd30c20da 100755 --- a/engine/schema/src/test/java/com/cloud/host/HostVOTest.java +++ b/engine/schema/src/test/java/com/cloud/host/HostVOTest.java @@ -17,7 +17,6 @@ package com.cloud.host; import com.cloud.service.ServiceOfferingVO; -import com.cloud.storage.Storage.ProvisioningType; import com.cloud.vm.VirtualMachine; import java.util.Arrays; import static org.junit.Assert.assertFalse; @@ -32,7 +31,8 @@ public class HostVOTest { @Before public void setUp() throws Exception { host = new HostVO(); - offering = new ServiceOfferingVO("TestSO", 0, 0, 0, 0, 0, false, "TestSO", ProvisioningType.THIN, true, true,"",false,VirtualMachine.Type.User,false); + offering = new ServiceOfferingVO("TestSO", 0, 0, 0, 0, 0, + false, "TestSO", false,VirtualMachine.Type.User,false); } @Test diff --git a/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java b/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java index 43637103912b..b48ae6d22dc0 100644 --- a/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java +++ b/engine/storage/datamotion/src/main/java/org/apache/cloudstack/storage/motion/StorageSystemDataMotionStrategy.java @@ -740,7 +740,7 @@ private void handleVolumeMigrationForXenServer(VolumeInfo srcVolumeInfo, VolumeI StoragePool destPool = (StoragePool)dataStoreMgr.getDataStore(destVolumeInfo.getDataStore().getId(), DataStoreRole.Primary); MigrateVolumeCommand command = new MigrateVolumeCommand(srcVolumeInfo.getId(), srcVolumeInfo.getPath(), destPool, srcVolumeInfo.getAttachedVmName(), - srcVolumeInfo.getVolumeType(), waitInterval); + srcVolumeInfo.getVolumeType(), waitInterval, null); Map details = new HashMap<>(); diff --git a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/DefaultVMSnapshotStrategy.java b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/DefaultVMSnapshotStrategy.java index d60e623661dc..b7d565f3df2f 100644 --- a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/DefaultVMSnapshotStrategy.java +++ b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/DefaultVMSnapshotStrategy.java @@ -338,7 +338,7 @@ private void publishUsageEvent(String type, VMSnapshot vmSnapshot, UserVm userVm Long offeringId = null; if (diskOfferingId != null) { DiskOfferingVO offering = diskOfferingDao.findById(diskOfferingId); - if (offering != null && (offering.getType() == DiskOfferingVO.Type.Disk)) { + if (offering != null && !offering.isComputeOnly()) { offeringId = offering.getId(); } } diff --git a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/ScaleIOVMSnapshotStrategy.java b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/ScaleIOVMSnapshotStrategy.java index a124a4adf2b1..34ea4908b7e3 100644 --- a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/ScaleIOVMSnapshotStrategy.java +++ b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/ScaleIOVMSnapshotStrategy.java @@ -456,7 +456,7 @@ private void publishUsageEvent(String type, VMSnapshot vmSnapshot, UserVm userVm Long offeringId = null; if (diskOfferingId != null) { DiskOfferingVO offering = diskOfferingDao.findById(diskOfferingId); - if (offering != null && (offering.getType() == DiskOfferingVO.Type.Disk)) { + if (offering != null && !offering.isComputeOnly()) { offeringId = offering.getId(); } } diff --git a/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java b/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java index af206a7378ec..41a832555842 100644 --- a/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java +++ b/engine/storage/src/main/java/org/apache/cloudstack/storage/allocator/AbstractStoragePoolAllocator.java @@ -33,6 +33,7 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.log4j.Logger; +import com.cloud.utils.Pair; import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; @@ -265,8 +266,8 @@ protected boolean filter(ExcludeList avoid, StoragePool pool, DiskProfile dskCh, } // check capacity - List requestVolumes = new ArrayList<>(); - requestVolumes.add(volume); + List> requestVolumeDiskProfilePairs = new ArrayList<>(); + requestVolumeDiskProfilePairs.add(new Pair<>(volume, dskCh)); if (dskCh.getHypervisorType() == HypervisorType.VMware) { // Skip the parent datastore cluster, consider only child storage pools in it if (pool.getPoolType() == Storage.StoragePoolType.DatastoreCluster && storageMgr.isStoragePoolDatastoreClusterParent(pool)) { @@ -281,7 +282,7 @@ protected boolean filter(ExcludeList avoid, StoragePool pool, DiskProfile dskCh, } try { - boolean isStoragePoolStoragepolicyComplaince = storageMgr.isStoragePoolCompliantWithStoragePolicy(requestVolumes, pool); + boolean isStoragePoolStoragepolicyComplaince = storageMgr.isStoragePoolCompliantWithStoragePolicy(requestVolumeDiskProfilePairs, pool); if (!isStoragePoolStoragepolicyComplaince) { return false; } @@ -290,7 +291,7 @@ protected boolean filter(ExcludeList avoid, StoragePool pool, DiskProfile dskCh, return false; } } - return storageMgr.storagePoolHasEnoughIops(requestVolumes, pool) && storageMgr.storagePoolHasEnoughSpace(requestVolumes, pool, plan.getClusterId()); + return storageMgr.storagePoolHasEnoughIops(requestVolumeDiskProfilePairs, pool) && storageMgr.storagePoolHasEnoughSpace(requestVolumeDiskProfilePairs, pool, plan.getClusterId()); } private boolean checkDiskProvisioningSupport(DiskProfile dskCh, StoragePool pool) { diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/vo/ServiceOfferingVO.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/vo/ServiceOfferingVO.java index ab6b9311c69d..0602f2dc7829 100644 --- a/framework/quota/src/main/java/org/apache/cloudstack/quota/vo/ServiceOfferingVO.java +++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/vo/ServiceOfferingVO.java @@ -16,26 +16,66 @@ //under the License. package org.apache.cloudstack.quota.vo; +import java.util.Date; import java.util.HashMap; import java.util.Map; import javax.persistence.Column; -import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; -import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.Table; +import javax.persistence.Id; +import javax.persistence.GenerationType; +import javax.persistence.GeneratedValue; +import javax.persistence.TemporalType; +import javax.persistence.Temporal; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; 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; +import com.cloud.utils.db.GenericDao; @Entity @Table(name = "service_offering") -@DiscriminatorValue(value = "Service") -@PrimaryKeyJoinColumn(name = "id") -public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering { +public class ServiceOfferingVO implements ServiceOffering { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + long id; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "name") + private String name = null; + + @Column(name = "unique_name") + private String uniqueName; + + @Column(name = "display_text", length = 4096) + private String displayText = null; + + @Column(name = "customized") + private boolean customized; + + @Column(name = GenericDao.REMOVED_COLUMN) + @Temporal(TemporalType.TIMESTAMP) + private Date removed; + + @Column(name = GenericDao.CREATED_COLUMN) + private Date created; + + @Enumerated(EnumType.STRING) + @Column(name = "state") + ServiceOffering.State state = ServiceOffering.State.Active; + + @Column(name = "disk_offering_id") + private Long diskOfferingId; + + @Column(name = "disk_offering_strictness") + private boolean diskOfferingStrictness = false; + @Column(name = "cpu") private Integer cpu; @@ -78,6 +118,9 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering @Column(name = "dynamic_scaling_enabled") private boolean dynamicScalingEnabled; + @Column(name = "system_use") + private boolean systemUse; + @Transient Map details = new HashMap(); @@ -88,56 +131,12 @@ protected ServiceOfferingVO() { super(); } - public ServiceOfferingVO(String name, Integer cpu, Integer ramSize, Integer speed, Integer rateMbps, Integer multicastRateMbps, boolean offerHA, String displayText, - 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; - this.rateMbps = rateMbps; - this.multicastRateMbps = multicastRateMbps; - this.offerHA = offerHA; - limitCpuUse = false; - volatileVm = false; - this.defaultUse = defaultUse; - this.vmType = vmType == null ? null : vmType.toString().toLowerCase(); - } - - public ServiceOfferingVO(String name, Integer cpu, Integer ramSize, Integer speed, Integer rateMbps, Integer multicastRateMbps, boolean offerHA, boolean limitCpuUse, - 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); - this.cpu = cpu; - this.ramSize = ramSize; - this.speed = speed; - this.rateMbps = rateMbps; - this.multicastRateMbps = multicastRateMbps; - this.offerHA = offerHA; - this.limitCpuUse = limitCpuUse; - this.volatileVm = volatileVm; - this.vmType = vmType == null ? null : vmType.toString().toLowerCase(); - } - - public ServiceOfferingVO(String name, Integer cpu, Integer ramSize, Integer speed, Integer rateMbps, Integer multicastRateMbps, boolean offerHA, 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, ramSize, speed, rateMbps, multicastRateMbps, offerHA, limitResourceUse, volatileVm, displayText, provisioningType, useLocalStorage, recreatable, tags, - systemUse, vmType, domainId); - this.hostTag = hostTag; - } - - public ServiceOfferingVO(String name, Integer cpu, Integer ramSize, Integer speed, Integer rateMbps, Integer multicastRateMbps, boolean offerHA, 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, ramSize, speed, rateMbps, multicastRateMbps, offerHA, limitResourceUse, volatileVm, displayText, provisioningType, useLocalStorage, recreatable, tags, - systemUse, vmType, domainId, hostTag); - this.deploymentPlanner = deploymentPlanner; - } - public ServiceOfferingVO(ServiceOfferingVO offering) { - super(offering.getId(), offering.getName(), offering.getDisplayText(), offering.getProvisioningType(), false, offering.getTags(), offering.isRecreatable(), - offering.isUseLocalStorage(), offering.isSystemUse(), true, offering.isCustomizedIops() == null ? false : offering.isCustomizedIops(), - offering.getMinIops(), offering.getMaxIops()); + id = offering.getId(); + diskOfferingId = offering.getDiskOfferingId(); + name = offering.getName(); + displayText = offering.getDisplayText(); + customized = true; cpu = offering.getCpu(); ramSize = offering.getRamSize(); speed = offering.getSpeed(); @@ -148,6 +147,8 @@ public ServiceOfferingVO(ServiceOfferingVO offering) { volatileVm = offering.isVolatileVm(); hostTag = offering.getHostTag(); vmType = offering.getSystemVmType(); + systemUse = offering.isSystemUse(); + dynamicScalingEnabled = offering.isDynamicScalingEnabled(); } @Override @@ -173,17 +174,6 @@ public boolean getDefaultUse() { return defaultUse; } - @Override - @Transient - public String[] getTagsArray() { - String tags = getTags(); - if (tags == null || tags.length() == 0) { - return new String[0]; - } - - return tags.split(","); - } - @Override public Integer getCpu() { return cpu; @@ -288,6 +278,108 @@ public void setDynamicFlag(boolean isdynamic) { isDynamic = isdynamic; } + @Override + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean isSystemUse() { + return systemUse; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public String getUniqueName() { + return uniqueName; + } + + @Override + public void setUniqueName(String uniqueName) { + this.uniqueName = uniqueName; + } + + @Override + public String getDisplayText() { + return displayText; + } + + @Override + public void setDisplayText(String displayText) { + this.displayText = displayText; + } + + @Override + public boolean isCustomized() { + return customized; + } + + @Override + public void setCustomized(boolean customized) { + this.customized = customized; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } + + @Override + public Date getRemoved() { + return removed; + } + + @Override + public Date getCreated() { + return created; + } + + @Override + public ServiceOffering.State getState() { + return state; + } + + @Override + public void setState(ServiceOffering.State state) { + this.state = state; + } + + @Override + public Long getDiskOfferingId() { + return diskOfferingId; + } + + @Override + public Boolean getDiskOfferingStrictness() { + return diskOfferingStrictness; + } + + @Override + public void setDiskOfferingStrictness(boolean diskOfferingStrictness) { + + } + + public void setDiskOfferingId(Long diskOfferingId) { + this.diskOfferingId = diskOfferingId; + } + + @Override + public String getUuid() { + return uuid; + } + @Override public boolean isDynamicScalingEnabled() { return dynamicScalingEnabled; diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java index 4106cc05799b..1f6545881795 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java @@ -4836,7 +4836,7 @@ public void testResizeVolumeCommand() { final boolean shrinkOk = true; final String vmInstance = "Test"; - final ResizeVolumeCommand command = new ResizeVolumeCommand(path, pool, currentSize, newSize, shrinkOk, vmInstance); + final ResizeVolumeCommand command = new ResizeVolumeCommand(path, pool, currentSize, newSize, shrinkOk, vmInstance, null); final KVMStoragePoolManager storagePoolMgr = Mockito.mock(KVMStoragePoolManager.class); final KVMStoragePool storagePool = Mockito.mock(KVMStoragePool.class); @@ -4889,7 +4889,7 @@ public void testResizeVolumeCommandLinstorNotifyOnly() { final boolean shrinkOk = false; final String vmInstance = "Test"; - final ResizeVolumeCommand command = new ResizeVolumeCommand(path, pool, currentSize, newSize, shrinkOk, vmInstance); + final ResizeVolumeCommand command = new ResizeVolumeCommand(path, pool, currentSize, newSize, shrinkOk, vmInstance, null); final KVMStoragePoolManager storagePoolMgr = Mockito.mock(KVMStoragePoolManager.class); final KVMStoragePool storagePool = Mockito.mock(KVMStoragePool.class); @@ -4929,7 +4929,7 @@ public void testResizeVolumeCommandSameSize() { final boolean shrinkOk = false; final String vmInstance = "Test"; - final ResizeVolumeCommand command = new ResizeVolumeCommand(path, pool, currentSize, newSize, shrinkOk, vmInstance); + final ResizeVolumeCommand command = new ResizeVolumeCommand(path, pool, currentSize, newSize, shrinkOk, vmInstance, null); final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance(); assertNotNull(wrapper); @@ -4947,7 +4947,7 @@ public void testResizeVolumeCommandShrink() { final boolean shrinkOk = true; final String vmInstance = "Test"; - final ResizeVolumeCommand command = new ResizeVolumeCommand(path, pool, currentSize, newSize, shrinkOk, vmInstance); + final ResizeVolumeCommand command = new ResizeVolumeCommand(path, pool, currentSize, newSize, shrinkOk, vmInstance, null); final KVMStoragePoolManager storagePoolMgr = Mockito.mock(KVMStoragePoolManager.class); final KVMStoragePool storagePool = Mockito.mock(KVMStoragePool.class); @@ -4976,7 +4976,7 @@ public void testResizeVolumeCommandException() { final boolean shrinkOk = false; final String vmInstance = "Test"; - final ResizeVolumeCommand command = new ResizeVolumeCommand(path, pool, currentSize, newSize, shrinkOk, vmInstance); + final ResizeVolumeCommand command = new ResizeVolumeCommand(path, pool, currentSize, newSize, shrinkOk, vmInstance, null); final KVMStoragePoolManager storagePoolMgr = Mockito.mock(KVMStoragePoolManager.class); final KVMStoragePool storagePool = Mockito.mock(KVMStoragePool.class); @@ -5024,7 +5024,7 @@ public void testResizeVolumeCommandException2() { final boolean shrinkOk = false; final String vmInstance = "Test"; - final ResizeVolumeCommand command = new ResizeVolumeCommand(path, pool, currentSize, newSize, shrinkOk, vmInstance); + final ResizeVolumeCommand command = new ResizeVolumeCommand(path, pool, currentSize, newSize, shrinkOk, vmInstance, null); final KVMStoragePoolManager storagePoolMgr = Mockito.mock(KVMStoragePoolManager.class); final KVMStoragePool storagePool = Mockito.mock(KVMStoragePool.class); diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java index fa5c6423687b..a5e4140357ba 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java @@ -454,8 +454,13 @@ private Long getImportingVMGuestOs(VirtualMachineConfigSummary configSummary) { */ private ServiceOfferingVO createServiceOfferingForVMImporting(Integer cpus, Integer memory, Integer maxCpuUsage) { String name = "Imported-" + cpus + "-" + memory; - ServiceOfferingVO vo = new ServiceOfferingVO(name, cpus, memory, maxCpuUsage, null, null, false, name, Storage.ProvisioningType.THIN, false, false, null, false, Type.User, + + DiskOfferingVO diskOfferingVO = new DiskOfferingVO(name, name, Storage.ProvisioningType.THIN, false, null, false, false, true); + diskOfferingVO = diskOfferingDao.persistDefaultDiskOffering(diskOfferingVO); + + ServiceOfferingVO vo = new ServiceOfferingVO(name, cpus, memory, maxCpuUsage, null, null, false, name, false, Type.User, false); + vo.setDiskOfferingId(diskOfferingVO.getId()); return serviceOfferingDao.persist(vo); } @@ -665,7 +670,7 @@ private VMInstanceVO getVM(String vmInternalName, long templateId, long guestOsI vmInternalName, id, vmInternalName, templateId, guestOsId, serviceOfferingId)); UserVmVO vmInstanceVO = new UserVmVO(id, vmInternalName, vmInternalName, templateId, HypervisorType.VMware, guestOsId, false, false, domainId, accountId, userId, - serviceOfferingId, null, vmInternalName, null); + serviceOfferingId, null, vmInternalName); vmInstanceVO.setDataCenterId(zoneId); return userVmDao.persist(vmInstanceVO); } diff --git a/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixRequestWrapperTest.java b/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixRequestWrapperTest.java index 219c76a26f71..9d18e73afb40 100755 --- a/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixRequestWrapperTest.java +++ b/plugins/hypervisors/xenserver/src/test/java/com/cloud/hypervisor/xenserver/resource/wrapper/xenbase/CitrixRequestWrapperTest.java @@ -430,7 +430,7 @@ public void testDeleteStoragePoolCommand() { public void testResizeVolumeCommand() { final StorageFilerTO pool = Mockito.mock(StorageFilerTO.class); - final ResizeVolumeCommand resizeCommand = new ResizeVolumeCommand("Test", pool, 1l, 3l, false, "Tests-1"); + final ResizeVolumeCommand resizeCommand = new ResizeVolumeCommand("Test", pool, 1l, 3l, false, "Tests-1", null); final CitrixRequestWrapper wrapper = CitrixRequestWrapper.getInstance(); assertNotNull(wrapper); diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java index 1ffc62efb1e6..20be84dda448 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java @@ -380,7 +380,7 @@ protected UserVm createKubernetesNode(String joinIp) throws ManagementServerExce nodeVm = userVmService.createAdvancedVirtualMachine(zone, serviceOffering, clusterTemplate, networkIds, owner, hostName, hostName, null, null, null, Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST, base64UserData, kubernetesCluster.getKeyPair(), - null, addrs, null, null, null, customParameterMap, null, null, null, null, true, UserVmManager.CKS_NODE); + null, addrs, null, null, null, customParameterMap, null, null, null, null, true, UserVmManager.CKS_NODE, null); if (LOGGER.isInfoEnabled()) { LOGGER.info(String.format("Created node VM : %s, %s in the Kubernetes cluster : %s", hostName, nodeVm.getUuid(), kubernetesCluster.getName())); } diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java index dc34d37c38fe..9bed248f7b52 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterStartWorker.java @@ -209,7 +209,7 @@ private UserVm createKubernetesControlNode(final Network network, String serverI controlVm = userVmService.createAdvancedVirtualMachine(zone, serviceOffering, clusterTemplate, networkIds, owner, hostName, hostName, null, null, null, Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST, base64UserData, kubernetesCluster.getKeyPair(), - requestedIps, addrs, null, null, null, customParameterMap, null, null, null, null, true, UserVmManager.CKS_NODE); + requestedIps, addrs, null, null, null, customParameterMap, null, null, null, null, true, UserVmManager.CKS_NODE, null); if (LOGGER.isInfoEnabled()) { LOGGER.info(String.format("Created control VM ID: %s, %s in the Kubernetes cluster : %s", controlVm.getUuid(), hostName, kubernetesCluster.getName())); } @@ -264,7 +264,7 @@ private UserVm createKubernetesAdditionalControlNode(final String joinIp, final additionalControlVm = userVmService.createAdvancedVirtualMachine(zone, serviceOffering, clusterTemplate, networkIds, owner, hostName, hostName, null, null, null, Hypervisor.HypervisorType.None, BaseCmd.HTTPMethod.POST, base64UserData, kubernetesCluster.getKeyPair(), - null, addrs, null, null, null, customParameterMap, null, null, null, null, true, UserVmManager.CKS_NODE); + null, addrs, null, null, null, customParameterMap, null, null, null, null, true, UserVmManager.CKS_NODE, null); if (LOGGER.isInfoEnabled()) { LOGGER.info(String.format("Created control VM ID : %s, %s in the Kubernetes cluster : %s", additionalControlVm.getUuid(), hostName, kubernetesCluster.getName())); } diff --git a/plugins/network-elements/internal-loadbalancer/src/test/java/org/apache/cloudstack/internallbvmmgr/InternalLBVMManagerTest.java b/plugins/network-elements/internal-loadbalancer/src/test/java/org/apache/cloudstack/internallbvmmgr/InternalLBVMManagerTest.java index 103fcd9cf31a..62fd2dd2845d 100644 --- a/plugins/network-elements/internal-loadbalancer/src/test/java/org/apache/cloudstack/internallbvmmgr/InternalLBVMManagerTest.java +++ b/plugins/network-elements/internal-loadbalancer/src/test/java/org/apache/cloudstack/internallbvmmgr/InternalLBVMManagerTest.java @@ -58,7 +58,6 @@ 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.storage.Storage.ProvisioningType; import com.cloud.user.AccountManager; import com.cloud.user.AccountVO; @@ -121,7 +120,7 @@ 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", Storage.ProvisioningType.THIN, false, false, null, false, VirtualMachine.Type.InternalLoadBalancerVm, false); + 1, 1, 1, false, "alena", false, VirtualMachine.Type.InternalLoadBalancerVm, false); off = setId(off, 1); List list = new ArrayList(); list.add(off); @@ -341,7 +340,7 @@ private static ServiceOfferingVO setId(final ServiceOfferingVO vo, final long id final ServiceOfferingVO voToReturn = vo; final Class c = voToReturn.getClass(); try { - final Field f = c.getSuperclass().getDeclaredField("id"); + final Field f = c.getDeclaredField("id"); f.setAccessible(true); f.setLong(voToReturn, id); } catch (final NoSuchFieldException ex) { diff --git a/plugins/network-elements/internal-loadbalancer/src/test/java/org/apache/cloudstack/internallbvmmgr/InternalLBVMServiceTest.java b/plugins/network-elements/internal-loadbalancer/src/test/java/org/apache/cloudstack/internallbvmmgr/InternalLBVMServiceTest.java index 898b7e5121be..9141190df1f9 100644 --- a/plugins/network-elements/internal-loadbalancer/src/test/java/org/apache/cloudstack/internallbvmmgr/InternalLBVMServiceTest.java +++ b/plugins/network-elements/internal-loadbalancer/src/test/java/org/apache/cloudstack/internallbvmmgr/InternalLBVMServiceTest.java @@ -45,7 +45,6 @@ import com.cloud.network.router.VirtualRouter.Role; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; -import com.cloud.storage.Storage; import com.cloud.storage.Storage.ProvisioningType; import com.cloud.user.AccountManager; import com.cloud.user.AccountVO; @@ -93,7 +92,7 @@ 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", Storage.ProvisioningType.THIN, false, false, null, false, VirtualMachine.Type.InternalLoadBalancerVm, false); + 1, 1, 1, false, "alena", false, VirtualMachine.Type.InternalLoadBalancerVm, false); off = setId(off, 1); List list = new ArrayList(); list.add(off); @@ -251,7 +250,7 @@ private static ServiceOfferingVO setId(final ServiceOfferingVO vo, final long id final ServiceOfferingVO voToReturn = vo; final Class c = voToReturn.getClass(); try { - final Field f = c.getSuperclass().getDeclaredField("id"); + final Field f = c.getDeclaredField("id"); f.setAccessible(true); f.setLong(voToReturn, id); } catch (final NoSuchFieldException ex) { diff --git a/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ServiceVirtualMachine.java b/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ServiceVirtualMachine.java index 676afc9ae8aa..b9a446b69a04 100644 --- a/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ServiceVirtualMachine.java +++ b/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ServiceVirtualMachine.java @@ -23,6 +23,6 @@ public class ServiceVirtualMachine extends UserVmVO { public ServiceVirtualMachine(long id, String instanceName, String name, long templateId, long serviceOfferingId, HypervisorType hypervisorType, long guestOSId, long dataCenterId, long domainId, long accountId, long userId, boolean haEnabled) { - super(id, instanceName, name, templateId, hypervisorType, guestOSId, false, false, domainId, accountId, userId, serviceOfferingId, null, name, null); + super(id, instanceName, name, templateId, hypervisorType, guestOSId, false, false, domainId, accountId, userId, serviceOfferingId, null, name); } } diff --git a/plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/ManagementServerMock.java b/plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/ManagementServerMock.java index 64a036a2c624..859330071e9f 100644 --- a/plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/ManagementServerMock.java +++ b/plugins/network-elements/juniper-contrail/src/test/java/org/apache/cloudstack/network/contrail/management/ManagementServerMock.java @@ -240,7 +240,7 @@ public Object answer(InvocationOnMock invocation) { long id = _userVmDao.getNextInSequence(Long.class, "id"); UserVmVO vm = new UserVmVO(id, name, name, tmpl.getId(), HypervisorType.XenServer, tmpl.getGuestOSId(), false, false, _zone.getDomainId(), Account.ACCOUNT_ID_SYSTEM, - 1, small.getId(), null, name, null); + 1, small.getId(), null, name); vm.setState(com.cloud.vm.VirtualMachine.State.Running); vm.setHostId(_hostId); vm.setDataCenterId(network.getDataCenterId()); diff --git a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java index 19cf297b5ad4..320860380cee 100644 --- a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java +++ b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/driver/LinstorPrimaryDataStoreDriverImpl.java @@ -617,7 +617,7 @@ private CreateCmdResult notifyResize( ResizeVolumeCommand resizeCmd = new ResizeVolumeCommand(vol.getPath(), new StorageFilerTO(pool), oldSize, resizeParameter.newSize, resizeParameter.shrinkOk, - resizeParameter.instanceName); + resizeParameter.instanceName, null); CreateCmdResult result = new CreateCmdResult(null, null); try { ResizeVolumeAnswer answer = (ResizeVolumeAnswer) _storageMgr.sendToPool(pool, resizeParameter.hosts, resizeCmd); diff --git a/server/src/main/java/com/cloud/api/ApiDBUtils.java b/server/src/main/java/com/cloud/api/ApiDBUtils.java index 7f4199348c94..e6413a61ba4b 100644 --- a/server/src/main/java/com/cloud/api/ApiDBUtils.java +++ b/server/src/main/java/com/cloud/api/ApiDBUtils.java @@ -1043,14 +1043,26 @@ public static String findClusterDetails(long clusterId, String name) { return null; } + public static DiskOfferingVO findComputeOnlyDiskOfferingById(Long diskOfferingId) { + DiskOfferingVO off = s_diskOfferingDao.findByIdIncludingRemoved(diskOfferingId); + if (off.isComputeOnly()) { + return off; + } + return null; + } + public static DiskOfferingVO findDiskOfferingById(Long diskOfferingId) { DiskOfferingVO off = s_diskOfferingDao.findByIdIncludingRemoved(diskOfferingId); - if (off.getType() == DiskOfferingVO.Type.Disk) { + if (!off.isComputeOnly()) { return off; } return null; } + public static ServiceOfferingVO findServiceOfferingByComputeOnlyDiskOffering(Long diskOfferingId) { + ServiceOfferingVO off = s_serviceOfferingDao.findServiceOfferingByComputeOnlyDiskOffering(diskOfferingId); + return off; + } public static DomainVO findDomainById(Long domainId) { return s_domainDao.findByIdIncludingRemoved(domainId); } diff --git a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java index 6f5b272dc203..57ac7d033943 100644 --- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.ListIterator; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -30,6 +31,7 @@ import javax.inject.Inject; +import com.cloud.storage.VolumeApiServiceImpl; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.affinity.AffinityGroupDomainMapVO; import org.apache.cloudstack.affinity.AffinityGroupResponse; @@ -124,6 +126,7 @@ import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao; +import org.apache.commons.collections.CollectionUtils; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -194,6 +197,7 @@ import com.cloud.network.security.SecurityGroupVMMapVO; import com.cloud.network.security.dao.SecurityGroupVMMapDao; import com.cloud.org.Grouping; +import com.cloud.offering.DiskOffering; import com.cloud.projects.Project; import com.cloud.projects.Project.ListProjectResourcesCriteria; import com.cloud.projects.ProjectInvitation; @@ -213,17 +217,19 @@ import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.service.dao.ServiceOfferingDetailsDao; import com.cloud.storage.DataStoreRole; -import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.ScopeType; import com.cloud.storage.Storage; import com.cloud.storage.Storage.ImageFormat; import com.cloud.storage.Storage.TemplateType; import com.cloud.storage.StoragePoolTagVO; +import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.VMTemplateVO; import com.cloud.storage.Volume; import com.cloud.storage.dao.StoragePoolTagsDao; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VMTemplateDetailsDao; +import com.cloud.storage.dao.DiskOfferingDao; +import com.cloud.storage.dao.VolumeDao; import com.cloud.tags.ResourceTagVO; import com.cloud.tags.dao.ResourceTagDao; import com.cloud.template.VirtualMachineTemplate.State; @@ -366,6 +372,9 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q @Inject private ServiceOfferingDetailsDao _srvOfferingDetailsDao; + @Inject + private DiskOfferingDao _diskOfferingDao; + @Inject private DataCenterJoinDao _dcJoinDao; @@ -445,6 +454,8 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q @Inject private VirtualMachineManager virtualMachineManager; + @Inject + private VolumeDao volumeDao; @Inject private ResourceIconDao resourceIconDao; @@ -2077,7 +2088,10 @@ private Pair, Integer> searchForVolumesInternal(ListVolumesCm sb.and("display", sb.entity().isDisplayVolume(), SearchCriteria.Op.EQ); sb.and("state", sb.entity().getState(), SearchCriteria.Op.EQ); sb.and("stateNEQ", sb.entity().getState(), SearchCriteria.Op.NEQ); - sb.and("systemUse", sb.entity().isSystemUse(), SearchCriteria.Op.NEQ); + sb.and().op("systemUse", sb.entity().isSystemUse(), SearchCriteria.Op.NEQ); + sb.or("nulltype", sb.entity().isSystemUse(), SearchCriteria.Op.NULL); + sb.cp(); + // display UserVM volumes only sb.and().op("type", sb.entity().getVmType(), SearchCriteria.Op.NIN); sb.or("nulltype", sb.entity().getVmType(), SearchCriteria.Op.NULL); @@ -2882,7 +2896,7 @@ private Pair, Integer> searchForDiskOfferingsInternal(L Filter searchFilter = new Filter(DiskOfferingJoinVO.class, "sortKey", SortKeyAscending.value(), cmd.getStartIndex(), cmd.getPageSizeVal()); searchFilter.addOrderBy(DiskOfferingJoinVO.class, "id", true); SearchCriteria sc = _diskOfferingJoinDao.createSearchCriteria(); - sc.addAnd("type", Op.EQ, DiskOfferingVO.Type.Disk); + sc.addAnd("computeOnly", Op.EQ, false); Account account = CallContext.current().getCallingAccount(); Object name = cmd.getDiskOfferingName(); @@ -2892,6 +2906,8 @@ private Pair, Integer> searchForDiskOfferingsInternal(L Boolean isRootAdmin = _accountMgr.isRootAdmin(account.getAccountId()); Boolean isRecursive = cmd.isRecursive(); Long zoneId = cmd.getZoneId(); + Long volumeId = cmd.getVolumeId(); + Long storagePoolId = cmd.getStoragePoolId(); // Keeping this logic consistent with domain specific zones // if a domainId is provided, we just return the disk offering // associated with this domain @@ -2922,6 +2938,10 @@ private Pair, Integer> searchForDiskOfferingsInternal(L } + if (volumeId != null && storagePoolId != null) { + throw new InvalidParameterValueException("Both volume ID and storage pool ID are not allowed at the same time"); + } + if (keyword != null) { SearchCriteria ssc = _diskOfferingJoinDao.createSearchCriteria(); ssc.addOr("displayText", SearchCriteria.Op.LIKE, "%" + keyword + "%"); @@ -2948,6 +2968,23 @@ private Pair, Integer> searchForDiskOfferingsInternal(L sc.addAnd("zoneId", SearchCriteria.Op.SC, zoneSC); } + DiskOffering currentDiskOffering = null; + if (volumeId != null) { + Volume volume = volumeDao.findById(volumeId); + if (volume == null) { + throw new InvalidParameterValueException(String.format("Unable to find a volume with specified id %s", volumeId)); + } + currentDiskOffering = _diskOfferingDao.findByIdIncludingRemoved(volume.getDiskOfferingId()); + if (!currentDiskOffering.isComputeOnly() && currentDiskOffering.getDiskSizeStrictness()) { + SearchCriteria ssc = _diskOfferingJoinDao.createSearchCriteria(); + ssc.addOr("diskSize", Op.EQ, volume.getSize()); + ssc.addOr("customized", SearchCriteria.Op.EQ, true); + sc.addAnd("diskSizeOrCustomized", SearchCriteria.Op.SC, ssc); + } + sc.addAnd("id", SearchCriteria.Op.NEQ, currentDiskOffering.getId()); + sc.addAnd("diskSizeStrictness", Op.EQ, currentDiskOffering.getDiskSizeStrictness()); + } + // Filter offerings that are not associated with caller's domain // Fetch the offering ids from the details table since theres no smart way to filter them in the join ... yet! Account caller = CallContext.current().getCallingAccount(); @@ -2971,6 +3008,28 @@ private Pair, Integer> searchForDiskOfferingsInternal(L } Pair, Integer> result = _diskOfferingJoinDao.searchAndCount(sc, searchFilter); + String[] requiredTagsArray = new String[0]; + if (CollectionUtils.isNotEmpty(result.first()) && VolumeApiServiceImpl.MatchStoragePoolTagsWithDiskOffering.valueIn(zoneId)) { + if (volumeId != null) { + Volume volume = volumeDao.findById(volumeId); + currentDiskOffering = _diskOfferingDao.findByIdIncludingRemoved(volume.getDiskOfferingId()); + requiredTagsArray = currentDiskOffering.getTagsArray(); + } else if (storagePoolId != null) { + requiredTagsArray = _storageTagDao.getStoragePoolTags(storagePoolId).toArray(new String[0]); + } + } + if (requiredTagsArray.length != 0) { + ListIterator iteratorForTagsChecking = result.first().listIterator(); + while (iteratorForTagsChecking.hasNext()) { + DiskOfferingJoinVO offering = iteratorForTagsChecking.next(); + String offeringTags = offering.getTags(); + String[] offeringTagsArray = (offeringTags == null || offeringTags.isEmpty()) ? new String[0] : offeringTags.split(","); + if (!CollectionUtils.isSubCollection(Arrays.asList(requiredTagsArray), Arrays.asList(offeringTagsArray))) { + iteratorForTagsChecking.remove(); + } + } + } + return new Pair<>(result.first(), result.second()); } @@ -3054,6 +3113,13 @@ private Pair, Integer> searchForServiceOfferingsInte sc.addAnd("id", SearchCriteria.Op.NEQ, currentVmOffering.getId()); } + if (currentVmOffering.getDiskOfferingStrictness()) { + sc.addAnd("diskOfferingId", Op.EQ, currentVmOffering.getDiskOfferingId()); + sc.addAnd("diskOfferingStrictness", Op.EQ, true); + } else { + sc.addAnd("diskOfferingStrictness", Op.EQ, false); + } + boolean isRootVolumeUsingLocalStorage = virtualMachineManager.isRootVolumeOnLocalStorage(vmId); // 1. Only return offerings with the same storage type than the storage pool where the VM's root volume is allocated @@ -3199,7 +3265,8 @@ private Pair, Integer> searchForServiceOfferingsInte } if (currentVmOffering != null) { - List storageTags = com.cloud.utils.StringUtils.csvTagsToList(currentVmOffering.getTags()); + DiskOfferingVO diskOffering = _diskOfferingDao.findByIdIncludingRemoved(currentVmOffering.getDiskOfferingId()); + List storageTags = com.cloud.utils.StringUtils.csvTagsToList(diskOffering.getTags()); if (!storageTags.isEmpty()) { SearchBuilder sb = _srvOfferingJoinDao.createSearchBuilder(); for(String tag : storageTags) { diff --git a/server/src/main/java/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java index 685f30169b4b..cf9395d179b0 100644 --- a/server/src/main/java/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/DiskOfferingJoinDaoImpl.java @@ -139,6 +139,7 @@ public DiskOfferingResponse newDiskOfferingResponse(DiskOfferingJoinVO offering) diskOfferingResponse.setVsphereStoragePolicy(vsphereStoragePolicyVO.getName()); } } + diskOfferingResponse.setDiskSizeStrictness(offering.getDiskSizeStrictness()); return diskOfferingResponse; } diff --git a/server/src/main/java/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java index 3381848b198c..75eee3fc52a5 100644 --- a/server/src/main/java/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/ServiceOfferingJoinDaoImpl.java @@ -24,6 +24,7 @@ import com.cloud.user.AccountManager; import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.annotation.dao.AnnotationDao; +import com.cloud.storage.DiskOfferingVO; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.response.ServiceOfferingResponse; import org.apache.cloudstack.context.CallContext; @@ -141,6 +142,13 @@ public ServiceOfferingResponse newServiceOfferingResponse(ServiceOfferingJoinVO long rootDiskSizeInGb = (long) offering.getRootDiskSize() / GB_TO_BYTES; offeringResponse.setRootDiskSize(rootDiskSizeInGb); + offeringResponse.setDiskOfferingStrictness(offering.getDiskOfferingStrictness()); + DiskOfferingVO diskOfferingVO = ApiDBUtils.findDiskOfferingById(offering.getDiskOfferingId()); + if (diskOfferingVO != null) { + offeringResponse.setDiskOfferingId(offering.getDiskOfferingUuid()); + offeringResponse.setDiskOfferingName(offering.getDiskOfferingName()); + offeringResponse.setDiskOfferingDisplayText(offering.getDiskOfferingDisplayText()); + } offeringResponse.setHasAnnotation(annotationDao.hasAnnotations(offering.getUuid(), AnnotationService.EntityType.SERVICE_OFFERING.name(), accountManager.isRootAdmin(CallContext.current().getCallingAccount().getId()))); diff --git a/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java index 7a867620900f..ed52b03ce9c4 100644 --- a/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/UserVmJoinDaoImpl.java @@ -28,6 +28,7 @@ import javax.inject.Inject; +import com.cloud.storage.DiskOfferingVO; import org.apache.cloudstack.affinity.AffinityGroupResponse; import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.annotation.dao.AnnotationDao; @@ -183,8 +184,11 @@ public UserVmResponse newUserVmResponse(ResponseView view, String objectName, Us userVmResponse.setServiceOfferingName(userVm.getServiceOfferingName()); } if (details.contains(VMDetails.all) || details.contains(VMDetails.diskoff)) { - userVmResponse.setDiskOfferingId(userVm.getDiskOfferingUuid()); - userVmResponse.setDiskOfferingName(userVm.getDiskOfferingName()); + DiskOfferingVO diskOfferingVO = ApiDBUtils.findDiskOfferingById(userVm.getDiskOfferingId()); + if (diskOfferingVO != null) { + userVmResponse.setDiskOfferingId(userVm.getDiskOfferingUuid()); + userVmResponse.setDiskOfferingName(userVm.getDiskOfferingName()); + } } if (details.contains(VMDetails.all) || details.contains(VMDetails.backoff)) { userVmResponse.setBackupOfferingId(userVm.getBackupOfferingUuid()); diff --git a/server/src/main/java/com/cloud/api/query/dao/VolumeJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/VolumeJoinDaoImpl.java index 517ac63bf07b..f1014daeee89 100644 --- a/server/src/main/java/com/cloud/api/query/dao/VolumeJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/VolumeJoinDaoImpl.java @@ -21,6 +21,7 @@ import javax.inject.Inject; +import com.cloud.offering.DiskOffering; import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.annotation.dao.AnnotationDao; import org.apache.cloudstack.api.ResponseObject.ResponseView; @@ -178,10 +179,12 @@ public VolumeResponse newVolumeResponse(ResponseView view, VolumeJoinVO volume) ApiResponseHelper.populateOwner(volResponse, volume); if (volume.getDiskOfferingId() > 0) { - if (ApiDBUtils.findServiceOfferingByUuid(volume.getDiskOfferingUuid()) != null) { - volResponse.setServiceOfferingId(volume.getDiskOfferingUuid()); - volResponse.setServiceOfferingName(volume.getDiskOfferingName()); - volResponse.setServiceOfferingDisplayText(volume.getDiskOfferingDisplayText()); + DiskOffering computeOnlyDiskOffering = ApiDBUtils.findComputeOnlyDiskOfferingById(volume.getDiskOfferingId()); + if (computeOnlyDiskOffering != null) { + ServiceOffering serviceOffering = ApiDBUtils.findServiceOfferingByComputeOnlyDiskOffering(volume.getDiskOfferingId()); + volResponse.setServiceOfferingId(String.valueOf(serviceOffering.getId())); + volResponse.setServiceOfferingName(serviceOffering.getName()); + volResponse.setServiceOfferingDisplayText(serviceOffering.getDisplayText()); } else { volResponse.setDiskOfferingId(volume.getDiskOfferingUuid()); volResponse.setDiskOfferingName(volume.getDiskOfferingName()); diff --git a/server/src/main/java/com/cloud/api/query/vo/DiskOfferingJoinVO.java b/server/src/main/java/com/cloud/api/query/vo/DiskOfferingJoinVO.java index 707c2a3e3b05..2013c37161dd 100644 --- a/server/src/main/java/com/cloud/api/query/vo/DiskOfferingJoinVO.java +++ b/server/src/main/java/com/cloud/api/query/vo/DiskOfferingJoinVO.java @@ -29,7 +29,6 @@ import org.apache.cloudstack.api.InternalIdentity; import com.cloud.offering.DiskOffering; -import com.cloud.offering.DiskOffering.Type; import com.cloud.storage.Storage; import com.cloud.utils.db.GenericDao; @@ -62,9 +61,6 @@ public class DiskOfferingJoinVO extends BaseViewVO implements InternalIdentity, @Column(name = "use_local_storage") private boolean useLocalStorage; - @Column(name = "system_use") - private boolean systemUse; - @Column(name = "customized") private boolean customized; @@ -122,8 +118,8 @@ public class DiskOfferingJoinVO extends BaseViewVO implements InternalIdentity, @Column(name = "cache_mode") String cacheMode; - @Column(name = "type") - Type type; + @Column(name = "compute_only") + boolean computeOnly; @Column(name = GenericDao.CREATED_COLUMN) private Date created; @@ -162,6 +158,9 @@ public class DiskOfferingJoinVO extends BaseViewVO implements InternalIdentity, @Column(name = "vsphere_storage_policy") String vsphereStoragePolicy; + @Column(name = "disk_size_strictness") + boolean diskSizeStrictness; + public DiskOfferingJoinVO() { } @@ -199,10 +198,6 @@ public boolean isUseLocalStorage() { return useLocalStorage; } - public boolean isSystemUse() { - return systemUse; - } - public boolean isCustomized() { return customized; } @@ -247,8 +242,8 @@ public int getSortKey() { return sortKey; } - public Type getType() { - return type; + public boolean isComputeOnly() { + return computeOnly; } public Long getBytesReadRate() { @@ -350,4 +345,8 @@ public void setState(DiskOffering.State state) { public String getVsphereStoragePolicy() { return vsphereStoragePolicy; } + + public boolean getDiskSizeStrictness() { + return diskSizeStrictness; + } } diff --git a/server/src/main/java/com/cloud/api/query/vo/ServiceOfferingJoinVO.java b/server/src/main/java/com/cloud/api/query/vo/ServiceOfferingJoinVO.java index e9b2f2d79b66..825f8b5cdfb6 100644 --- a/server/src/main/java/com/cloud/api/query/vo/ServiceOfferingJoinVO.java +++ b/server/src/main/java/com/cloud/api/query/vo/ServiceOfferingJoinVO.java @@ -196,6 +196,21 @@ public class ServiceOfferingJoinVO extends BaseViewVO implements InternalIdentit @Column(name = "dynamic_scaling_enabled") private boolean dynamicScalingEnabled; + @Column(name = "disk_offering_strictness") + private boolean diskOfferingStrictness; + + @Column(name = "disk_offering_id") + private long diskOfferingId; + + @Column(name = "disk_offering_uuid") + private String diskOfferingUuid; + + @Column(name = "disk_offering_name") + private String diskOfferingName; + + @Column(name = "disk_offering_display_text") + private String diskOfferingDisplayText; + public ServiceOfferingJoinVO() { } @@ -408,4 +423,24 @@ public boolean isDynamicScalingEnabled() { public void setDynamicScalingEnabled(boolean dynamicScalingEnabled) { this.dynamicScalingEnabled = dynamicScalingEnabled; } + + public boolean getDiskOfferingStrictness() { + return diskOfferingStrictness; + } + + public long getDiskOfferingId() { + return diskOfferingId; + } + + public String getDiskOfferingUuid() { + return diskOfferingUuid; + } + + public String getDiskOfferingName() { + return diskOfferingName; + } + + public String getDiskOfferingDisplayText() { + return diskOfferingDisplayText; + } } diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java index f38dd5e69090..4101b4ca9d65 100755 --- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java @@ -2865,6 +2865,14 @@ public ServiceOffering createServiceOffering(final CreateServiceOfferingCmd cmd) } } + final Long diskOfferingId = cmd.getDiskOfferingId(); + if (diskOfferingId != null) { + DiskOfferingVO diskOffering = _diskOfferingDao.findById(diskOfferingId); + if ((diskOffering == null) || diskOffering.isComputeOnly()) { + throw new InvalidParameterValueException("Please specify a valid disk offering."); + } + } + return createServiceOffering(userId, cmd.isSystem(), vmType, cmd.getServiceOfferingName(), cpuNumber, memory, cpuSpeed, cmd.getDisplayText(), cmd.getProvisioningType(), localStorageRequired, offerHA, limitCpuUse, volatileVm, cmd.getTags(), cmd.getDomainIds(), cmd.getZoneIds(), cmd.getHostTag(), cmd.getNetworkRate(), cmd.getDeploymentPlanner(), details, cmd.getRootDiskSize(), isCustomizedIops, cmd.getMinIops(), cmd.getMaxIops(), @@ -2872,7 +2880,7 @@ public ServiceOffering createServiceOffering(final CreateServiceOfferingCmd cmd) cmd.getBytesWriteRate(), cmd.getBytesWriteRateMax(), cmd.getBytesWriteRateMaxLength(), cmd.getIopsReadRate(), cmd.getIopsReadRateMax(), cmd.getIopsReadRateMaxLength(), cmd.getIopsWriteRate(), cmd.getIopsWriteRateMax(), cmd.getIopsWriteRateMaxLength(), - cmd.getHypervisorSnapshotReserve(), cmd.getCacheMode(), storagePolicyId, cmd.getDynamicScalingEnabled()); + cmd.getHypervisorSnapshotReserve(), cmd.getCacheMode(), storagePolicyId, cmd.getDynamicScalingEnabled(), diskOfferingId, cmd.getDiskOfferingStrictness(), cmd.isCustomized()); } protected ServiceOfferingVO createServiceOffering(final long userId, final boolean isSystem, final VirtualMachine.Type vmType, @@ -2883,7 +2891,7 @@ protected ServiceOfferingVO createServiceOffering(final long userId, final boole Long bytesWriteRate, Long bytesWriteRateMax, Long bytesWriteRateMaxLength, Long iopsReadRate, Long iopsReadRateMax, Long iopsReadRateMaxLength, Long iopsWriteRate, Long iopsWriteRateMax, Long iopsWriteRateMaxLength, - final Integer hypervisorSnapshotReserve, String cacheMode, final Long storagePolicyID, final boolean dynamicScalingEnabled) { + final Integer hypervisorSnapshotReserve, String cacheMode, final Long storagePolicyID, final boolean dynamicScalingEnabled, final Long diskOfferingId, final boolean diskOfferingStrictness, final boolean isCustomized) { // Filter child domains when both parent and child domains are present List filteredDomainIds = filterChildSubDomains(domainIds); @@ -2913,55 +2921,9 @@ protected ServiceOfferingVO createServiceOffering(final long userId, final boole tags = com.cloud.utils.StringUtils.cleanupTags(tags); - ServiceOfferingVO offering = new ServiceOfferingVO(name, cpu, ramSize, speed, networkRate, null, offerHA, - limitResourceUse, volatileVm, displayText, typedProvisioningType, localStorageRequired, false, tags, isSystem, vmType, - hostTag, deploymentPlanner, dynamicScalingEnabled); - - if (Boolean.TRUE.equals(isCustomizedIops) || isCustomizedIops == null) { - minIops = null; - maxIops = null; - } else { - if (minIops == null && maxIops == null) { - minIops = 0L; - maxIops = 0L; - } else { - if (minIops == null || minIops <= 0) { - throw new InvalidParameterValueException("The min IOPS must be greater than 0."); - } - - if (maxIops == null) { - maxIops = 0L; - } - - if (minIops > maxIops) { - throw new InvalidParameterValueException("The min IOPS must be less than or equal to the max IOPS."); - } - } - } - - if (rootDiskSizeInGiB != null && rootDiskSizeInGiB <= 0L) { - throw new InvalidParameterValueException(String.format("The Root disk size is of %s GB but it must be greater than 0.", rootDiskSizeInGiB)); - } else if (rootDiskSizeInGiB != null) { - long rootDiskSizeInBytes = rootDiskSizeInGiB * GiB_TO_BYTES; - offering.setDiskSize(rootDiskSizeInBytes); - } - - offering.setCustomizedIops(isCustomizedIops); - offering.setMinIops(minIops); - offering.setMaxIops(maxIops); - - setBytesRate(offering, bytesReadRate, bytesReadRateMax, bytesReadRateMaxLength, bytesWriteRate, bytesWriteRateMax, bytesWriteRateMaxLength); - setIopsRate(offering, iopsReadRate, iopsReadRateMax, iopsReadRateMaxLength, iopsWriteRate, iopsWriteRateMax, iopsWriteRateMaxLength); - - if(cacheMode != null) { - offering.setCacheMode(DiskOffering.DiskCacheMode.valueOf(cacheMode.toUpperCase())); - } - - if (hypervisorSnapshotReserve != null && hypervisorSnapshotReserve < 0) { - throw new InvalidParameterValueException("If provided, Hypervisor Snapshot Reserve must be greater than or equal to 0."); - } - - offering.setHypervisorSnapshotReserve(hypervisorSnapshotReserve); + ServiceOfferingVO serviceOffering = new ServiceOfferingVO(name, cpu, ramSize, speed, networkRate, null, offerHA, + limitResourceUse, volatileVm, displayText, isSystem, vmType, + hostTag, deploymentPlanner, dynamicScalingEnabled, isCustomized); List detailsVO = new ArrayList(); if (details != null) { @@ -2996,51 +2958,137 @@ protected ServiceOfferingVO createServiceOffering(final long userId, final boole // Add in disk offering details continue; } - detailsVO.add(new ServiceOfferingDetailsVO(offering.getId(), detailEntry.getKey(), detailEntryValue, true)); + detailsVO.add(new ServiceOfferingDetailsVO(serviceOffering.getId(), detailEntry.getKey(), detailEntryValue, true)); } } if (storagePolicyID != null) { - detailsVO.add(new ServiceOfferingDetailsVO(offering.getId(), ApiConstants.STORAGE_POLICY, String.valueOf(storagePolicyID), false)); + detailsVO.add(new ServiceOfferingDetailsVO(serviceOffering.getId(), ApiConstants.STORAGE_POLICY, String.valueOf(storagePolicyID), false)); + } + + serviceOffering.setDiskOfferingStrictness(diskOfferingStrictness); + + DiskOfferingVO diskOffering = null; + if (diskOfferingId == null) { + diskOffering = createDiskOfferingInternal(userId, isSystem, vmType, + name, cpu, ramSize, speed, displayText, typedProvisioningType, localStorageRequired, + offerHA, limitResourceUse, volatileVm, tags, domainIds, zoneIds, hostTag, + networkRate, deploymentPlanner, details, rootDiskSizeInGiB, isCustomizedIops, minIops, maxIops, + bytesReadRate, bytesReadRateMax, bytesReadRateMaxLength, + bytesWriteRate, bytesWriteRateMax, bytesWriteRateMaxLength, + iopsReadRate, iopsReadRateMax, iopsReadRateMaxLength, + iopsWriteRate, iopsWriteRateMax, iopsWriteRateMaxLength, + hypervisorSnapshotReserve, cacheMode, storagePolicyID); + } else { + diskOffering = _diskOfferingDao.findById(diskOfferingId); + } + if (diskOffering != null) { + serviceOffering.setDiskOfferingId(diskOffering.getId()); + } else { + return null; } - if ((offering = _serviceOfferingDao.persist(offering)) != null) { + if ((serviceOffering = _serviceOfferingDao.persist(serviceOffering)) != null) { for (Long domainId : filteredDomainIds) { - detailsVO.add(new ServiceOfferingDetailsVO(offering.getId(), ApiConstants.DOMAIN_ID, String.valueOf(domainId), false)); + detailsVO.add(new ServiceOfferingDetailsVO(serviceOffering.getId(), ApiConstants.DOMAIN_ID, String.valueOf(domainId), false)); } if (CollectionUtils.isNotEmpty(zoneIds)) { for (Long zoneId : zoneIds) { - detailsVO.add(new ServiceOfferingDetailsVO(offering.getId(), ApiConstants.ZONE_ID, String.valueOf(zoneId), false)); + detailsVO.add(new ServiceOfferingDetailsVO(serviceOffering.getId(), ApiConstants.ZONE_ID, String.valueOf(zoneId), false)); } } - if (!detailsVO.isEmpty()) { + if (CollectionUtils.isNotEmpty(detailsVO)) { for (ServiceOfferingDetailsVO detail : detailsVO) { - detail.setResourceId(offering.getId()); + detail.setResourceId(serviceOffering.getId()); } _serviceOfferingDetailsDao.saveDetails(detailsVO); } + CallContext.current().setEventDetails("Service offering id=" + serviceOffering.getId()); + return serviceOffering; + } else { + return null; + } + } + + private DiskOfferingVO createDiskOfferingInternal(final long userId, final boolean isSystem, final VirtualMachine.Type vmType, + final String name, final Integer cpu, final Integer ramSize, final Integer speed, final String displayText, final ProvisioningType typedProvisioningType, final boolean localStorageRequired, + final boolean offerHA, final boolean limitResourceUse, final boolean volatileVm, String tags, final List domainIds, List zoneIds, final String hostTag, + final Integer networkRate, final String deploymentPlanner, final Map details, Long rootDiskSizeInGiB, final Boolean isCustomizedIops, Long minIops, Long maxIops, + Long bytesReadRate, Long bytesReadRateMax, Long bytesReadRateMaxLength, + Long bytesWriteRate, Long bytesWriteRateMax, Long bytesWriteRateMaxLength, + Long iopsReadRate, Long iopsReadRateMax, Long iopsReadRateMaxLength, + Long iopsWriteRate, Long iopsWriteRateMax, Long iopsWriteRateMaxLength, + final Integer hypervisorSnapshotReserve, String cacheMode, final Long storagePolicyID) { + + DiskOfferingVO diskOffering = new DiskOfferingVO(name, displayText, typedProvisioningType, false, tags, false, localStorageRequired, false); + + if (Boolean.TRUE.equals(isCustomizedIops) || isCustomizedIops == null) { + minIops = null; + maxIops = null; + } else { + if (minIops == null && maxIops == null) { + minIops = 0L; + maxIops = 0L; + } else { + if (minIops == null || minIops <= 0) { + throw new InvalidParameterValueException("The min IOPS must be greater than 0."); + } + + if (maxIops == null) { + maxIops = 0L; + } + + if (minIops > maxIops) { + throw new InvalidParameterValueException("The min IOPS must be less than or equal to the max IOPS."); + } + } + } + + if (rootDiskSizeInGiB != null && rootDiskSizeInGiB <= 0L) { + throw new InvalidParameterValueException(String.format("The Root disk size is of %s GB but it must be greater than 0.", rootDiskSizeInGiB)); + } else if (rootDiskSizeInGiB != null) { + long rootDiskSizeInBytes = rootDiskSizeInGiB * GiB_TO_BYTES; + diskOffering.setDiskSize(rootDiskSizeInBytes); + } + + diskOffering.setCustomizedIops(isCustomizedIops); + diskOffering.setMinIops(minIops); + diskOffering.setMaxIops(maxIops); + + setBytesRate(diskOffering, bytesReadRate, bytesReadRateMax, bytesReadRateMaxLength, bytesWriteRate, bytesWriteRateMax, bytesWriteRateMaxLength); + setIopsRate(diskOffering, iopsReadRate, iopsReadRateMax, iopsReadRateMaxLength, iopsWriteRate, iopsWriteRateMax, iopsWriteRateMaxLength); + + if(cacheMode != null) { + diskOffering.setCacheMode(DiskOffering.DiskCacheMode.valueOf(cacheMode.toUpperCase())); + } + + if (hypervisorSnapshotReserve != null && hypervisorSnapshotReserve < 0) { + throw new InvalidParameterValueException("If provided, Hypervisor Snapshot Reserve must be greater than or equal to 0."); + } + + diskOffering.setHypervisorSnapshotReserve(hypervisorSnapshotReserve); + + if ((diskOffering = _diskOfferingDao.persist(diskOffering)) != null) { if (details != null && !details.isEmpty()) { List diskDetailsVO = new ArrayList(); // Support disk offering details for below parameters if (details.containsKey(Volume.BANDWIDTH_LIMIT_IN_MBPS)) { - diskDetailsVO.add(new DiskOfferingDetailVO(offering.getId(), Volume.BANDWIDTH_LIMIT_IN_MBPS, details.get(Volume.BANDWIDTH_LIMIT_IN_MBPS), false)); + diskDetailsVO.add(new DiskOfferingDetailVO(diskOffering.getId(), Volume.BANDWIDTH_LIMIT_IN_MBPS, details.get(Volume.BANDWIDTH_LIMIT_IN_MBPS), false)); } if (details.containsKey(Volume.IOPS_LIMIT)) { - diskDetailsVO.add(new DiskOfferingDetailVO(offering.getId(), Volume.IOPS_LIMIT, details.get(Volume.IOPS_LIMIT), false)); + diskDetailsVO.add(new DiskOfferingDetailVO(diskOffering.getId(), Volume.IOPS_LIMIT, details.get(Volume.IOPS_LIMIT), false)); } if (!diskDetailsVO.isEmpty()) { diskOfferingDetailsDao.saveDetails(diskDetailsVO); } } - - CallContext.current().setEventDetails("Service offering id=" + offering.getId()); - return offering; } else { return null; } - } + return diskOffering; + } private void setIopsRate(DiskOffering offering, Long iopsReadRate, Long iopsReadRateMax, Long iopsReadRateMaxLength, Long iopsWriteRate, Long iopsWriteRateMax, Long iopsWriteRateMaxLength) { if (iopsReadRate != null && iopsReadRate > 0) { offering.setIopsReadRate(iopsReadRate); @@ -3196,7 +3244,9 @@ public ServiceOffering updateServiceOffering(final UpdateServiceOfferingCmd cmd) offering.setSortKey(sortKey); } - updateOfferingTagsIfIsNotNull(storageTags, offering); + DiskOfferingVO diskOffering = _diskOfferingDao.findById(offeringHandle.getDiskOfferingId()); + updateOfferingTagsIfIsNotNull(storageTags, diskOffering); + _diskOfferingDao.update(diskOffering.getId(), diskOffering); updateServiceOfferingHostTagsIfNotNull(hostTags, offering); @@ -3261,7 +3311,7 @@ protected DiskOfferingVO createDiskOffering(final Long userId, final List Long bytesWriteRate, Long bytesWriteRateMax, Long bytesWriteRateMaxLength, Long iopsReadRate, Long iopsReadRateMax, Long iopsReadRateMaxLength, Long iopsWriteRate, Long iopsWriteRateMax, Long iopsWriteRateMaxLength, - final Integer hypervisorSnapshotReserve, String cacheMode, final Map details, final Long storagePolicyID) { + final Integer hypervisorSnapshotReserve, String cacheMode, final Map details, final Long storagePolicyID, final boolean diskSizeStrictness) { long diskSize = 0;// special case for custom disk offerings if (numGibibytes != null && numGibibytes <= 0) { throw new InvalidParameterValueException("Please specify a disk size of at least 1 Gb."); @@ -3343,6 +3393,7 @@ protected DiskOfferingVO createDiskOffering(final Long userId, final List } newDiskOffering.setHypervisorSnapshotReserve(hypervisorSnapshotReserve); + newDiskOffering.setDiskSizeStrictness(diskSizeStrictness); CallContext.current().setEventDetails("Disk offering id=" + newDiskOffering.getId()); final DiskOfferingVO offering = _diskOfferingDao.persist(newDiskOffering); @@ -3391,6 +3442,7 @@ public DiskOffering createDiskOffering(final CreateDiskOfferingCmd cmd) { final List zoneIds = cmd.getZoneIds(); final Map details = cmd.getDetails(); final Long storagePolicyId = cmd.getStoragePolicy(); + final boolean diskSizeStrictness = cmd.getDiskSizeStrictness(); // check if valid domain if (CollectionUtils.isNotEmpty(domainIds)) { @@ -3466,7 +3518,7 @@ public DiskOffering createDiskOffering(final CreateDiskOfferingCmd cmd) { localStorageRequired, isDisplayOfferingEnabled, isCustomizedIops, minIops, maxIops, bytesReadRate, bytesReadRateMax, bytesReadRateMaxLength, bytesWriteRate, bytesWriteRateMax, bytesWriteRateMaxLength, iopsReadRate, iopsReadRateMax, iopsReadRateMaxLength, iopsWriteRate, iopsWriteRateMax, iopsWriteRateMaxLength, - hypervisorSnapshotReserve, cacheMode, details, storagePolicyId); + hypervisorSnapshotReserve, cacheMode, details, storagePolicyId, diskSizeStrictness); } /** @@ -3858,6 +3910,12 @@ public boolean deleteServiceOffering(final DeleteServiceOfferingCmd cmd) { throw new InvalidParameterValueException("unable to find service offering " + offeringId); } + // Verify disk offering id mapped to the service offering + final DiskOfferingVO diskOffering = _diskOfferingDao.findById(offering.getDiskOfferingId()); + if (diskOffering == null) { + throw new InvalidParameterValueException("unable to find disk offering " + offering.getDiskOfferingId() + " mapped to the service offering " + offeringId); + } + if (offering.getDefaultUse()) { throw new InvalidParameterValueException("Default service offerings cannot be deleted"); } @@ -3882,7 +3940,13 @@ public boolean deleteServiceOffering(final DeleteServiceOfferingCmd cmd) { } annotationDao.removeByEntityType(AnnotationService.EntityType.SERVICE_OFFERING.name(), offering.getUuid()); - offering.setState(DiskOffering.State.Inactive); + if (diskOffering.isComputeOnly()) { + diskOffering.setState(DiskOffering.State.Inactive); + if (!_diskOfferingDao.update(diskOffering.getId(), diskOffering)) { + throw new CloudRuntimeException(String.format("Unable to delete disk offering %s mapped to the service offering %s", diskOffering.getUuid(), offering.getUuid())); + } + } + offering.setState(ServiceOffering.State.Inactive); if (_serviceOfferingDao.update(offeringId, offering)) { CallContext.current().setEventDetails("Service offering id=" + offeringId); return true; diff --git a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java index 436828f6293a..49937a9d192d 100644 --- a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java +++ b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java @@ -17,7 +17,6 @@ package com.cloud.deploy; import java.util.ArrayList; -import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; @@ -113,7 +112,6 @@ import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.GuestOSVO; import com.cloud.storage.ScopeType; -import com.cloud.storage.Storage; import com.cloud.storage.StorageManager; import com.cloud.storage.StoragePool; import com.cloud.storage.StoragePoolHostVO; @@ -1404,6 +1402,7 @@ public int compare(Volume v1, Volume v2) { List allVolumes = new ArrayList<>(); allVolumes.addAll(volumesOrderBySizeDesc); + List> volumeDiskProfilePair = getVolumeDiskProfilePairs(allVolumes); for (StoragePool storagePool : suitablePools) { haveEnoughSpace = false; hostCanAccessPool = false; @@ -1412,7 +1411,7 @@ public int compare(Volume v1, Volume v2) { hostCanAccessPool = true; if (potentialHost.getHypervisorType() == HypervisorType.VMware) { try { - boolean isStoragePoolStoragepolicyComplaince = _storageMgr.isStoragePoolCompliantWithStoragePolicy(allVolumes, storagePool); + boolean isStoragePoolStoragepolicyComplaince = _storageMgr.isStoragePoolCompliantWithStoragePolicy(volumeDiskProfilePair, storagePool); if (!isStoragePoolStoragepolicyComplaince) { continue; } @@ -1448,10 +1447,10 @@ public int compare(Volume v1, Volume v2) { else requestVolumes = new ArrayList(); requestVolumes.add(vol); - + List> volumeDiskProfilePair = getVolumeDiskProfilePairs(requestVolumes); if (potentialHost.getHypervisorType() == HypervisorType.VMware) { try { - boolean isStoragePoolStoragepolicyComplaince = _storageMgr.isStoragePoolCompliantWithStoragePolicy(requestVolumes, potentialSPool); + boolean isStoragePoolStoragepolicyComplaince = _storageMgr.isStoragePoolCompliantWithStoragePolicy(volumeDiskProfilePair, potentialSPool); if (!isStoragePoolStoragepolicyComplaince) { continue; } @@ -1461,8 +1460,8 @@ public int compare(Volume v1, Volume v2) { } } - if (!_storageMgr.storagePoolHasEnoughIops(requestVolumes, potentialSPool) || - !_storageMgr.storagePoolHasEnoughSpace(requestVolumes, potentialSPool, potentialHost.getClusterId())) + if (!_storageMgr.storagePoolHasEnoughIops(volumeDiskProfilePair, potentialSPool) || + !_storageMgr.storagePoolHasEnoughSpace(volumeDiskProfilePair, potentialSPool, potentialHost.getClusterId())) continue; volumeAllocationMap.put(potentialSPool, requestVolumes); } @@ -1498,6 +1497,16 @@ public int compare(Volume v1, Volume v2) { return null; } + private List> getVolumeDiskProfilePairs(List volumes) { + List> volumeDiskProfilePairs = new ArrayList<>(); + for (Volume volume: volumes) { + DiskOfferingVO diskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId()); + DiskProfile diskProfile = new DiskProfile(volume, diskOffering, _volsDao.getHypervisorType(volume.getId())); + volumeDiskProfilePairs.add(new Pair<>(volume, diskProfile)); + } + return volumeDiskProfilePairs; + } + /** * True if: * - Affinity is not enabled (preferred host is empty) @@ -1639,10 +1648,11 @@ protected Pair>, List> findSuitablePoolsFo DiskOfferingVO diskOffering = _diskOfferingDao.findById(toBeCreated.getDiskOfferingId()); - if ((vmProfile.getTemplate().getFormat() == Storage.ImageFormat.ISO || toBeCreated.getVolumeType() == Volume.Type.ROOT) - && vmProfile.getServiceOffering().getTagsArray().length != 0) { - diskOffering.setTagsArray(Arrays.asList(vmProfile.getServiceOffering().getTagsArray())); - } + //FR123 check how is it different for service offering getTagsArray and disk offering's + //if ((vmProfile.getTemplate().getFormat() == Storage.ImageFormat.ISO || toBeCreated.getVolumeType() == Volume.Type.ROOT) + // && vmProfile.getServiceOffering().getTagsArray().length != 0) { + // diskOffering.setTagsArray(Arrays.asList(vmProfile.getServiceOffering().getTagsArray())); + //} DiskProfile diskProfile = new DiskProfile(toBeCreated, diskOffering, vmProfile.getHypervisorType()); boolean useLocalStorage = false; @@ -1656,19 +1666,6 @@ protected Pair>, List> findSuitablePoolsFo } } else { useLocalStorage = diskOffering.isUseLocalStorage(); - - // TODO: this is a hacking fix for the problem of deploy - // ISO-based VM on local storage - // when deploying VM based on ISO, we have a service offering - // and an additional disk offering, use-local storage flag is - // actually - // saved in service offering, override the flag from service - // offering when it is a ROOT disk - if (!useLocalStorage && vmProfile.getServiceOffering().isUseLocalStorage()) { - if (toBeCreated.getVolumeType() == Volume.Type.ROOT) { - useLocalStorage = true; - } - } } diskProfile.setUseLocalStorage(useLocalStorage); diff --git a/server/src/main/java/com/cloud/ha/HighAvailabilityManagerImpl.java b/server/src/main/java/com/cloud/ha/HighAvailabilityManagerImpl.java index e05bc5cb5bc7..82b378a26eae 100644 --- a/server/src/main/java/com/cloud/ha/HighAvailabilityManagerImpl.java +++ b/server/src/main/java/com/cloud/ha/HighAvailabilityManagerImpl.java @@ -276,7 +276,7 @@ public void scheduleRestartForVmsOnHost(final HostVO host, boolean investigate) for (VMInstanceVO vm : reorderedVMList) { ServiceOfferingVO vmOffering = _serviceOfferingDao.findById(vm.getServiceOfferingId()); - if (vmOffering.isUseLocalStorage()) { + if (_itMgr.isRootVolumeOnLocalStorage(vm.getId())) { if (s_logger.isDebugEnabled()){ s_logger.debug("Skipping HA on vm " + vm + ", because it uses local storage. Its fate is tied to the host."); } diff --git a/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java b/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java index 11ecfb503a29..36b419beda14 100644 --- a/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java +++ b/server/src/main/java/com/cloud/network/as/AutoScaleManagerImpl.java @@ -29,6 +29,7 @@ import javax.inject.Inject; +import com.cloud.offering.DiskOffering; import org.apache.log4j.Logger; import com.google.gson.Gson; @@ -1307,6 +1308,11 @@ private long createNewVM(AutoScaleVmGroupVO asGroup) { throw new InvalidParameterValueException("Unable to find service offering: " + serviceOfferingId); } + DiskOffering diskOffering = _entityMgr.findById(DiskOffering.class, serviceOffering.getDiskOfferingId()); + if (diskOffering == null) { + throw new InvalidParameterValueException("Unable to find disk offering: " + serviceOffering.getDiskOfferingId()); + } + VirtualMachineTemplate template = _entityMgr.findById(VirtualMachineTemplate.class, templateId); // Make sure a valid template ID was specified if (template == null) { @@ -1314,8 +1320,8 @@ private long createNewVM(AutoScaleVmGroupVO asGroup) { } if (!zone.isLocalStorageEnabled()) { - if (serviceOffering.isUseLocalStorage()) { - throw new InvalidParameterValueException("Zone is not configured to use local storage but service offering " + serviceOffering.getName() + " uses it"); + if (diskOffering.isUseLocalStorage()) { + throw new InvalidParameterValueException("Zone is not configured to use local storage but disk offering " + diskOffering.getName() + " associated to the service offering " + serviceOffering.getName() + " uses it"); } } @@ -1325,18 +1331,18 @@ private long createNewVM(AutoScaleVmGroupVO asGroup) { vm = _userVmService.createBasicSecurityGroupVirtualMachine(zone, serviceOffering, template, null, owner, "autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(), "autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(), null, null, null, HypervisorType.XenServer, HTTPMethod.GET, null, null, null, - null, true, null, null, null, null, null, null, null, true); + null, true, null, null, null, null, null, null, null, true, null); } else { if (zone.isSecurityGroupEnabled()) { vm = _userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, template, null, null, owner, "autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(), "autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(), null, null, null, HypervisorType.XenServer, HTTPMethod.GET, null, null, - null, null, true, null, null, null, null, null, null, null, true); + null, null, true, null, null, null, null, null, null, null, true, null); } else { vm = _userVmService.createAdvancedVirtualMachine(zone, serviceOffering, template, null, owner, "autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(), "autoScaleVm-" + asGroup.getId() + "-" + getCurrentTimeStampString(), - null, null, null, HypervisorType.XenServer, HTTPMethod.GET, null, null, null, addrs, true, null, null, null, null, null, null, null, true, null); + null, null, null, HypervisorType.XenServer, HTTPMethod.GET, null, null, null, addrs, true, null, null, null, null, null, null, null, true, null, null); } } diff --git a/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index 39d4c5f55df6..40d5e677e500 100644 --- a/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -45,6 +45,7 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.offering.DiskOffering; import org.apache.cloudstack.alert.AlertService; import org.apache.cloudstack.alert.AlertService.AlertType; import org.apache.cloudstack.api.command.admin.router.RebootRouterCmd; @@ -417,6 +418,10 @@ public VirtualRouter upgradeRouter(final UpgradeRouterCmd cmd) { if (newServiceOffering == null) { throw new InvalidParameterValueException("Unable to find service offering with id " + serviceOfferingId); } + DiskOffering newDiskOffering = _entityMgr.findById(DiskOffering.class, newServiceOffering.getDiskOfferingId()); + if (newDiskOffering == null) { + throw new InvalidParameterValueException("Unable to find disk offering: " + newServiceOffering.getDiskOfferingId()); + } // check if it is a system service offering, if yes return with error as // it cannot be used for user vms @@ -436,9 +441,9 @@ public VirtualRouter upgradeRouter(final UpgradeRouterCmd cmd) { // Check that the service offering being upgraded to has the same // storage pool preference as the VM's current service // offering - if (currentServiceOffering.isUseLocalStorage() != newServiceOffering.isUseLocalStorage()) { - throw new InvalidParameterValueException("Can't upgrade, due to new local storage status : " + newServiceOffering.isUseLocalStorage() + " is different from " - + "curruent local storage status: " + currentServiceOffering.isUseLocalStorage()); + if (_itMgr.isRootVolumeOnLocalStorage(routerId) != newDiskOffering.isUseLocalStorage()) { + throw new InvalidParameterValueException("Can't upgrade, due to new local storage status : " + newDiskOffering.isUseLocalStorage() + " is different from " + + "current local storage status of router " + routerId); } router.setServiceOfferingId(serviceOfferingId); diff --git a/server/src/main/java/com/cloud/network/security/SecurityManagerMBeanImpl.java b/server/src/main/java/com/cloud/network/security/SecurityManagerMBeanImpl.java index 7b768ab93327..307a44cb290e 100644 --- a/server/src/main/java/com/cloud/network/security/SecurityManagerMBeanImpl.java +++ b/server/src/main/java/com/cloud/network/security/SecurityManagerMBeanImpl.java @@ -128,7 +128,7 @@ public void tryRulesetUpdateForVmBypassSchedulerVeryDangerous(Long vmId, Long se @Override public void simulateVmStart(Long vmId) { //all we need is the vmId - VMInstanceVO vm = new VMInstanceVO(vmId, 5, "foo", "foo", Type.User, null, HypervisorType.Any, 8, 1, 1, 1, false, false, null); + VMInstanceVO vm = new VMInstanceVO(vmId, 5, "foo", "foo", Type.User, null, HypervisorType.Any, 8, 1, 1, 1, false, false); _sgMgr.handleVmStarted(vm); } diff --git a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java index 33c0c2197df0..b28e3de05592 100644 --- a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java +++ b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java @@ -920,18 +920,22 @@ private DiskOfferingVO createDefaultDiskOffering(String name, String description DiskOfferingVO newDiskOffering = new DiskOfferingVO(name, description, provisioningType, diskSize, tags, isCustomized, null, null, null); newDiskOffering.setUniqueName("Cloud.Com-" + name); - // leaving the above reference to cloud.com in as it is an identifyer and has no real world relevance - newDiskOffering.setSystemUse(isSystemUse); - newDiskOffering = _diskOfferingDao.persistDeafultDiskOffering(newDiskOffering); + // leaving the above reference to cloud.com in as it is an identifier and has no real world relevance + newDiskOffering = _diskOfferingDao.persistDefaultDiskOffering(newDiskOffering); return newDiskOffering; } 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); + DiskOfferingVO diskOfferingVO = new DiskOfferingVO(name, displayText, provisioningType, false, tags, false, false, true); + diskOfferingVO.setUniqueName("Cloud.Com-" + name); + diskOfferingVO = _diskOfferingDao.persistDefaultDiskOffering(diskOfferingVO); + ServiceOfferingVO offering = - new ServiceOfferingVO(name, cpu, ramSize, speed, null, null, offerHA, displayText, provisioningType, localStorageRequired, false, tags, false, null, false); + new ServiceOfferingVO(name, cpu, ramSize, speed, null, null, offerHA, displayText, false, null, false); offering.setUniqueName("Cloud.Com-" + name); + offering.setDiskOfferingId(diskOfferingVO.getId()); // leaving the above reference to cloud.com in as it is an identifyer and has no real world relevance offering = _serviceOfferingDao.persistSystemServiceOffering(offering); return offering; diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index 790a5709b0f0..cc53eed7d644 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -509,6 +509,7 @@ import org.apache.cloudstack.api.command.user.vmsnapshot.RevertToVMSnapshotCmd; import org.apache.cloudstack.api.command.user.volume.AddResourceDetailCmd; import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd; +import org.apache.cloudstack.api.command.user.volume.ChangeOfferingForVolumeCmd; import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd; import org.apache.cloudstack.api.command.user.volume.DeleteVolumeCmd; import org.apache.cloudstack.api.command.user.volume.DestroyVolumeCmd; @@ -1482,6 +1483,24 @@ private boolean hasSuitablePoolsForVolume(final VolumeVO volume, final Host host @Override public Pair, List> listStoragePoolsForMigrationOfVolume(final Long volumeId) { + + Pair, List> allPoolsAndSuitablePoolsPair = listStoragePoolsForMigrationOfVolumeInternal(volumeId, null, null, null, null, false); + List allPools = allPoolsAndSuitablePoolsPair.first(); + List suitablePools = allPoolsAndSuitablePoolsPair.second(); + List avoidPools = new ArrayList<>(); + + final VolumeVO volume = _volumeDao.findById(volumeId); + StoragePool srcVolumePool = _poolDao.findById(volume.getPoolId()); + if (srcVolumePool.getParent() != 0L) { + StoragePool datastoreCluster = _poolDao.findById(srcVolumePool.getParent()); + avoidPools.add(datastoreCluster); + } + abstractDataStoreClustersList((List) allPools, new ArrayList()); + abstractDataStoreClustersList((List) suitablePools, avoidPools); + return new Pair, List>(allPools, suitablePools); + } + + public Pair, List> listStoragePoolsForMigrationOfVolumeInternal(final Long volumeId, Long newDiskOfferingId, Long newSize, Long newMinIops, Long newMaxIops, boolean keepSourceStoragePool) { final Account caller = getCaller(); if (!_accountMgr.isRootAdmin(caller.getId())) { if (s_logger.isDebugEnabled()) { @@ -1497,6 +1516,11 @@ public Pair, List> listStorag throw ex; } + Long diskOfferingId = volume.getDiskOfferingId(); + if (newDiskOfferingId != null) { + diskOfferingId = newDiskOfferingId; + } + // Volume must be attached to an instance for live migration. List allPools = new ArrayList(); List suitablePools = new ArrayList(); @@ -1550,24 +1574,35 @@ public Pair, List> listStorag Host vmHost = hostClusterPair.first(); List clusters = hostClusterPair.second(); allPools = getAllStoragePoolCompatibleWithVolumeSourceStoragePool(srcVolumePool, hypervisorType, clusters); - allPools.remove(srcVolumePool); + ExcludeList avoid = new ExcludeList(); + if (!keepSourceStoragePool) { + allPools.remove(srcVolumePool); + avoid.addPool(srcVolumePool.getId()); + } if (vm != null) { - suitablePools = findAllSuitableStoragePoolsForVm(volume, vm, vmHost, srcVolumePool, + suitablePools = findAllSuitableStoragePoolsForVm(volume, diskOfferingId, newSize, newMinIops, newMaxIops, vm, vmHost, avoid, CollectionUtils.isNotEmpty(clusters) ? clusters.get(0) : null, hypervisorType); } else { - suitablePools = findAllSuitableStoragePoolsForDetachedVolume(volume, allPools); + suitablePools = findAllSuitableStoragePoolsForDetachedVolume(volume, diskOfferingId, allPools); } - List avoidPools = new ArrayList<>(); - if (srcVolumePool.getParent() != 0L) { - StoragePool datastoreCluster = _poolDao.findById(srcVolumePool.getParent()); - avoidPools.add(datastoreCluster); - } - abstractDataStoreClustersList((List) allPools, new ArrayList()); - abstractDataStoreClustersList((List) suitablePools, avoidPools); + removeDataStoreClusterParents((List) allPools); + removeDataStoreClusterParents((List) suitablePools); return new Pair, List>(allPools, suitablePools); } - private void abstractDataStoreClustersList(List storagePools, List avoidPools) { + private void removeDataStoreClusterParents(List storagePools) { + Predicate childDatastorePredicate = pool -> (pool.getParent() != 0); + List childDatastores = storagePools.stream().filter(childDatastorePredicate).collect(Collectors.toList()); + if (!childDatastores.isEmpty()) { + Set parentStoragePoolIds = childDatastores.stream().map(mo -> mo.getParent()).collect(Collectors.toSet()); + for (Long parentStoragePoolId : parentStoragePoolIds) { + StoragePool parentPool = _poolDao.findById(parentStoragePoolId); + storagePools.remove(parentPool); + } + } + } + + private void abstractDataStoreClustersList(List storagePools, List avoidPools) { Predicate childDatastorePredicate = pool -> (pool.getParent() != 0); List childDatastores = storagePools.stream().filter(childDatastorePredicate).collect(Collectors.toList()); storagePools.removeAll(avoidPools); @@ -1637,10 +1672,8 @@ private List getAllStoragePoolCompatibleWithVolumeSourceS * * Side note: the idea behind this method is to provide power for administrators of manually overriding deployments defined by CloudStack. */ - private List findAllSuitableStoragePoolsForVm(final VolumeVO volume, VMInstanceVO vm, Host vmHost, StoragePool srcVolumePool, Cluster srcCluster, HypervisorType hypervisorType) { + private List findAllSuitableStoragePoolsForVm(final VolumeVO volume, Long diskOfferingId, Long newSize, Long newMinIops, Long newMaxIops, VMInstanceVO vm, Host vmHost, ExcludeList avoid, Cluster srcCluster, HypervisorType hypervisorType) { List suitablePools = new ArrayList<>(); - ExcludeList avoid = new ExcludeList(); - avoid.addPool(srcVolumePool.getId()); Long clusterId = null; Long podId = null; if (srcCluster != null) { @@ -1652,8 +1685,13 @@ private List findAllSuitableStoragePoolsForVm(final VolumeVO volume VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm); // OfflineVmwareMigration: vm might be null here; deal! - DiskOfferingVO diskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId()); + DiskOfferingVO diskOffering = _diskOfferingDao.findById(diskOfferingId); DiskProfile diskProfile = new DiskProfile(volume, diskOffering, hypervisorType); + if (volume.getDiskOfferingId() != diskOfferingId) { + diskProfile.setSize(newSize); + diskProfile.setMinIops(newMinIops); + diskProfile.setMaxIops(newMaxIops); + } for (StoragePoolAllocator allocator : _storagePoolAllocators) { List pools = allocator.allocateToPool(diskProfile, profile, plan, avoid, StoragePoolAllocator.RETURN_UPTO_ALL, true); @@ -1671,12 +1709,12 @@ private List findAllSuitableStoragePoolsForVm(final VolumeVO volume return suitablePools; } - private List findAllSuitableStoragePoolsForDetachedVolume(Volume volume, List allPools) { + private List findAllSuitableStoragePoolsForDetachedVolume(Volume volume, Long diskOfferingId, List allPools) { List suitablePools = new ArrayList<>(); if (CollectionUtils.isEmpty(allPools)) { return suitablePools; } - DiskOfferingVO diskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId()); + DiskOfferingVO diskOffering = _diskOfferingDao.findById(diskOfferingId); List tags = new ArrayList<>(); String[] tagsArray = diskOffering.getTagsArray(); if (tagsArray != null && tagsArray.length > 0) { @@ -3313,6 +3351,7 @@ public List> getCommands() { cmdList.add(UploadVolumeCmd.class); cmdList.add(DestroyVolumeCmd.class); cmdList.add(RecoverVolumeCmd.class); + cmdList.add(ChangeOfferingForVolumeCmd.class); cmdList.add(CreateStaticRouteCmd.class); cmdList.add(CreateVPCCmd.class); cmdList.add(DeleteStaticRouteCmd.class); diff --git a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java index 84d058dbc2b8..502cc2cc5446 100644 --- a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java @@ -2312,7 +2312,7 @@ private long getUsedSize(StoragePool pool) { } @Override - public boolean storagePoolHasEnoughIops(List requestedVolumes, StoragePool pool) { + public boolean storagePoolHasEnoughIops(List> requestedVolumes, StoragePool pool) { if (requestedVolumes == null || requestedVolumes.isEmpty() || pool == null) { return false; } @@ -2330,8 +2330,13 @@ public boolean storagePoolHasEnoughIops(List requestedVolumes, StoragePo long requestedIops = 0; - for (Volume requestedVolume : requestedVolumes) { + for (Pair volumeDiskProfilePair : requestedVolumes) { + Volume requestedVolume = volumeDiskProfilePair.first(); + DiskProfile diskProfile = volumeDiskProfilePair.second(); Long minIops = requestedVolume.getMinIops(); + if (requestedVolume.getDiskOfferingId() != diskProfile.getDiskOfferingId()) { + minIops = diskProfile.getMinIops(); + } if (minIops != null && minIops > 0) { requestedIops += minIops; @@ -2344,13 +2349,13 @@ public boolean storagePoolHasEnoughIops(List requestedVolumes, StoragePo } @Override - public boolean storagePoolHasEnoughSpace(List volumes, StoragePool pool) { - return storagePoolHasEnoughSpace(volumes, pool, null); + public boolean storagePoolHasEnoughSpace(List> volumeDiskProfilePairs, StoragePool pool) { + return storagePoolHasEnoughSpace(volumeDiskProfilePairs, pool, null); } @Override - public boolean storagePoolHasEnoughSpace(List volumes, StoragePool pool, Long clusterId) { - if (volumes == null || volumes.isEmpty()) { + public boolean storagePoolHasEnoughSpace(List> volumeDiskProfilesList, StoragePool pool, Long clusterId) { + if (CollectionUtils.isEmpty(volumeDiskProfilesList)) { return false; } @@ -2367,10 +2372,12 @@ public boolean storagePoolHasEnoughSpace(List volumes, StoragePool pool, long allocatedSizeWithTemplate = _capacityMgr.getAllocatedPoolCapacity(poolVO, null); long totalAskingSize = 0; - for (Volume volume : volumes) { + for (Pair volumeDiskProfilePair : volumeDiskProfilesList) { // refreshing the volume from the DB to get latest hv_ss_reserve (hypervisor snapshot reserve) field // I could have just assigned this to "volume", but decided to make a new variable for it so that it - // might be clearer that this "volume" in "volumes" still might have an old value for hv_ss_reverse. + // might be clearer that this "volume" in "volumeDiskProfilesList" still might have an old value for hv_ss_reverse. + Volume volume = volumeDiskProfilePair.first(); + DiskProfile diskProfile = volumeDiskProfilePair.second(); VolumeVO volumeVO = _volumeDao.findById(volume.getId()); if (volumeVO.getHypervisorSnapshotReserve() == null) { @@ -2398,7 +2405,7 @@ public boolean storagePoolHasEnoughSpace(List volumes, StoragePool pool, // A ready-state volume is already allocated in a pool, so the asking size is zero for it. // In case the volume is moving across pools or is not ready yet, the asking size has to be computed. if ((volumeVO.getState() != Volume.State.Ready) || (volumeVO.getPoolId() != pool.getId())) { - totalAskingSize += getDataObjectSizeIncludingHypervisorSnapshotReserve(volumeVO, poolVO); + totalAskingSize += getDataObjectSizeIncludingHypervisorSnapshotReserve(volumeVO, diskProfile, poolVO); totalAskingSize += getAskingSizeForTemplateBasedOnClusterAndStoragePool(volumeVO.getTemplateId(), clusterId, poolVO); } @@ -2427,14 +2434,16 @@ public boolean storagePoolHasEnoughSpaceForResize(StoragePool pool, long current } @Override - public boolean isStoragePoolCompliantWithStoragePolicy(List volumes, StoragePool pool) throws StorageUnavailableException { + public boolean isStoragePoolCompliantWithStoragePolicy(List> volumes, StoragePool pool) throws StorageUnavailableException { if (CollectionUtils.isEmpty(volumes)) { return false; } List> answers = new ArrayList>(); - for (Volume volume : volumes) { + for (Pair volumeDiskProfilePair : volumes) { String storagePolicyId = null; + Volume volume = volumeDiskProfilePair.first(); + DiskProfile diskProfile = volumeDiskProfilePair.second(); if (volume.getVolumeType() == Type.ROOT) { Long vmId = volume.getInstanceId(); if (vmId != null) { @@ -2442,7 +2451,7 @@ public boolean isStoragePoolCompliantWithStoragePolicy(List volumes, Sto storagePolicyId = _serviceOfferingDetailsDao.getDetail(vm.getServiceOfferingId(), ApiConstants.STORAGE_POLICY); } } else { - storagePolicyId = _diskOfferingDetailsDao.getDetail(volume.getDiskOfferingId(), ApiConstants.STORAGE_POLICY); + storagePolicyId = _diskOfferingDetailsDao.getDetail(diskProfile.getDiskOfferingId(), ApiConstants.STORAGE_POLICY); } if (StringUtils.isNotEmpty(storagePolicyId)) { VsphereStoragePolicyVO storagePolicyVO = _vsphereStoragePolicyDao.findById(Long.parseLong(storagePolicyId)); @@ -2556,7 +2565,7 @@ private long getAskingSizeForTemplateBasedOnClusterAndStoragePool(Long templateI return 0; } - private long getDataObjectSizeIncludingHypervisorSnapshotReserve(Volume volume, StoragePool pool) { + private long getDataObjectSizeIncludingHypervisorSnapshotReserve(Volume volume, DiskProfile diskProfile, StoragePool pool) { DataStoreProvider storeProvider = _dataStoreProviderMgr.getDataStoreProvider(pool.getStorageProviderName()); DataStoreDriver storeDriver = storeProvider.getDataStoreDriver(); @@ -2564,7 +2573,9 @@ private long getDataObjectSizeIncludingHypervisorSnapshotReserve(Volume volume, PrimaryDataStoreDriver primaryStoreDriver = (PrimaryDataStoreDriver)storeDriver; VolumeInfo volumeInfo = volFactory.getVolume(volume.getId()); - + if (volume.getDiskOfferingId() != diskProfile.getDiskOfferingId()) { + return diskProfile.getSize(); + } return primaryStoreDriver.getDataObjectSizeIncludingHypervisorSnapshotReserve(volumeInfo, pool); } @@ -3244,8 +3255,8 @@ public void cleanupDownloadUrls() { // get bytesReadRate from service_offering, disk_offering and vm.disk.throttling.bytes_read_rate @Override public Long getDiskBytesReadRate(final ServiceOffering offering, final DiskOffering diskOffering) { - if ((offering != null) && (offering.getBytesReadRate() != null) && (offering.getBytesReadRate() > 0)) { - return offering.getBytesReadRate(); + if ((diskOffering != null) && (diskOffering.getBytesReadRate() != null) && (diskOffering.getBytesReadRate() > 0)) { + return diskOffering.getBytesReadRate(); } else if ((diskOffering != null) && (diskOffering.getBytesReadRate() != null) && (diskOffering.getBytesReadRate() > 0)) { return diskOffering.getBytesReadRate(); } else { @@ -3260,9 +3271,7 @@ public Long getDiskBytesReadRate(final ServiceOffering offering, final DiskOffer // get bytesWriteRate from service_offering, disk_offering and vm.disk.throttling.bytes_write_rate @Override public Long getDiskBytesWriteRate(final ServiceOffering offering, final DiskOffering diskOffering) { - if ((offering != null) && (offering.getBytesWriteRate() != null) && (offering.getBytesWriteRate() > 0)) { - return offering.getBytesWriteRate(); - } else if ((diskOffering != null) && (diskOffering.getBytesWriteRate() != null) && (diskOffering.getBytesWriteRate() > 0)) { + if ((diskOffering != null) && (diskOffering.getBytesWriteRate() != null) && (diskOffering.getBytesWriteRate() > 0)) { return diskOffering.getBytesWriteRate(); } else { Long bytesWriteRate = Long.parseLong(_configDao.getValue(Config.VmDiskThrottlingBytesWriteRate.key())); @@ -3276,9 +3285,7 @@ public Long getDiskBytesWriteRate(final ServiceOffering offering, final DiskOffe // get iopsReadRate from service_offering, disk_offering and vm.disk.throttling.iops_read_rate @Override public Long getDiskIopsReadRate(final ServiceOffering offering, final DiskOffering diskOffering) { - if ((offering != null) && (offering.getIopsReadRate() != null) && (offering.getIopsReadRate() > 0)) { - return offering.getIopsReadRate(); - } else if ((diskOffering != null) && (diskOffering.getIopsReadRate() != null) && (diskOffering.getIopsReadRate() > 0)) { + if ((diskOffering != null) && (diskOffering.getIopsReadRate() != null) && (diskOffering.getIopsReadRate() > 0)) { return diskOffering.getIopsReadRate(); } else { Long iopsReadRate = Long.parseLong(_configDao.getValue(Config.VmDiskThrottlingIopsReadRate.key())); @@ -3292,9 +3299,7 @@ public Long getDiskIopsReadRate(final ServiceOffering offering, final DiskOfferi // get iopsWriteRate from service_offering, disk_offering and vm.disk.throttling.iops_write_rate @Override public Long getDiskIopsWriteRate(final ServiceOffering offering, final DiskOffering diskOffering) { - if ((offering != null) && (offering.getIopsWriteRate() != null) && (offering.getIopsWriteRate() > 0)) { - return offering.getIopsWriteRate(); - } else if ((diskOffering != null) && (diskOffering.getIopsWriteRate() != null) && (diskOffering.getIopsWriteRate() > 0)) { + if ((diskOffering != null) && (diskOffering.getIopsWriteRate() != null) && (diskOffering.getIopsWriteRate() > 0)) { return diskOffering.getIopsWriteRate(); } else { Long iopsWriteRate = Long.parseLong(_configDao.getValue(Config.VmDiskThrottlingIopsWriteRate.key())); diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java index 345e83f5c108..2f10c78627b8 100644 --- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java +++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java @@ -33,7 +33,17 @@ import javax.inject.Inject; +import com.cloud.api.query.dao.ServiceOfferingJoinDao; +import com.cloud.api.query.vo.ServiceOfferingJoinVO; +import com.cloud.server.ManagementService; +import com.cloud.service.ServiceOfferingVO; +import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.vm.DiskProfile; +import com.cloud.vm.UserVmDetailVO; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd; +import org.apache.cloudstack.api.command.user.volume.ChangeOfferingForVolumeCmd; import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd; import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd; import org.apache.cloudstack.api.command.user.volume.ExtractVolumeCmd; @@ -99,8 +109,6 @@ import com.cloud.agent.api.to.DataTO; import com.cloud.agent.api.to.DiskTO; import com.cloud.api.ApiDBUtils; -import com.cloud.api.query.dao.ServiceOfferingJoinDao; -import com.cloud.api.query.vo.ServiceOfferingJoinVO; import com.cloud.configuration.Config; import com.cloud.configuration.ConfigurationManager; import com.cloud.configuration.Resource.ResourceType; @@ -247,6 +255,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic @Inject private DiskOfferingDao _diskOfferingDao; @Inject + private ServiceOfferingDao _serviceOfferingDao; + @Inject private DiskOfferingDetailsDao _diskOfferingDetailsDao; @Inject private AccountDao _accountDao; @@ -290,6 +300,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic public TaggedResourceService taggedResourceService; @Inject VirtualMachineManager virtualMachineManager; + @Inject + private ManagementService managementService; protected Gson _gson; @@ -309,10 +321,14 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic public static final ConfigKey AllowUserExpungeRecoverVolume = new ConfigKey("Advanced", Boolean.class, "allow.user.expunge.recover.volume", "true", "Determines whether users can expunge or recover their volume", true, ConfigKey.Scope.Account); + public static final ConfigKey MatchStoragePoolTagsWithDiskOffering = new ConfigKey("Advanced", Boolean.class, "match.storage.pool.tags.with.disk.offering", "true", + "If true, volume's disk offering can be changed only with the matched storage tags", true, ConfigKey.Scope.Zone); + private long _maxVolumeSizeInGb; private final StateMachine2 _volStateMachine; private static final Set STATES_VOLUME_CANNOT_BE_DESTROYED = new HashSet<>(Arrays.asList(Volume.State.Destroy, Volume.State.Expunging, Volume.State.Expunged, Volume.State.Allocated)); + private static final long GiB_TO_BYTES = 1024 * 1024 * 1024; protected VolumeApiServiceImpl() { _volStateMachine = Volume.State.getStateMachine(); @@ -471,7 +487,7 @@ private boolean validateVolume(Account caller, long ownerId, Long zoneId, String // Check that the the disk offering specified is valid if (diskOfferingId != null) { DiskOfferingVO diskOffering = _diskOfferingDao.findById(diskOfferingId); - if ((diskOffering == null) || diskOffering.getRemoved() != null || !DiskOfferingVO.Type.Disk.equals(diskOffering.getType())) { + if ((diskOffering == null) || diskOffering.getRemoved() != null || diskOffering.isComputeOnly()) { throw new InvalidParameterValueException("Please specify a valid disk offering."); } if (!diskOffering.isCustomized()) { @@ -632,7 +648,7 @@ public VolumeVO allocVolume(CreateVolumeCmd cmd) throws ResourceAllocationExcept // Check that the the disk offering is specified diskOffering = _diskOfferingDao.findById(diskOfferingId); - if ((diskOffering == null) || diskOffering.getRemoved() != null || !DiskOfferingVO.Type.Disk.equals(diskOffering.getType())) { + if ((diskOffering == null) || diskOffering.getRemoved() != null || diskOffering.isComputeOnly()) { throw new InvalidParameterValueException("Please specify a valid disk offering."); } @@ -799,7 +815,7 @@ public VolumeVO allocVolume(CreateVolumeCmd cmd) throws ResourceAllocationExcept } private VolumeVO commitVolume(final CreateVolumeCmd cmd, final Account caller, final Account owner, 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, final Map details) { + final Storage.ProvisioningType provisioningType, final Long size, final Long minIops, final Long maxIops, final VolumeVO parentVolume, final String userSpecifiedName, final String uuid, final Map details) { return Transaction.execute(new TransactionCallback() { @Override public VolumeVO doInTransaction(TransactionStatus status) { @@ -936,10 +952,10 @@ protected VolumeVO createVolumeFromSnapshot(VolumeVO volume, long snapshotId, Lo @DB @ActionEvent(eventType = EventTypes.EVENT_VOLUME_RESIZE, eventDescription = "resizing volume", async = true) public VolumeVO resizeVolume(ResizeVolumeCmd cmd) throws ResourceAllocationException { - Long newSize; - Long newMinIops; - Long newMaxIops; - Integer newHypervisorSnapshotReserve; + Long newSize = cmd.getSize(); + Long newMinIops = cmd.getMinIops(); + Long newMaxIops = cmd.getMaxIops(); + Integer newHypervisorSnapshotReserve = null; boolean shrinkOk = cmd.isShrinkOk(); VolumeVO volume = _volsDao.findById(cmd.getEntityId()); @@ -976,14 +992,12 @@ public VolumeVO resizeVolume(ResizeVolumeCmd cmd) throws ResourceAllocationExcep // if we are to use the existing disk offering if (newDiskOffering == null) { - newSize = cmd.getSize(); newHypervisorSnapshotReserve = volume.getHypervisorSnapshotReserve(); // if the caller is looking to change the size of the volume if (newSize != null) { - if (!diskOffering.isCustomized() && !volume.getVolumeType().equals(Volume.Type.ROOT)) { - throw new InvalidParameterValueException("To change a volume's size without providing a new disk offering, its current disk offering must be " - + "customizable or it must be a root volume (if providing a disk offering, make sure it is different from the current disk offering)."); + if (diskOffering.getDiskSizeStrictness()) { + throw new InvalidParameterValueException(String.format("Resize of volume %s is not allowed, since disk size is strictly fixed as per the disk offering", volume.getUuid())); } if (isNotPossibleToResize(volume, diskOffering)) { @@ -1028,6 +1042,23 @@ public VolumeVO resizeVolume(ResizeVolumeCmd cmd) throws ResourceAllocationExcep throw new InvalidParameterValueException("Requested disk offering has been removed."); } + if (diskOffering.getDiskSizeStrictness() != newDiskOffering.getDiskSizeStrictness()) { + throw new InvalidParameterValueException("Disk offering size strictness does not match with new disk offering"); + } + + if (diskOffering.getDiskSizeStrictness() && (diskOffering.getDiskSize() != newDiskOffering.getDiskSize())) { + throw new InvalidParameterValueException(String.format("Resize volume for %s is not allowed since disk offering's size is fixed", volume.getName())); + } + + Long instanceId = volume.getInstanceId(); + VMInstanceVO vmInstanceVO = _vmInstanceDao.findById(instanceId); + if (volume.getVolumeType().equals(Volume.Type.ROOT)) { + ServiceOfferingVO serviceOffering = _serviceOfferingDao.findById(vmInstanceVO.getServiceOfferingId()); + if (serviceOffering != null && serviceOffering.getDiskOfferingStrictness()) { + throw new InvalidParameterValueException(String.format("Cannot resize ROOT volume [%s] with new disk offering since existing disk offering is strictly assigned to the ROOT volume.", volume.getName())); + } + } + if (diskOffering.getTags() != null) { if (!com.cloud.utils.StringUtils.areTagsEqual(diskOffering.getTags(), newDiskOffering.getTags())) { throw new InvalidParameterValueException("The tags on the new and old disk offerings must match."); @@ -1038,7 +1069,7 @@ public VolumeVO resizeVolume(ResizeVolumeCmd cmd) throws ResourceAllocationExcep _configMgr.checkDiskOfferingAccess(_accountMgr.getActiveAccountById(volume.getAccountId()), newDiskOffering, _dcDao.findById(volume.getDataCenterId())); - if (newDiskOffering.getDiskSize() > 0 && DiskOfferingVO.Type.Service.equals(newDiskOffering.getType())) { + if (newDiskOffering.getDiskSize() > 0 && !newDiskOffering.isComputeOnly()) { newSize = newDiskOffering.getDiskSize(); } else if (newDiskOffering.isCustomized()) { newSize = cmd.getSize(); @@ -1056,9 +1087,6 @@ public VolumeVO resizeVolume(ResizeVolumeCmd cmd) throws ResourceAllocationExcep newSize = newDiskOffering.getDiskSize(); } - - Long instanceId = volume.getInstanceId(); - VMInstanceVO vmInstanceVO = _vmInstanceDao.findById(instanceId); checkIfVolumeIsRootAndVmIsRunning(newSize, volume, vmInstanceVO); if (newDiskOffering.isCustomizedIops() != null && newDiskOffering.isCustomizedIops()) { @@ -1250,7 +1278,7 @@ private void validateIops(Long minIops, Long maxIops, Storage.StoragePoolType po } private VolumeVO orchestrateResizeVolume(long volumeId, long currentSize, long newSize, Long newMinIops, Long newMaxIops, Integer newHypervisorSnapshotReserve, Long newDiskOfferingId, - boolean shrinkOk) { + boolean shrinkOk) { final VolumeVO volume = _volsDao.findById(volumeId); UserVmVO userVm = _userVmDao.findById(volume.getInstanceId()); StoragePoolVO storagePool = _storagePoolDao.findById(volume.getPoolId()); @@ -1355,6 +1383,13 @@ private VolumeVO orchestrateResizeVolume(long volumeId, long currentSize, long n } _volsDao.update(volume.getId(), volume); + if (userVm != null) { + UserVmDetailVO userVmDetailVO = userVmDetailsDao.findDetail(userVm.getId(), VmDetailConstants.ROOT_DISK_SIZE); + if (userVmDetailVO != null) { + userVmDetailVO.setValue(String.valueOf(newSize/ GiB_TO_BYTES)); + userVmDetailsDao.update(userVmDetailVO.getId(), userVmDetailVO); + } + } /* Update resource count for the account on primary storage resource */ if (!shrinkOk) { @@ -1602,6 +1637,349 @@ public Volume recoverVolume(long volumeId) { return volume; } + @Override + @ActionEvent(eventType = EventTypes.EVENT_VOLUME_CHANGE_DISK_OFFERING, eventDescription = "Changing disk offering of a volume") + public Volume changeDiskOfferingForVolume(ChangeOfferingForVolumeCmd cmd) throws ResourceAllocationException { + Long newSize = cmd.getSize(); + Long newMinIops = cmd.getMinIops(); + Long newMaxIops = cmd.getMaxIops(); + Long newDiskOfferingId = cmd.getNewDiskOfferingId(); + boolean shrinkOk = cmd.isShrinkOk(); + boolean autoMigrateVolume = cmd.getAutoMigrate(); + + VolumeVO volume = _volsDao.findById(cmd.getId()); + if (volume == null) { + throw new InvalidParameterValueException("No such volume"); + } + + /* Does the caller have authority to act on this volume? */ + _accountMgr.checkAccess(CallContext.current().getCallingAccount(), null, true, volume); + + return changeDiskOfferingForVolumeInternal(volume, newDiskOfferingId, newSize, newMinIops, newMaxIops, autoMigrateVolume, shrinkOk); + } + + private Volume changeDiskOfferingForVolumeInternal(VolumeVO volume, Long newDiskOfferingId, Long newSize, Long newMinIops, Long newMaxIops, boolean autoMigrateVolume, boolean shrinkOk) throws ResourceAllocationException { + DiskOfferingVO existingDiskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId()); + DiskOfferingVO newDiskOffering = _diskOfferingDao.findById(newDiskOfferingId); + Integer newHypervisorSnapshotReserve = null; + + boolean volumeMigrateRequired = false; + boolean volumeResizeRequired = false; + + // VALIDATIONS + Long updateNewSize[] = {newSize}; + Long updateNewMinIops[] = {newMinIops}; + Long updateNewMaxIops[] = {newMaxIops}; + Integer updateNewHypervisorSnapshotReserve[] = {newHypervisorSnapshotReserve}; + validateVolumeResizeWithNewDiskOfferingAndLoad(volume, existingDiskOffering, newDiskOffering, updateNewSize, updateNewMinIops, updateNewMaxIops, updateNewHypervisorSnapshotReserve); + newSize = updateNewSize[0]; + newMinIops = updateNewMinIops[0]; + newMaxIops = updateNewMaxIops[0]; + newHypervisorSnapshotReserve = updateNewHypervisorSnapshotReserve[0]; + long currentSize = volume.getSize(); + validateVolumeResizeWithSize(volume, currentSize, newSize, shrinkOk); + + /* If this volume has never been beyond allocated state, short circuit everything and simply update the database. */ + // We need to publish this event to usage_volume table + if (volume.getState() == Volume.State.Allocated) { + s_logger.debug(String.format("Volume %s is in the allocated state, but has never been created. Simply updating database with new size and IOPS.", volume.getUuid())); + + volume.setSize(newSize); + volume.setMinIops(newMinIops); + volume.setMaxIops(newMaxIops); + volume.setHypervisorSnapshotReserve(newHypervisorSnapshotReserve); + + if (newDiskOffering != null) { + volume.setDiskOfferingId(newDiskOfferingId); + } + + _volsDao.update(volume.getId(), volume); + if (currentSize != newSize) { + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_RESIZE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), + volume.getDiskOfferingId(), volume.getTemplateId(), volume.getSize(), Volume.class.getName(), volume.getUuid()); + } + return volume; + } + + if (currentSize != newSize || newMaxIops != volume.getMaxIops() || newMinIops != volume.getMinIops()) { + volumeResizeRequired = true; + validateVolumeReadyStateAndHypervisorChecks(volume, currentSize, newSize); + } + + StoragePoolVO existingStoragePool = _storagePoolDao.findById(volume.getPoolId()); + + Pair, List> poolsPair = managementService.listStoragePoolsForMigrationOfVolumeInternal(volume.getId(), newDiskOffering.getId(), newSize, newMinIops, newMaxIops, true); + List suitableStoragePools = poolsPair.second(); + + if (!suitableStoragePools.stream().anyMatch(p -> (p.getId() == existingStoragePool.getId()))) { + volumeMigrateRequired = true; + if (!autoMigrateVolume) { + throw new InvalidParameterValueException(String.format("Failed to change offering for volume %s since automigrate is set to false but volume needs to migrated", volume.getUuid())); + } + } + + if (!volumeMigrateRequired && !volumeResizeRequired) { + _volsDao.updateDiskOffering(volume.getId(), newDiskOffering.getId()); + volume = _volsDao.findById(volume.getId()); + return volume; + } + + if (volumeMigrateRequired) { + if (CollectionUtils.isEmpty(poolsPair.first()) && CollectionUtils.isEmpty(poolsPair.second())) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Volume change offering operation failed for volume ID: %s as no suitable pool(s) found for migrating to support new disk offering", volume.getUuid())); + } + Collections.shuffle(suitableStoragePools); + MigrateVolumeCmd migrateVolumeCmd = new MigrateVolumeCmd(volume.getId(), suitableStoragePools.get(0).getId(), newDiskOffering.getId(), true); + try { + volume = (VolumeVO) migrateVolume(migrateVolumeCmd); + if (volume == null) { + throw new CloudRuntimeException(String.format("Volume change offering operation failed for volume ID: %s migration failed to storage pool %s", volume.getUuid(), suitableStoragePools.get(0).getId())); + } + } catch (Exception e) { + throw new CloudRuntimeException(String.format("Volume change offering operation failed for volume ID: %s migration failed to storage pool %s due to %s", volume.getUuid(), suitableStoragePools.get(0).getId(), e.getMessage())); + } + } + + if (volumeResizeRequired) { + // refresh volume data + volume = _volsDao.findById(volume.getId()); + try { + volume = resizeVolumeInternal(volume, newDiskOffering, currentSize, newSize, newMinIops, newMaxIops, newHypervisorSnapshotReserve, shrinkOk); + } catch (Exception e) { + if (volumeMigrateRequired) { + s_logger.warn(String.format("Volume change offering operation succeeded for volume ID: %s but volume resize operation failed, so please try resize volume operation separately", volume.getUuid())); + } else { + throw new CloudRuntimeException(String.format("Volume change offering operation failed for volume ID: %s due to resize volume operation failed", volume.getUuid())); + } + } + } + + return volume; + } + + private VolumeVO resizeVolumeInternal(VolumeVO volume, DiskOfferingVO newDiskOffering, Long currentSize, Long newSize, Long newMinIops, Long newMaxIops, Integer newHypervisorSnapshotReserve, boolean shrinkOk) throws ResourceAllocationException { + UserVmVO userVm = _userVmDao.findById(volume.getInstanceId()); + HypervisorType hypervisorType = _volsDao.getHypervisorType(volume.getId()); + + if (userVm != null) { + if (volume.getVolumeType().equals(Volume.Type.ROOT) && userVm.getPowerState() != VirtualMachine.PowerState.PowerOff && hypervisorType == HypervisorType.VMware) { + s_logger.error(" For ROOT volume resize VM should be in Power Off state."); + throw new InvalidParameterValueException("VM current state is : " + userVm.getPowerState() + ". But VM should be in " + VirtualMachine.PowerState.PowerOff + " state."); + } + // serialize VM operation + AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); + + if (jobContext.isJobDispatchedBy(VmWorkConstants.VM_WORK_JOB_DISPATCHER)) { + // avoid re-entrance + + VmWorkJobVO placeHolder = null; + + placeHolder = createPlaceHolderWork(userVm.getId()); + + try { + return orchestrateResizeVolume(volume.getId(), currentSize, newSize, newMinIops, newMaxIops, newHypervisorSnapshotReserve, + newDiskOffering != null ? newDiskOffering.getId() : null, shrinkOk); + } finally { + _workJobDao.expunge(placeHolder.getId()); + } + } else { + Outcome outcome = resizeVolumeThroughJobQueue(userVm.getId(), volume.getId(), currentSize, newSize, newMinIops, newMaxIops, newHypervisorSnapshotReserve, + newDiskOffering != null ? newDiskOffering.getId() : null, shrinkOk); + + try { + outcome.get(); + } catch (InterruptedException e) { + throw new RuntimeException("Operation was interrupted", e); + } catch (java.util.concurrent.ExecutionException e) { + throw new RuntimeException("Execution exception", e); + } + + Object jobResult = _jobMgr.unmarshallResultObject(outcome.getJob()); + + if (jobResult != null) { + if (jobResult instanceof ConcurrentOperationException) { + throw (ConcurrentOperationException)jobResult; + } else if (jobResult instanceof ResourceAllocationException) { + throw (ResourceAllocationException)jobResult; + } else if (jobResult instanceof RuntimeException) { + throw (RuntimeException)jobResult; + } else if (jobResult instanceof Throwable) { + throw new RuntimeException("Unexpected exception", (Throwable)jobResult); + } else if (jobResult instanceof Long) { + return _volsDao.findById((Long)jobResult); + } + } + + return volume; + } + } + + return orchestrateResizeVolume(volume.getId(), currentSize, newSize, newMinIops, newMaxIops, newHypervisorSnapshotReserve, newDiskOffering != null ? newDiskOffering.getId() : null, + shrinkOk); + } + + private void validateVolumeReadyStateAndHypervisorChecks(VolumeVO volume, long currentSize, Long newSize) { + // checking if there are any ongoing snapshots on the volume which is to be resized + List ongoingSnapshots = _snapshotDao.listByStatus(volume.getId(), Snapshot.State.Creating, Snapshot.State.CreatedOnPrimary, Snapshot.State.BackingUp); + if (ongoingSnapshots.size() > 0) { + throw new CloudRuntimeException("There is/are unbacked up snapshot(s) on this volume, resize volume is not permitted, please try again later."); + } + + /* Only works for KVM/XenServer/VMware (or "Any") for now, and volumes with 'None' since they're just allocated in DB */ + HypervisorType hypervisorType = _volsDao.getHypervisorType(volume.getId()); + + if (hypervisorType != HypervisorType.KVM && hypervisorType != HypervisorType.XenServer + && hypervisorType != HypervisorType.VMware && hypervisorType != HypervisorType.Any + && hypervisorType != HypervisorType.None) { + throw new InvalidParameterValueException("Hypervisor " + hypervisorType + " does not support volume resize"); + } + + if (volume.getState() != Volume.State.Ready && volume.getState() != Volume.State.Allocated) { + throw new InvalidParameterValueException("Volume should be in ready or allocated state before attempting a resize. Volume " + volume.getUuid() + " is in state " + volume.getState() + "."); + } + + if (hypervisorType.equals(HypervisorType.VMware) && newSize < currentSize) { + throw new InvalidParameterValueException("VMware doesn't support shrinking volume from larger size: " + currentSize + " GB to a smaller size: " + newSize + " GB"); + } + + UserVmVO userVm = _userVmDao.findById(volume.getInstanceId()); + if (userVm != null) { + if (volume.getVolumeType().equals(Volume.Type.ROOT) && userVm.getPowerState() != VirtualMachine.PowerState.PowerOff && hypervisorType == HypervisorType.VMware) { + s_logger.error(" For ROOT volume resize VM should be in Power Off state."); + throw new InvalidParameterValueException("VM current state is : " + userVm.getPowerState() + ". But VM should be in " + VirtualMachine.PowerState.PowerOff + " state."); + } + } + } + + private void validateVolumeResizeWithNewDiskOfferingAndLoad(VolumeVO volume, DiskOfferingVO existingDiskOffering, DiskOfferingVO newDiskOffering, Long[] newSize, Long[] newMinIops, Long[] newMaxIops, Integer[] newHypervisorSnapshotReserve) { + if (newDiskOffering.getRemoved() != null) { + throw new InvalidParameterValueException("Requested disk offering has been removed."); + } + + if (newDiskOffering.getId() == existingDiskOffering.getId()) { + throw new InvalidParameterValueException(String.format("Volume %s already have the new disk offering %s provided", volume.getUuid(), existingDiskOffering.getUuid())); + } + + if (existingDiskOffering.getDiskSizeStrictness() != newDiskOffering.getDiskSizeStrictness()) { + throw new InvalidParameterValueException("Disk offering size strictness does not match with new disk offering"); + } + + if (MatchStoragePoolTagsWithDiskOffering.valueIn(volume.getDataCenterId())) { + if (!doesNewDiskOfferingHasTagsAsOldDiskOffering(existingDiskOffering, newDiskOffering)) { + throw new InvalidParameterValueException(String.format("Selected disk offering %s does not have tags as in existing disk offering of volume %s", existingDiskOffering.getUuid(), volume.getUuid())); + } + } + + Long instanceId = volume.getInstanceId(); + VMInstanceVO vmInstanceVO = _vmInstanceDao.findById(instanceId); + if (volume.getVolumeType().equals(Volume.Type.ROOT)) { + ServiceOfferingVO serviceOffering = _serviceOfferingDao.findById(vmInstanceVO.getServiceOfferingId()); + if (serviceOffering != null && serviceOffering.getDiskOfferingStrictness()) { + throw new InvalidParameterValueException(String.format("Cannot resize ROOT volume [%s] with new disk offering since existing disk offering is strictly assigned to the ROOT volume.", volume.getName())); + } + } + + _configMgr.checkDiskOfferingAccess(_accountMgr.getActiveAccountById(volume.getAccountId()), newDiskOffering, _dcDao.findById(volume.getDataCenterId())); + + if (newDiskOffering.getDiskSize() > 0 && !newDiskOffering.isComputeOnly()) { + newSize[0] = (Long) newDiskOffering.getDiskSize(); + } else if (newDiskOffering.isCustomized()) { + if (newSize[0] == null) { + throw new InvalidParameterValueException("The new disk offering requires that a size be specified."); + } + + // convert from GiB to bytes + newSize[0] = newSize[0] << 30; + } else { + if (newSize[0] != null) { + throw new InvalidParameterValueException("You cannot pass in a custom disk size to a non-custom disk offering."); + } + + if (newDiskOffering.isComputeOnly() && newDiskOffering.getDiskSize() == 0) { + newSize[0] = volume.getSize(); + } else { + newSize[0] = newDiskOffering.getDiskSize(); + } + if (newDiskOffering.isCustomizedIops() != null && newDiskOffering.isCustomizedIops()) { + newMinIops[0] = newMinIops[0] != null ? newMinIops[0] : volume.getMinIops(); + newMaxIops[0] = newMaxIops[0] != null ? newMaxIops[0] : volume.getMaxIops(); + + validateIops(newMinIops[0], newMaxIops[0], volume.getPoolType()); + } else { + newMinIops[0] = newDiskOffering.getMinIops(); + newMaxIops[0] = newDiskOffering.getMaxIops(); + } + + // if the hypervisor snapshot reserve value is null, it must remain null (currently only KVM uses null and null is all KVM uses for a value here) + newHypervisorSnapshotReserve[0] = volume.getHypervisorSnapshotReserve() != null ? newDiskOffering.getHypervisorSnapshotReserve() : null; + } + + if (existingDiskOffering.getDiskSizeStrictness() && !(volume.getSize().equals(newSize[0]))) { + throw new InvalidParameterValueException(String.format("Resize volume for %s is not allowed since disk offering's size is fixed", volume.getName())); + } + checkIfVolumeIsRootAndVmIsRunning(newSize[0], volume, vmInstanceVO); + + } + + private void validateVolumeResizeWithSize(VolumeVO volume, long currentSize, Long newSize, boolean shrinkOk) throws ResourceAllocationException { + + // if the caller is looking to change the size of the volume + if (currentSize != newSize) { + if (volume.getInstanceId() != null) { + // Check that VM to which this volume is attached does not have VM snapshots + if (_vmSnapshotDao.findByVm(volume.getInstanceId()).size() > 0) { + throw new InvalidParameterValueException("A volume that is attached to a VM with any VM snapshots cannot be resized."); + } + } + + if (!validateVolumeSizeRange(newSize)) { + throw new InvalidParameterValueException("Requested size out of range"); + } + + Long storagePoolId = volume.getPoolId(); + + if (storagePoolId != null) { + StoragePoolVO storagePoolVO = _storagePoolDao.findById(storagePoolId); + + if (storagePoolVO.isManaged()) { + Long instanceId = volume.getInstanceId(); + + if (instanceId != null) { + VMInstanceVO vmInstanceVO = _vmInstanceDao.findById(instanceId); + + if (vmInstanceVO.getHypervisorType() == HypervisorType.KVM && vmInstanceVO.getState() != State.Stopped) { + throw new CloudRuntimeException("This kind of KVM disk cannot be resized while it is connected to a VM that's not in the Stopped state."); + } + } + } + } + + /* + * Let's make certain they (think they) know what they're doing if they + * want to shrink by forcing them to provide the shrinkok parameter. + * This will be checked again at the hypervisor level where we can see + * the actual disk size. + */ + if (currentSize > newSize) { + if (volume != null && ImageFormat.QCOW2.equals(volume.getFormat()) && !Volume.State.Allocated.equals(volume.getState())) { + String message = "Unable to shrink volumes of type QCOW2"; + s_logger.warn(message); + throw new InvalidParameterValueException(message); + } + } + if (currentSize > newSize && !shrinkOk) { + throw new InvalidParameterValueException("Going from existing size of " + currentSize + " to size of " + newSize + " would shrink the volume." + + "Need to sign off by supplying the shrinkok parameter with value of true."); + } + + if (newSize > currentSize) { + /* Check resource limit for this account on primary storage resource */ + _resourceLimitMgr.checkResourceLimit(_accountMgr.getAccount(volume.getAccountId()), ResourceType.primary_storage, volume.isDisplayVolume(), + new Long(newSize - currentSize).longValue()); + } + } + } + @Override @ActionEvent(eventType = EventTypes.EVENT_VOLUME_ATTACH, eventDescription = "attaching volume", async = true) public Volume attachVolumeToVM(AttachVolumeCmd command) { @@ -2396,6 +2774,10 @@ public Volume migrateVolume(MigrateVolumeCmd cmd) { throw new InvalidParameterValueException("Cannot migrate volume " + vol + "to the destination storage pool " + destPool.getName() + " as the storage pool is in maintenance mode."); } + DiskOfferingVO diskOffering = _diskOfferingDao.findById(vol.getDiskOfferingId()); + if (diskOffering == null) { + throw new CloudRuntimeException("volume '" + vol.getUuid() + "', has no diskoffering. Migration target cannot be checked."); + } String poolUuid = destPool.getUuid(); if (destPool.getPoolType() == Storage.StoragePoolType.DatastoreCluster) { DataCenter dc = _entityMgr.findById(DataCenter.class, vol.getDataCenterId()); @@ -2408,16 +2790,15 @@ public Volume migrateVolume(MigrateVolumeCmd cmd) { throw new CloudRuntimeException("Storage pool " + destPool.getName() + " is not suitable to migrate volume " + vol.getName()); } - if (!storageMgr.storagePoolHasEnoughSpace(Collections.singletonList(vol), destPool)) { + HypervisorType hypervisorType = _volsDao.getHypervisorType(volumeId); + DiskProfile diskProfile = new DiskProfile(vol, diskOffering, hypervisorType); + Pair volumeDiskProfilePair = new Pair<>(vol, diskProfile); + if (!storageMgr.storagePoolHasEnoughSpace(Collections.singletonList(volumeDiskProfilePair), destPool)) { throw new CloudRuntimeException("Storage pool " + destPool.getName() + " does not have enough space to migrate volume " + vol.getName()); } // OfflineVmwareMigration: check storage tags on disk(offering)s in comparison to destination storage pool // OfflineVmwareMigration: if no match return a proper error now - DiskOfferingVO diskOffering = _diskOfferingDao.findById(vol.getDiskOfferingId()); - if (diskOffering == null) { - throw new CloudRuntimeException("volume '" + vol.getUuid() + "', has no diskoffering. Migration target cannot be checked."); - } if (liveMigrateVolume && State.Running.equals(vm.getState()) && destPool.getClusterId() != null && srcClusterId != null) { @@ -2444,10 +2825,9 @@ public Volume migrateVolume(MigrateVolumeCmd cmd) { } } - HypervisorType hypervisorType = _volsDao.getHypervisorType(volumeId); if (hypervisorType.equals(HypervisorType.VMware)) { try { - boolean isStoragePoolStoragepolicyComplaince = storageMgr.isStoragePoolCompliantWithStoragePolicy(Arrays.asList(vol), destPool); + boolean isStoragePoolStoragepolicyComplaince = storageMgr.isStoragePoolCompliantWithStoragePolicy(Arrays.asList(volumeDiskProfilePair), destPool); if (!isStoragePoolStoragepolicyComplaince) { throw new CloudRuntimeException(String.format("Storage pool %s is not storage policy compliance with the volume %s", poolUuid, vol.getUuid())); } @@ -2458,6 +2838,7 @@ public Volume migrateVolume(MigrateVolumeCmd cmd) { DiskOfferingVO newDiskOffering = retrieveAndValidateNewDiskOffering(cmd); validateConditionsToReplaceDiskOfferingOfVolume(vol, newDiskOffering, destPool); + if (vm != null) { // serialize VM operation AsyncJobExecutionContext jobContext = AsyncJobExecutionContext.getCurrentExecutionContext(); @@ -2517,13 +2898,13 @@ public Volume migrateVolume(MigrateVolumeCmd cmd) { * If all checks pass, we move forward returning the disk offering object. */ private DiskOfferingVO retrieveAndValidateNewDiskOffering(MigrateVolumeCmd cmd) { - String newDiskOfferingUuid = cmd.getNewDiskOfferingUuid(); - if (StringUtils.isBlank(newDiskOfferingUuid)) { + Long newDiskOfferingId = cmd.getNewDiskOfferingId(); + if (newDiskOfferingId == null) { return null; } - DiskOfferingVO newDiskOffering = _diskOfferingDao.findByUuid(newDiskOfferingUuid); + DiskOfferingVO newDiskOffering = _diskOfferingDao.findById(newDiskOfferingId); if (newDiskOffering == null) { - throw new InvalidParameterValueException(String.format("The disk offering informed is not valid [id=%s].", newDiskOfferingUuid)); + throw new InvalidParameterValueException(String.format("The disk offering informed is not valid [id=%s].", newDiskOfferingId)); } if (newDiskOffering.getRemoved() != null) { throw new InvalidParameterValueException(String.format("We cannot assign a removed disk offering [id=%s] to a volume. ", newDiskOffering.getUuid())); @@ -2535,6 +2916,10 @@ private DiskOfferingVO retrieveAndValidateNewDiskOffering(MigrateVolumeCmd cmd) zone = _dcDao.findById(volume.getDataCenterId()); } _accountMgr.checkAccess(caller, newDiskOffering, zone); + DiskOfferingVO currentDiskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId()); + if (VolumeApiServiceImpl.MatchStoragePoolTagsWithDiskOffering.valueIn(zone.getId()) && !doesNewDiskOfferingHasTagsAsOldDiskOffering(currentDiskOffering, newDiskOffering)) { + throw new InvalidParameterValueException(String.format("Existing disk offering storage tags of the volume %s does not contain in the new disk offering %s ", volume.getUuid(), newDiskOffering.getUuid())); + } return newDiskOffering; } @@ -2554,12 +2939,25 @@ protected void validateConditionsToReplaceDiskOfferingOfVolume(VolumeVO volume, return; } if ((destPool.isShared() && newDiskOffering.isUseLocalStorage()) || destPool.isLocal() && newDiskOffering.isShared()) { - throw new InvalidParameterValueException("You cannot move the volume to a shared storage and assing a disk offering for local storage and vice versa."); + throw new InvalidParameterValueException("You cannot move the volume to a shared storage and assign a disk offering for local storage and vice versa."); } if (!doesTargetStorageSupportDiskOffering(destPool, newDiskOffering)) { - throw new InvalidParameterValueException(String.format("Target Storage [id=%s] tags [%s] does not match new disk offering [id=%s] tags [%s].", destPool.getUuid(), - getStoragePoolTags(destPool), newDiskOffering.getUuid(), newDiskOffering.getTags())); + throw new InvalidParameterValueException(String.format("Migration failed: target pool [%s, tags:%s] has no matching tags for volume [%s, uuid:%s, tags:%s]", destPool.getName(), + getStoragePoolTags(destPool), volume.getName(), volume.getUuid(), newDiskOffering.getTags())); + } + if (volume.getVolumeType().equals(Volume.Type.ROOT)) { + VMInstanceVO vm = null; + if (volume.getInstanceId() != null) { + vm = _vmInstanceDao.findById(volume.getInstanceId()); + } + if (vm != null) { + ServiceOfferingVO serviceOffering = _serviceOfferingDao.findById(vm.getServiceOfferingId()); + if (serviceOffering != null && serviceOffering.getDiskOfferingStrictness()) { + throw new InvalidParameterValueException(String.format("Disk offering cannot be changed to the volume %s since existing disk offering is strictly associated with the volume", volume.getUuid())); + } + } } + if (volume.getSize() != newDiskOffering.getDiskSize()) { DiskOfferingVO oldDiskOffering = this._diskOfferingDao.findById(volume.getDiskOfferingId()); s_logger.warn(String.format( @@ -2621,6 +3019,18 @@ public boolean doesTargetStorageSupportDiskOffering(StoragePool destPool, String return CollectionUtils.isSubCollection(Arrays.asList(newDiskOfferingTagsAsStringArray), Arrays.asList(storageTagsAsStringArray)); } + public boolean doesNewDiskOfferingHasTagsAsOldDiskOffering(DiskOfferingVO oldDO, DiskOfferingVO newDO) { + String[] oldDOStorageTags = oldDO.getTagsArray(); + String[] newDOStorageTags = newDO.getTagsArray(); + if (oldDOStorageTags.length == 0) { + return true; + } + if (newDOStorageTags.length == 0) { + return false; + } + return CollectionUtils.isSubCollection(Arrays.asList(oldDOStorageTags), Arrays.asList(newDOStorageTags)); + } + /** * Retrieves the storage pool tags as a {@link String}. If the storage pool does not have tags we return a null value. */ @@ -3651,7 +4061,7 @@ public Outcome detachVolumeFromVmThroughJobQueue(final Long vmId, final } public Outcome resizeVolumeThroughJobQueue(final Long vmId, final long volumeId, final long currentSize, final long newSize, final Long newMinIops, final Long newMaxIops, - final Integer newHypervisorSnapshotReserve, final Long newServiceOfferingId, final boolean shrinkOk) { + final Integer newHypervisorSnapshotReserve, final Long newServiceOfferingId, final boolean shrinkOk) { final CallContext context = CallContext.current(); final User callingUser = context.getCallingUser(); final Account callingAccount = context.getCallingAccount(); @@ -3745,7 +4155,7 @@ private Outcome migrateVolumeThroughJobQueue(VMInstanceVO vm, VolumeVO v } public Outcome takeVolumeSnapshotThroughJobQueue(final Long vmId, final Long volumeId, final Long policyId, final Long snapshotId, final Long accountId, final boolean quiesceVm, - final Snapshot.LocationType locationType, final boolean asyncBackup) { + final Snapshot.LocationType locationType, final boolean asyncBackup) { final CallContext context = CallContext.current(); final User callingUser = context.getCallingUser(); @@ -3852,6 +4262,6 @@ public String getConfigComponentName() { @Override public ConfigKey[] getConfigKeys() { - return new ConfigKey[] {ConcurrentMigrationsThresholdPerDatastore, AllowUserExpungeRecoverVolume}; + return new ConfigKey[] {ConcurrentMigrationsThresholdPerDatastore, AllowUserExpungeRecoverVolume, MatchStoragePoolTagsWithDiskOffering}; } } diff --git a/server/src/main/java/com/cloud/test/DatabaseConfig.java b/server/src/main/java/com/cloud/test/DatabaseConfig.java index 4e0cbac6735f..0dda96b3893b 100644 --- a/server/src/main/java/com/cloud/test/DatabaseConfig.java +++ b/server/src/main/java/com/cloud/test/DatabaseConfig.java @@ -932,30 +932,38 @@ protected void saveServiceOffering() { } else { useLocalStorage = false; } + DiskOfferingVO diskOfferingVO = new DiskOfferingVO(name, displayText, provisioningType, false, null, false, false, true); ServiceOfferingVO serviceOffering = new ServiceOfferingVO(name, cpu, ramSize, speed, null, null, ha, displayText, - provisioningType, useLocalStorage, false, null, false, null, false); + false, null, false); Long bytesReadRate = Long.parseLong(_currentObjectParams.get("bytesReadRate")); if ((bytesReadRate != null) && (bytesReadRate > 0)) - serviceOffering.setBytesReadRate(bytesReadRate); + diskOfferingVO.setBytesReadRate(bytesReadRate); Long bytesWriteRate = Long.parseLong(_currentObjectParams.get("bytesWriteRate")); if ((bytesWriteRate != null) && (bytesWriteRate > 0)) - serviceOffering.setBytesWriteRate(bytesWriteRate); + diskOfferingVO.setBytesWriteRate(bytesWriteRate); Long iopsReadRate = Long.parseLong(_currentObjectParams.get("iopsReadRate")); if ((iopsReadRate != null) && (iopsReadRate > 0)) - serviceOffering.setIopsReadRate(iopsReadRate); + diskOfferingVO.setIopsReadRate(iopsReadRate); Long iopsWriteRate = Long.parseLong(_currentObjectParams.get("iopsWriteRate")); if ((iopsWriteRate != null) && (iopsWriteRate > 0)) - serviceOffering.setIopsWriteRate(iopsWriteRate); + diskOfferingVO.setIopsWriteRate(iopsWriteRate); - ServiceOfferingDaoImpl dao = ComponentContext.inject(ServiceOfferingDaoImpl.class); + DiskOfferingDaoImpl DiskOfferinDao = ComponentContext.inject(DiskOfferingDaoImpl.class); try { - dao.persist(serviceOffering); + DiskOfferinDao.persist(diskOfferingVO); } catch (Exception e) { - s_logger.error("error creating service offering", e); + s_logger.error("error creating disk offering", e); + } + serviceOffering.setDiskOfferingId(diskOfferingVO.getId()); + ServiceOfferingDaoImpl serviceOfferingDao = ComponentContext.inject(ServiceOfferingDaoImpl.class); + try { + serviceOfferingDao.persist(serviceOffering); + } catch (Exception e) { + s_logger.error("error creating service offering", e); } /* String insertSql = "INSERT INTO `cloud`.`service_offering` (id, name, cpu, ram_size, speed, nw_rate, mc_rate, created, ha_enabled, mirrored, display_text, guest_ip_type, use_local_storage) " + diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 76da7e5d2502..275f7018b40e 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -84,6 +84,7 @@ import org.apache.cloudstack.api.command.user.vm.UpgradeVMCmd; import org.apache.cloudstack.api.command.user.vmgroup.CreateVMGroupCmd; import org.apache.cloudstack.api.command.user.vmgroup.DeleteVMGroupCmd; +import org.apache.cloudstack.api.command.user.volume.ChangeOfferingForVolumeCmd; import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd; import org.apache.cloudstack.backup.Backup; import org.apache.cloudstack.backup.BackupManager; @@ -1115,57 +1116,7 @@ public UserVm upgradeVirtualMachine(UpgradeVMCmd cmd) throws ResourceAllocationE _accountMgr.checkAccess(caller, null, true, vmInstance); - // Check resource limits for CPU and Memory. - Map customParameters = cmd.getDetails(); - ServiceOfferingVO newServiceOffering = _offeringDao.findById(svcOffId); - if (newServiceOffering.getState() == DiskOffering.State.Inactive) { - throw new InvalidParameterValueException(String.format("Unable to upgrade virtual machine %s with an inactive service offering %s", vmInstance.getUuid(), newServiceOffering.getUuid())); - } - if (newServiceOffering.isDynamic()) { - newServiceOffering.setDynamicFlag(true); - validateCustomParameters(newServiceOffering, cmd.getDetails()); - newServiceOffering = _offeringDao.getComputeOffering(newServiceOffering, customParameters); - } else { - validateOfferingMaxResource(newServiceOffering); - } - - ServiceOfferingVO currentServiceOffering = _offeringDao.findByIdIncludingRemoved(vmInstance.getId(), vmInstance.getServiceOfferingId()); - - int newCpu = newServiceOffering.getCpu(); - int newMemory = newServiceOffering.getRamSize(); - int currentCpu = currentServiceOffering.getCpu(); - int currentMemory = currentServiceOffering.getRamSize(); - - Account owner = _accountMgr.getActiveAccountById(vmInstance.getAccountId()); - if (! VirtualMachineManager.ResourceCountRunningVMsonly.value()) { - if (newCpu > currentCpu) { - _resourceLimitMgr.checkResourceLimit(owner, ResourceType.cpu, newCpu - currentCpu); - } - if (newMemory > currentMemory) { - _resourceLimitMgr.checkResourceLimit(owner, ResourceType.memory, newMemory - currentMemory); - } - } - - // Check that the specified service offering ID is valid - _itMgr.checkIfCanUpgrade(vmInstance, newServiceOffering); - - resizeRootVolumeOfVmWithNewOffering(vmInstance, newServiceOffering); - - _itMgr.upgradeVmDb(vmId, newServiceOffering, currentServiceOffering); - - // Increment or decrement CPU and Memory count accordingly. - if (! VirtualMachineManager.ResourceCountRunningVMsonly.value()) { - if (newCpu > currentCpu) { - _resourceLimitMgr.incrementResourceCount(owner.getAccountId(), ResourceType.cpu, new Long(newCpu - currentCpu)); - } else if (currentCpu > newCpu) { - _resourceLimitMgr.decrementResourceCount(owner.getAccountId(), ResourceType.cpu, new Long(currentCpu - newCpu)); - } - if (newMemory > currentMemory) { - _resourceLimitMgr.incrementResourceCount(owner.getAccountId(), ResourceType.memory, new Long(newMemory - currentMemory)); - } else if (currentMemory > newMemory) { - _resourceLimitMgr.decrementResourceCount(owner.getAccountId(), ResourceType.memory, new Long(currentMemory - newMemory)); - } - } + upgradeStoppedVirtualMachine(vmId, svcOffId, cmd.getDetails()); // Generate usage event for VM upgrade UserVmVO userVm = _vmDao.findById(vmId); @@ -1230,19 +1181,13 @@ public void validateCustomParameters(ServiceOfferingVO serviceOffering, Map customParameters) throws ResourceAllocationException { - Account caller = CallContext.current().getCallingAccount(); - // Verify input parameters - //UserVmVO vmInstance = _vmDao.findById(vmId); VMInstanceVO vmInstance = _vmInstanceDao.findById(vmId); - if (vmInstance == null) { - throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId); - } - - _accountMgr.checkAccess(caller, null, true, vmInstance); - // Check resource limits for CPU and Memory. ServiceOfferingVO newServiceOffering = _offeringDao.findById(svcOffId); + if (newServiceOffering.getState() == ServiceOffering.State.Inactive) { + throw new InvalidParameterValueException(String.format("Unable to upgrade virtual machine %s with an inactive service offering %s", vmInstance.getUuid(), newServiceOffering.getUuid())); + } if (newServiceOffering.isDynamic()) { newServiceOffering.setDynamicFlag(true); validateCustomParameters(newServiceOffering, customParameters); @@ -1252,6 +1197,8 @@ private UserVm upgradeStoppedVirtualMachine(Long vmId, Long svcOffId, Map vols = _volsDao.findReadyAndAllocatedRootVolumesByInstance(vmInstance.getId()); - - for (final VolumeVO rootVolumeOfVm : vols) { - DiskOfferingVO currentRootDiskOffering = _diskOfferingDao.findById(rootVolumeOfVm.getDiskOfferingId()); - - ResizeVolumeCmd resizeVolumeCmd = prepareResizeVolumeCmd(rootVolumeOfVm, currentRootDiskOffering, newRootDiskOffering); - - if (rootVolumeOfVm.getDiskOfferingId() != newRootDiskOffering.getId()) { - rootVolumeOfVm.setDiskOfferingId(newRootDiskOffering.getId()); - _volsDao.update(rootVolumeOfVm.getId(), rootVolumeOfVm); - } - HypervisorType hypervisorType = _volsDao.getHypervisorType(rootVolumeOfVm.getId()); - if (HypervisorType.Simulator != hypervisorType) { - _volumeService.resizeVolume(resizeVolumeCmd); - } else if (newRootDiskOffering.getDiskSize() > 0 && currentRootDiskOffering.getDiskSize() != newRootDiskOffering.getDiskSize()) { - throw new InvalidParameterValueException("Hypervisor " + hypervisorType + " does not support volume resize"); - } - } + // resize and migrate the root volume if required + DiskOfferingVO newDiskOffering = _diskOfferingDao.findById(newServiceOffering.getDiskOfferingId()); + changeDiskOfferingForRootVolume(vmId, newDiskOffering, customParameters); _itMgr.upgradeVmDb(vmId, newServiceOffering, currentServiceOffering); @@ -1349,19 +1278,6 @@ protected ResizeVolumeCmd prepareResizeVolumeCmd(VolumeVO rootVolume, DiskOfferi return resizeVolumeCmd; } - private void resizeRootVolumeOfVmWithNewOffering(VMInstanceVO vmInstance, ServiceOfferingVO newServiceOffering) - throws ResourceAllocationException { - DiskOfferingVO newROOTDiskOffering = _diskOfferingDao.findById(newServiceOffering.getId()); - List vols = _volsDao.findReadyAndAllocatedRootVolumesByInstance(vmInstance.getId()); - - for (final VolumeVO rootVolumeOfVm : vols) { - rootVolumeOfVm.setDiskOfferingId(newROOTDiskOffering.getId()); - ResizeVolumeCmd resizeVolumeCmd = new ResizeVolumeCmd(rootVolumeOfVm.getId(), newROOTDiskOffering.getMinIops(), newROOTDiskOffering.getMaxIops()); - _volumeService.resizeVolume(resizeVolumeCmd); - _volsDao.update(rootVolumeOfVm.getId(), rootVolumeOfVm); - } - } - @Override @ActionEvent(eventType = EventTypes.EVENT_NIC_CREATE, eventDescription = "Creating Nic", async = true) public UserVm addNicToVirtualMachine(AddNicToVMCmd cmd) throws InvalidParameterValueException, PermissionDeniedException, CloudRuntimeException { @@ -1920,7 +1836,8 @@ public boolean upgradeVirtualMachine(Long vmId, Long newServiceOfferingId, Map customParameters) throws ResourceAllocationException { + + List vols = _volsDao.findReadyAndAllocatedRootVolumesByInstance(vmId); + + for (final VolumeVO rootVolumeOfVm : vols) { + DiskOfferingVO currentRootDiskOffering = _diskOfferingDao.findById(rootVolumeOfVm.getDiskOfferingId()); + HypervisorType hypervisorType = _volsDao.getHypervisorType(rootVolumeOfVm.getId()); + if (HypervisorType.Simulator != hypervisorType) { + Long minIopsInNewDiskOffering = null; + Long maxIopsInNewDiskOffering = null; + boolean autoMigrate = false; + boolean shrinkOk = false; + if (customParameters.containsKey(ApiConstants.MIN_IOPS)) { + minIopsInNewDiskOffering = Long.parseLong(customParameters.get(ApiConstants.MIN_IOPS)); + } + if (customParameters.containsKey(ApiConstants.MAX_IOPS)) { + minIopsInNewDiskOffering = Long.parseLong(customParameters.get(ApiConstants.MAX_IOPS)); + } + if (customParameters.containsKey(ApiConstants.AUTO_MIGRATE)) { + autoMigrate = Boolean.parseBoolean(customParameters.get(ApiConstants.AUTO_MIGRATE)); + } + if (customParameters.containsKey(ApiConstants.SHRINK_OK)) { + shrinkOk = Boolean.parseBoolean(customParameters.get(ApiConstants.SHRINK_OK)); + } + ChangeOfferingForVolumeCmd changeOfferingForVolumeCmd = new ChangeOfferingForVolumeCmd(rootVolumeOfVm.getId(), newDiskOffering.getId(), minIopsInNewDiskOffering, maxIopsInNewDiskOffering, autoMigrate, shrinkOk); + Volume result = _volumeService.changeDiskOfferingForVolume(changeOfferingForVolumeCmd); + if (result == null) { + throw new CloudRuntimeException("Failed to change disk offering of the root volume"); + } + } else if (newDiskOffering.getDiskSize() > 0 && currentRootDiskOffering.getDiskSize() != newDiskOffering.getDiskSize()) { + throw new InvalidParameterValueException("Hypervisor " + hypervisorType + " does not support volume resize"); + } + } + } + @Override public HashMap getVirtualMachineStatistics(long hostId, String hostName, List vmIds) throws CloudRuntimeException { HashMap vmStatsById = new HashMap(); @@ -2258,7 +2226,7 @@ public UserVm recoverVirtualMachine(RecoverVMCmd cmd) throws ResourceAllocationE Long offeringId = null; if (diskOfferingId != null) { DiskOfferingVO offering = _diskOfferingDao.findById(diskOfferingId); - if (offering != null && (offering.getType() == DiskOfferingVO.Type.Disk)) { + if (offering != null && !offering.isComputeOnly()) { offeringId = offering.getId(); } } @@ -3433,7 +3401,7 @@ public UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOff Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData, String sshKeyPair, Map requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, List affinityGroupIdList, Map customParametes, String customId, Map> dhcpOptionMap, - Map dataDiskTemplateToDiskOfferingMap, Map userVmOVFProperties, boolean dynamicScalingEnabled) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, + Map dataDiskTemplateToDiskOfferingMap, Map userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { Account caller = CallContext.current().getCallingAccount(); @@ -3482,7 +3450,7 @@ public UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOff return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, securityGroupIdList, group, httpmethod, userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayVm, keyboard, affinityGroupIdList, customParametes, customId, dhcpOptionMap, - dataDiskTemplateToDiskOfferingMap, userVmOVFProperties, dynamicScalingEnabled, null); + dataDiskTemplateToDiskOfferingMap, userVmOVFProperties, dynamicScalingEnabled, null, overrideDiskOfferingId); } @@ -3492,7 +3460,7 @@ public UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, Service List securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData, String sshKeyPair, Map requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, List affinityGroupIdList, Map customParameters, String customId, Map> dhcpOptionMap, - Map dataDiskTemplateToDiskOfferingMap, Map userVmOVFProperties, boolean dynamicScalingEnabled) throws InsufficientCapacityException, ConcurrentOperationException, + Map dataDiskTemplateToDiskOfferingMap, Map userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { Account caller = CallContext.current().getCallingAccount(); @@ -3593,7 +3561,7 @@ public UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, Service return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, securityGroupIdList, group, httpmethod, userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayVm, keyboard, affinityGroupIdList, customParameters, customId, dhcpOptionMap, dataDiskTemplateToDiskOfferingMap, - userVmOVFProperties, dynamicScalingEnabled, null); + userVmOVFProperties, dynamicScalingEnabled, null, overrideDiskOfferingId); } @Override @@ -3602,7 +3570,7 @@ public UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serv String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData, String sshKeyPair, Map requestedIps, IpAddresses defaultIps, Boolean displayvm, String keyboard, List affinityGroupIdList, Map customParametrs, String customId, Map> dhcpOptionsMap, Map dataDiskTemplateToDiskOfferingMap, - Map userVmOVFPropertiesMap, boolean dynamicScalingEnabled, String type) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, + Map userVmOVFPropertiesMap, boolean dynamicScalingEnabled, String type, Long overrideDiskOfferingId) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { Account caller = CallContext.current().getCallingAccount(); @@ -3654,7 +3622,7 @@ public UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serv return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, null, group, httpmethod, userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, displayvm, keyboard, affinityGroupIdList, customParametrs, customId, dhcpOptionsMap, - dataDiskTemplateToDiskOfferingMap, userVmOVFPropertiesMap, dynamicScalingEnabled, type); + dataDiskTemplateToDiskOfferingMap, userVmOVFPropertiesMap, dynamicScalingEnabled, type, overrideDiskOfferingId); } private NetworkVO getNetworkToAddToNetworkList(VirtualMachineTemplate template, Account owner, HypervisorType hypervisor, @@ -3773,7 +3741,7 @@ private UserVm createVirtualMachine(DataCenter zone, ServiceOffering serviceOffe String sshKeyPair, HypervisorType hypervisor, Account caller, Map requestedIps, IpAddresses defaultIps, Boolean isDisplayVm, String keyboard, List affinityGroupIdList, Map customParameters, String customId, Map> dhcpOptionMap, Map datadiskTemplateToDiskOfferringMap, - Map userVmOVFPropertiesMap, boolean dynamicScalingEnabled, String type) throws InsufficientCapacityException, ResourceUnavailableException, + Map userVmOVFPropertiesMap, boolean dynamicScalingEnabled, String type, Long overrideDiskOfferingId) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException, StorageUnavailableException, ResourceAllocationException { _accountMgr.checkAccess(caller, null, true, owner); @@ -3834,23 +3802,28 @@ private UserVm createVirtualMachine(DataCenter zone, ServiceOffering serviceOffe // check if account/domain is with in resource limits to create a new vm boolean isIso = Storage.ImageFormat.ISO == template.getFormat(); - long size = configureCustomRootDiskSize(customParameters, template, hypervisorType, offering); - - if (diskOfferingId != null) { - DiskOfferingVO diskOffering = _diskOfferingDao.findById(diskOfferingId); - if (diskOffering != null && diskOffering.isCustomized()) { - if (diskSize == null) { - throw new InvalidParameterValueException("This disk offering requires a custom size specified"); + Long rootDiskOfferingId = offering.getDiskOfferingId(); + if (isIso) { + if (diskOfferingId == null) { + DiskOfferingVO diskOffering = _diskOfferingDao.findById(rootDiskOfferingId); + if (diskOffering.isComputeOnly()) { + throw new InvalidParameterValueException("Installing from ISO requires a disk offering to be specified for the root disk."); } - Long customDiskOfferingMaxSize = VolumeOrchestrationService.CustomDiskOfferingMaxSize.value(); - Long customDiskOfferingMinSize = VolumeOrchestrationService.CustomDiskOfferingMinSize.value(); - if ((diskSize < customDiskOfferingMinSize) || (diskSize > customDiskOfferingMaxSize)) { - throw new InvalidParameterValueException("VM Creation failed. Volume size: " + diskSize + "GB is out of allowed range. Max: " + customDiskOfferingMaxSize - + " Min:" + customDiskOfferingMinSize); - } - size += diskSize * GiB_TO_BYTES; + } else { + rootDiskOfferingId = diskOfferingId; + diskOfferingId = null; } - size += _diskOfferingDao.findById(diskOfferingId).getDiskSize(); + } + if (!offering.getDiskOfferingStrictness() && overrideDiskOfferingId != null) { + rootDiskOfferingId = overrideDiskOfferingId; + } + + DiskOfferingVO rootdiskOffering = _diskOfferingDao.findById(rootDiskOfferingId); + long size = configureCustomRootDiskSize(customParameters, template, hypervisorType, rootdiskOffering); + + if (!isIso && diskOfferingId != null) { + DiskOfferingVO diskOffering = _diskOfferingDao.findById(diskOfferingId); + size += verifyAndGetDiskSize(diskOffering, diskSize); } if (! VirtualMachineManager.ResourceCountRunningVMsonly.value()) { resourceLimitCheck(owner, isDisplayVm, new Long(offering.getCpu()), new Long(offering.getRamSize())); @@ -4137,7 +4110,7 @@ private UserVm createVirtualMachine(DataCenter zone, ServiceOffering serviceOffe UserVmVO vm = commitUserVm(zone, template, hostName, displayName, owner, diskOfferingId, diskSize, userData, caller, isDisplayVm, keyboard, accountId, userId, offering, isIso, sshPublicKey, networkNicMap, id, instanceName, uuidName, hypervisorType, customParameters, dhcpOptionMap, - datadiskTemplateToDiskOfferringMap, userVmOVFPropertiesMap, dynamicScalingEnabled, type); + datadiskTemplateToDiskOfferringMap, userVmOVFPropertiesMap, dynamicScalingEnabled, type, rootDiskOfferingId); // Assign instance to the group try { @@ -4161,6 +4134,24 @@ private UserVm createVirtualMachine(DataCenter zone, ServiceOffering serviceOffe return vm; } + private long verifyAndGetDiskSize(DiskOfferingVO diskOffering, Long diskSize) { + long size = 0l; + if (diskOffering != null && diskOffering.isCustomized() && !diskOffering.isComputeOnly()) { + if (diskSize == null) { + throw new InvalidParameterValueException("This disk offering requires a custom size specified"); + } + Long customDiskOfferingMaxSize = VolumeOrchestrationService.CustomDiskOfferingMaxSize.value(); + Long customDiskOfferingMinSize = VolumeOrchestrationService.CustomDiskOfferingMinSize.value(); + if ((diskSize < customDiskOfferingMinSize) || (diskSize > customDiskOfferingMaxSize)) { + throw new InvalidParameterValueException("VM Creation failed. Volume size: " + diskSize + "GB is out of allowed range. Max: " + customDiskOfferingMaxSize + + " Min:" + customDiskOfferingMinSize); + } + size += diskSize * GiB_TO_BYTES; + } + size += diskOffering.getDiskSize(); + return size; + } + @Override public boolean checkIfDynamicScalingCanBeEnabled(VirtualMachine vm, ServiceOffering offering, VirtualMachineTemplate template, Long zoneId) { boolean canEnableDynamicScaling = (vm != null ? vm.isDynamicallyScalable() : true) && offering.isDynamicScalingEnabled() && template.isDynamicallyScalable() && UserVmManager.EnableDynamicallyScaleVm.valueIn(zoneId); @@ -4175,10 +4166,9 @@ public boolean checkIfDynamicScalingCanBeEnabled(VirtualMachine vm, ServiceOffer * Configures the Root disk size via User`s custom parameters. * If the Service Offering has the Root Disk size field configured then the User`s root disk custom parameter is overwritten by the service offering. */ - protected long configureCustomRootDiskSize(Map customParameters, VMTemplateVO template, HypervisorType hypervisorType, ServiceOfferingVO serviceOffering) { + protected long configureCustomRootDiskSize(Map customParameters, VMTemplateVO template, HypervisorType hypervisorType, DiskOfferingVO rootDiskOffering) { verifyIfHypervisorSupportsRootdiskSizeOverride(hypervisorType); - DiskOfferingVO diskOffering = _diskOfferingDao.findById(serviceOffering.getId()); - long rootDiskSizeInBytes = diskOffering.getDiskSize(); + long rootDiskSizeInBytes = verifyAndGetDiskSize(rootDiskOffering, NumbersUtil.parseLong(customParameters.get(VmDetailConstants.ROOT_DISK_SIZE), -1)); if (rootDiskSizeInBytes > 0) { //if the size at DiskOffering is not zero then the Service Offering had it configured, it holds priority over the User custom size long rootDiskSizeInGiB = rootDiskSizeInBytes / GiB_TO_BYTES; customParameters.put(VmDetailConstants.ROOT_DISK_SIZE, String.valueOf(rootDiskSizeInGiB)); @@ -4249,12 +4239,12 @@ private UserVmVO commitUserVm(final boolean isImport, final DataCenter zone, fin final long accountId, final long userId, final ServiceOffering offering, final boolean isIso, final String sshPublicKey, final LinkedHashMap> networkNicMap, final long id, final String instanceName, final String uuidName, final HypervisorType hypervisorType, final Map customParameters, final Map> extraDhcpOptionMap, final Map dataDiskTemplateToDiskOfferingMap, - final Map userVmOVFPropertiesMap, final VirtualMachine.PowerState powerState, final boolean dynamicScalingEnabled, String type) throws InsufficientCapacityException { + final Map userVmOVFPropertiesMap, final VirtualMachine.PowerState powerState, final boolean dynamicScalingEnabled, String type, final Long rootDiskOfferingId) throws InsufficientCapacityException { return Transaction.execute(new TransactionCallbackWithException() { @Override public UserVmVO doInTransaction(TransactionStatus status) throws InsufficientCapacityException { UserVmVO vm = new UserVmVO(id, instanceName, displayName, template.getId(), hypervisorType, template.getGuestOSId(), offering.isOfferHA(), - offering.getLimitCpuUse(), owner.getDomainId(), owner.getId(), userId, offering.getId(), userData, hostName, diskOfferingId); + offering.getLimitCpuUse(), owner.getDomainId(), owner.getId(), userId, offering.getId(), userData, hostName); vm.setUuid(uuidName); vm.setDynamicallyScalable(dynamicScalingEnabled); @@ -4370,15 +4360,17 @@ public UserVmVO doInTransaction(TransactionStatus status) throws InsufficientCap computeTags.add(offering.getHostTag()); List rootDiskTags = new ArrayList(); - rootDiskTags.add(offering.getTags()); + DiskOfferingVO rootDiskOfferingVO = _diskOfferingDao.findById(rootDiskOfferingId); + rootDiskTags.add(rootDiskOfferingVO.getTags()); if (isIso) { _orchSrvc.createVirtualMachineFromScratch(vm.getUuid(), Long.toString(owner.getAccountId()), vm.getIsoId().toString(), hostName, displayName, hypervisorType.name(), guestOSCategory.getName(), offering.getCpu(), offering.getSpeed(), offering.getRamSize(), diskSize, computeTags, rootDiskTags, - networkNicMap, plan, extraDhcpOptionMap); + networkNicMap, plan, extraDhcpOptionMap, rootDiskOfferingId); } else { _orchSrvc.createVirtualMachine(vm.getUuid(), Long.toString(owner.getAccountId()), Long.toString(template.getId()), hostName, displayName, hypervisorType.name(), - offering.getCpu(), offering.getSpeed(), offering.getRamSize(), diskSize, computeTags, rootDiskTags, networkNicMap, plan, rootDiskSize, extraDhcpOptionMap, dataDiskTemplateToDiskOfferingMap); + offering.getCpu(), offering.getSpeed(), offering.getRamSize(), diskSize, computeTags, rootDiskTags, networkNicMap, plan, rootDiskSize, extraDhcpOptionMap, + dataDiskTemplateToDiskOfferingMap, diskOfferingId, rootDiskOfferingId); } if (s_logger.isDebugEnabled()) { @@ -4444,13 +4436,13 @@ private UserVmVO commitUserVm(final DataCenter zone, final VirtualMachineTemplat final long accountId, final long userId, final ServiceOfferingVO offering, final boolean isIso, final String sshPublicKey, final LinkedHashMap> networkNicMap, final long id, final String instanceName, final String uuidName, final HypervisorType hypervisorType, final Map customParameters, final Map> extraDhcpOptionMap, final Map dataDiskTemplateToDiskOfferingMap, - Map userVmOVFPropertiesMap, final boolean dynamicScalingEnabled, String type) throws InsufficientCapacityException { + Map userVmOVFPropertiesMap, final boolean dynamicScalingEnabled, String type, final Long rootDiskOfferingId) throws InsufficientCapacityException { return commitUserVm(false, zone, null, null, template, hostName, displayName, owner, diskOfferingId, diskSize, userData, caller, isDisplayVm, keyboard, accountId, userId, offering, isIso, sshPublicKey, networkNicMap, id, instanceName, uuidName, hypervisorType, customParameters, extraDhcpOptionMap, dataDiskTemplateToDiskOfferingMap, - userVmOVFPropertiesMap, null, dynamicScalingEnabled, type); + userVmOVFPropertiesMap, null, dynamicScalingEnabled, type, rootDiskOfferingId); } public void validateRootDiskResize(final HypervisorType hypervisorType, Long rootDiskSize, VMTemplateVO templateVO, UserVmVO vm, final Map customParameters) throws InvalidParameterValueException @@ -5585,12 +5577,17 @@ public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityE } Long serviceOfferingId = cmd.getServiceOfferingId(); + Long overrideDiskOfferingId = cmd.getOverrideDiskOfferingId(); ServiceOffering serviceOffering = _entityMgr.findById(ServiceOffering.class, serviceOfferingId); if (serviceOffering == null) { throw new InvalidParameterValueException("Unable to find service offering: " + serviceOfferingId); } + if (serviceOffering.getDiskOfferingStrictness() && overrideDiskOfferingId != null) { + throw new InvalidParameterValueException(String.format("Cannot override disk offering id %d since provided service offering is strictly mapped to its disk offering", overrideDiskOfferingId)); + } + if (!serviceOffering.isDynamic()) { for(String detail: cmd.getDetails().keySet()) { if(detail.equalsIgnoreCase(VmDetailConstants.CPU_NUMBER) || detail.equalsIgnoreCase(VmDetailConstants.CPU_SPEED) || detail.equalsIgnoreCase(VmDetailConstants.MEMORY)) { @@ -5617,7 +5614,7 @@ public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityE } if (cmd.getDetails().get("rootdisksize") != null) { - throw new InvalidParameterValueException("Overriding root disk size isn't supported for VMs deployed from defploy as-is templates"); + throw new InvalidParameterValueException("Overriding root disk size isn't supported for VMs deployed from deploy as-is templates"); } // Bootmode and boottype are not supported on VMWare dpeloy-as-is templates (since 4.15) @@ -5633,11 +5630,15 @@ public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityE if (diskOffering == null) { throw new InvalidParameterValueException("Unable to find disk offering " + diskOfferingId); } + if (diskOffering.isComputeOnly()) { + throw new InvalidParameterValueException(String.format("The disk offering id %d provided is directly mapped to a service offering, please provide an individual disk offering", diskOfferingId)); + } } if (!zone.isLocalStorageEnabled()) { - if (serviceOffering.isUseLocalStorage()) { - throw new InvalidParameterValueException("Zone is not configured to use local storage but service offering " + serviceOffering.getName() + " uses it"); + DiskOffering diskOfferingMappedInServiceOffering = _entityMgr.findById(DiskOffering.class, serviceOffering.getDiskOfferingId()); + if (diskOfferingMappedInServiceOffering.isUseLocalStorage()) { + throw new InvalidParameterValueException("Zone is not configured to use local storage but disk offering " + diskOfferingMappedInServiceOffering.getName() + " mapped in service offering uses it"); } if (diskOffering != null && diskOffering.isUseLocalStorage()) { throw new InvalidParameterValueException("Zone is not configured to use local storage but disk offering " + diskOffering.getName() + " uses it"); @@ -5680,14 +5681,14 @@ public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityE vm = createBasicSecurityGroupVirtualMachine(zone, serviceOffering, template, getSecurityGroupIdList(cmd), owner, name, displayName, diskOfferingId, size , group , cmd.getHypervisor(), cmd.getHttpMethod(), userData , sshKeyPairName , cmd.getIpToNetworkMap(), addrs, displayVm , keyboard , cmd.getAffinityGroupIdList(), cmd.getDetails(), cmd.getCustomId(), cmd.getDhcpOptionsMap(), - dataDiskTemplateToDiskOfferingMap, userVmOVFProperties, dynamicScalingEnabled); + dataDiskTemplateToDiskOfferingMap, userVmOVFProperties, dynamicScalingEnabled, overrideDiskOfferingId); } } else { if (zone.isSecurityGroupEnabled()) { vm = createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, template, networkIds, getSecurityGroupIdList(cmd), owner, name, displayName, diskOfferingId, size, group, cmd.getHypervisor(), cmd.getHttpMethod(), userData, sshKeyPairName, cmd.getIpToNetworkMap(), addrs, displayVm, keyboard, cmd.getAffinityGroupIdList(), cmd.getDetails(), cmd.getCustomId(), cmd.getDhcpOptionsMap(), - dataDiskTemplateToDiskOfferingMap, userVmOVFProperties, dynamicScalingEnabled); + dataDiskTemplateToDiskOfferingMap, userVmOVFProperties, dynamicScalingEnabled, overrideDiskOfferingId); } else { if (cmd.getSecurityGroupIdList() != null && !cmd.getSecurityGroupIdList().isEmpty()) { @@ -5695,7 +5696,7 @@ public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityE } vm = createAdvancedVirtualMachine(zone, serviceOffering, template, networkIds, owner, name, displayName, diskOfferingId, size, group, cmd.getHypervisor(), cmd.getHttpMethod(), userData, sshKeyPairName, cmd.getIpToNetworkMap(), addrs, displayVm, keyboard, cmd.getAffinityGroupIdList(), cmd.getDetails(), - cmd.getCustomId(), cmd.getDhcpOptionsMap(), dataDiskTemplateToDiskOfferingMap, userVmOVFProperties, dynamicScalingEnabled, null); + cmd.getCustomId(), cmd.getDhcpOptionsMap(), dataDiskTemplateToDiskOfferingMap, userVmOVFProperties, dynamicScalingEnabled, null, overrideDiskOfferingId); } } // check if this templateId has a child ISO @@ -6655,7 +6656,10 @@ private Map getVolumePoolMappingForMigrateVmWithStorage(VMInstanceVO HypervisorType hypervisorType = _volsDao.getHypervisorType(volume.getId()); if (hypervisorType.equals(HypervisorType.VMware)) { try { - boolean isStoragePoolStoragepolicyCompliance = storageManager.isStoragePoolCompliantWithStoragePolicy(Arrays.asList(volume), pool); + DiskOffering diskOffering = _diskOfferingDao.findById(volume.getDiskOfferingId()); + DiskProfile diskProfile = new DiskProfile(volume, diskOffering, _volsDao.getHypervisorType(volume.getId())); + Pair volumeDiskProfilePair = new Pair<>(volume, diskProfile); + boolean isStoragePoolStoragepolicyCompliance = storageManager.isStoragePoolCompliantWithStoragePolicy(Arrays.asList(volumeDiskProfilePair), pool); if (!isStoragePoolStoragepolicyCompliance) { throw new CloudRuntimeException(String.format("Storage pool %s is not storage policy compliance with the volume %s", pool.getUuid(), volume.getUuid())); } @@ -7829,7 +7833,7 @@ public UserVm importVM(final DataCenter zone, final Host host, final VirtualMach null, null, userData, caller, isDisplayVm, keyboard, accountId, userId, serviceOffering, template.getFormat().equals(ImageFormat.ISO), sshPublicKey, null, id, instanceName, uuidName, hypervisorType, customParameters, - null, null, null, powerState, dynamicScalingEnabled, null); + null, null, null, powerState, dynamicScalingEnabled, null, serviceOffering.getDiskOfferingId()); } @Override diff --git a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java index 62298110eae7..3bba233aaf55 100644 --- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java @@ -95,6 +95,7 @@ import com.cloud.server.ManagementService; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; +import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.GuestOS; import com.cloud.storage.GuestOSHypervisor; import com.cloud.storage.Snapshot; @@ -392,16 +393,6 @@ private boolean storagePoolSupportsDiskOffering(StoragePool pool, DiskOffering d return volumeApiService.doesTargetStorageSupportDiskOffering(pool, diskOffering.getTags()); } - private boolean storagePoolSupportsServiceOffering(StoragePool pool, ServiceOffering serviceOffering) { - if (pool == null) { - return false; - } - if (serviceOffering == null) { - return false; - } - return volumeApiService.doesTargetStorageSupportDiskOffering(pool, serviceOffering.getTags()); - } - private ServiceOfferingVO getUnmanagedInstanceServiceOffering(final UnmanagedInstanceTO instance, ServiceOfferingVO serviceOffering, final Account owner, final DataCenter zone, final Map details) throws ServerApiException, PermissionDeniedException, ResourceAllocationException { if (instance == null) { @@ -560,9 +551,6 @@ private void checkUnmanagedDiskAndOfferingForImport(UnmanagedInstanceTO.Disk dis if (diskOffering != null && !migrateAllowed && !storagePoolSupportsDiskOffering(storagePool, diskOffering)) { throw new InvalidParameterValueException(String.format("Disk offering: %s is not compatible with storage pool: %s of unmanaged disk: %s", diskOffering.getUuid(), storagePool.getUuid(), disk.getDiskId())); } - if (serviceOffering != null && !migrateAllowed && !storagePoolSupportsServiceOffering(storagePool, serviceOffering)) { - throw new InvalidParameterValueException(String.format("Service offering: %s is not compatible with storage pool: %s of unmanaged disk: %s", serviceOffering.getUuid(), storagePool.getUuid(), disk.getDiskId())); - } } private void checkUnmanagedDiskAndOfferingForImport(List disks, final Map diskOfferingMap, final Account owner, final DataCenter zone, final Cluster cluster, final boolean migrateAllowed) @@ -811,14 +799,11 @@ private UserVm migrateImportedVM(HostVO sourceHost, VirtualMachineTemplate templ continue; } boolean poolSupportsOfferings = storagePoolSupportsDiskOffering(diskProfileStoragePool.second(), dOffering); - if (poolSupportsOfferings && profile.getType() == Volume.Type.ROOT) { - poolSupportsOfferings = storagePoolSupportsServiceOffering(diskProfileStoragePool.second(), serviceOffering); - } if (poolSupportsOfferings) { continue; } LOGGER.debug(String.format("Volume %s needs to be migrated", volumeVO.getUuid())); - Pair, List> poolsPair = managementService.listStoragePoolsForMigrationOfVolume(profile.getVolumeId()); + Pair, List> poolsPair = managementService.listStoragePoolsForMigrationOfVolumeInternal(profile.getVolumeId(), null, null, null, null, false); if (CollectionUtils.isEmpty(poolsPair.first()) && CollectionUtils.isEmpty(poolsPair.second())) { cleanupFailedImportVM(vm); throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("VM import failed for unmanaged vm: %s during volume ID: %s migration as no suitable pool(s) found", userVm.getInstanceName(), volumeVO.getUuid())); @@ -828,9 +813,8 @@ private UserVm migrateImportedVM(HostVO sourceHost, VirtualMachineTemplate templ if (CollectionUtils.isNotEmpty(storagePools)) { for (StoragePool pool : storagePools) { if (diskProfileStoragePool.second().getId() != pool.getId() && - storagePoolSupportsDiskOffering(pool, dOffering) && - (!profile.getType().equals(Volume.Type.ROOT) || - profile.getType().equals(Volume.Type.ROOT) && storagePoolSupportsServiceOffering(pool, serviceOffering))) { + storagePoolSupportsDiskOffering(pool, dOffering) + ) { storagePool = pool; break; } @@ -841,9 +825,8 @@ private UserVm migrateImportedVM(HostVO sourceHost, VirtualMachineTemplate templ storagePools = poolsPair.first(); for (StoragePool pool : storagePools) { if (diskProfileStoragePool.second().getId() != pool.getId() && - storagePoolSupportsDiskOffering(pool, dOffering) && - (!profile.getType().equals(Volume.Type.ROOT) || - profile.getType().equals(Volume.Type.ROOT) && storagePoolSupportsServiceOffering(pool, serviceOffering))) { + storagePoolSupportsDiskOffering(pool, dOffering) + ) { storagePool = pool; break; } @@ -1021,7 +1004,8 @@ private UserVm importVirtualMachineInternal(final UnmanagedInstanceTO unmanagedI if (details.containsKey("maxIops")) { maxIops = Long.parseLong(details.get("maxIops")); } - diskProfileStoragePoolList.add(importDisk(rootDisk, userVm, cluster, serviceOffering, Volume.Type.ROOT, String.format("ROOT-%d", userVm.getId()), + DiskOfferingVO diskOffering = diskOfferingDao.findById(serviceOffering.getDiskOfferingId()); + diskProfileStoragePoolList.add(importDisk(rootDisk, userVm, cluster, diskOffering, Volume.Type.ROOT, String.format("ROOT-%d", userVm.getId()), (rootDisk.getCapacity() / Resource.ResourceType.bytesToGiB), minIops, maxIops, template, owner, null)); for (UnmanagedInstanceTO.Disk disk : dataDisks) { diff --git a/server/src/main/java/org/cloud/network/router/deployment/RouterDeploymentDefinition.java b/server/src/main/java/org/cloud/network/router/deployment/RouterDeploymentDefinition.java index 24bc501ca73a..6c344e3cb653 100644 --- a/server/src/main/java/org/cloud/network/router/deployment/RouterDeploymentDefinition.java +++ b/server/src/main/java/org/cloud/network/router/deployment/RouterDeploymentDefinition.java @@ -23,6 +23,8 @@ import com.cloud.network.dao.NetworkDetailVO; import com.cloud.network.dao.NetworkDetailsDao; import com.cloud.network.router.VirtualRouter; +import com.cloud.storage.DiskOfferingVO; +import com.cloud.storage.dao.DiskOfferingDao; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.log4j.Logger; @@ -89,6 +91,7 @@ public class RouterDeploymentDefinition { protected VirtualRouterProviderDao vrProviderDao; protected NetworkOfferingDao networkOfferingDao; protected ServiceOfferingDao serviceOfferingDao; + protected DiskOfferingDao diskOfferingDao; protected IpAddressManager ipAddrMgr; protected VMInstanceDao vmDao; protected HostPodDao podDao; @@ -405,8 +408,9 @@ private void verifyServiceOfferingByUuid(String offeringUuid) { logger.debug("Verifying router service offering with uuid : " + offeringUuid); ServiceOfferingVO serviceOffering = serviceOfferingDao.findByUuid(offeringUuid); if (serviceOffering != null && serviceOffering.isSystemUse()) { + DiskOfferingVO diskOffering = diskOfferingDao.findById(serviceOffering.getDiskOfferingId()); boolean isLocalStorage = ConfigurationManagerImpl.SystemVMUseLocalStorage.valueIn(dest.getDataCenter().getId()); - if (isLocalStorage == serviceOffering.isUseLocalStorage()) { + if (isLocalStorage == diskOffering.isUseLocalStorage()) { logger.debug(String.format("Service offering %s (uuid: %s) will be used on virtual router", serviceOffering.getName(), serviceOffering.getUuid())); serviceOfferingId = serviceOffering.getId(); } diff --git a/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java b/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java index 97e15de6f846..87266883d90c 100644 --- a/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java +++ b/server/src/test/java/com/cloud/deploy/DeploymentPlanningManagerImplTest.java @@ -98,7 +98,6 @@ import com.cloud.org.Grouping.AllocationState; 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; @@ -213,8 +212,7 @@ public void testSetUp() { public void dataCenterAvoidTest() throws InsufficientServerCapacityException, AffinityConflictException { ServiceOfferingVO svcOffering = new ServiceOfferingVO("testOffering", 1, 512, 500, 1, 1, false, false, false, "test dpm", - ProvisioningType.THIN, false, false, null, false, VirtualMachine.Type.User, - null, "FirstFitPlanner", true); + false, VirtualMachine.Type.User, null, "FirstFitPlanner", true, false); Mockito.when(vmProfile.getServiceOffering()).thenReturn(svcOffering); DataCenterDeployment plan = new DataCenterDeployment(dataCenterId); @@ -228,8 +226,7 @@ public void dataCenterAvoidTest() throws InsufficientServerCapacityException, Af public void plannerCannotHandleTest() throws InsufficientServerCapacityException, AffinityConflictException { ServiceOfferingVO svcOffering = new ServiceOfferingVO("testOffering", 1, 512, 500, 1, 1, false, false, false, "test dpm", - ProvisioningType.THIN, false, false, null, false, VirtualMachine.Type.User, - null, "UserDispersingPlanner", true); + false, VirtualMachine.Type.User, null, "UserDispersingPlanner", true, false); Mockito.when(vmProfile.getServiceOffering()).thenReturn(svcOffering); DataCenterDeployment plan = new DataCenterDeployment(dataCenterId); @@ -244,8 +241,7 @@ public void plannerCannotHandleTest() throws InsufficientServerCapacityException public void emptyClusterListTest() throws InsufficientServerCapacityException, AffinityConflictException { ServiceOfferingVO svcOffering = new ServiceOfferingVO("testOffering", 1, 512, 500, 1, 1, false, false, false, "test dpm", - ProvisioningType.THIN, false, false, null, false, VirtualMachine.Type.User, - null, "FirstFitPlanner", true); + false, VirtualMachine.Type.User, null, "FirstFitPlanner", true, false); Mockito.when(vmProfile.getServiceOffering()).thenReturn(svcOffering); DataCenterDeployment plan = new DataCenterDeployment(dataCenterId); diff --git a/server/src/test/java/com/cloud/network/element/VirtualRouterElementTest.java b/server/src/test/java/com/cloud/network/element/VirtualRouterElementTest.java index 1b5343881b8b..66898942d76f 100644 --- a/server/src/test/java/com/cloud/network/element/VirtualRouterElementTest.java +++ b/server/src/test/java/com/cloud/network/element/VirtualRouterElementTest.java @@ -95,7 +95,6 @@ import com.cloud.resource.ResourceManager; 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.dao.GuestOSDao; import com.cloud.storage.dao.VMTemplateDao; @@ -336,10 +335,6 @@ private void mockDAOs(final NetworkVO network, final NetworkOfferingVO offering) /* multicastRateMbps */ 0, /* offerHA */ false, "displayText", - ProvisioningType.THIN, - /* useLocalStorage */ false, - /* recreatable */ false, - "tags", /* systemUse */ false, VirtualMachine.Type.DomainRouter, /* defaultUse */ false); diff --git a/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java b/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java index f317ea8acf3b..dac091fbbb3d 100644 --- a/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java +++ b/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java @@ -37,6 +37,8 @@ import com.cloud.api.query.dao.ServiceOfferingJoinDao; import com.cloud.api.query.vo.ServiceOfferingJoinVO; +import com.cloud.service.ServiceOfferingVO; +import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.dao.VMTemplateDao; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.SecurityChecker.AccessType; @@ -158,6 +160,8 @@ public class VolumeApiServiceImplTest { private VMTemplateDao templateDao; @Mock private ServiceOfferingJoinDao serviceOfferingJoinDao; + @Mock + private ServiceOfferingDao serviceOfferingDao; private DetachVolumeCmd detachCmd = new DetachVolumeCmd(); private Class _detachCmdClass = detachCmd.getClass(); @@ -213,7 +217,7 @@ public void setup() throws InterruptedException, ExecutionException { VolumeVO volumeOfRunningVm = new VolumeVO("root", 1L, 1L, 1L, 1L, 1L, "root", "root", Storage.ProvisioningType.THIN, 1, null, null, "root", Volume.Type.ROOT); when(volumeDaoMock.findById(1L)).thenReturn(volumeOfRunningVm); - UserVmVO runningVm = new UserVmVO(1L, "vm", "vm", 1, HypervisorType.XenServer, 1L, false, false, 1L, 1L, 1, 1L, null, "vm", null); + UserVmVO runningVm = new UserVmVO(1L, "vm", "vm", 1, HypervisorType.XenServer, 1L, false, false, 1L, 1L, 1, 1L, null, "vm"); runningVm.setState(State.Running); runningVm.setDataCenterId(1L); when(userVmDaoMock.findById(1L)).thenReturn(runningVm); @@ -223,13 +227,13 @@ public void setup() throws InterruptedException, ExecutionException { volumeOfStoppedVm.setPoolId(1L); when(volumeDaoMock.findById(2L)).thenReturn(volumeOfStoppedVm); - UserVmVO stoppedVm = new UserVmVO(2L, "vm", "vm", 1, HypervisorType.XenServer, 1L, false, false, 1L, 1L, 1, 1L, null, "vm", null); + UserVmVO stoppedVm = new UserVmVO(2L, "vm", "vm", 1, HypervisorType.XenServer, 1L, false, false, 1L, 1L, 1, 1L, null, "vm"); stoppedVm.setState(State.Stopped); stoppedVm.setDataCenterId(1L); when(userVmDaoMock.findById(2L)).thenReturn(stoppedVm); // volume of hyperV vm id=3 - UserVmVO hyperVVm = new UserVmVO(3L, "vm", "vm", 1, HypervisorType.Hyperv, 1L, false, false, 1L, 1L, 1, 1L, null, "vm", null); + UserVmVO hyperVVm = new UserVmVO(3L, "vm", "vm", 1, HypervisorType.Hyperv, 1L, false, false, 1L, 1L, 1, 1L, null, "vm"); hyperVVm.setState(State.Stopped); hyperVVm.setDataCenterId(1L); when(userVmDaoMock.findById(3L)).thenReturn(hyperVVm); @@ -285,7 +289,7 @@ public void setup() throws InterruptedException, ExecutionException { when(volumeDaoMock.findById(7L)).thenReturn(managedVolume1); // vm having root volume - UserVmVO vmHavingRootVolume = new UserVmVO(4L, "vm", "vm", 1, HypervisorType.XenServer, 1L, false, false, 1L, 1L, 1, 1L, null, "vm", null); + UserVmVO vmHavingRootVolume = new UserVmVO(4L, "vm", "vm", 1, HypervisorType.XenServer, 1L, false, false, 1L, 1L, 1, 1L, null, "vm"); vmHavingRootVolume.setState(State.Stopped); vmHavingRootVolume.setDataCenterId(1L); when(userVmDaoMock.findById(4L)).thenReturn(vmHavingRootVolume); @@ -570,6 +574,27 @@ public void validateConditionsToReplaceDiskOfferingOfVolumeTestNoNewDiskOffering @Test public void validateConditionsToReplaceDiskOfferingOfVolumeTestRootVolume() { Mockito.lenient().when(volumeVoMock.getVolumeType()).thenReturn(Type.ROOT); + Mockito.doReturn(vmInstanceMockId).when(volumeVoMock).getInstanceId(); + UserVmVO vm = Mockito.mock(UserVmVO.class); + when(_vmInstanceDao.findById(anyLong())).thenReturn(vm); + when(vm.getServiceOfferingId()).thenReturn(1L); + ServiceOfferingVO serviceOfferingVO = Mockito.mock(ServiceOfferingVO.class); + serviceOfferingVO.setDiskOfferingStrictness(false); + when(serviceOfferingDao.findById(anyLong())).thenReturn(serviceOfferingVO); + + volumeApiServiceImpl.validateConditionsToReplaceDiskOfferingOfVolume(volumeVoMock, newDiskOfferingMock, storagePoolMock); + } + + @Test(expected = InvalidParameterValueException.class) + public void validateConditionsToReplaceDiskOfferingOfVolumeTestRootVolumeWithDiskOfferingStrictnessTrue() { + Mockito.lenient().when(volumeVoMock.getVolumeType()).thenReturn(Type.ROOT); + Mockito.doReturn(vmInstanceMockId).when(volumeVoMock).getInstanceId(); + UserVmVO vm = Mockito.mock(UserVmVO.class); + when(_vmInstanceDao.findById(anyLong())).thenReturn(vm); + when(vm.getServiceOfferingId()).thenReturn(1L); + ServiceOfferingVO serviceOfferingVO = Mockito.mock(ServiceOfferingVO.class); + when(serviceOfferingDao.findById(anyLong())).thenReturn(serviceOfferingVO); + when(serviceOfferingVO.getDiskOfferingStrictness()).thenReturn(true); volumeApiServiceImpl.validateConditionsToReplaceDiskOfferingOfVolume(volumeVoMock, newDiskOfferingMock, storagePoolMock); } diff --git a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java index a9821094f067..c9c07c48b525 100644 --- a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java +++ b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java @@ -72,7 +72,6 @@ import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.GuestOSVO; -import com.cloud.storage.Storage; import com.cloud.storage.dao.GuestOSDao; import com.cloud.user.Account; import com.cloud.user.AccountManager; @@ -96,9 +95,6 @@ public class UserVmManagerImplTest { @Mock private DiskOfferingDao diskOfferingDao; - @Mock - private ServiceOfferingVO serviceOfferingVO; - @Mock private DataCenterDao _dcDao; @Mock @@ -223,8 +219,9 @@ private ServiceOfferingVO getSvcoffering(int ramSize) { boolean ha = false; boolean useLocalStorage = false; - ServiceOfferingVO serviceOffering = new ServiceOfferingVO(name, cpu, ramSize, speed, null, null, ha, displayText, Storage.ProvisioningType.THIN, useLocalStorage, false, null, false, null, + ServiceOfferingVO serviceOffering = new ServiceOfferingVO(name, cpu, ramSize, speed, null, null, ha, displayText, false, null, false); + serviceOffering.setDiskOfferingId(1l); return serviceOffering; } @@ -471,16 +468,13 @@ private void prepareAndRunConfigureCustomRootDiskSizeTest(Map cu VMTemplateVO template = Mockito.mock(VMTemplateVO.class); Mockito.when(template.getId()).thenReturn(1l); Mockito.when(template.getSize()).thenReturn(99L * GiB_TO_BYTES); - ServiceOfferingVO offering = Mockito.mock(ServiceOfferingVO.class); - Mockito.when(offering.getId()).thenReturn(1l); Mockito.when(templateDao.findById(Mockito.anyLong())).thenReturn(template); DiskOfferingVO diskfferingVo = Mockito.mock(DiskOfferingVO.class); - Mockito.when(diskOfferingDao.findById(Mockito.anyLong())).thenReturn(diskfferingVo); Mockito.when(diskfferingVo.getDiskSize()).thenReturn(offeringRootDiskSize); - long rootDiskSize = userVmManagerImpl.configureCustomRootDiskSize(customParameters, template, Hypervisor.HypervisorType.KVM, offering); + long rootDiskSize = userVmManagerImpl.configureCustomRootDiskSize(customParameters, template, Hypervisor.HypervisorType.KVM, diskfferingVo); Assert.assertEquals(expectedRootDiskSize, rootDiskSize); Mockito.verify(userVmManagerImpl, Mockito.times(timesVerifyIfHypervisorSupports)).verifyIfHypervisorSupportsRootdiskSizeOverride(Mockito.any()); diff --git a/server/src/test/java/com/cloud/vm/UserVmManagerTest.java b/server/src/test/java/com/cloud/vm/UserVmManagerTest.java index 37cd6e512999..a3fb72c71bfe 100644 --- a/server/src/test/java/com/cloud/vm/UserVmManagerTest.java +++ b/server/src/test/java/com/cloud/vm/UserVmManagerTest.java @@ -97,7 +97,6 @@ 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.storage.VMTemplateVO; import com.cloud.storage.VolumeVO; import com.cloud.storage.dao.VMTemplateDao; @@ -452,7 +451,7 @@ private ServiceOfferingVO getSvcoffering(int ramSize) { boolean ha = false; boolean useLocalStorage = false; - ServiceOfferingVO serviceOffering = new ServiceOfferingVO(name, cpu, ramSize, speed, null, null, ha, displayText, Storage.ProvisioningType.THIN, useLocalStorage, false, null, false, null, + ServiceOfferingVO serviceOffering = new ServiceOfferingVO(name, cpu, ramSize, speed, null, null, ha, displayText, false, null, false); return serviceOffering; } @@ -517,7 +516,7 @@ public void testMoveVmToUser2() throws Exception { AccountVO newAccount = new AccountVO("testaccount", 1, "networkdomain", (short)1, UUID.randomUUID().toString()); newAccount.setId(2L); - UserVmVO vm = new UserVmVO(10L, "test", "test", 1L, HypervisorType.Any, 1L, false, false, 1L, 1L, 1, 5L, "test", "test", 1L); + UserVmVO vm = new UserVmVO(10L, "test", "test", 1L, HypervisorType.Any, 1L, false, false, 1L, 1L, 1, 5L, "test", "test"); vm.setState(VirtualMachine.State.Stopped); when(_vmDao.findById(anyLong())).thenReturn(vm); diff --git a/server/src/test/java/com/cloud/vm/dao/UserVmDaoImplTest.java b/server/src/test/java/com/cloud/vm/dao/UserVmDaoImplTest.java index 49de99ad8039..7dbfba304e85 100644 --- a/server/src/test/java/com/cloud/vm/dao/UserVmDaoImplTest.java +++ b/server/src/test/java/com/cloud/vm/dao/UserVmDaoImplTest.java @@ -48,7 +48,7 @@ public void makeAndVerifyEntry(Long vmId, String instanceName, String displayNam // Persist the data. UserVmVO vo = new UserVmVO(vmId, instanceName, displayName, templateId, hypervisor, guestOsId, haEnabled, limitCpuUse, domainId, accountId, 1, serviceOfferingId, userdata, - name, diskOfferingId); + name); dao.persist(vo); vo = dao.findById(vmId); diff --git a/server/src/test/java/org/apache/cloudstack/affinity/AffinityApiUnitTest.java b/server/src/test/java/org/apache/cloudstack/affinity/AffinityApiUnitTest.java index 0d3664833005..8d7a645e6bcf 100644 --- a/server/src/test/java/org/apache/cloudstack/affinity/AffinityApiUnitTest.java +++ b/server/src/test/java/org/apache/cloudstack/affinity/AffinityApiUnitTest.java @@ -205,7 +205,7 @@ public void deleteAffinityGroupNullIdName() throws ResourceInUseException { @Test(expected = InvalidParameterValueException.class) public void updateAffinityGroupVMRunning() throws ResourceInUseException { - UserVmVO vm = new UserVmVO(10L, "test", "test", 101L, HypervisorType.Any, 21L, false, false, domainId, 200L, 1, 5L, "", "test", 1L); + UserVmVO vm = new UserVmVO(10L, "test", "test", 101L, HypervisorType.Any, 21L, false, false, domainId, 200L, 1, 5L, "", "test"); vm.setState(VirtualMachine.State.Running); when(_vmDao.findById(10L)).thenReturn(vm); diff --git a/server/src/test/java/org/apache/cloudstack/affinity/AffinityGroupServiceImplTest.java b/server/src/test/java/org/apache/cloudstack/affinity/AffinityGroupServiceImplTest.java index 8aa4aa8277ec..607bf0436d45 100644 --- a/server/src/test/java/org/apache/cloudstack/affinity/AffinityGroupServiceImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/affinity/AffinityGroupServiceImplTest.java @@ -270,7 +270,7 @@ public void deleteAffinityGroupNullIdName() throws ResourceInUseException { @Test(expected = InvalidParameterValueException.class) public void updateAffinityGroupVMRunning() throws ResourceInUseException { when(_acctMgr.finalizeOwner((Account)anyObject(), anyString(), anyLong(), anyLong())).thenReturn(acct); - UserVmVO vm = new UserVmVO(10L, "test", "test", 101L, HypervisorType.Any, 21L, false, false, DOMAIN_ID, 200L, 1, 5L, "", "test", 1L); + UserVmVO vm = new UserVmVO(10L, "test", "test", 101L, HypervisorType.Any, 21L, false, false, DOMAIN_ID, 200L, 1, 5L, "", "test"); vm.setState(VirtualMachine.State.Running); when(_vmDao.findById(10L)).thenReturn(vm); diff --git a/server/src/test/java/org/apache/cloudstack/service/ServiceOfferingVOTest.java b/server/src/test/java/org/apache/cloudstack/service/ServiceOfferingVOTest.java index 0d4630a38d3a..9fa67b0f2d22 100644 --- a/server/src/test/java/org/apache/cloudstack/service/ServiceOfferingVOTest.java +++ b/server/src/test/java/org/apache/cloudstack/service/ServiceOfferingVOTest.java @@ -21,7 +21,6 @@ import org.junit.Test; import org.mockito.MockitoAnnotations; -import com.cloud.storage.Storage; import com.cloud.service.ServiceOfferingVO; import com.cloud.vm.VirtualMachine; @@ -32,8 +31,8 @@ public class ServiceOfferingVOTest { @Before public void setup() { MockitoAnnotations.initMocks(this); - 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); + offeringCustom = new ServiceOfferingVO("custom", null, null, 500, 10, 10, false, "custom", false, VirtualMachine.Type.User, false); + offering = new ServiceOfferingVO("normal", 1, 1000, 500, 10, 10, false, "normal", false, VirtualMachine.Type.User, false); } // Test restoreVm when VM state not in running/stopped case diff --git a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java index a8c64f39d5fa..eadb1b3c665d 100644 --- a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java +++ b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java @@ -266,13 +266,13 @@ public void setUp() throws Exception { when(templateDao.findByName(Mockito.anyString())).thenReturn(template); ServiceOfferingVO serviceOffering = Mockito.mock(ServiceOfferingVO.class); when(serviceOffering.getId()).thenReturn(1L); - when(serviceOffering.getTags()).thenReturn(""); when(serviceOffering.isDynamic()).thenReturn(false); when(serviceOffering.getCpu()).thenReturn(instance.getCpuCores()); when(serviceOffering.getRamSize()).thenReturn(instance.getMemory()); when(serviceOffering.getSpeed()).thenReturn(instance.getCpuSpeed()); when(serviceOfferingDao.findById(Mockito.anyLong())).thenReturn(serviceOffering); DiskOfferingVO diskOfferingVO = Mockito.mock(DiskOfferingVO.class); + when(diskOfferingVO.getTags()).thenReturn(""); when(diskOfferingVO.isCustomized()).thenReturn(false); when(diskOfferingVO.getDiskSize()).thenReturn(Long.MAX_VALUE); when(diskOfferingDao.findById(Mockito.anyLong())).thenReturn(diskOfferingVO); diff --git a/test/integration/smoke/test_service_offerings.py b/test/integration/smoke/test_service_offerings.py index 3a942a10b62c..1f171913b474 100644 --- a/test/integration/smoke/test_service_offerings.py +++ b/test/integration/smoke/test_service_offerings.py @@ -25,6 +25,7 @@ cleanup_resources, random_gen) from marvin.lib.base import (ServiceOffering, + DiskOffering, Account, VirtualMachine) from marvin.lib.common import (list_service_offering, @@ -338,17 +339,17 @@ def setUpClass(cls): cls.apiclient, cls.services["service_offerings"]["tiny"] ) - template = get_test_template( + cls.template = get_test_template( cls.apiclient, cls.zone.id, cls.hypervisor ) - if template == FAILED: + if cls.template == FAILED: assert False, "get_test_template() failed to return template" # Set Zones and disk offerings cls.services["small"]["zoneid"] = cls.zone.id - cls.services["small"]["template"] = template.id + cls.services["small"]["template"] = cls.template.id # Create VMs, NAT Rules etc @@ -614,6 +615,204 @@ def test_04_change_offering_small(self): ) return + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_05_disk_offering_strictness_true(self): + """Test to see change service offering is not possible when disk offering strictness is set to true + """ + # Validate the following + # 1. Create service offering linked a disk offering and disk offering strictness is true + # 2. Create a VM with that service offering + # 3. Create another service offering with a different disk offering + # 4. Try change service offering for VM and it will fail since disk offering strictness is true (not allowed to change the disk offering) + + if self.hypervisor.lower() == "lxc": + self.skipTest("Skipping this test for {} due to bug CS-38153".format(self.hypervisor)) + offering_data = { + 'displaytext': 'TestDiskOfferingStrictnessTrue', + 'cpuspeed': 512, + 'cpunumber': 2, + 'name': 'TestDiskOfferingStrictnessTrue', + 'memory': 1024, + 'diskofferingstrictness': True + } + + self.serviceOfferingWithDiskOfferingStrictnessTrue = ServiceOffering.create( + self.apiclient, + offering_data, + ) + self._cleanup.append(self.serviceOfferingWithDiskOfferingStrictnessTrue) + + self.virtual_machine_with_diskoffering_strictness_true = VirtualMachine.create( + self.apiclient, + self.services["small"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.serviceOfferingWithDiskOfferingStrictnessTrue.id, + mode=self.services["mode"] + ) + + try: + self.virtual_machine_with_diskoffering_strictness_true.stop(self.apiclient) + + timeout = self.services["timeout"] + + while True: + time.sleep(self.services["sleep"]) + + # Ensure that VM is in stopped state + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.virtual_machine_with_diskoffering_strictness_true.id + ) + + if isinstance(list_vm_response, list): + vm = list_vm_response[0] + if vm.state == 'Stopped': + self.debug("VM state: %s" % vm.state) + break + + if timeout == 0: + raise Exception( + "Failed to stop VM (ID: %s) in change service offering" % vm.id) + + timeout = timeout - 1 + except Exception as e: + self.fail("Failed to stop VM: %s" % e) + + offering_data = { + 'displaytext': 'TestDiskOfferingStrictnessTrue2', + 'cpuspeed': 1000, + 'cpunumber': 2, + 'name': 'TestDiskOfferingStrictnessTrue2', + 'memory': 1024, + 'diskofferingstrictness': True + } + + self.serviceOfferingWithDiskOfferingStrictnessTrue2 = ServiceOffering.create( + self.apiclient, + offering_data, + ) + self._cleanup.append(self.serviceOfferingWithDiskOfferingStrictnessTrue2) + cmd = changeServiceForVirtualMachine.changeServiceForVirtualMachineCmd() + cmd.id = self.virtual_machine_with_diskoffering_strictness_true.id + cmd.serviceofferingid = self.serviceOfferingWithDiskOfferingStrictnessTrue2.id + + with self.assertRaises(Exception) as e: + self.apiclient.changeServiceForVirtualMachine(cmd) + self.debug("Upgrade VM with new service offering having different disk offering operation failed as expected with exception: %s" % + e.exception) + return + + @attr(tags=["advanced", "advancedns", "smoke"], required_hardware="true") + def test_06_disk_offering_strictness_false(self): + """Test to see change service offering is possible when disk offering strictness is set to false + """ + # Validate the following + # 1. Create service offering linked a disk offering and disk offering strictness is false + # 2. Create a VM with that service offering + # 3. Create another service offering with a different disk offering and disk offering strictness is false + # 4. Try change service offering for VM should succeed + + if self.hypervisor.lower() == "lxc": + self.skipTest("Skipping this test for {} due to bug CS-38153".format(self.hypervisor)) + + offering_data = { + 'displaytext': 'TestDiskOfferingStrictnessFalse', + 'cpuspeed': 512, + 'cpunumber': 2, + 'name': 'TestDiskOfferingStrictnessFalse', + 'memory': 1024, + 'diskofferingstrictness': False + } + + self.serviceOfferingWithDiskOfferingStrictnessFalse = ServiceOffering.create( + self.apiclient, + offering_data, + ) + self._cleanup.append(self.serviceOfferingWithDiskOfferingStrictnessFalse) + + self.virtual_machine_with_diskoffering_strictness_false = VirtualMachine.create( + self.apiclient, + self.services["small"], + accountid=self.account.name, + domainid=self.account.domainid, + serviceofferingid=self.serviceOfferingWithDiskOfferingStrictnessFalse.id, + mode=self.services["mode"] + ) + + try: + self.virtual_machine_with_diskoffering_strictness_false.stop(self.apiclient) + + timeout = self.services["timeout"] + + while True: + time.sleep(self.services["sleep"]) + + # Ensure that VM is in stopped state + list_vm_response = list_virtual_machines( + self.apiclient, + id=self.virtual_machine_with_diskoffering_strictness_false.id + ) + + if isinstance(list_vm_response, list): + vm = list_vm_response[0] + if vm.state == 'Stopped': + self.debug("VM state: %s" % vm.state) + break + + if timeout == 0: + raise Exception( + "Failed to stop VM (ID: %s) in change service offering" % vm.id) + + timeout = timeout - 1 + except Exception as e: + self.fail("Failed to stop VM: %s" % e) + + self.disk_offering2 = DiskOffering.create( + self.apiclient, + self.services["disk_offering"], + ) + self._cleanup.append(self.disk_offering2) + offering_data = { + 'displaytext': 'TestDiskOfferingStrictnessFalse2', + 'cpuspeed': 1000, + 'cpunumber': 2, + 'name': 'TestDiskOfferingStrictnessFalse2', + 'memory': 1024, + 'diskofferingstrictness': False, + 'diskofferingid': self.disk_offering2.id + } + + self.serviceOfferingWithDiskOfferingStrictnessFalse2 = ServiceOffering.create( + self.apiclient, + offering_data, + ) + self._cleanup.append(self.serviceOfferingWithDiskOfferingStrictnessFalse2) + cmd = changeServiceForVirtualMachine.changeServiceForVirtualMachineCmd() + cmd.id = self.virtual_machine_with_diskoffering_strictness_false.id + cmd.serviceofferingid = self.serviceOfferingWithDiskOfferingStrictnessFalse2.id + self.apiclient.changeServiceForVirtualMachine(cmd) + + list_vm_response = VirtualMachine.list( + self.apiclient, + id=self.virtual_machine_with_diskoffering_strictness_false.id + ) + + vm_response = list_vm_response[0] + self.assertEqual( + vm_response.id, + self.virtual_machine_with_diskoffering_strictness_false.id, + "Check virtual machine ID of upgraded VM" + ) + + self.assertEqual( + vm_response.serviceofferingid, + self.serviceOfferingWithDiskOfferingStrictnessFalse2.id, + "Check service offering of the VM" + ) + + return + class TestCpuCapServiceOfferings(cloudstackTestCase): def setUp(self): diff --git a/test/integration/smoke/test_volumes.py b/test/integration/smoke/test_volumes.py index 130828596821..c9b93494dc99 100644 --- a/test/integration/smoke/test_volumes.py +++ b/test/integration/smoke/test_volumes.py @@ -606,38 +606,6 @@ def test_07_resize_fail(self): with self.assertRaises(Exception): self.apiClient.resizeVolume(cmd) - # Ok, now let's try and resize a volume that is not custom. - cmd.id = self.volume.id - cmd.diskofferingid = self.services['diskofferingid'] - cmd.size = 4 - - self.debug( - "Attaching volume (ID: %s) to VM (ID: %s)" % ( - self.volume.id, - self.virtual_machine.id) - ) - # attach the volume - self.virtual_machine.attach_volume(self.apiClient, self.volume) - self.attached = True - # stop the vm if it is on xenserver - hosts = Host.list(self.apiClient, id=self.virtual_machine.hostid) - self.assertTrue(isinstance(hosts, list)) - self.assertTrue(len(hosts) > 0) - self.debug("Found %s host" % hosts[0].hypervisor) - - if hosts[0].hypervisor == "XenServer": - self.virtual_machine.stop(self.apiClient) - elif hosts[0].hypervisor.lower() == "hyperv": - self.skipTest("Resize Volume is unsupported on Hyper-V") - - # Attempting to resize it should throw an exception, as we're using a non - # customisable disk offering, therefore our size parameter should be ignored - with self.assertRaises(Exception): - self.apiClient.resizeVolume(cmd) - - if hosts[0].hypervisor == "XenServer": - self.virtual_machine.start(self.apiClient) - time.sleep(30) return @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="true") @@ -758,6 +726,67 @@ def test_08_resize_volume(self): time.sleep(30) return + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="true") + def test_12_resize_volume_with_only_size_parameter(self): + """Test resize a volume by providing only size parameter, disk offering id is not mandatory""" + # Verify the size is the new size is what we wanted it to be. + self.debug( + "Attaching volume (ID: %s) to VM (ID: %s)" % ( + self.volume.id, + self.virtual_machine.id + )) + + self.virtual_machine.attach_volume(self.apiClient, self.volume) + self.attached = True + hosts = Host.list(self.apiClient, id=self.virtual_machine.hostid) + self.assertTrue(isinstance(hosts, list)) + self.assertTrue(len(hosts) > 0) + self.debug("Found %s host" % hosts[0].hypervisor) + + if hosts[0].hypervisor == "XenServer": + self.virtual_machine.stop(self.apiClient) + elif hosts[0].hypervisor.lower() == "hyperv": + self.skipTest("Resize Volume is unsupported on Hyper-V") + + # resize the data disk + self.debug("Resize Volume ID: %s" % self.volume.id) + + cmd = resizeVolume.resizeVolumeCmd() + cmd.id = self.volume.id + cmd.size = 20 + + self.apiClient.resizeVolume(cmd) + + count = 0 + success = False + while count < 3: + list_volume_response = Volume.list( + self.apiClient, + id=self.volume.id, + type='DATADISK' + ) + for vol in list_volume_response: + if vol.id == self.volume.id and int(vol.size) == (20 * (1024 ** 3)) and vol.state == 'Ready': + success = True + if success: + break + else: + time.sleep(10) + count += 1 + + self.assertEqual( + success, + True, + "Check if the data volume resized appropriately" + ) + + # start the vm if it is on xenserver + + if hosts[0].hypervisor == "XenServer": + self.virtual_machine.start(self.apiClient) + time.sleep(30) + return + @attr(tags=["advanced", "advancedns", "smoke", "basic"], required_hardware="false") def test_09_delete_detached_volume(self): """Delete a Volume unattached to an VM diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py index 910e8bd7c047..6fa55153c8ed 100755 --- a/tools/marvin/marvin/lib/base.py +++ b/tools/marvin/marvin/lib/base.py @@ -2305,6 +2305,12 @@ def create(cls, apiclient, services, tags=None, domainid=None, cacheMode=None, * if "dynamicscalingenabled" in services: cmd.dynamicscalingenabled = services["dynamicscalingenabled"] + if "diskofferingstrictness" in services: + cmd.diskofferingstrictness = services["diskofferingstrictness"] + + if "diskofferingid" in services: + cmd.diskofferingid = services["diskofferingid"] + # Service Offering private to that domain if domainid: cmd.domainid = domainid diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 3fc27cd20b3d..0c14d8417ecf 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -470,6 +470,7 @@ "label.asyncbackup": "Async Backup", "label.authentication.method": "Authentication Method", "label.authentication.sshkey": "System SSH Key", +"label.automigrate.volume": "Auto migrate volume to another storage pool if required", "label.author.email": "Author e-mail", "label.author.name": "Author name", "label.auto.assign": "Automatically assign", @@ -558,6 +559,7 @@ "label.change.ip.addess": "Change IP Address", "label.change.ipaddress": "Change IP address for NIC", "label.change.service.offering": "Change service offering", +"label.change.offering.for.volume": "Change disk offering for the volume", "label.change.value": "Change value", "label.character": "Character", "label.chassis": "Chassis", @@ -863,6 +865,9 @@ "label.duration.in.sec": "Duration (in sec)", "label.dynamicscalingenabled": "Dynamic Scaling Enabled", "label.dynamicscalingenabled.tooltip": "VM can dynamically scale only when dynamic scaling is enabled on template, service offering and global setting", +"label.diskofferingstrictness": "Disk Offering Strictness", +"label.disksizestrictness": "Disk Size Strictness", +"label.computeonly.offering": "Compute only Disk Offering", "label.edit": "Edit", "label.edit.acl.list": "Edit ACL List", "label.edit.acl.rule": "Edit ACL rule", @@ -1440,6 +1445,7 @@ "label.migrate.to.host": "Migrate to host", "label.migrate.to.storage": "Migrate to storage", "label.migrate.volume": "Migrate Volume", +"message.migrate.volume.tooltip": "Volume can be migrated to any suitable storage pool. Admin has to choose the appropriate disk offering to replace, that supports the new storage pool", "label.migrate.volume.newdiskoffering.desc": "This option allows administrators to replace the old disk offering, using one that better suits the new placement of the volume.", "label.migrate.volume.to.primary.storage": "Migrate volume to another primary storage", "label.migrate.with.storage": "Migrate with storage", @@ -1607,6 +1613,8 @@ "label.override.rootdisk.size": "Override Root Disk Size", "label.overrideguesttraffic": "Override Guest-Traffic", "label.overridepublictraffic": "Override Public-Traffic", +"label.override.diskoffering": "Override Disk Offering", +"label.override.root.diskoffering": "Override Root Disk Offering", "label.ovf.properties": "vApp Properties", "label.ovm3cluster": "Native Clustering", "label.ovm3networklabel": "OVM3 Traffic Label", @@ -2675,6 +2683,10 @@ "message.basic.mode.desc": "Choose this network model if you do *not* want to enable any VLAN support. All virtual instances created under this network model will be assigned an IP directly from the network and security groups are used to provide security and segregation.", "message.certificate.upload.processing": "Certificate upload in progress", "message.change.offering.confirm": "Please confirm that you wish to change the service offering of this virtual instance.", +"message.confirm.change.offering.for.volume": "Please confirm that you want to change disk offering for the volume", +"message.change.offering.for.volume.failed": "Change offering for the volume failed", +"message.change.offering.for.volume.processing": "Changing offering for the volume...", +"message.change.offering.for.volume": "Successfully changed offering for the volume", "message.change.password": "Please change your password", "message.cluster.dedicated": "Cluster Dedicated", "message.cluster.dedication.released": "Cluster dedication released", diff --git a/ui/src/config/section/offering.js b/ui/src/config/section/offering.js index 05da70a98b1d..3480aa8016f4 100644 --- a/ui/src/config/section/offering.js +++ b/ui/src/config/section/offering.js @@ -31,7 +31,7 @@ export default { params: { isrecursive: 'true' }, columns: ['name', 'displaytext', 'cpunumber', 'cpuspeed', 'memory', 'domain', 'zone', 'order'], details: () => { - var fields = ['name', 'id', 'displaytext', 'offerha', 'provisioningtype', 'storagetype', 'iscustomized', 'iscustomizediops', 'limitcpuuse', 'cpunumber', 'cpuspeed', 'memory', 'hosttags', 'storagetags', 'domain', 'zone', 'created', 'dynamicscalingenabled'] + var fields = ['name', 'id', 'displaytext', 'offerha', 'provisioningtype', 'storagetype', 'iscustomized', 'iscustomizediops', 'limitcpuuse', 'cpunumber', 'cpuspeed', 'memory', 'hosttags', 'tags', 'storagetags', 'domain', 'zone', 'created', 'dynamicscalingenabled', 'diskofferingstrictness'] if (store.getters.apis.createServiceOffering && store.getters.apis.createServiceOffering.params.filter(x => x.name === 'storagepolicy').length > 0) { fields.splice(6, 0, 'vspherestoragepolicy') @@ -101,7 +101,7 @@ export default { permission: ['listServiceOfferings', 'listInfrastructure'], params: { issystem: 'true', isrecursive: 'true' }, columns: ['name', 'systemvmtype', 'cpunumber', 'cpuspeed', 'memory', 'storagetype', 'order'], - details: ['name', 'id', 'displaytext', 'systemvmtype', 'provisioningtype', 'storagetype', 'iscustomized', 'limitcpuuse', 'cpunumber', 'cpuspeed', 'memory', 'hosttags', 'tags', 'domain', 'zone', 'created', 'dynamicscalingenabled'], + details: ['name', 'id', 'displaytext', 'systemvmtype', 'provisioningtype', 'storagetype', 'iscustomized', 'limitcpuuse', 'cpunumber', 'cpuspeed', 'memory', 'hosttags', 'tags', 'domain', 'zone', 'created', 'dynamicscalingenabled', 'diskofferingstrictness'], actions: [{ api: 'createServiceOffering', icon: 'plus', @@ -141,7 +141,7 @@ export default { params: { isrecursive: 'true' }, columns: ['name', 'displaytext', 'disksize', 'domain', 'zone', 'order'], details: () => { - var fields = ['name', 'id', 'displaytext', 'disksize', 'provisioningtype', 'storagetype', 'iscustomized', 'iscustomizediops', 'tags', 'domain', 'zone', 'created'] + var fields = ['name', 'id', 'displaytext', 'disksize', 'provisioningtype', 'storagetype', 'iscustomized', 'disksizestrictness', 'iscustomizediops', 'tags', 'domain', 'zone', 'created'] if (store.getters.apis.createDiskOffering && store.getters.apis.createDiskOffering.params.filter(x => x.name === 'storagepolicy').length > 0) { fields.splice(6, 0, 'vspherestoragepolicy') diff --git a/ui/src/config/section/storage.js b/ui/src/config/section/storage.js index 48f10d08547e..9100104e7f4d 100644 --- a/ui/src/config/section/storage.js +++ b/ui/src/config/section/storage.js @@ -197,6 +197,17 @@ export default { popup: true, component: () => import('@/views/storage/MigrateVolume.vue') }, + { + api: 'changeOfferingForVolume', + icon: 'swap', + docHelp: 'adminguide/storage.html#id2', + label: 'label.change.offering.for.volume', + args: ['id', 'diskofferingid', 'size', 'miniops', 'maxiops', 'automigrate'], + dataView: true, + show: (record, store) => { return ['Allocated', 'Ready'].includes(record.state) && ['Admin'].includes(store.userInfo.roletype) }, + popup: true, + component: () => import('@/views/storage/ChangeOfferingForVolume.vue') + }, { api: 'extractVolume', icon: 'cloud-download', diff --git a/ui/src/views/compute/DeployVM.vue b/ui/src/views/compute/DeployVM.vue index 2d942115a2b0..b876e9feff9e 100644 --- a/ui/src/views/compute/DeployVM.vue +++ b/ui/src/views/compute/DeployVM.vue @@ -148,8 +148,9 @@ {{ $t('label.override.rootdisk.size') }}
{{ this.$t('message.deployasis') }}
@@ -273,6 +274,65 @@
+ + {{ $t('label.override.root.diskoffering') }} + + + + + + + + + + @@ -819,6 +879,7 @@ export default { networksAdd: [], zone: {}, sshKeyPair: {}, + overrideDiskOffering: {}, templateFilter: [ 'featured', 'community', @@ -839,10 +900,12 @@ export default { dataPreFill: {}, showDetails: false, showRootDiskSizeChanger: false, + showOverrideDiskOfferingOption: false, securitygroupids: [], rootDiskSizeFixed: 0, error: false, diskSelected: {}, + rootDiskSelected: {}, diskIOpsMin: 0, diskIOpsMax: 0, minIops: 0, @@ -1092,7 +1155,7 @@ export default { return this.diskSelected?.iscustomizediops || false }, isCustomizedIOPS () { - return this.serviceOffering?.iscustomizediops || false + return this.rootDiskSelected?.iscustomizediops || this.serviceOffering?.iscustomizediops || false } }, watch: { @@ -1126,7 +1189,25 @@ export default { } this.serviceOffering = _.find(this.options.serviceOfferings, (option) => option.id === instanceConfig.computeofferingid) - this.diskOffering = _.find(this.options.diskOfferings, (option) => option.id === instanceConfig.diskofferingid) + if (this.serviceOffering?.diskofferingid) { + if (iso) { + this.diskOffering = _.find(this.options.diskOfferings, (option) => option.id === this.serviceOffering.diskofferingid) + } else { + instanceConfig.overridediskofferingid = this.serviceOffering.diskofferingid + } + } + if (!iso && this.diskSelected) { + this.diskOffering = _.find(this.options.diskOfferings, (option) => option.id === instanceConfig.diskofferingid) + } + if (this.rootDiskSelected?.id) { + instanceConfig.overridediskofferingid = this.rootDiskSelected.id + } + console.log('overrided value ' + instanceConfig.overridediskofferingid) + if (instanceConfig.overridediskofferingid) { + this.overrideDiskOffering = _.find(this.options.diskOfferings, (option) => option.id === instanceConfig.overridediskofferingid) + } else { + this.overrideDiskOffering = null + } this.zone = _.find(this.options.zones, (option) => option.id === instanceConfig.zoneid) this.affinityGroups = _.filter(this.options.affinityGroups, (option) => _.includes(instanceConfig.affinitygroupids, option.id)) this.networks = _.filter(this.options.networks, (option) => _.includes(instanceConfig.networkids, option.id)) @@ -1244,6 +1325,7 @@ export default { }) this.form.getFieldDecorator('computeofferingid', { initialValue: undefined, preserve: true }) this.form.getFieldDecorator('diskofferingid', { initialValue: undefined, preserve: true }) + this.form.getFieldDecorator('overridediskofferingid', { initialValue: undefined, preserve: true }) this.form.getFieldDecorator('multidiskoffering', { initialValue: undefined, preserve: true }) this.form.getFieldDecorator('affinitygroupids', { initialValue: [], preserve: true }) this.form.getFieldDecorator('networkids', { initialValue: [], preserve: true }) @@ -1369,6 +1451,14 @@ export default { getImg (image) { return 'data:image/png;charset=utf-8;base64, ' + image }, + updateOverrideRootDiskShowParam (val) { + if (val) { + this.showRootDiskSizeChanger = false + } else { + this.rootDiskSelected = null + } + this.showOverrideDiskOfferingOption = val + }, async fetchDataByZone (zoneId) { this.fillValue('zoneid') this.options.zones = await this.fetchZones(zoneId) @@ -1503,6 +1593,17 @@ export default { diskofferingid: id }) }, + updateOverrideDiskOffering (id) { + if (id === '0') { + this.form.setFieldsValue({ + overridediskofferingid: undefined + }) + return + } + this.form.setFieldsValue({ + overridediskofferingid: id + }) + }, updateMultiDiskOffering (value) { this.form.setFieldsValue({ multidiskoffering: value @@ -1634,7 +1735,8 @@ export default { } else { deployVmData.templateid = values.isoid } - if (this.showRootDiskSizeChanger && values.rootdisksize && values.rootdisksize > 0) { + + if (values.rootdisksize && values.rootdisksize > 0) { deployVmData.rootdisksize = values.rootdisksize } else if (this.rootDiskSizeFixed > 0) { deployVmData.rootdisksize = this.rootDiskSizeFixed @@ -1661,6 +1763,9 @@ export default { if (this.selectedTemplateConfiguration) { deployVmData['details[0].configurationId'] = this.selectedTemplateConfiguration.id } + if (!this.serviceOffering.diskofferingstrictness && values.overridediskofferingid) { + deployVmData.overridediskofferingid = values.overridediskofferingid + } if (this.isCustomizedIOPS) { deployVmData['details[0].minIops'] = this.minIops deployVmData['details[0].maxIops'] = this.maxIops @@ -2222,6 +2327,9 @@ export default { onSelectDiskSize (rowSelected) { this.diskSelected = rowSelected }, + onSelectRootDiskSize (rowSelected) { + this.rootDiskSelected = rowSelected + }, updateIOPSValue (input, value) { this[input] = value }, diff --git a/ui/src/views/compute/ScaleVM.vue b/ui/src/views/compute/ScaleVM.vue index a0cf5b197a1e..4a3200a36319 100644 --- a/ui/src/views/compute/ScaleVM.vue +++ b/ui/src/views/compute/ScaleVM.vue @@ -48,6 +48,14 @@ @update-compute-cpuspeed="updateFieldValue" @update-compute-memory="updateFieldValue" /> + + + + +
{{ this.$t('label.cancel') }} {{ this.$t('label.ok') }} @@ -78,6 +86,7 @@ export default { offeringsMap: {}, offerings: [], selectedOffering: {}, + autoMigrate: true, total: 0, params: { id: this.resource.id }, loading: false, @@ -86,6 +95,10 @@ export default { memoryKey: 'details[0].memory' } }, + beforeCreate () { + this.form = this.$form.createForm(this) + this.apiParams = this.$getApiParams('scaleVirtualMachine') + }, created () { this.fetchData({ keyword: '', @@ -145,6 +158,7 @@ export default { this.params.serviceofferingid = id this.selectedOffering = this.offeringsMap[id] + this.params.automigrate = this.autoMigrate }, updateFieldValue (name, value) { this.params[name] = value diff --git a/ui/src/views/compute/wizard/ComputeSelection.vue b/ui/src/views/compute/wizard/ComputeSelection.vue index 5fe75d681780..8d68fb9c9d3e 100644 --- a/ui/src/views/compute/wizard/ComputeSelection.vue +++ b/ui/src/views/compute/wizard/ComputeSelection.vue @@ -16,7 +16,7 @@ // under the License.