mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
server: allows compute offering with or without constraints (#3245)
Problem: Custom compute offering does not allow setting min and max values for CPU and VRAM for custom VMs. Root Cause: Custom compute offerings cannot be created with a given range of CPU number and memory instead it allows only fixed values. Solution: createServiceOffering API has been modified to allow setting a defined range for CPU number and memory. Also, UI form for compute offering creation is provided with a new field named 'compute offering type’ with values - Fixed, Custom Constrained, Custom Constrained. It will allow the creation of compute offerings either with a fixed CPU speed and memory for fixed compute offering, or with a range of CPU number and memory for custom constrained compute offering or without predefined CPU number, CPU speed and memory for custom unconstrained compute offering. To allow the user to set CPU number, CPU speed and memory during VM deployment, UI form for VM deployment has been modified to provide controls to change these values. These controls are depicted in screenshots below for custom constrained and custom unconstrained compute offering types. Sample API calls using cmk to create a constrained service offering and deploying a VM using it, create serviceoffering name=Constrained displaytext=Constrained customized=true mincpunumber=2 maxcpunumber=4 cpuspeed=400 minmemory=256 maxmemory=1024 deploy virtualmachine displayname=ConstrainedVM serviceofferingid=60f3e500-6559-40b2-9a61-2192891c2bd6 templateid=8e0f4a3e-601b-11e9-9df4-a0afbd4a2d60 zoneid=9612a0c6-ed28-4fae-9a48-6eb207af29e3 details[0].cpuNumber=3 details[0].memory=800 Signed-off-by: Abhishek Kumar <abhishek.kumar@shapeblue.com>
This commit is contained in:
parent
2ead7359d1
commit
2020bfb6a3
@ -202,6 +202,10 @@ public class ApiConstants {
|
||||
public static final String MAX = "max";
|
||||
public static final String MAC_ADDRESS = "macaddress";
|
||||
public static final String MAX_SNAPS = "maxsnaps";
|
||||
public static final String MAX_CPU_NUMBER = "maxcpunumber";
|
||||
public static final String MAX_MEMORY = "maxmemory";
|
||||
public static final String MIN_CPU_NUMBER = "mincpunumber";
|
||||
public static final String MIN_MEMORY = "minmemory";
|
||||
public static final String MEMORY = "memory";
|
||||
public static final String MODE = "mode";
|
||||
public static final String KEEPALIVE_ENABLED = "keepaliveenabled";
|
||||
|
||||
@ -18,10 +18,8 @@ package org.apache.cloudstack.api.command.admin.offering;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import com.cloud.storage.Storage;
|
||||
import org.apache.cloudstack.api.APICommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.ApiErrorCode;
|
||||
@ -30,10 +28,14 @@ import org.apache.cloudstack.api.Parameter;
|
||||
import org.apache.cloudstack.api.ServerApiException;
|
||||
import org.apache.cloudstack.api.response.DomainResponse;
|
||||
import org.apache.cloudstack.api.response.ServiceOfferingResponse;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.offering.ServiceOffering;
|
||||
import com.cloud.storage.Storage;
|
||||
import com.cloud.user.Account;
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
@APICommand(name = "createServiceOffering", description = "Creates a service offering.", responseObject = ServiceOfferingResponse.class,
|
||||
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
|
||||
@ -162,6 +164,37 @@ public class CreateServiceOfferingCmd extends BaseCmd {
|
||||
since = "4.4")
|
||||
private Integer hypervisorSnapshotReserve;
|
||||
|
||||
// Introduce 4 new optional paramaters to work custom compute offerings
|
||||
@Parameter(name = ApiConstants.CUSTOMIZED,
|
||||
type = CommandType.BOOLEAN,
|
||||
since = "4.13",
|
||||
description = "Whether service offering size is custom or not")
|
||||
private Boolean customized;
|
||||
|
||||
@Parameter(name = ApiConstants.MAX_CPU_NUMBER,
|
||||
type = CommandType.INTEGER,
|
||||
description = "The maximum number of CPUs to be set with Custom Computer Offering",
|
||||
since = "4.13")
|
||||
private Integer maxCPU;
|
||||
|
||||
@Parameter(name = ApiConstants.MIN_CPU_NUMBER,
|
||||
type = CommandType.INTEGER,
|
||||
description = "The minimum number of CPUs to be set with Custom Computer Offering",
|
||||
since = "4.13")
|
||||
private Integer minCPU;
|
||||
|
||||
@Parameter(name = ApiConstants.MAX_MEMORY,
|
||||
type = CommandType.INTEGER,
|
||||
description = "The maximum memroy 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",
|
||||
since = "4.13")
|
||||
private Integer minMemory;
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////////// Accessors ///////////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
@ -175,6 +208,9 @@ public class CreateServiceOfferingCmd extends BaseCmd {
|
||||
}
|
||||
|
||||
public String getDisplayText() {
|
||||
if (Strings.isNullOrEmpty(displayText)) {
|
||||
throw new InvalidParameterValueException("Failed to create service offering because the offering display text has not been spified.");
|
||||
}
|
||||
return displayText;
|
||||
}
|
||||
|
||||
@ -187,6 +223,9 @@ public class CreateServiceOfferingCmd extends BaseCmd {
|
||||
}
|
||||
|
||||
public String getServiceOfferingName() {
|
||||
if (Strings.isNullOrEmpty(serviceOfferingName)) {
|
||||
throw new InvalidParameterValueException("Failed to create service offering because offering name has not been spified.");
|
||||
}
|
||||
return serviceOfferingName;
|
||||
}
|
||||
|
||||
@ -234,18 +273,12 @@ public class CreateServiceOfferingCmd extends BaseCmd {
|
||||
return deploymentPlanner;
|
||||
}
|
||||
|
||||
public boolean isCustomized() {
|
||||
return (cpuNumber == null || memory == null || cpuSpeed == null);
|
||||
}
|
||||
|
||||
public Map<String, String> getDetails() {
|
||||
Map<String, String> detailsMap = null;
|
||||
if (details != null && !details.isEmpty()) {
|
||||
detailsMap = new HashMap<String, String>();
|
||||
Map<String, String> detailsMap = new HashMap<>();
|
||||
if (MapUtils.isNotEmpty(details)) {
|
||||
Collection<?> props = details.values();
|
||||
Iterator<?> iter = props.iterator();
|
||||
while (iter.hasNext()) {
|
||||
HashMap<String, String> detail = (HashMap<String, String>) iter.next();
|
||||
for (Object prop : props) {
|
||||
HashMap<String, String> detail = (HashMap<String, String>) prop;
|
||||
detailsMap.put(detail.get("key"), detail.get("value"));
|
||||
}
|
||||
}
|
||||
@ -316,6 +349,36 @@ public class CreateServiceOfferingCmd extends BaseCmd {
|
||||
return hypervisorSnapshotReserve;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @return true if the following conditions are satisfied;
|
||||
* - cpuNumber, memory and cpuSpeed are all null when customized parameter is set to true
|
||||
* - min/max CPU/Memory params are all null or all set
|
||||
*/
|
||||
public boolean isCustomized() {
|
||||
if (customized != null){
|
||||
return customized;
|
||||
}
|
||||
return (cpuNumber == null || memory == null);
|
||||
}
|
||||
|
||||
public Integer getMaxCPUs() {
|
||||
return maxCPU;
|
||||
}
|
||||
|
||||
public Integer getMinCPUs() {
|
||||
return minCPU;
|
||||
}
|
||||
|
||||
public Integer getMaxMemory() {
|
||||
return maxMemory;
|
||||
}
|
||||
|
||||
public Integer getMinMemory() {
|
||||
return minMemory;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
/////////////// API Implementation///////////////////
|
||||
/////////////////////////////////////////////////////
|
||||
|
||||
@ -331,4 +331,8 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering
|
||||
public void setDynamicFlag(boolean isdynamic) {
|
||||
isDynamic = isdynamic;
|
||||
}
|
||||
|
||||
public boolean isCustomCpuSpeedSupported() {
|
||||
return isCustomized() && getDetail("minCPU") != null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -612,9 +612,15 @@ public class CapacityManagerImpl extends ManagerBase implements CapacityManager,
|
||||
usedMemory +=
|
||||
((Integer.parseInt(vmDetails.get(UsageEventVO.DynamicParameters.memory.name())) * 1024L * 1024L) / ramOvercommitRatio) *
|
||||
clusterRamOvercommitRatio;
|
||||
if(vmDetails.containsKey(UsageEventVO.DynamicParameters.cpuSpeed.name())) {
|
||||
usedCpu +=
|
||||
((Integer.parseInt(vmDetails.get(UsageEventVO.DynamicParameters.cpuNumber.name())) * Integer.parseInt(vmDetails.get(UsageEventVO.DynamicParameters.cpuSpeed.name()))) / cpuOvercommitRatio) *
|
||||
clusterCpuOvercommitRatio;
|
||||
} else {
|
||||
usedCpu +=
|
||||
((Integer.parseInt(vmDetails.get(UsageEventVO.DynamicParameters.cpuNumber.name())) * so.getSpeed()) / cpuOvercommitRatio) *
|
||||
clusterCpuOvercommitRatio;
|
||||
}
|
||||
usedCpuCore += Integer.parseInt(vmDetails.get(UsageEventVO.DynamicParameters.cpuNumber.name()));
|
||||
} else {
|
||||
usedMemory += ((so.getRamSize() * 1024L * 1024L) / ramOvercommitRatio) * clusterRamOvercommitRatio;
|
||||
@ -645,9 +651,15 @@ public class CapacityManagerImpl extends ManagerBase implements CapacityManager,
|
||||
reservedMemory +=
|
||||
((Integer.parseInt(vmDetails.get(UsageEventVO.DynamicParameters.memory.name())) * 1024L * 1024L) / ramOvercommitRatio) *
|
||||
clusterRamOvercommitRatio;
|
||||
if(vmDetails.containsKey(UsageEventVO.DynamicParameters.cpuSpeed.name())) {
|
||||
reservedCpu +=
|
||||
((Integer.parseInt(vmDetails.get(UsageEventVO.DynamicParameters.cpuNumber.name())) * Integer.parseInt(vmDetails.get(UsageEventVO.DynamicParameters.cpuSpeed.name()))) / cpuOvercommitRatio) *
|
||||
clusterCpuOvercommitRatio;
|
||||
} else {
|
||||
reservedCpu +=
|
||||
((Integer.parseInt(vmDetails.get(UsageEventVO.DynamicParameters.cpuNumber.name())) * so.getSpeed()) / cpuOvercommitRatio) *
|
||||
clusterCpuOvercommitRatio;
|
||||
}
|
||||
reservedCpuCore += Integer.parseInt(vmDetails.get(UsageEventVO.DynamicParameters.cpuNumber.name()));
|
||||
} else {
|
||||
reservedMemory += ((so.getRamSize() * 1024L * 1024L) / ramOvercommitRatio) * clusterRamOvercommitRatio;
|
||||
|
||||
@ -35,12 +35,11 @@ import java.util.UUID;
|
||||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import org.apache.cloudstack.acl.SecurityChecker;
|
||||
import org.apache.cloudstack.affinity.AffinityGroup;
|
||||
import org.apache.cloudstack.affinity.AffinityGroupService;
|
||||
import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd;
|
||||
import org.apache.cloudstack.api.command.admin.network.CreateManagementNetworkIpRangeCmd;
|
||||
import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd;
|
||||
@ -232,6 +231,7 @@ import com.cloud.vm.dao.VMInstanceDao;
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
public class ConfigurationManagerImpl extends ManagerBase implements ConfigurationManager, ConfigurationService, Configurable {
|
||||
public static final Logger s_logger = Logger.getLogger(ConfigurationManagerImpl.class);
|
||||
@ -2218,6 +2218,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
||||
@ActionEvent(eventType = EventTypes.EVENT_SERVICE_OFFERING_CREATE, eventDescription = "creating service offering")
|
||||
public ServiceOffering createServiceOffering(final CreateServiceOfferingCmd cmd) {
|
||||
final Long userId = CallContext.current().getCallingUserId();
|
||||
final Map<String, String> details = cmd.getDetails();
|
||||
final String offeringName = cmd.getServiceOfferingName();
|
||||
|
||||
final String name = cmd.getServiceOfferingName();
|
||||
if (name == null || name.length() == 0) {
|
||||
@ -2233,21 +2235,54 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
||||
final Integer cpuSpeed = cmd.getCpuSpeed();
|
||||
final Integer memory = cmd.getMemory();
|
||||
|
||||
//restricting the createserviceoffering to allow setting all or none of the dynamic parameters to null
|
||||
if (cpuNumber == null || cpuSpeed == null || memory == null) {
|
||||
if (cpuNumber != null || cpuSpeed != null || memory != null) {
|
||||
throw new InvalidParameterValueException("For creating a custom compute offering cpu, cpu speed and memory all should be null");
|
||||
}
|
||||
}
|
||||
// Optional Custom Parameters
|
||||
Integer maxCPU = cmd.getMaxCPUs();
|
||||
Integer minCPU = cmd.getMinCPUs();
|
||||
Integer maxMemory = cmd.getMaxMemory();
|
||||
Integer minMemory = cmd.getMinMemory();
|
||||
|
||||
// Check if service offering is Custom,
|
||||
// If Customized, the following conditions must hold
|
||||
// 1. cpuNumber, cpuSpeed and memory should be all null
|
||||
// 2. minCPU, maxCPU, minMemory and maxMemory should all be null or all specified
|
||||
boolean isCustomized = cmd.isCustomized();
|
||||
if (isCustomized) {
|
||||
// validate specs
|
||||
//restricting the createserviceoffering to allow setting all or none of the dynamic parameters to null
|
||||
if (cpuNumber != null || memory != null) {
|
||||
throw new InvalidParameterValueException("For creating a custom compute offering cpu and memory all should be null");
|
||||
}
|
||||
// if any of them is null, then all of them shoull be null
|
||||
if (maxCPU == null || minCPU == null || maxMemory == null || minMemory == null) {
|
||||
if (maxCPU != null || minCPU != null || maxMemory != null || minMemory != null) {
|
||||
throw new InvalidParameterValueException("For creating a custom compute offering min/max cpu and min/max memory should all be specified");
|
||||
}
|
||||
} else {
|
||||
if (cpuSpeed != null && (cpuSpeed.intValue() < 0 || cpuSpeed.longValue() > Integer.MAX_VALUE)) {
|
||||
throw new InvalidParameterValueException("Failed to create service offering " + offeringName + ": specify the cpu speed value between 1 and " + Integer.MAX_VALUE);
|
||||
}
|
||||
if ((maxCPU <= 0 || maxCPU.longValue() > Integer.MAX_VALUE) || (minCPU <= 0 || minCPU.longValue() > Integer.MAX_VALUE ) ) {
|
||||
throw new InvalidParameterValueException("Failed to create service offering " + offeringName + ": specify the minimum or minimum cpu number value between 1 and " + Integer.MAX_VALUE);
|
||||
}
|
||||
if (minMemory < 32 || (minMemory.longValue() > Integer.MAX_VALUE) || (maxMemory.longValue() > Integer.MAX_VALUE)) {
|
||||
throw new InvalidParameterValueException("Failed to create service offering " + offeringName + ": specify the memory value between 32 and " + Integer.MAX_VALUE + " MB");
|
||||
}
|
||||
// Persist min/max CPU and Memory parameters in the service_offering_details table
|
||||
details.put(ApiConstants.MIN_MEMORY, minMemory.toString());
|
||||
details.put(ApiConstants.MAX_MEMORY, maxMemory.toString());
|
||||
details.put(ApiConstants.MIN_CPU_NUMBER, minCPU.toString());
|
||||
details.put(ApiConstants.MAX_CPU_NUMBER, maxCPU.toString());
|
||||
}
|
||||
} else {
|
||||
if (cpuNumber != null && (cpuNumber.intValue() <= 0 || cpuNumber.longValue() > Integer.MAX_VALUE)) {
|
||||
throw new InvalidParameterValueException("Failed to create service offering " + name + ": specify the cpu number value between 1 and " + Integer.MAX_VALUE);
|
||||
throw new InvalidParameterValueException("Failed to create service offering " + offeringName + ": specify the cpu number value between 1 and " + Integer.MAX_VALUE);
|
||||
}
|
||||
if (cpuSpeed != null && (cpuSpeed.intValue() < 0 || cpuSpeed.longValue() > Integer.MAX_VALUE)) {
|
||||
throw new InvalidParameterValueException("Failed to create service offering " + name + ": specify the cpu speed value between 0 and " + Integer.MAX_VALUE);
|
||||
throw new InvalidParameterValueException("Failed to create service offering " + offeringName + ": specify the cpu speed value between 0 and " + Integer.MAX_VALUE);
|
||||
}
|
||||
if (memory != null && (memory.intValue() < 32 || memory.longValue() > Integer.MAX_VALUE)) {
|
||||
throw new InvalidParameterValueException("Failed to create service offering " + name + ": specify the memory value between 32 and " + Integer.MAX_VALUE + " MB");
|
||||
throw new InvalidParameterValueException("Failed to create service offering " + offeringName + ": specify the memory value between 32 and " + Integer.MAX_VALUE + " MB");
|
||||
}
|
||||
}
|
||||
|
||||
// check if valid domain
|
||||
@ -2330,7 +2365,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
|
||||
|
||||
return createServiceOffering(userId, cmd.isSystem(), vmType, cmd.getServiceOfferingName(), cpuNumber, memory, cpuSpeed, cmd.getDisplayText(),
|
||||
cmd.getProvisioningType(), localStorageRequired, offerHA, limitCpuUse, volatileVm, cmd.getTags(), cmd.getDomainId(), cmd.getHostTag(),
|
||||
cmd.getNetworkRate(), cmd.getDeploymentPlanner(), cmd.getDetails(), isCustomizedIops, cmd.getMinIops(), cmd.getMaxIops(),
|
||||
cmd.getNetworkRate(), cmd.getDeploymentPlanner(), details, isCustomizedIops, cmd.getMinIops(), cmd.getMaxIops(),
|
||||
cmd.getBytesReadRate(), cmd.getBytesReadRateMax(), cmd.getBytesReadRateMaxLength(),
|
||||
cmd.getBytesWriteRate(), cmd.getBytesWriteRateMax(), cmd.getBytesWriteRateMaxLength(),
|
||||
cmd.getIopsReadRate(), cmd.getIopsReadRateMax(), cmd.getIopsReadRateMaxLength(),
|
||||
|
||||
@ -1017,14 +1017,18 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
||||
|
||||
@Override
|
||||
public void validateCustomParameters(ServiceOfferingVO serviceOffering, Map<String, String> customParameters) {
|
||||
//TODO need to validate custom cpu, and memory against min/max CPU/Memory ranges from service_offering_details table
|
||||
if (customParameters.size() != 0) {
|
||||
Map<String, String> offeringDetails = serviceOfferingDetailsDao.listDetailsKeyPairs(serviceOffering.getId());
|
||||
if (serviceOffering.getCpu() == null) {
|
||||
String cpuNumber = customParameters.get(UsageEventVO.DynamicParameters.cpuNumber.name());
|
||||
if ((cpuNumber == null) || (NumbersUtil.parseInt(cpuNumber, -1) <= 0)) {
|
||||
throw new InvalidParameterValueException("Invalid cpu cores value, specify a value between 1 and " + Integer.MAX_VALUE);
|
||||
int minCPU = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MIN_CPU_NUMBER), 1);
|
||||
int maxCPU = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MAX_CPU_NUMBER), Integer.MAX_VALUE);
|
||||
int cpuNumber = NumbersUtil.parseInt(customParameters.get(UsageEventVO.DynamicParameters.cpuNumber.name()), -1);
|
||||
if (cpuNumber < minCPU || cpuNumber > maxCPU) {
|
||||
throw new InvalidParameterValueException(String.format("Invalid cpu cores value, specify a value between %d and %d", minCPU, maxCPU));
|
||||
}
|
||||
} else if (customParameters.containsKey(UsageEventVO.DynamicParameters.cpuNumber.name())) {
|
||||
throw new InvalidParameterValueException("The cpu cores of this offering id:" + serviceOffering.getId()
|
||||
throw new InvalidParameterValueException("The cpu cores of this offering id:" + serviceOffering.getUuid()
|
||||
+ " is not customizable. This is predefined in the template.");
|
||||
}
|
||||
|
||||
@ -1033,18 +1037,20 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
|
||||
if ((cpuSpeed == null) || (NumbersUtil.parseInt(cpuSpeed, -1) <= 0)) {
|
||||
throw new InvalidParameterValueException("Invalid cpu speed value, specify a value between 1 and " + Integer.MAX_VALUE);
|
||||
}
|
||||
} else if (customParameters.containsKey(UsageEventVO.DynamicParameters.cpuSpeed.name())) {
|
||||
throw new InvalidParameterValueException("The cpu speed of this offering id:" + serviceOffering.getId()
|
||||
} else if (!serviceOffering.isCustomCpuSpeedSupported() && customParameters.containsKey(UsageEventVO.DynamicParameters.cpuSpeed.name())) {
|
||||
throw new InvalidParameterValueException("The cpu speed of this offering id:" + serviceOffering.getUuid()
|
||||
+ " is not customizable. This is predefined in the template.");
|
||||
}
|
||||
|
||||
if (serviceOffering.getRamSize() == null) {
|
||||
String memory = customParameters.get(UsageEventVO.DynamicParameters.memory.name());
|
||||
if (memory == null || (NumbersUtil.parseInt(memory, -1) < 32)) {
|
||||
throw new InvalidParameterValueException("Invalid memory value, specify a value between 32 and " + Integer.MAX_VALUE + " MB");
|
||||
int minMemory = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MIN_MEMORY), 32);
|
||||
int maxMemory = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MAX_MEMORY), Integer.MAX_VALUE);
|
||||
int memory = NumbersUtil.parseInt(customParameters.get(UsageEventVO.DynamicParameters.memory.name()), -1);
|
||||
if (memory < minMemory || memory > maxMemory) {
|
||||
throw new InvalidParameterValueException(String.format("Invalid memory value, specify a value between %d and %d", minMemory, maxMemory));
|
||||
}
|
||||
} else if (customParameters.containsKey(UsageEventVO.DynamicParameters.memory.name())) {
|
||||
throw new InvalidParameterValueException("The memory of this offering id:" + serviceOffering.getId() + " is not customizable. This is predefined in the template.");
|
||||
throw new InvalidParameterValueException("The memory of this offering id:" + serviceOffering.getUuid() + " is not customizable. This is predefined in the template.");
|
||||
}
|
||||
} else {
|
||||
throw new InvalidParameterValueException("Need to specify custom parameter values cpu, cpu speed and memory when using custom offering");
|
||||
|
||||
@ -6117,11 +6117,11 @@ label.error {
|
||||
margin-top: 9px !important;
|
||||
}
|
||||
|
||||
.multi-wizard.instance-wizard .custom-disk-size .select-container {
|
||||
.multi-wizard.instance-wizard .custom-slider-container .select-container {
|
||||
height: 279px;
|
||||
}
|
||||
|
||||
.multi-wizard.instance-wizard .custom-disk-size .select-container {
|
||||
.multi-wizard.instance-wizard .custom-slider-container .select-container {
|
||||
height: 213px;
|
||||
margin: -7px 6px 0 8px;
|
||||
/*+border-radius:6px;*/
|
||||
@ -6220,7 +6220,11 @@ label.error {
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.instance-wizard .step.data-disk-offering.custom-disk-size .select-container {
|
||||
.instance-wizard .step.data-disk-offering.custom-slider-container .select-container {
|
||||
height: 272px;
|
||||
}
|
||||
|
||||
.instance-wizard .step.service-offering.custom-slider-container .select-container {
|
||||
height: 272px;
|
||||
}
|
||||
|
||||
@ -6228,11 +6232,15 @@ label.error {
|
||||
height: 240px;
|
||||
}
|
||||
|
||||
.instance-wizard .step.data-disk-offering.custom-disk-size.custom-iops-do .select-container {
|
||||
.instance-wizard .step.data-disk-offering.custom-slider-container.custom-iops-do .select-container {
|
||||
height: 176px;
|
||||
}
|
||||
|
||||
.instance-wizard .step.data-disk-offering.required.custom-disk-size .select-container {
|
||||
.instance-wizard .step.service-offering.required.custom-slider-container .select-container {
|
||||
height: 315px;
|
||||
}
|
||||
|
||||
.instance-wizard .step.data-disk-offering.required.custom-slider-container .select-container {
|
||||
height: 315px;
|
||||
}
|
||||
|
||||
@ -6240,7 +6248,7 @@ label.error {
|
||||
height: 295px;
|
||||
}
|
||||
|
||||
.instance-wizard .step.data-disk-offering.required.custom-disk-size.custom-iops-do .select-container {
|
||||
.instance-wizard .step.data-disk-offering.required.custom-slider-container.custom-iops-do .select-container {
|
||||
height: 223px;
|
||||
}
|
||||
|
||||
|
||||
@ -206,7 +206,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Custom size slider -->
|
||||
<div class="section custom-size">
|
||||
<div class="section custom-size custom-no-limits">
|
||||
<div class="field">
|
||||
<label><translate key="label.num.cpu.cores"/></label>
|
||||
<input type="text" class="required disallowSpecialCharacters" name="compute-cpu-cores" />
|
||||
@ -221,6 +221,31 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Custom size slider -->
|
||||
<div class="section custom-size custom-slider-container">
|
||||
<div class="slider-cpu-cores">
|
||||
<label><translate key="label.num.cpu.cores"/></label>
|
||||
<!-- Slider -->
|
||||
<label class="size min"><span></span></label>
|
||||
<div class="slider custom-size"></div>
|
||||
<label class="size max"><span></span></label>
|
||||
<input type="text" class="required digits" name="slider-compute-cpu-cores" value="0" />
|
||||
<label class="size">Cores</label>
|
||||
</div>
|
||||
<div class="slider-memory-mb">
|
||||
<label><translate key="label.memory.mb"/></label>
|
||||
<!-- Slider -->
|
||||
<label class="size min"><span></span></label>
|
||||
<div class="slider custom-size"></div>
|
||||
<label class="size max"><span></span></label>
|
||||
<input type="text" class="required disallowSpecialCharacters" name="slider-compute-memory" value="0"/>
|
||||
<label class="size">MB</label>
|
||||
</div>
|
||||
<div class="slider-cpu-speed">
|
||||
<input type="text" style="display:none;" name="slider-compute-cpu-speed" value="0" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Custom iops -->
|
||||
<div class="section custom-iops">
|
||||
<div class="field">
|
||||
@ -248,7 +273,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Custom size slider -->
|
||||
<div class="section custom-size custom-disk-size">
|
||||
<div class="section custom-size custom-slider-container">
|
||||
<label><translate key="label.disk.size"/></label>
|
||||
|
||||
<!-- Slider -->
|
||||
|
||||
@ -553,6 +553,10 @@ var dictionary = {
|
||||
"label.compute":"Compute",
|
||||
"label.compute.and.storage":"Compute and Storage",
|
||||
"label.compute.offering":"Compute offering",
|
||||
"label.compute.offering.type":"Compute offering type",
|
||||
"label.compute.offering.custom.constrained":"Custom Constrained",
|
||||
"label.compute.offering.custom.unconstrained":"Custom Unconstrained",
|
||||
"label.compute.offering.fixed":"Fixed Offering",
|
||||
"label.compute.offerings":"Compute Offerings",
|
||||
"label.configuration":"Configuration",
|
||||
"label.configure":"Configure",
|
||||
@ -1037,6 +1041,8 @@ var dictionary = {
|
||||
"label.memory.allocated":"Memory Allocated",
|
||||
"label.memory.limits":"Memory limits (MiB)",
|
||||
"label.memory.mb":"Memory (in MB)",
|
||||
"label.memory.minimum.mb":"Min Memory (in MB)",
|
||||
"label.memory.maximum.mb":"Max Memory (in MB)",
|
||||
"label.memory.total":"Memory Total",
|
||||
"label.memory.used":"Memory Used",
|
||||
"label.menu.accounts":"Accounts",
|
||||
@ -1219,6 +1225,8 @@ var dictionary = {
|
||||
"label.not.found":"Not Found",
|
||||
"label.notifications":"Notifications",
|
||||
"label.num.cpu.cores":"# of CPU Cores",
|
||||
"label.min.cpu.cores":"Min CPU Cores",
|
||||
"label.max.cpu.cores":"Max CPU Cores",
|
||||
"label.number.of.clusters":"Number of Clusters",
|
||||
"label.number.of.cpu.sockets":"The Number of CPU Sockets",
|
||||
"label.number.of.hosts":"Number of Hosts",
|
||||
|
||||
@ -136,11 +136,70 @@
|
||||
});
|
||||
}
|
||||
},
|
||||
isCustomized: {
|
||||
label: 'label.custom',
|
||||
isBoolean: true,
|
||||
isReverse: true,
|
||||
isChecked: false
|
||||
offeringType: {
|
||||
label: 'label.compute.offering.type',
|
||||
docID: 'helpComputeOfferingType',
|
||||
select: function(args) {
|
||||
var items = [];
|
||||
items.push({
|
||||
id: 'fixed',
|
||||
description: _l('label.compute.offering.fixed')
|
||||
});
|
||||
items.push({
|
||||
id: 'customConstrained',
|
||||
description: _l('label.compute.offering.custom.constrained')
|
||||
});
|
||||
items.push({
|
||||
id: 'customUnconstrained',
|
||||
description: _l('label.compute.offering.custom.unconstrained')
|
||||
});
|
||||
|
||||
args.response.success({
|
||||
data: items
|
||||
});
|
||||
|
||||
args.$select.change(function() {
|
||||
var $form = $(this).closest('form');
|
||||
|
||||
var $cpuNumber = $form.find('.form-item[rel=cpuNumber]');
|
||||
var $cpuSpeed = $form.find('.form-item[rel=cpuSpeed]');
|
||||
var $memory = $form.find('.form-item[rel=memory]');
|
||||
|
||||
var $minCPUNumber = $form.find('.form-item[rel=minCPUNumber]');
|
||||
var $maxCPUNumber = $form.find('.form-item[rel=maxCPUNumber]');
|
||||
var $minMemory = $form.find('.form-item[rel=minMemory]');
|
||||
var $maxMemory = $form.find('.form-item[rel=maxMemory]');
|
||||
|
||||
var type = $(this).val();
|
||||
if (type == 'fixed') {
|
||||
$minCPUNumber.hide();
|
||||
$maxCPUNumber.hide();
|
||||
$minMemory.hide();
|
||||
$maxMemory.hide();
|
||||
|
||||
$cpuNumber.css('display', 'inline-block');
|
||||
$cpuSpeed.css('display', 'inline-block');
|
||||
$memory.css('display', 'inline-block');
|
||||
} else if (type == 'customConstrained') {
|
||||
$cpuNumber.hide();
|
||||
$memory.hide();
|
||||
|
||||
$cpuSpeed.css('display', 'inline-block');
|
||||
$minCPUNumber.css('display', 'inline-block');
|
||||
$maxCPUNumber.css('display', 'inline-block');
|
||||
$minMemory.css('display', 'inline-block');
|
||||
$maxMemory.css('display', 'inline-block');
|
||||
} else {
|
||||
$cpuNumber.hide();
|
||||
$memory.hide();
|
||||
$cpuSpeed.hide();
|
||||
$minCPUNumber.hide();
|
||||
$maxCPUNumber.hide();
|
||||
$minMemory.hide();
|
||||
$maxMemory.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
cpuNumber: {
|
||||
label: 'label.num.cpu.cores',
|
||||
@ -169,6 +228,39 @@
|
||||
number: true
|
||||
}
|
||||
},
|
||||
// Custom Compute Offering
|
||||
minCPUNumber: {
|
||||
label: 'label.min.cpu.cores',
|
||||
docID: 'helpComputeOfferingMinCPUCores',
|
||||
validation: {
|
||||
required: true,
|
||||
number: true
|
||||
}
|
||||
},
|
||||
maxCPUNumber: {
|
||||
label: 'label.max.cpu.cores',
|
||||
docID: 'helpComputeOfferingMaxCPUCores',
|
||||
validation: {
|
||||
required: true,
|
||||
number: true
|
||||
}
|
||||
},
|
||||
minMemory: {
|
||||
label: 'label.memory.minimum.mb',
|
||||
docID: 'helpComputeOfferingMinMemory',
|
||||
validation: {
|
||||
required: true,
|
||||
number: true
|
||||
}
|
||||
},
|
||||
maxMemory: {
|
||||
label: 'label.memory.maximum.mb',
|
||||
docID: 'helpComputeOfferingMaxMemory',
|
||||
validation: {
|
||||
required: true,
|
||||
number: true
|
||||
}
|
||||
},
|
||||
networkRate: {
|
||||
label: 'label.network.rate',
|
||||
docID: 'helpComputeOfferingNetworkRate',
|
||||
@ -588,17 +680,18 @@
|
||||
},
|
||||
|
||||
action: function(args) {
|
||||
var isFixedOfferingType = args.data.offeringType == 'fixed';
|
||||
var data = {
|
||||
issystem: false,
|
||||
name: args.data.name,
|
||||
displaytext: args.data.description,
|
||||
storageType: args.data.storageType,
|
||||
provisioningType :args.data.provisioningType,
|
||||
customized: (args.data.isCustomized == "on")
|
||||
customized: !isFixedOfferingType
|
||||
};
|
||||
|
||||
//custom fields (begin)
|
||||
if (args.data.isCustomized != "on") {
|
||||
if (isFixedOfferingType) {
|
||||
$.extend(data, {
|
||||
cpuNumber: args.data.cpuNumber
|
||||
});
|
||||
@ -608,6 +701,24 @@
|
||||
$.extend(data, {
|
||||
memory: args.data.memory
|
||||
});
|
||||
} else {
|
||||
if(args.data.cpuSpeed != null && args.data.minCPUNumber != null && args.data.maxCPUNumber != null && args.data.minMemory != null && args.data.maxMemory != null){
|
||||
$.extend(data, {
|
||||
cpuSpeed: args.data.cpuSpeed
|
||||
});
|
||||
$.extend(data, {
|
||||
mincpunumber: args.data.minCPUNumber
|
||||
});
|
||||
$.extend(data, {
|
||||
maxcpunumber: args.data.maxCPUNumber
|
||||
});
|
||||
$.extend(data, {
|
||||
minmemory: args.data.minMemory
|
||||
});
|
||||
$.extend(data, {
|
||||
maxmemory: args.data.maxMemory
|
||||
});
|
||||
}
|
||||
}
|
||||
//custom fields (end)
|
||||
|
||||
|
||||
@ -1326,5 +1326,30 @@ cloudStack.docs = {
|
||||
helpL2UserData: {
|
||||
desc: 'Pass user and meta data to VMs (via ConfigDrive)',
|
||||
externalLink: ''
|
||||
},
|
||||
|
||||
helpComputeOfferingMinCPUCores: {
|
||||
desc: 'This will be used for the setting the range (min-max) of the number of cpu cores that should be allowed for VMs using this custom offering.',
|
||||
externalLink: ''
|
||||
},
|
||||
|
||||
helpComputeOfferingMaxCPUCores: {
|
||||
desc: 'This will be used for the setting the range (min-max) of the number of cpu cores that should be allowed for VMs using this custom offering.',
|
||||
externalLink: ''
|
||||
},
|
||||
|
||||
helpComputeOfferingMinMemory: {
|
||||
desc: 'This will be used for the setting the range (min-max) amount of memory that should be allowed for VMs using this custom offering.',
|
||||
externalLink: ''
|
||||
},
|
||||
|
||||
helpComputeOfferingMaxMemory: {
|
||||
desc: 'This will be used for the setting the range (min-max) amount of memory that should be allowed for VMs using this custom offering.',
|
||||
externalLink: ''
|
||||
},
|
||||
|
||||
helpComputeOfferingType: {
|
||||
desc: 'This will be used for setting the type of compute offering - whether it is fixed, custom constrained or custom unconstrained.',
|
||||
externalLink: ''
|
||||
}
|
||||
};
|
||||
|
||||
@ -811,6 +811,17 @@
|
||||
'details[0].memory' : args.$wizard.find('input[name=compute-memory]').val()
|
||||
});
|
||||
}
|
||||
} else if (args.$wizard.find('input[name=slider-compute-cpu-cores]').parent().parent().css('display') != 'none') {
|
||||
if (args.$wizard.find('input[name=slider-compute-cpu-cores]').val().length > 0) {
|
||||
$.extend(deployVmData, {
|
||||
'details[0].cpuNumber' : args.$wizard.find('input[name=slider-compute-cpu-cores]').val()
|
||||
});
|
||||
}
|
||||
if (args.$wizard.find('input[name=slider-compute-memory]').val().length > 0) {
|
||||
$.extend(deployVmData, {
|
||||
'details[0].memory' : args.$wizard.find('input[name=slider-compute-memory]').val()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (args.$wizard.find('input[name=disk-min-iops]').parent().parent().css('display') != 'none') {
|
||||
|
||||
@ -431,7 +431,7 @@
|
||||
|
||||
if (custom) {
|
||||
$step.find('.section.custom-size').hide();
|
||||
$step.removeClass('custom-disk-size');
|
||||
$step.removeClass('custom-slider-container');
|
||||
}
|
||||
|
||||
$step.find('input[type=radio]').bind('change', function() {
|
||||
@ -464,10 +464,10 @@
|
||||
var hypervisor = item['hypervisor'];
|
||||
if (hypervisor == 'KVM' || hypervisor == 'XenServer' || hypervisor == 'VMware') {
|
||||
$step.find('.section.custom-size').show();
|
||||
$step.addClass('custom-disk-size');
|
||||
$step.addClass('custom-slider-container');
|
||||
} else {
|
||||
$step.find('.section.custom-size').hide();
|
||||
$step.removeClass('custom-disk-size');
|
||||
$step.removeClass('custom-slider-container');
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -516,8 +516,65 @@
|
||||
var custom = item[args.customFlag];
|
||||
|
||||
if (custom) {
|
||||
// contains min/max CPU and Memory values
|
||||
$step.addClass('custom-size');
|
||||
var offeringDetails = item['serviceofferingdetails'];
|
||||
var offeringCpuSpeed = item['cpuspeed'];
|
||||
$step.find('.custom-no-limits').hide();
|
||||
$step.find('.custom-slider-container').hide();
|
||||
|
||||
var minCpuNumber = 0, maxCpuNumber = 0, minMemory = 0, maxMemory = 0;
|
||||
if (offeringDetails){
|
||||
minCpuNumber = offeringDetails['mincpunumber'];
|
||||
maxCpuNumber = offeringDetails['maxcpunumber'];
|
||||
minMemory = offeringDetails['minmemory'];
|
||||
maxMemory = offeringDetails['maxmemory'];
|
||||
}
|
||||
|
||||
if (minCpuNumber > 0 && maxCpuNumber > 0 && minMemory > 0 && maxMemory > 0) {
|
||||
$step.find('.custom-slider-container.slider-cpu-speed input[type=text]').val(parseInt(offeringCpuSpeed));
|
||||
$step.find('.custom-slider-container').show();
|
||||
var setupSlider = function(sliderClassName, minVal, maxVal) {
|
||||
$step.find('.custom-slider-container .' + sliderClassName + ' .size.min span').html(minVal);
|
||||
$step.find('.custom-slider-container .' + sliderClassName + ' input[type=text]').val(minVal);
|
||||
$step.find('.custom-slider-container .' + sliderClassName + ' .size.max span').html(maxVal);
|
||||
$step.find('.custom-slider-container .' + sliderClassName + ' .slider').each(function() {
|
||||
var $slider = $(this);
|
||||
$slider.slider({
|
||||
min: parseInt(minVal),
|
||||
max: parseInt(maxVal),
|
||||
slide: function(event, ui) {
|
||||
$slider.closest('.section.custom-size .' + sliderClassName + '').find('input[type=text]').val(ui.value);
|
||||
$step.find('span.custom-slider-container .' + sliderClassName + '').html(ui.value);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$step.find('.custom-slider-container .' + sliderClassName + ' input[type=text]').bind('change', function() {
|
||||
var val = $step.find('.custom-slider-container .' + sliderClassName + ' input[type=text]').val();
|
||||
if (val < minVal || val > maxVal) {
|
||||
cloudStack.dialog.notice({ message: $.validator.format(_l('message.validate.range'), [minVal, maxVal]) });
|
||||
}
|
||||
if (val < minVal) {
|
||||
val = minVal;
|
||||
$step.find('.custom-slider-container .' + sliderClassName + ' input[type=text]').val(val);
|
||||
}
|
||||
if(val > maxVal) {
|
||||
val = maxVal;
|
||||
$step.find('.custom-slider-container .' + sliderClassName + ' input[type=text]').val(val);
|
||||
}
|
||||
$step.find('span.custom-slider-container .' + sliderClassName).html(_s(val));
|
||||
});
|
||||
}
|
||||
setupSlider('slider-cpu-cores', minCpuNumber, maxCpuNumber);
|
||||
setupSlider('slider-memory-mb', minMemory, maxMemory);
|
||||
} else {
|
||||
$step.find('.custom-slider-container.slider-cpu-speed.slider-compute-cpu-speed').val(0);
|
||||
$step.find('.custom-no-limits').show();
|
||||
}
|
||||
} else {
|
||||
$step.find('.custom-no-limits').hide();
|
||||
$step.find('.custom-slider-container').hide();
|
||||
$step.removeClass('custom-size');
|
||||
}
|
||||
|
||||
@ -557,7 +614,7 @@
|
||||
var multiDisk = args.multiDisk;
|
||||
|
||||
$step.find('.multi-disk-select-container').remove();
|
||||
$step.removeClass('custom-disk-size');
|
||||
$step.removeClass('custom-slider-container');
|
||||
$step.find('.main-desc, p.no-datadisk').remove();
|
||||
|
||||
if (!multiDisk){
|
||||
@ -675,7 +732,7 @@
|
||||
} else {
|
||||
// handle removal of custom size controls
|
||||
$step.find('.section.custom-size').hide();
|
||||
$step.removeClass('custom-disk-size');
|
||||
$step.removeClass('custom-slider-container');
|
||||
|
||||
// handle removal of custom IOPS controls
|
||||
$step.removeClass('custom-iops-do');
|
||||
@ -694,7 +751,7 @@
|
||||
$('<span>').addClass('custom-size-label')
|
||||
.append(': ')
|
||||
.append(
|
||||
$('<span>').addClass('custom-disk-size').html(
|
||||
$('<span>').addClass('custom-slider-container').html(
|
||||
$step.find('.custom-size input[name=size]').val()
|
||||
)
|
||||
)
|
||||
@ -705,14 +762,14 @@
|
||||
$('<span>').addClass('custom-size-label')
|
||||
.append(', ')
|
||||
.append(
|
||||
$('<span>').addClass('custom-disk-size').html(
|
||||
$('<span>').addClass('custom-slider-container').html(
|
||||
$step.find('.custom-size input[name=size]').val()
|
||||
)
|
||||
)
|
||||
.append(' GB')
|
||||
);
|
||||
$step.find('.section.custom-size').show();
|
||||
$step.addClass('custom-disk-size');
|
||||
$step.addClass('custom-slider-container');
|
||||
$target.closest('.select-container').scrollTop(
|
||||
$target.position().top
|
||||
);
|
||||
@ -723,7 +780,7 @@
|
||||
$(this).closest('.disk-select-group').removeClass('custom-size');
|
||||
} else {
|
||||
$step.find('.section.custom-size').hide();
|
||||
$step.removeClass('custom-disk-size');
|
||||
$step.removeClass('custom-slider-container');
|
||||
}
|
||||
}
|
||||
|
||||
@ -1333,9 +1390,9 @@
|
||||
args.maxDiskOfferingSize() : 100;
|
||||
|
||||
// Setup tabs and slider
|
||||
$wizard.find('.section.custom-size.custom-disk-size .size.min span').html(minCustomDiskSize);
|
||||
$wizard.find('.section.custom-size.custom-disk-size input[type=text]').val(minCustomDiskSize);
|
||||
$wizard.find('.section.custom-size.custom-disk-size .size.max span').html(maxCustomDiskSize);
|
||||
$wizard.find('.section.custom-size.custom-slider-container .size.min span').html(minCustomDiskSize);
|
||||
$wizard.find('.section.custom-size.custom-slider-container input[type=text]').val(minCustomDiskSize);
|
||||
$wizard.find('.section.custom-size.custom-slider-container .size.max span').html(maxCustomDiskSize);
|
||||
$wizard.find('.tab-view').tabs();
|
||||
$wizard.find('.slider').each(function() {
|
||||
var $slider = $(this);
|
||||
@ -1350,7 +1407,7 @@
|
||||
$slider.closest('.section.custom-size').find('input[type=text]').val(
|
||||
ui.value
|
||||
);
|
||||
$slider.closest('.step').find('span.custom-disk-size').html(
|
||||
$slider.closest('.step').find('span.custom-slider-container').html(
|
||||
ui.value
|
||||
);
|
||||
}
|
||||
@ -1359,10 +1416,9 @@
|
||||
|
||||
$wizard.find('div.data-disk-offering div.custom-size input[type=text]').bind('change', function() {
|
||||
var old = $wizard.find('div.data-disk-offering div.custom-size input[type=text]').val();
|
||||
$wizard.find('div.data-disk-offering span.custom-disk-size').html(_s(old));
|
||||
$wizard.find('div.data-disk-offering span.custom-slider-container').html(_s(old));
|
||||
});
|
||||
|
||||
|
||||
var wizardDialog = $wizard.dialog({
|
||||
title: _l('label.vm.add'),
|
||||
width: 896,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user