diff --git a/api/src/main/java/com/cloud/agent/api/to/VolumeTO.java b/api/src/main/java/com/cloud/agent/api/to/VolumeTO.java index 2ad5e0b26f70..6432f52a158f 100644 --- a/api/src/main/java/com/cloud/agent/api/to/VolumeTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/VolumeTO.java @@ -42,6 +42,7 @@ protected VolumeTO() { private Long iopsReadRate; private Long iopsWriteRate; private String cacheMode; + private String errorPolicy; private Long chainSize; public VolumeTO(long id, Volume.Type type, StoragePoolType poolType, String poolUuid, String name, String mountPoint, String path, long size, String chainInfo) { @@ -187,6 +188,14 @@ public String getCacheMode() { return cacheMode; } + public void setErrorPolicy(String errorPolicy) { + this.errorPolicy = errorPolicy; + } + + public String getErrorPolicy() { + return errorPolicy; + } + public Long getChainSize() { return chainSize; } diff --git a/api/src/main/java/com/cloud/offering/DiskOffering.java b/api/src/main/java/com/cloud/offering/DiskOffering.java index d74f5703cc99..ec37a383ac1a 100644 --- a/api/src/main/java/com/cloud/offering/DiskOffering.java +++ b/api/src/main/java/com/cloud/offering/DiskOffering.java @@ -51,6 +51,21 @@ public String toString() { } }; + enum DiskErrorPolicy { + REPORT("report"), STOP("stop"), IGNORE("ignore"), ENOSPACE("enospace"); + + private final String _diskErrorPolicy; + + DiskErrorPolicy(String errorPolicy) { + _diskErrorPolicy = errorPolicy; + } + + @Override + public String toString() { + return _diskErrorPolicy; + } + }; + String getUniqueName(); boolean isUseLocalStorage(); @@ -145,6 +160,10 @@ public String toString() { void setCacheMode(DiskCacheMode cacheMode); + DiskErrorPolicy getErrorPolicy(); + + void setErrorPolicy(DiskErrorPolicy errorPolicy); + boolean isComputeOnly(); boolean getDiskSizeStrictness(); diff --git a/api/src/main/java/com/cloud/vm/DiskProfile.java b/api/src/main/java/com/cloud/vm/DiskProfile.java index 766573d4d40b..e76aece9363e 100644 --- a/api/src/main/java/com/cloud/vm/DiskProfile.java +++ b/api/src/main/java/com/cloud/vm/DiskProfile.java @@ -42,6 +42,7 @@ public class DiskProfile { private Long iopsReadRate; private Long iopsWriteRate; private String cacheMode; + private String errorPolicy; private Long minIops; private Long maxIops; private boolean requiresEncryption; @@ -238,6 +239,14 @@ public String getCacheMode() { return cacheMode; } + public void setErrorPolicy(String errorPolicy) { + this.errorPolicy = errorPolicy; + } + + public String getErrorPolicy() { + return errorPolicy; + } + public Long getMinIops() { return minIops; } 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 7485029f22cb..7b25db61247a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -1293,6 +1293,7 @@ public class ApiConstants { public static final String LAST_UPDATED = "lastupdated"; public static final String PERFORM_FRESH_CHECKS = "performfreshchecks"; public static final String CACHE_MODE = "cachemode"; + public static final String ERROR_POLICY = "errorpolicy"; public static final String CONSOLE_END_POINT = "consoleendpoint"; public static final String EXTERNAL_LOAD_BALANCER_IP_ADDRESS = "externalloadbalanceripaddress"; 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 e1ff90e4bd89..c6b21dcf180c 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 @@ -154,6 +154,12 @@ public class CreateDiskOfferingCmd extends BaseCmd { since = "4.14") private String cacheMode; + @Parameter(name = ApiConstants.ERROR_POLICY, + type = CommandType.STRING, + description = "The disk error policy to use for this disk offering. report, stop, ignore or enospace. Only applicable to KVM", + since = "24.0.0") + private String errorPolicy; + @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; @@ -297,6 +303,10 @@ public String getCacheMode() { return cacheMode; } + public String getErrorPolicy() { + return errorPolicy; + } + public Map getDetails() { Map detailsMap = new HashMap<>(); if (MapUtils.isNotEmpty(details)) { 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 4363d6861ba1..5ea8e0e10a7d 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 @@ -193,6 +193,12 @@ public class CreateServiceOfferingCmd extends BaseCmd { since = "4.14") private String cacheMode; + @Parameter(name = ApiConstants.ERROR_POLICY, + type = CommandType.STRING, + description = "The disk error policy to use for the root disk of this compute offering. report, stop, ignore or enospace. Only applicable to KVM", + since = "24.0.0") + private String errorPolicy; + // Introduce 4 new optional parameters to work custom compute offerings @Parameter(name = ApiConstants.CUSTOMIZED, type = CommandType.BOOLEAN, @@ -475,6 +481,10 @@ public String getCacheMode() { return cacheMode; } + public String getErrorPolicy() { + return errorPolicy; + } + /** * If customized parameter is true, then cpuNumber, memory and cpuSpeed must be null * Check if the optional params min/max CPU/Memory have been specified diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/UpdateDiskOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/UpdateDiskOfferingCmd.java index 4d48327eeb77..f9f004592deb 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/UpdateDiskOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/offering/UpdateDiskOfferingCmd.java @@ -122,6 +122,9 @@ public class UpdateDiskOfferingCmd extends BaseCmd implements DomainAndZoneIdRes @Parameter(name = ApiConstants.CACHE_MODE, type = CommandType.STRING, description = "The cache mode to use for this disk offering", since = "4.15") private String cacheMode; + @Parameter(name = ApiConstants.ERROR_POLICY, type = CommandType.STRING, description = "The disk error policy to use for this disk offering. Only applicable to KVM", since = "24.0.0") + private String errorPolicy; + @Parameter(name = ApiConstants.STATE, type = CommandType.STRING, description = "state of the disk offering") private String diskOfferingState; @@ -165,6 +168,10 @@ public String getCacheMode() { return cacheMode; } + public String getErrorPolicy() { + return errorPolicy; + } + public Long getBytesReadRate() { return bytesReadRate; } 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 528890ea5fae..68b7ac3b5b3c 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 @@ -152,6 +152,10 @@ public class DiskOfferingResponse extends BaseResponseWithAnnotations { @Param(description = "The cache mode to use for this disk offering. none, writeback or writethrough", since = "4.4") private String cacheMode; + @SerializedName("errorpolicy") + @Param(description = "The disk error policy to use for this disk offering. report, stop, ignore or enospace", since = "24.0.0") + private String errorPolicy; + @SerializedName("displayoffering") @Param(description = "Whether to display the offering to the end user or not.") private Boolean displayOffering; @@ -322,6 +326,14 @@ public void setCacheMode(String cacheMode) { this.cacheMode = cacheMode; } + public String getErrorPolicy() { + return errorPolicy; + } + + public void setErrorPolicy(String errorPolicy) { + this.errorPolicy = errorPolicy; + } + public String getStorageType() { return storageType; } 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 99c50829f048..c92d6757efb2 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 @@ -200,6 +200,10 @@ public class ServiceOfferingResponse extends BaseResponseWithAnnotations { @Param(description = "The cache mode to use for this disk offering. none, writeback, writethrough or hypervisor default", since = "4.14") private String cacheMode; + @SerializedName("errorpolicy") + @Param(description = "The disk error policy to use for the root disk of this compute offering. report, stop, ignore or enospace", since = "24.0.0") + private String errorPolicy; + @SerializedName("vspherestoragepolicy") @Param(description = "The vsphere storage policy tagged to the service offering in case of VMware", since = "4.15") private String vsphereStoragePolicy; @@ -553,6 +557,10 @@ public void setCacheMode(String cacheMode) { this.cacheMode = cacheMode; } + public void setErrorPolicy(String errorPolicy) { + this.errorPolicy = errorPolicy; + } + public Integer getLeaseDuration() { return leaseDuration; } diff --git a/core/src/main/java/org/apache/cloudstack/storage/to/VolumeObjectTO.java b/core/src/main/java/org/apache/cloudstack/storage/to/VolumeObjectTO.java index df98149faab6..7f742d2678da 100644 --- a/core/src/main/java/org/apache/cloudstack/storage/to/VolumeObjectTO.java +++ b/core/src/main/java/org/apache/cloudstack/storage/to/VolumeObjectTO.java @@ -27,6 +27,7 @@ import com.cloud.agent.api.to.DataTO; import com.cloud.hypervisor.Hypervisor; import com.cloud.offering.DiskOffering.DiskCacheMode; +import com.cloud.offering.DiskOffering.DiskErrorPolicy; import com.cloud.storage.MigrationOptions; import com.cloud.storage.Storage; import com.cloud.storage.Volume; @@ -68,6 +69,7 @@ public class VolumeObjectTO extends DownloadableObjectTO implements DataTO, Seri private Long iopsWriteRateMax; private Long iopsWriteRateMaxLength; private DiskCacheMode cacheMode; + private DiskErrorPolicy errorPolicy; private Hypervisor.HypervisorType hypervisorType; private MigrationOptions migrationOptions; private boolean directDownload; @@ -119,6 +121,7 @@ public VolumeObjectTO(VolumeInfo volume) { iopsWriteRateMaxLength = volume.getIopsWriteRateMaxLength(); hypervisorType = volume.getHypervisorType(); setCacheMode(volume.getCacheMode()); + setErrorPolicy(volume.getErrorPolicy()); setDeviceId(volume.getDeviceId()); this.migrationOptions = volume.getMigrationOptions(); this.directDownload = volume.isDirectDownload(); @@ -355,6 +358,14 @@ public DiskCacheMode getCacheMode() { return cacheMode; } + public void setErrorPolicy(DiskErrorPolicy errorPolicy) { + this.errorPolicy = errorPolicy; + } + + public DiskErrorPolicy getErrorPolicy() { + return errorPolicy; + } + public MigrationOptions getMigrationOptions() { return migrationOptions; } diff --git a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java index 4937edd33d1f..f4c49caad030 100644 --- a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java +++ b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeInfo.java @@ -21,6 +21,7 @@ import com.cloud.agent.api.Answer; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.offering.DiskOffering.DiskCacheMode; +import com.cloud.offering.DiskOffering.DiskErrorPolicy; import com.cloud.storage.MigrationOptions; import com.cloud.storage.Storage; import com.cloud.storage.Volume; @@ -79,6 +80,8 @@ public interface VolumeInfo extends DownloadableDataInfo, Volume { DiskCacheMode getCacheMode(); + DiskErrorPolicy getErrorPolicy(); + /** * Currently available for KVM volumes */ 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 7f6b6d8adf0e..c27aea4bce30 100644 --- a/engine/schema/src/main/java/com/cloud/storage/DiskOfferingVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/DiskOfferingVO.java @@ -137,6 +137,10 @@ public class DiskOfferingVO implements DiskOffering { @Enumerated(value = EnumType.STRING) private DiskCacheMode cacheMode; + @Column(name = "error_policy", updatable = true, nullable = false) + @Enumerated(value = EnumType.STRING) + private DiskErrorPolicy errorPolicy; + @Column(name = "provisioning_type") Storage.ProvisioningType provisioningType; @@ -288,6 +292,16 @@ public void setCacheMode(DiskCacheMode cacheMode) { this.cacheMode = cacheMode; } + @Override + public DiskErrorPolicy getErrorPolicy() { + return errorPolicy; + } + + @Override + public void setErrorPolicy(DiskErrorPolicy errorPolicy) { + this.errorPolicy = errorPolicy; + } + @Override public String getUniqueName() { return uniqueName; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-42300to2400.sql b/engine/schema/src/main/resources/META-INF/db/schema-42300to2400.sql index 3365f1f855ba..7622fd5c96a8 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-42300to2400.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-42300to2400.sql @@ -239,3 +239,5 @@ CREATE TABLE IF NOT EXISTS `cloud`.`instance_boot_group_details` ( PRIMARY KEY (`id`), CONSTRAINT `fk_instance_boot_group_details__group_id` FOREIGN KEY (`boot_group_id`) REFERENCES `instance_boot_group` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +ALTER TABLE `cloud`.`disk_offering` ADD COLUMN `error_policy` VARCHAR(16) DEFAULT 'report' COMMENT 'disk error policy (report/stop/ignore/enospace) used by KVM hypervisor'; diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.disk_offering_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.disk_offering_view.sql index dffaec575ceb..65def2628ba3 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.disk_offering_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.disk_offering_view.sql @@ -49,6 +49,7 @@ SELECT `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`.`error_policy` AS `error_policy`, `disk_offering`.`sort_key` AS `sort_key`, `disk_offering`.`compute_only` AS `compute_only`, `disk_offering`.`display_offering` AS `display_offering`, diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.service_offering_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.service_offering_view.sql index eb987af3ffb6..768f324475e4 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.service_offering_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.service_offering_view.sql @@ -53,6 +53,7 @@ SELECT `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`.`error_policy` AS `error_policy`, `disk_offering`.`disk_size` AS `root_disk_size`, `disk_offering`.`encrypt` AS `encrypt_root`, `service_offering`.`cpu` AS `cpu`, diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.volume_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.volume_view.sql index 8ba7e5c6df77..e08b5f837619 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.volume_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.volume_view.sql @@ -82,6 +82,7 @@ SELECT `disk_offering`.`iops_read_rate` AS `iops_read_rate`, `disk_offering`.`iops_write_rate` AS `iops_write_rate`, `disk_offering`.`cache_mode` AS `cache_mode`, + `disk_offering`.`error_policy` AS `error_policy`, `storage_pool`.`id` AS `pool_id`, `storage_pool`.`uuid` AS `pool_uuid`, `storage_pool`.`name` AS `pool_name`, diff --git a/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeObject.java b/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeObject.java index de85067aae57..bc7399b34c2f 100644 --- a/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeObject.java +++ b/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeObject.java @@ -67,6 +67,7 @@ import com.cloud.exception.ConcurrentOperationException; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.offering.DiskOffering.DiskCacheMode; +import com.cloud.offering.DiskOffering.DiskErrorPolicy; import com.cloud.storage.DataStoreRole; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.Storage; @@ -350,6 +351,12 @@ public DiskCacheMode getCacheMode() { return diskOfferingVO == null ? null : diskOfferingVO.getCacheMode(); } + @Override + public DiskErrorPolicy getErrorPolicy() { + DiskOfferingVO diskOfferingVO = getDiskOfferingVO(); + return diskOfferingVO == null ? null : diskOfferingVO.getErrorPolicy(); + } + @Override public MigrationOptions getMigrationOptions() { return migrationOptions; diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java index acffa395494d..0631cdce8c9a 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -3929,6 +3929,10 @@ public int compare(final DiskTO arg0, final DiskTO arg1) { disk.setCacheMode(DiskDef.DiskCacheMode.valueOf(volumeObjectTO.getCacheMode().toString().toUpperCase())); } + if (volumeObjectTO.getErrorPolicy() != null) { + disk.setErrorPolicy(DiskDef.DiskErrorPolicy.valueOf(volumeObjectTO.getErrorPolicy().toString().toUpperCase())); + } + if (volumeObjectTO.requiresEncryption() && pool.getType().encryptionSupportMode() == Storage.EncryptionSupport.Hypervisor ) { String secretUuid = createLibvirtVolumeSecret(conn, volumeObjectTO.getPath(), volumeObjectTO.getPassphrase()); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java index 1a4d23af1c90..296866240620 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java @@ -90,6 +90,7 @@ public boolean parseDomainXML(String domXML) { if (type.equalsIgnoreCase("network")) { String diskFmtType = getAttrValue("driver", "type", disk); String diskCacheMode = getAttrValue("driver", "cache", disk); + String diskErrorPolicy = getAttrValue("driver", "error_policy", disk); String diskPath = getAttrValue("source", "name", disk); String protocol = getAttrValue("source", "protocol", disk); String authUserName = getAttrValue("auth", "username", disk); @@ -114,9 +115,13 @@ public boolean parseDomainXML(String domXML) { if (StringUtils.isNotBlank(diskCacheMode)) { def.setCacheMode(DiskDef.DiskCacheMode.valueOf(diskCacheMode.toUpperCase())); } + if (StringUtils.isNotBlank(diskErrorPolicy)) { + def.setErrorPolicy(DiskDef.DiskErrorPolicy.valueOf(diskErrorPolicy.toUpperCase())); + } } else { String diskFmtType = getAttrValue("driver", "type", disk); String diskCacheMode = getAttrValue("driver", "cache", disk); + String diskErrorPolicy = getAttrValue("driver", "error_policy", disk); String diskFile = getAttrValue("source", "file", disk); String diskDev = getAttrValue("source", "dev", disk); @@ -141,6 +146,9 @@ public boolean parseDomainXML(String domXML) { if (StringUtils.isNotBlank(diskCacheMode)) { def.setCacheMode(DiskDef.DiskCacheMode.valueOf(diskCacheMode.toUpperCase())); } + if (StringUtils.isNotBlank(diskErrorPolicy)) { + def.setErrorPolicy(DiskDef.DiskErrorPolicy.valueOf(diskErrorPolicy.toUpperCase())); + } } NodeList iotune = disk.getElementsByTagName("iotune"); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java index b6b03bc4e112..e94b633c614a 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java @@ -926,6 +926,20 @@ public String toString() { } } + public enum DiskErrorPolicy { + REPORT("report"), STOP("stop"), IGNORE("ignore"), ENOSPACE("enospace"); + String _diskErrorPolicy; + + DiskErrorPolicy(String errorPolicy) { + _diskErrorPolicy = errorPolicy; + } + + @Override + public String toString() { + return _diskErrorPolicy; + } + } + public enum DiscardType { IGNORE("ignore"), UNMAP("unmap"); String _discardType; @@ -982,6 +996,7 @@ public String toString() { private Long _iopsWriteRateMax; private Long _iopsWriteRateMaxLength; private DiskCacheMode _diskCacheMode; + private DiskErrorPolicy _errorPolicy; private String _serial; private boolean qemuDriver = true; private DiscardType _discard = DiscardType.IGNORE; @@ -1328,6 +1343,14 @@ public DiskCacheMode getCacheMode() { return _diskCacheMode; } + public void setErrorPolicy(DiskErrorPolicy errorPolicy) { + _errorPolicy = errorPolicy; + } + + public DiskErrorPolicy getErrorPolicy() { + return _errorPolicy; + } + public void setQemuDriver(boolean qemuDriver){ this.qemuDriver = qemuDriver; } @@ -1391,6 +1414,10 @@ public String toString() { diskBuilder.append("discard='" + _discard.toString() + "' "); } + if (_errorPolicy != null) { + diskBuilder.append("error_policy='" + _errorPolicy + "' "); + } + if(ioDriver != null) { diskBuilder.append(String.format("io='%s' ", ioDriver)); } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCreateCommandWrapper.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCreateCommandWrapper.java index 5ec00889df96..d3276491ab24 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCreateCommandWrapper.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCreateCommandWrapper.java @@ -76,6 +76,7 @@ public Answer execute(final CreateCommand command, final LibvirtComputingResourc volume.setIopsReadRate(dskch.getIopsReadRate()); volume.setIopsWriteRate(dskch.getIopsWriteRate()); volume.setCacheMode(dskch.getCacheMode()); + volume.setErrorPolicy(dskch.getErrorPolicy()); return new CreateAnswer(command, volume); } catch (final CloudRuntimeException e) { logger.debug("Failed to create volume: " + e.toString()); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java index 0cb32da318ea..c64af23bb224 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/KVMStorageProcessor.java @@ -1593,11 +1593,11 @@ protected synchronized void attachOrDetachDisk(final Connect conn, final boolean final String serial, final Long bytesReadRate, final Long bytesReadRateMax, final Long bytesReadRateMaxLength, final Long bytesWriteRate, final Long bytesWriteRateMax, final Long bytesWriteRateMaxLength, final Long iopsReadRate, final Long iopsReadRateMax, final Long iopsReadRateMaxLength, final Long iopsWriteRate, final Long iopsWriteRateMax, - final Long iopsWriteRateMaxLength, final String cacheMode, final DiskDef.LibvirtDiskEncryptDetails encryptDetails, Map details, Map controllerInfo) + final Long iopsWriteRateMaxLength, final String cacheMode, final String errorPolicy, final DiskDef.LibvirtDiskEncryptDetails encryptDetails, Map details, Map controllerInfo) throws LibvirtException, InternalErrorException { attachOrDetachDisk(conn, attach, vmName, attachingDisk, devId, serial, bytesReadRate, bytesReadRateMax, bytesReadRateMaxLength, bytesWriteRate, bytesWriteRateMax, bytesWriteRateMaxLength, iopsReadRate, iopsReadRateMax, iopsReadRateMaxLength, iopsWriteRate, - iopsWriteRateMax, iopsWriteRateMaxLength, cacheMode, encryptDetails, 0l, details, controllerInfo); + iopsWriteRateMax, iopsWriteRateMaxLength, cacheMode, errorPolicy, encryptDetails, 0l, details, controllerInfo); } /** @@ -1632,7 +1632,7 @@ protected synchronized void attachOrDetachDisk(final Connect conn, final boolean final String serial, final Long bytesReadRate, final Long bytesReadRateMax, final Long bytesReadRateMaxLength, final Long bytesWriteRate, final Long bytesWriteRateMax, final Long bytesWriteRateMaxLength, final Long iopsReadRate, final Long iopsReadRateMax, final Long iopsReadRateMaxLength, final Long iopsWriteRate, final Long iopsWriteRateMax, - final Long iopsWriteRateMaxLength, final String cacheMode, final DiskDef.LibvirtDiskEncryptDetails encryptDetails, + final Long iopsWriteRateMaxLength, final String cacheMode, final String errorPolicy, final DiskDef.LibvirtDiskEncryptDetails encryptDetails, long waitDetachDevice, Map details, Map controllerInfo) throws LibvirtException, InternalErrorException { @@ -1759,6 +1759,9 @@ protected synchronized void attachOrDetachDisk(final Connect conn, final boolean if (cacheMode != null) { diskdef.setCacheMode(DiskDef.DiskCacheMode.valueOf(cacheMode.toUpperCase())); } + if (errorPolicy != null) { + diskdef.setErrorPolicy(DiskDef.DiskErrorPolicy.valueOf(errorPolicy.toUpperCase())); + } diskdef.isIothreadsEnabled(details != null && details.containsKey(VmDetailConstants.IOTHREADS)); @@ -1841,13 +1844,14 @@ public Answer attachVolume(final AttachCommand cmd) { final KVMPhysicalDisk phyDisk = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), vol.getPath()); final String volCacheMode = vol.getCacheMode() == null ? null : vol.getCacheMode().toString(); + final String volErrorPolicy = vol.getErrorPolicy() == null ? null : vol.getErrorPolicy().toString(); logger.debug(String.format("Attaching physical disk %s with format %s", phyDisk.getPath(), phyDisk.getFormat())); attachOrDetachDisk(conn, true, vmName, phyDisk, disk.getDiskSeq().intValue(), serial, vol.getBytesReadRate(), vol.getBytesReadRateMax(), vol.getBytesReadRateMaxLength(), vol.getBytesWriteRate(), vol.getBytesWriteRateMax(), vol.getBytesWriteRateMaxLength(), vol.getIopsReadRate(), vol.getIopsReadRateMax(), vol.getIopsReadRateMaxLength(), - vol.getIopsWriteRate(), vol.getIopsWriteRateMax(), vol.getIopsWriteRateMaxLength(), volCacheMode, + vol.getIopsWriteRate(), vol.getIopsWriteRateMax(), vol.getIopsWriteRateMaxLength(), volCacheMode, volErrorPolicy, encryptDetails, disk.getDetails(), cmd.getControllerInfo()); resource.recreateCheckpointsOnVm(List.of((VolumeObjectTO) disk.getData()), vmName, conn); @@ -1882,12 +1886,13 @@ public Answer dettachVolume(final DettachCommand cmd) { final KVMPhysicalDisk phyDisk = storagePoolMgr.getPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), vol.getPath()); final String volCacheMode = vol.getCacheMode() == null ? null : vol.getCacheMode().toString(); + final String volErrorPolicy = vol.getErrorPolicy() == null ? null : vol.getErrorPolicy().toString(); attachOrDetachDisk(conn, false, vmName, phyDisk, disk.getDiskSeq().intValue(), serial, vol.getBytesReadRate(), vol.getBytesReadRateMax(), vol.getBytesReadRateMaxLength(), vol.getBytesWriteRate(), vol.getBytesWriteRateMax(), vol.getBytesWriteRateMaxLength(), vol.getIopsReadRate(), vol.getIopsReadRateMax(), vol.getIopsReadRateMaxLength(), - vol.getIopsWriteRate(), vol.getIopsWriteRateMax(), vol.getIopsWriteRateMaxLength(), volCacheMode, null, waitDetachDevice, null, null); + vol.getIopsWriteRate(), vol.getIopsWriteRateMax(), vol.getIopsWriteRateMaxLength(), volCacheMode, volErrorPolicy, null, waitDetachDevice, null, null); storagePoolMgr.disconnectPhysicalDisk(primaryStore.getPoolType(), primaryStore.getUuid(), vol.getPath()); diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDefTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDefTest.java index 56ad267eac7e..b3ec841b666c 100644 --- a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDefTest.java +++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDefTest.java @@ -263,6 +263,51 @@ public void testDiskDef() { assertEquals(expectedXml, xmlDef); } + @Test + public void testDiskDefWithErrorPolicy() { + String filePath = "/var/lib/libvirt/images/disk.qcow2"; + String diskLabel = "vda"; + + DiskDef disk = new DiskDef(); + DiskDef.DiskBus bus = DiskDef.DiskBus.VIRTIO; + DiskDef.DiskFmtType type = DiskDef.DiskFmtType.QCOW2; + DiskDef.DiskCacheMode cacheMode = DiskDef.DiskCacheMode.WRITEBACK; + DiskDef.DiskErrorPolicy errorPolicy = DiskDef.DiskErrorPolicy.STOP; + + disk.defFileBasedDisk(filePath, diskLabel, bus, type); + disk.setCacheMode(cacheMode); + disk.setErrorPolicy(errorPolicy); + + assertEquals(filePath, disk.getDiskPath()); + assertEquals(diskLabel, disk.getDiskLabel()); + assertEquals(bus, disk.getBusType()); + assertEquals(DiskDef.DeviceType.DISK, disk.getDeviceType()); + assertEquals(errorPolicy, disk.getErrorPolicy()); + + String xmlDef = disk.toString(); + String expectedXml = "\n\n" + + "\n\n\n"; + + assertEquals(expectedXml, xmlDef); + } + + @Test + public void testDiskDefWithoutErrorPolicy() { + String filePath = "/var/lib/libvirt/images/disk.qcow2"; + String diskLabel = "vda"; + + DiskDef disk = new DiskDef(); + DiskDef.DiskBus bus = DiskDef.DiskBus.VIRTIO; + DiskDef.DiskFmtType type = DiskDef.DiskFmtType.QCOW2; + + disk.defFileBasedDisk(filePath, diskLabel, bus, type); + + assertNull(disk.getErrorPolicy()); + + String xmlDef = disk.toString(); + assertFalse(xmlDef.contains("error_policy")); + } + @Test public void testDiskDefWithEncryption() { String passphraseUuid = UUID.randomUUID().toString(); 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 e53f94fa0c3e..c79937db9947 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 @@ -137,6 +137,7 @@ public DiskOfferingResponse newDiskOfferingResponse(DiskOfferingJoinVO offering) diskOfferingResponse.setIopsWriteRateMax(offering.getIopsWriteRateMax()); diskOfferingResponse.setIopsWriteRateMaxLength(offering.getIopsWriteRateMaxLength()); diskOfferingResponse.setCacheMode(offering.getCacheMode()); + diskOfferingResponse.setErrorPolicy(offering.getErrorPolicy()); diskOfferingResponse.setObjectName("diskoffering"); Map offeringDetails = ApiDBUtils.getResourceDetails(offering.getId(), ResourceTag.ResourceObjectType.DiskOffering); diskOfferingResponse.setDetails(offeringDetails); 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 579425a68c13..a2c2029d57fc 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 @@ -171,6 +171,7 @@ public ServiceOfferingResponse newServiceOfferingResponse(ServiceOfferingJoinVO offeringResponse.setObjectName("serviceoffering"); offeringResponse.setIscutomized(offering.isDynamic()); offeringResponse.setCacheMode(offering.getCacheMode()); + offeringResponse.setErrorPolicy(offering.getErrorPolicy()); offeringResponse.setDynamicScalingEnabled(offering.isDynamicScalingEnabled()); offeringResponse.setEncryptRoot(offering.getEncryptRoot()); 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 206c59f3a410..f490de01379f 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 @@ -118,6 +118,9 @@ public class DiskOfferingJoinVO extends BaseViewVO implements InternalIdentity, @Column(name = "cache_mode") String cacheMode; + @Column(name = "error_policy") + String errorPolicy; + @Column(name = "compute_only") boolean computeOnly; @@ -229,6 +232,14 @@ public void setCacheMode(String cacheMode) { this.cacheMode = cacheMode; } + public String getErrorPolicy() { + return errorPolicy; + } + + public void setErrorPolicy(String errorPolicy) { + this.errorPolicy = errorPolicy; + } + public boolean isDisplayOffering() { return displayOffering; } 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 1d86d14cf63a..ab3dc44cad14 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 @@ -181,6 +181,9 @@ public class ServiceOfferingJoinVO extends BaseViewVO implements InternalIdentit @Column(name = "cache_mode") String cacheMode; + @Column(name = "error_policy") + String errorPolicy; + @Column(name = "min_cpu") Integer minCpu; @@ -448,6 +451,10 @@ public String getCacheMode() { return cacheMode; } + public String getErrorPolicy() { + return errorPolicy; + } + public Integer getMinCpu() { return minCpu; } diff --git a/server/src/main/java/com/cloud/api/query/vo/VolumeJoinVO.java b/server/src/main/java/com/cloud/api/query/vo/VolumeJoinVO.java index e18d49c9c31f..3b65b3e5f0a7 100644 --- a/server/src/main/java/com/cloud/api/query/vo/VolumeJoinVO.java +++ b/server/src/main/java/com/cloud/api/query/vo/VolumeJoinVO.java @@ -222,6 +222,9 @@ public class VolumeJoinVO extends BaseViewWithTagInformationVO implements Contro @Column(name = "cache_mode") String cacheMode; + @Column(name = "error_policy") + String errorPolicy; + @Column(name = "pool_id") private long poolId; @@ -502,6 +505,10 @@ public String getCacheMode() { return cacheMode; } + public String getErrorPolicy() { + return errorPolicy; + } + public long getPoolId() { return poolId; } diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java index c68dc390df2a..c85bde51d7df 100644 --- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java @@ -3441,6 +3441,9 @@ public ServiceOffering createServiceOffering(final CreateServiceOfferingCmd cmd) // check if cache_mode parameter is valid validateCacheMode(cmd.getCacheMode()); + // check if error_policy parameter is valid + validateErrorPolicy(cmd.getErrorPolicy()); + final Boolean offerHA = cmd.isOfferHa(); boolean localStorageRequired = false; @@ -3543,7 +3546,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(), diskOfferingId, + cmd.getHypervisorSnapshotReserve(), cmd.getCacheMode(), cmd.getErrorPolicy(), storagePolicyId, cmd.getDynamicScalingEnabled(), diskOfferingId, cmd.getDiskOfferingStrictness(), cmd.isCustomized(), cmd.getEncryptRoot(), vgpuProfileId, gpuCount, cmd.getGpuDisplay(), cmd.isPurgeResources(), leaseDuration, leaseExpiryAction); } @@ -3572,7 +3575,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 Integer hypervisorSnapshotReserve, String cacheMode, String errorPolicy, final Long storagePolicyID, final boolean dynamicScalingEnabled, final Long diskOfferingId, final boolean diskOfferingStrictness, final boolean isCustomized, final boolean encryptRoot, Long vgpuProfileId, Integer gpuCount, Boolean gpuDisplay, final boolean purgeResources, Integer leaseDuration, VMLeaseManager.ExpiryAction leaseExpiryAction) { @@ -3669,7 +3672,7 @@ protected ServiceOfferingVO createServiceOffering(final long userId, final boole bytesWriteRate, bytesWriteRateMax, bytesWriteRateMaxLength, iopsReadRate, iopsReadRateMax, iopsReadRateMaxLength, iopsWriteRate, iopsWriteRateMax, iopsWriteRateMaxLength, - hypervisorSnapshotReserve, cacheMode, storagePolicyID, encryptRoot); + hypervisorSnapshotReserve, cacheMode, errorPolicy, storagePolicyID, encryptRoot); } else { diskOffering = _diskOfferingDao.findById(diskOfferingId); String diskStoragePolicyId = diskOfferingDetailsDao.getDetail(diskOfferingId, ApiConstants.STORAGE_POLICY); @@ -3752,7 +3755,7 @@ private DiskOfferingVO createDiskOfferingInternal(final String name, final Strin 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, boolean encrypt) { + final Integer hypervisorSnapshotReserve, String cacheMode, String errorPolicy, final Long storagePolicyID, boolean encrypt) { DiskOfferingVO diskOffering = new DiskOfferingVO(name, displayText, typedProvisioningType, false, tags, false, localStorageRequired, false); @@ -3801,6 +3804,10 @@ private DiskOfferingVO createDiskOfferingInternal(final String name, final Strin diskOffering.setCacheMode(DiskOffering.DiskCacheMode.valueOf(cacheMode.toUpperCase())); } + if (errorPolicy != null) { + diskOffering.setErrorPolicy(DiskOffering.DiskErrorPolicy.valueOf(errorPolicy.toUpperCase())); + } + if (hypervisorSnapshotReserve != null && hypervisorSnapshotReserve < 0) { throw new InvalidParameterValueException("If provided, Hypervisor Snapshot Reserve must be greater than or equal to 0."); } @@ -3947,6 +3954,9 @@ public ServiceOffering cloneServiceOffering(final CloneServiceOfferingCmd cmd) { if (cmd.getCacheMode() != null) { validateCacheMode(cmd.getCacheMode()); } + if (cmd.getErrorPolicy() != null) { + validateErrorPolicy(cmd.getErrorPolicy()); + } final Integer finalGpuCount = validateVgpuProfileAndGetGpuCount(vgpuProfileId, gpuCount); final Map mergedDetails = mergeOfferingDetails(cmd, sourceOffering, customParams); @@ -3967,7 +3977,7 @@ public ServiceOffering cloneServiceOffering(final CloneServiceOfferingCmd cmd) { diskParams.bytesWriteRate, diskParams.bytesWriteRateMax, diskParams.bytesWriteRateMaxLength, diskParams.iopsReadRate, diskParams.iopsReadRateMax, diskParams.iopsReadRateMaxLength, diskParams.iopsWriteRate, diskParams.iopsWriteRateMax, diskParams.iopsWriteRateMaxLength, - diskParams.hypervisorSnapshotReserve, diskParams.cacheMode, customParams.storagePolicy, dynamicScalingEnabled, + diskParams.hypervisorSnapshotReserve, diskParams.cacheMode, diskParams.errorPolicy, customParams.storagePolicy, dynamicScalingEnabled, diskOfferingId, diskOfferingStrictness, isCustomized, encryptRoot, vgpuProfileId, finalGpuCount, gpuDisplay, purgeResources, leaseParams.leaseDuration, leaseParams.leaseExpiryAction); } @@ -4057,6 +4067,12 @@ private ClonedDiskOfferingParams resolveDiskOfferingParams(CloneServiceOfferingC params.cacheMode = sourceDiskOffering.getCacheMode().toString(); } + if (cmd.getErrorPolicy() != null) { + params.errorPolicy = cmd.getErrorPolicy(); + } else if (sourceDiskOffering != null && sourceDiskOffering.getErrorPolicy() != null) { + params.errorPolicy = sourceDiskOffering.getErrorPolicy().toString(); + } + return params; } @@ -4169,6 +4185,7 @@ private static class ClonedDiskOfferingParams { Long maxIops; Integer hypervisorSnapshotReserve; String cacheMode; + String errorPolicy; } private static class CustomOfferingParams { @@ -4214,6 +4231,7 @@ public DiskOffering cloneDiskOffering(final CloneDiskOfferingCmd cmd) { final Integer hypervisorSnapshotReserve = getOrDefault(cmd.getHypervisorSnapshotReserve(), sourceOffering.getHypervisorSnapshotReserve()); final String cacheMode = resolveCacheMode(cmd, sourceOffering); + final String errorPolicy = resolveErrorPolicy(cmd, sourceOffering); final Long storagePolicy = resolveStoragePolicyForDiskOffering(cmd, sourceOffering); final Map mergedDetails = mergeDiskOfferingDetails(cmd, sourceOffering); @@ -4221,6 +4239,9 @@ public DiskOffering cloneDiskOffering(final CloneDiskOfferingCmd cmd) { if (cmd.getCacheMode() != null) { validateCacheMode(cmd.getCacheMode()); } + if (cmd.getErrorPolicy() != null) { + validateErrorPolicy(cmd.getErrorPolicy()); + } validateMaxRateEqualsOrGreater(iopsParams.iopsReadRate, iopsParams.iopsReadRateMax, IOPS_READ_RATE); validateMaxRateEqualsOrGreater(iopsParams.iopsWriteRate, iopsParams.iopsWriteRateMax, IOPS_WRITE_RATE); @@ -4235,7 +4256,7 @@ public DiskOffering cloneDiskOffering(final CloneDiskOfferingCmd cmd) { rateParams.bytesWriteRate, rateParams.bytesWriteRateMax, rateParams.bytesWriteRateMaxLength, iopsParams.iopsReadRate, iopsParams.iopsReadRateMax, iopsParams.iopsReadRateMaxLength, iopsParams.iopsWriteRate, iopsParams.iopsWriteRateMax, iopsParams.iopsWriteRateMaxLength, - hypervisorSnapshotReserve, cacheMode, mergedDetails, storagePolicy, diskSizeStrictness, encrypt); + hypervisorSnapshotReserve, cacheMode, errorPolicy, mergedDetails, storagePolicy, diskSizeStrictness, encrypt); } private DiskOfferingVO getAndValidateSourceDiskOffering(Long sourceOfferingId) { @@ -4279,6 +4300,16 @@ private String resolveCacheMode(CloneDiskOfferingCmd cmd, DiskOfferingVO sourceO return null; } + private String resolveErrorPolicy(CloneDiskOfferingCmd cmd, DiskOfferingVO sourceOffering) { + if (cmd.getErrorPolicy() != null) { + return cmd.getErrorPolicy(); + } + if (sourceOffering.getErrorPolicy() != null) { + return sourceOffering.getErrorPolicy().toString(); + } + return null; + } + private Long resolveStoragePolicyForDiskOffering(CloneDiskOfferingCmd cmd, DiskOfferingVO sourceOffering) { Long storagePolicy = cmd.getStoragePolicy(); if (storagePolicy == null) { @@ -4587,7 +4618,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, String errorPolicy, final Map details, final Long storagePolicyID, final boolean diskSizeStrictness, final boolean encrypt) { long diskSize = 0;// special case for custom disk offerings long maxVolumeSizeInGb = VolumeOrchestrationService.MaxVolumeSize.value(); @@ -4666,6 +4697,10 @@ protected DiskOfferingVO createDiskOffering(final Long userId, final List newDiskOffering.setCacheMode(DiskOffering.DiskCacheMode.valueOf(cacheMode.toUpperCase())); } + if (errorPolicy != null) { + newDiskOffering.setErrorPolicy(DiskOffering.DiskErrorPolicy.valueOf(errorPolicy.toUpperCase())); + } + if (hypervisorSnapshotReserve != null && hypervisorSnapshotReserve < 0) { throw new InvalidParameterValueException("If provided, Hypervisor Snapshot Reserve must be greater than or equal to 0."); } @@ -4750,6 +4785,9 @@ public DiskOffering createDiskOffering(final CreateDiskOfferingCmd cmd) { // check if cache_mode parameter is valid validateCacheMode(cmd.getCacheMode()); + // check if error_policy parameter is valid + validateErrorPolicy(cmd.getErrorPolicy()); + boolean localStorageRequired = false; final String storageType = cmd.getStorageType(); if (storageType != null) { @@ -4783,6 +4821,7 @@ public DiskOffering createDiskOffering(final CreateDiskOfferingCmd cmd) { final Long iopsWriteRateMaxLength = cmd.getIopsWriteRateMaxLength(); final Integer hypervisorSnapshotReserve = cmd.getHypervisorSnapshotReserve(); final String cacheMode = cmd.getCacheMode(); + final String errorPolicy = cmd.getErrorPolicy(); final boolean encrypt = cmd.getEncrypt(); validateMaxRateEqualsOrGreater(iopsReadRate, iopsReadRateMax, IOPS_READ_RATE); @@ -4797,7 +4836,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, diskSizeStrictness, encrypt); + hypervisorSnapshotReserve, cacheMode, errorPolicy, details, storagePolicyId, diskSizeStrictness, encrypt); } /** @@ -4876,6 +4915,7 @@ public DiskOffering updateDiskOffering(final UpdateDiskOfferingCmd cmd) { Long iopsWriteRateMax = cmd.getIopsWriteRateMax(); Long iopsWriteRateMaxLength = cmd.getIopsWriteRateMaxLength(); String cacheMode = cmd.getCacheMode(); + String errorPolicy = cmd.getErrorPolicy(); DiskOffering.State state = cmd.getState(); // Check if diskOffering exists @@ -4930,7 +4970,7 @@ public DiskOffering updateDiskOffering(final UpdateDiskOfferingCmd cmd) { throw new InvalidParameterValueException(String.format("Unable to update disk offering: %s by id user: %s because it is not root-admin or domain-admin", diskOfferingHandle, user)); } - boolean updateNeeded = shouldUpdateDiskOffering(name, displayText, sortKey, displayDiskOffering, tags, cacheMode, state) || + boolean updateNeeded = shouldUpdateDiskOffering(name, displayText, sortKey, displayDiskOffering, tags, cacheMode, errorPolicy, state) || shouldUpdateIopsRateParameters(iopsReadRate, iopsReadRateMax, iopsReadRateMaxLength, iopsWriteRate, iopsWriteRateMax, iopsWriteRateMaxLength) || shouldUpdateBytesRateParameters(bytesReadRate, bytesReadRateMax, bytesReadRateMaxLength, bytesWriteRate, bytesWriteRateMax, bytesWriteRateMaxLength); @@ -4958,6 +4998,11 @@ public DiskOffering updateDiskOffering(final UpdateDiskOfferingCmd cmd) { diskOffering.setCacheMode(DiskOffering.DiskCacheMode.valueOf(cacheMode.toUpperCase())); } + if (errorPolicy != null) { + validateErrorPolicy(errorPolicy); + diskOffering.setErrorPolicy(DiskOffering.DiskErrorPolicy.valueOf(errorPolicy.toUpperCase())); + } + if (state != null) { diskOffering.setState(state); } @@ -5184,8 +5229,8 @@ protected void updateServiceOfferingHostTagsIfNotNull(String hostTags, ServiceOf * Check if it needs to update any parameter when updateDiskoffering is called * Verify if name or displayText are not blank, tags is not null, sortkey and displayDiskOffering is not null */ - protected boolean shouldUpdateDiskOffering(String name, String displayText, Integer sortKey, Boolean displayDiskOffering, String tags, String cacheMode, DiskOffering.State state) { - return !StringUtils.isAllBlank(name, displayText, cacheMode) || tags != null || sortKey != null || displayDiskOffering != null || state != null; + protected boolean shouldUpdateDiskOffering(String name, String displayText, Integer sortKey, Boolean displayDiskOffering, String tags, String cacheMode, String errorPolicy, DiskOffering.State state) { + return !StringUtils.isAllBlank(name, displayText, cacheMode, errorPolicy) || tags != null || sortKey != null || displayDiskOffering != null || state != null; } protected boolean shouldUpdateBytesRateParameters(Long bytesReadRate, Long bytesReadRateMax, Long bytesReadRateMaxLength, Long bytesWriteRate, Long bytesWriteRateMax, Long bytesWriteRateMaxLength) { @@ -9412,6 +9457,15 @@ protected void validateCacheMode(String cacheMode){ } } + protected void validateErrorPolicy(String errorPolicy){ + if(errorPolicy != null && + !Enums.getIfPresent(DiskOffering.DiskErrorPolicy.class, + errorPolicy.toUpperCase()).isPresent()) { + throw new InvalidParameterValueException(String.format("Invalid error policy (%s). Please specify one of the following " + + "valid error policy parameters: report, stop, ignore or enospace.", errorPolicy)); + } + } + public List getSecChecker() { return _secChecker; } diff --git a/server/src/test/java/com/cloud/configuration/ConfigurationManagerCloneIntegrationTest.java b/server/src/test/java/com/cloud/configuration/ConfigurationManagerCloneIntegrationTest.java index 67d43bf6933b..33093a60d0cd 100644 --- a/server/src/test/java/com/cloud/configuration/ConfigurationManagerCloneIntegrationTest.java +++ b/server/src/test/java/com/cloud/configuration/ConfigurationManagerCloneIntegrationTest.java @@ -164,7 +164,7 @@ public void setUp() { anyLong(), anyString(), anyBoolean(), anyBoolean(), anyBoolean(), any(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), - anyInt(), anyString(), any(), anyLong(), anyBoolean(), anyBoolean()); + anyInt(), anyString(), anyString(), any(), anyLong(), anyBoolean(), anyBoolean()); // User/Account DAO stubs used by createDiskOffering @@ -250,7 +250,7 @@ public void testCloneServiceOfferingInheritsAllPropertiesFromSource() { anyBoolean(), anyBoolean(), anyBoolean(), anyString(), anyList(), anyList(), anyString(), any(Integer.class), anyString(), anyMap(), anyLong(), any(Boolean.class), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), - any(Integer.class), anyString(), anyLong(), anyBoolean(), anyLong(), anyBoolean(), anyBoolean(), anyBoolean(), + any(Integer.class), anyString(), anyString(), anyLong(), anyBoolean(), anyLong(), anyBoolean(), anyBoolean(), anyBoolean(), anyLong(), any(Integer.class), any(Boolean.class), anyBoolean(), any(Integer.class), any(VMLeaseManager.ExpiryAction.class) ); @@ -326,7 +326,7 @@ public void testCloneServiceOfferingOverridesProvidedParameters() { anyString(), anyList(), anyList(), anyString(), anyInt(), anyString(), any(), anyLong(), anyBoolean(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), - anyLong(), anyLong(), anyInt(), anyString(), anyLong(), anyBoolean(), anyLong(), + anyLong(), anyLong(), anyInt(), anyString(), anyString(), anyLong(), anyBoolean(), anyLong(), anyBoolean(), anyBoolean(), anyBoolean(), anyLong(), anyInt(), anyBoolean(), anyBoolean(), anyInt(), any()); @@ -395,7 +395,7 @@ public void testCloneDiskOfferingInheritsAllPropertiesFromSource() { anyLong(), anyString(), anyBoolean(), anyBoolean(), anyBoolean(), any(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), - anyInt(), anyString(), any(), anyLong(), anyBoolean(), anyBoolean()); + anyInt(), anyString(), anyString(), any(), anyLong(), anyBoolean(), anyBoolean()); DiskOffering result = configurationManager.cloneDiskOffering(cmd); @@ -451,7 +451,7 @@ public void testCloneDiskOfferingOverridesProvidedParameters() { anyLong(), eq("tag1,tag2"), anyBoolean(), anyBoolean(), anyBoolean(), any(), eq(100L), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), - anyInt(), anyString(), any(), anyLong(), anyBoolean(), anyBoolean()); + anyInt(), anyString(), anyString(), any(), anyLong(), anyBoolean(), anyBoolean()); DiskOffering result = configurationManager.cloneDiskOffering(cmd); @@ -504,7 +504,7 @@ public void testCloneDiskOfferingInheritsDomainAndZoneRestrictions() { anyLong(), anyString(), anyBoolean(), anyBoolean(), anyBoolean(), any(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), - anyInt(), anyString(), any(), anyLong(), anyBoolean(), anyBoolean()); + anyInt(), anyString(), anyString(), any(), anyLong(), anyBoolean(), anyBoolean()); DiskOffering result = configurationManager.cloneDiskOffering(cmd); @@ -551,7 +551,7 @@ public void testCloneServiceOfferingCanInheritDetailsFromSource() { anyString(), anyList(), anyList(), anyString(), anyInt(), anyString(), any(), anyLong(), anyBoolean(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), - anyLong(), anyLong(), anyInt(), anyString(), anyLong(), anyBoolean(), anyLong(), + anyLong(), anyLong(), anyInt(), anyString(), anyString(), anyLong(), anyBoolean(), anyLong(), anyBoolean(), anyBoolean(), anyBoolean(), anyLong(), anyInt(), anyBoolean(), anyBoolean(), anyInt(), any()); @@ -597,7 +597,7 @@ public void testCloneDiskOfferingVerifiesInheritedValues() { anyString(), eq(50L), eq("production,ssd"), anyBoolean(), anyBoolean(), anyBoolean(), any(), eq(1000L), eq(5000L), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), - anyLong(), anyLong(), eq(20), anyString(), any(), anyLong(), anyBoolean(), + anyLong(), anyLong(), eq(20), anyString(), anyString(), any(), anyLong(), anyBoolean(), eq(true)); DiskOffering result = configurationManager.cloneDiskOffering(cmd); @@ -651,7 +651,7 @@ public void testCloneDiskOfferingVerifiesOverriddenValues() { eq("production,high-performance"), anyBoolean(), anyBoolean(), anyBoolean(), any(), eq(2000L), eq(10000L), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), - anyLong(), anyInt(), anyString(), any(), anyLong(), anyBoolean(), eq(true)); + anyLong(), anyInt(), anyString(), anyString(), any(), anyLong(), anyBoolean(), eq(true)); DiskOffering result = configurationManager.cloneDiskOffering(cmd); @@ -706,7 +706,7 @@ public void testCloneDiskOfferingInheritsBytesReadWriteRates() { anyLong(), anyString(), anyBoolean(), anyBoolean(), anyBoolean(), any(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), - anyInt(), anyString(), any(), anyLong(), anyBoolean(), anyBoolean()); + anyInt(), anyString(), anyString(), any(), anyLong(), anyBoolean(), anyBoolean()); DiskOffering result = configurationManager.cloneDiskOffering(cmd); diff --git a/server/src/test/java/com/cloud/configuration/ConfigurationManagerTest.java b/server/src/test/java/com/cloud/configuration/ConfigurationManagerTest.java index 8873d58de605..5947ed0ba4a9 100644 --- a/server/src/test/java/com/cloud/configuration/ConfigurationManagerTest.java +++ b/server/src/test/java/com/cloud/configuration/ConfigurationManagerTest.java @@ -1085,18 +1085,34 @@ public void validateMaximumIopsAndBytesLengthTestDefaultLengthConfigs() { @Test public void shouldUpdateDiskOfferingTests(){ - assertTrue(configurationMgr.shouldUpdateDiskOffering(Mockito.anyString(), Mockito.anyString(), Mockito.anyInt(), Mockito.anyBoolean(), Mockito.anyString(), Mockito.anyString(), Mockito.any(DiskOffering.State.class))); - assertTrue(configurationMgr.shouldUpdateDiskOffering(Mockito.anyString(), nullable(String.class), nullable(Integer.class), nullable(Boolean.class), nullable(String.class), nullable(String.class), nullable(DiskOffering.State.class))); - assertTrue(configurationMgr.shouldUpdateDiskOffering(nullable(String.class), nullable(String.class), nullable(Integer.class), nullable(Boolean.class), nullable(String.class), nullable(String.class), Mockito.any(DiskOffering.State.class))); - assertTrue(configurationMgr.shouldUpdateDiskOffering(nullable(String.class), Mockito.anyString(), nullable(Integer.class), nullable(Boolean.class), nullable(String.class), nullable(String.class), nullable(DiskOffering.State.class))); - assertTrue(configurationMgr.shouldUpdateDiskOffering(nullable(String.class), nullable(String.class), Mockito.anyInt(), nullable(Boolean.class), nullable(String.class), nullable(String.class), nullable(DiskOffering.State.class))); - assertTrue(configurationMgr.shouldUpdateDiskOffering(nullable(String.class), nullable(String.class), nullable(int.class), Mockito.anyBoolean(), nullable(String.class), nullable(String.class), nullable(DiskOffering.State.class))); - assertTrue(configurationMgr.shouldUpdateDiskOffering(nullable(String.class), nullable(String.class), nullable(int.class), nullable(Boolean.class), Mockito.anyString(), Mockito.anyString(), nullable(DiskOffering.State.class))); + assertTrue(configurationMgr.shouldUpdateDiskOffering(Mockito.anyString(), Mockito.anyString(), Mockito.anyInt(), Mockito.anyBoolean(), Mockito.anyString(), Mockito.anyString(), nullable(String.class), Mockito.any(DiskOffering.State.class))); + assertTrue(configurationMgr.shouldUpdateDiskOffering(Mockito.anyString(), nullable(String.class), nullable(Integer.class), nullable(Boolean.class), nullable(String.class), nullable(String.class), nullable(String.class), nullable(DiskOffering.State.class))); + assertTrue(configurationMgr.shouldUpdateDiskOffering(nullable(String.class), nullable(String.class), nullable(Integer.class), nullable(Boolean.class), nullable(String.class), nullable(String.class), nullable(String.class), Mockito.any(DiskOffering.State.class))); + assertTrue(configurationMgr.shouldUpdateDiskOffering(nullable(String.class), Mockito.anyString(), nullable(Integer.class), nullable(Boolean.class), nullable(String.class), nullable(String.class), nullable(String.class), nullable(DiskOffering.State.class))); + assertTrue(configurationMgr.shouldUpdateDiskOffering(nullable(String.class), nullable(String.class), Mockito.anyInt(), nullable(Boolean.class), nullable(String.class), nullable(String.class), nullable(String.class), nullable(DiskOffering.State.class))); + assertTrue(configurationMgr.shouldUpdateDiskOffering(nullable(String.class), nullable(String.class), nullable(int.class), Mockito.anyBoolean(), nullable(String.class), nullable(String.class), nullable(String.class), nullable(DiskOffering.State.class))); + assertTrue(configurationMgr.shouldUpdateDiskOffering(nullable(String.class), nullable(String.class), nullable(int.class), nullable(Boolean.class), Mockito.anyString(), Mockito.anyString(), nullable(String.class), nullable(DiskOffering.State.class))); + assertTrue(configurationMgr.shouldUpdateDiskOffering(nullable(String.class), nullable(String.class), nullable(int.class), nullable(Boolean.class), nullable(String.class), nullable(String.class), Mockito.anyString(), nullable(DiskOffering.State.class))); } @Test public void shouldUpdateDiskOfferingTestFalse(){ - assertFalse(configurationMgr.shouldUpdateDiskOffering(null, null, null, null, null, null, null)); + assertFalse(configurationMgr.shouldUpdateDiskOffering(null, null, null, null, null, null, null, null)); + } + + @Test + public void testValidateErrorPolicy() { + configurationMgr.validateErrorPolicy(null); + configurationMgr.validateErrorPolicy("report"); + configurationMgr.validateErrorPolicy("stop"); + configurationMgr.validateErrorPolicy("ignore"); + configurationMgr.validateErrorPolicy("enospace"); + configurationMgr.validateErrorPolicy("REPORT"); + } + + @Test(expected = InvalidParameterValueException.class) + public void testValidateErrorPolicyInvalid() { + configurationMgr.validateErrorPolicy("invalid_error_policy_type"); } @Test diff --git a/test/integration/smoke/test_disk_offerings.py b/test/integration/smoke/test_disk_offerings.py index dc23a52a0260..20b20ab8147e 100644 --- a/test/integration/smoke/test_disk_offerings.py +++ b/test/integration/smoke/test_disk_offerings.py @@ -342,6 +342,78 @@ def test_08_create_encrypted_disk_offering(self): ) return + @attr(tags=["advanced", "basic", "eip", "sg", "advancedns", "smoke"], required_hardware="false") + def test_09_create_disk_offering_with_error_policy_type(self): + """Test to create disk offering with each one of the valid error policy types : report, stop, ignore and enospace + + # Validate the following: + # 1. createDiskOfferings should return valid info for new offering + # 2. The Cloud Database contains the valid information + """ + error_policy_types=["report", "stop", "ignore", "enospace"] + for i in range(4): + disk_offering = DiskOffering.create( + self.apiclient, + self.services["disk_offering"], + errorPolicy=error_policy_types[i] + ) + self.cleanup.append(disk_offering) + + self.debug("Created Disk offering with valid errorPolicy param with ID: %s" % disk_offering.id) + + list_disk_response = list_disk_offering( + self.apiclient, + id=disk_offering.id + ) + self.assertEqual( + isinstance(list_disk_response, list), + True, + "Check list response returns a valid list" + ) + self.assertNotEqual( + len(list_disk_response), + 0, + "Check Disk offering is created" + ) + disk_response = list_disk_response[0] + + self.assertEqual( + disk_response.displaytext, + self.services["disk_offering"]["displaytext"], + "Check server id in createServiceOffering" + ) + self.assertEqual( + disk_response.name, + self.services["disk_offering"]["name"], + "Check name in createServiceOffering" + ) + self.assertEqual( + disk_response.errorpolicy, + error_policy_types[i], + "Check errorPolicy in createServiceOffering" + ) + + return + + @attr(tags=["advanced", "basic", "eip", "sg", "advancedns", "smoke"], required_hardware="false") + def test_10_create_disk_offering_with_invalid_error_policy_type(self): + """Test to create disk offering with invalid errorPolicy type + + # Validate the following: + # 1. createDiskOfferings should return valid info for new offering + # 2. The Cloud Database contains the valid information + """ + + with self.assertRaises(Exception): + disk_offering = DiskOffering.create( + self.apiclient, + self.services["disk_offering"], + errorPolicy="invalid_error_policy_type" + ) + + + return + class TestDiskOfferings(cloudstackTestCase): def setUp(self): diff --git a/test/integration/smoke/test_service_offerings.py b/test/integration/smoke/test_service_offerings.py index a523cd0e6987..69746e714eac 100644 --- a/test/integration/smoke/test_service_offerings.py +++ b/test/integration/smoke/test_service_offerings.py @@ -514,6 +514,109 @@ def update_lease_feature(self, value=None): value=value): self.fail(f'instance.lease.enabled should be: {value}') + @attr( + tags=[ + "advanced", + "advancedns", + "smoke", + "basic", + "eip", + "sg"], + required_hardware="false") + def test_09_create_service_offering_with_error_policy_type(self): + """Test to create service offering with each one of the valid error policy types : report, stop, ignore and enospace""" + + # Validate the following: + # 1. createServiceOfferings should return a valid information + # for newly created offering + # 2. The Cloud Database contains the valid information + + error_policy_types=["report", "stop", "ignore", "enospace"] + for i in range(4): + service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offerings"]["tiny"], + errorPolicy=error_policy_types[i] + ) + self.cleanup.append(service_offering) + + self.debug( + "Created service offering with ID: %s" % + service_offering.id) + + list_service_response = list_service_offering( + self.apiclient, + id=service_offering.id + ) + self.assertEqual( + isinstance(list_service_response, list), + True, + "Check list response returns a valid list" + ) + + self.assertNotEqual( + len(list_service_response), + 0, + "Check Service offering is created" + ) + + self.assertEqual( + list_service_response[0].cpunumber, + self.services["service_offerings"]["tiny"]["cpunumber"], + "Check server id in createServiceOffering" + ) + self.assertEqual( + list_service_response[0].cpuspeed, + self.services["service_offerings"]["tiny"]["cpuspeed"], + "Check cpuspeed in createServiceOffering" + ) + self.assertEqual( + list_service_response[0].displaytext, + self.services["service_offerings"]["tiny"]["displaytext"], + "Check server displaytext in createServiceOfferings" + ) + self.assertEqual( + list_service_response[0].memory, + self.services["service_offerings"]["tiny"]["memory"], + "Check memory in createServiceOffering" + ) + self.assertEqual( + list_service_response[0].name, + self.services["service_offerings"]["tiny"]["name"], + "Check name in createServiceOffering" + ) + self.assertEqual( + list_service_response[0].errorpolicy, + error_policy_types[i], + "Check errorPolicy in createServiceOffering" + ) + return + + @attr( + tags=[ + "advanced", + "advancedns", + "smoke", + "basic", + "eip", + "sg"], + required_hardware="false") + def test_10_create_service_offering_with_invalid_error_policy_type(self): + """Test to create service offering with invalid error policy type""" + + # Validate the following: + # 1. createServiceOfferings should return a valid information + # for newly created offering + # 2. The Cloud Database contains the valid information + + with self.assertRaises(Exception): + service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offerings"]["tiny"], + errorPolicy="invalid_error_policy_type" + ) + return + class TestServiceOfferings(cloudstackTestCase): diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 7a29e700958c..707c6087b34f 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -1121,6 +1121,11 @@ "label.entitytype": "Entity Type", "label.error": "Error", "label.errormessage": "Error message", +"label.errorpolicy": "Disk error policy", +"label.errorpolicy.report": "Report", +"label.errorpolicy.stop": "Stop", +"label.errorpolicy.ignore": "Ignore", +"label.errorpolicy.enospace": "No space", "label.error.caught": "Error caught", "label.error.code": "Error code", "label.error.file.read": "Cannot read file.", diff --git a/ui/src/components/offering/ComputeOfferingForm.vue b/ui/src/components/offering/ComputeOfferingForm.vue index 0e293895f3fc..1226541cde21 100644 --- a/ui/src/components/offering/ComputeOfferingForm.vue +++ b/ui/src/components/offering/ComputeOfferingForm.vue @@ -401,6 +401,17 @@ {{ $t('label.hypervisor.default') }} + + + + {{ $t('label.errorpolicy.report') }} + {{ $t('label.errorpolicy.stop') }} + {{ $t('label.errorpolicy.ignore') }} + {{ $t('label.errorpolicy.enospace') }} + + {{ $t('label.none') }} @@ -616,6 +627,7 @@ export default { storagetype: 'shared', provisioningtype: 'thin', cachemode: 'none', + errorpolicy: 'report', qostype: '', iscustomizeddiskiops: false, diskofferingid: null, @@ -629,6 +641,7 @@ export default { storageType: 'shared', provisioningType: 'thin', cacheMode: 'none', + errorPolicy: 'report', offeringType: 'fixed', isCustomizedDiskIops: false, isPublic: true, @@ -785,6 +798,7 @@ export default { handleStorageTypeChange (val) { this.storageType = val }, handleProvisioningTypeChange (val) { this.provisioningType = val }, handleCacheModeChange (val) { this.cacheMode = val }, + handleErrorPolicyChange (val) { this.errorPolicy = val }, handleComputeOfferingTypeChange (val) { this.offeringType = val }, handleQosTypeChange (val) { this.qosType = val }, handleDeploymentPlannerChange (planner) { this.selectedDeploymentPlanner = planner; this.plannerModeVisible = false; if (this.selectedDeploymentPlanner === 'ImplicitDedicationPlanner') this.plannerModeVisible = isAdmin() }, diff --git a/ui/src/components/offering/DiskOfferingForm.vue b/ui/src/components/offering/DiskOfferingForm.vue index f3f39647fefa..d8847b2e89c1 100644 --- a/ui/src/components/offering/DiskOfferingForm.vue +++ b/ui/src/components/offering/DiskOfferingForm.vue @@ -213,6 +213,28 @@ + + + + + {{ $t('label.errorpolicy.report') }} + + + {{ $t('label.errorpolicy.stop') }} + + + {{ $t('label.errorpolicy.ignore') }} + + + {{ $t('label.errorpolicy.enospace') }} + + +