Skip to content

Commit ee95f45

Browse files
committed
server: allows compute offering with or without constraints
Changes allow admin to create compute offerings with unconstrained CPU, Memory or constrained range CPU, memory which can be later set by user while deploying VM. Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
1 parent 34030be commit ee95f45

12 files changed

Lines changed: 424 additions & 62 deletions

File tree

api/src/main/java/org/apache/cloudstack/api/ApiConstants.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,10 @@ public class ApiConstants {
201201
public static final String MAX = "max";
202202
public static final String MAC_ADDRESS = "macaddress";
203203
public static final String MAX_SNAPS = "maxsnaps";
204+
public static final String MAX_CPU_NUMBER = "maxcpunumber";
205+
public static final String MAX_MEMORY = "maxmemory";
206+
public static final String MIN_CPU_NUMBER = "mincpunumber";
207+
public static final String MIN_MEMORY = "minmemory";
204208
public static final String MEMORY = "memory";
205209
public static final String MODE = "mode";
206210
public static final String KEEPALIVE_ENABLED = "keepaliveenabled";

api/src/main/java/org/apache/cloudstack/api/command/admin/offering/CreateServiceOfferingCmd.java

Lines changed: 75 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,8 @@
1818

1919
import java.util.Collection;
2020
import java.util.HashMap;
21-
import java.util.Iterator;
2221
import java.util.Map;
2322

24-
import com.cloud.storage.Storage;
2523
import org.apache.cloudstack.api.APICommand;
2624
import org.apache.cloudstack.api.ApiConstants;
2725
import org.apache.cloudstack.api.ApiErrorCode;
@@ -30,10 +28,14 @@
3028
import org.apache.cloudstack.api.ServerApiException;
3129
import org.apache.cloudstack.api.response.DomainResponse;
3230
import org.apache.cloudstack.api.response.ServiceOfferingResponse;
31+
import org.apache.commons.collections.MapUtils;
3332
import org.apache.log4j.Logger;
3433

34+
import com.cloud.exception.InvalidParameterValueException;
3535
import com.cloud.offering.ServiceOffering;
36+
import com.cloud.storage.Storage;
3637
import com.cloud.user.Account;
38+
import com.google.common.base.Strings;
3739

3840
@APICommand(name = "createServiceOffering", description = "Creates a service offering.", responseObject = ServiceOfferingResponse.class,
3941
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
@@ -162,6 +164,37 @@ public class CreateServiceOfferingCmd extends BaseCmd {
162164
since = "4.4")
163165
private Integer hypervisorSnapshotReserve;
164166

167+
// Introduce 4 new optional paramaters to work custom compute offerings
168+
@Parameter(name = ApiConstants.CUSTOMIZED,
169+
type = CommandType.BOOLEAN,
170+
since = "4.13",
171+
description = "Whether service offering size is custom or not")
172+
private Boolean customized;
173+
174+
@Parameter(name = ApiConstants.MAX_CPU_NUMBER,
175+
type = CommandType.INTEGER,
176+
description = "The maximum number of CPUs to be set with Custom Computer Offering",
177+
since = "4.13")
178+
private Integer maxCPU;
179+
180+
@Parameter(name = ApiConstants.MIN_CPU_NUMBER,
181+
type = CommandType.INTEGER,
182+
description = "The minimum number of CPUs to be set with Custom Computer Offering",
183+
since = "4.13")
184+
private Integer minCPU;
185+
186+
@Parameter(name = ApiConstants.MAX_MEMORY,
187+
type = CommandType.INTEGER,
188+
description = "The maximum memroy size of the custom service offering in MB",
189+
since = "4.11")
190+
private Integer maxMemory;
191+
192+
@Parameter(name = ApiConstants.MIN_MEMORY,
193+
type = CommandType.INTEGER,
194+
description = "The minimum memroy size of the custom service offering in MB",
195+
since = "4.13")
196+
private Integer minMemory;
197+
165198
/////////////////////////////////////////////////////
166199
/////////////////// Accessors ///////////////////////
167200
/////////////////////////////////////////////////////
@@ -175,6 +208,9 @@ public Integer getCpuSpeed() {
175208
}
176209

177210
public String getDisplayText() {
211+
if (Strings.isNullOrEmpty(displayText)) {
212+
throw new InvalidParameterValueException("Failed to create service offering because the offering display text has not been spified.");
213+
}
178214
return displayText;
179215
}
180216

@@ -187,6 +223,9 @@ public Integer getMemory() {
187223
}
188224

189225
public String getServiceOfferingName() {
226+
if (Strings.isNullOrEmpty(serviceOfferingName)) {
227+
throw new InvalidParameterValueException("Failed to create service offering because offering name has not been spified.");
228+
}
190229
return serviceOfferingName;
191230
}
192231

@@ -234,18 +273,12 @@ public String getDeploymentPlanner() {
234273
return deploymentPlanner;
235274
}
236275

237-
public boolean isCustomized() {
238-
return (cpuNumber == null || memory == null || cpuSpeed == null);
239-
}
240-
241276
public Map<String, String> getDetails() {
242-
Map<String, String> detailsMap = null;
243-
if (details != null && !details.isEmpty()) {
244-
detailsMap = new HashMap<String, String>();
277+
Map<String, String> detailsMap = new HashMap<>();
278+
if (MapUtils.isNotEmpty(details)) {
245279
Collection<?> props = details.values();
246-
Iterator<?> iter = props.iterator();
247-
while (iter.hasNext()) {
248-
HashMap<String, String> detail = (HashMap<String, String>) iter.next();
280+
for (Object prop : props) {
281+
HashMap<String, String> detail = (HashMap<String, String>) prop;
249282
detailsMap.put(detail.get("key"), detail.get("value"));
250283
}
251284
}
@@ -316,6 +349,36 @@ public Integer getHypervisorSnapshotReserve() {
316349
return hypervisorSnapshotReserve;
317350
}
318351

352+
/**
353+
* If customized parameter is true, then cpuNumber, memory and cpuSpeed must be null
354+
* Check if the optional params min/max CPU/Memory have been specified
355+
* @return true if the following conditions are satisfied;
356+
* - cpuNumber, memory and cpuSpeed are all null when customized parameter is set to true
357+
* - min/max CPU/Memory params are all null or all set
358+
*/
359+
public boolean isCustomized() {
360+
if (customized != null){
361+
return customized;
362+
}
363+
return (cpuNumber == null || memory == null);
364+
}
365+
366+
public Integer getMaxCPUs() {
367+
return maxCPU;
368+
}
369+
370+
public Integer getMinCPUs() {
371+
return minCPU;
372+
}
373+
374+
public Integer getMaxMemory() {
375+
return maxMemory;
376+
}
377+
378+
public Integer getMinMemory() {
379+
return minMemory;
380+
}
381+
319382
/////////////////////////////////////////////////////
320383
/////////////// API Implementation///////////////////
321384
/////////////////////////////////////////////////////

engine/schema/src/main/java/com/cloud/service/ServiceOfferingVO.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,4 +331,8 @@ public boolean isDynamic() {
331331
public void setDynamicFlag(boolean isdynamic) {
332332
isDynamic = isdynamic;
333333
}
334+
335+
public boolean isCustomCpuSpeedSupported() {
336+
return isCustomized() && getDetail("minCPU") != null;
337+
}
334338
}

server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,11 @@
3535
import javax.inject.Inject;
3636
import javax.naming.ConfigurationException;
3737

38-
import com.google.common.collect.Sets;
39-
4038
import org.apache.cloudstack.acl.SecurityChecker;
4139
import org.apache.cloudstack.affinity.AffinityGroup;
4240
import org.apache.cloudstack.affinity.AffinityGroupService;
4341
import org.apache.cloudstack.affinity.dao.AffinityGroupDao;
42+
import org.apache.cloudstack.api.ApiConstants;
4443
import org.apache.cloudstack.api.command.admin.config.UpdateCfgCmd;
4544
import org.apache.cloudstack.api.command.admin.network.CreateManagementNetworkIpRangeCmd;
4645
import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd;
@@ -232,6 +231,7 @@
232231
import com.google.common.base.MoreObjects;
233232
import com.google.common.base.Preconditions;
234233
import com.google.common.base.Strings;
234+
import com.google.common.collect.Sets;
235235

236236
public class ConfigurationManagerImpl extends ManagerBase implements ConfigurationManager, ConfigurationService, Configurable {
237237
public static final Logger s_logger = Logger.getLogger(ConfigurationManagerImpl.class);
@@ -2218,6 +2218,8 @@ public DataCenter createZone(final CreateZoneCmd cmd) {
22182218
@ActionEvent(eventType = EventTypes.EVENT_SERVICE_OFFERING_CREATE, eventDescription = "creating service offering")
22192219
public ServiceOffering createServiceOffering(final CreateServiceOfferingCmd cmd) {
22202220
final Long userId = CallContext.current().getCallingUserId();
2221+
final Map<String, String> details = cmd.getDetails();
2222+
final String offeringName = cmd.getServiceOfferingName();
22212223

22222224
final String name = cmd.getServiceOfferingName();
22232225
if (name == null || name.length() == 0) {
@@ -2233,21 +2235,54 @@ public ServiceOffering createServiceOffering(final CreateServiceOfferingCmd cmd)
22332235
final Integer cpuSpeed = cmd.getCpuSpeed();
22342236
final Integer memory = cmd.getMemory();
22352237

2236-
//restricting the createserviceoffering to allow setting all or none of the dynamic parameters to null
2237-
if (cpuNumber == null || cpuSpeed == null || memory == null) {
2238-
if (cpuNumber != null || cpuSpeed != null || memory != null) {
2239-
throw new InvalidParameterValueException("For creating a custom compute offering cpu, cpu speed and memory all should be null");
2238+
// Optional Custom Parameters
2239+
Integer maxCPU = cmd.getMaxCPUs();
2240+
Integer minCPU = cmd.getMinCPUs();
2241+
Integer maxMemory = cmd.getMaxMemory();
2242+
Integer minMemory = cmd.getMinMemory();
2243+
2244+
// Check if service offering is Custom,
2245+
// If Customized, the following conditions must hold
2246+
// 1. cpuNumber, cpuSpeed and memory should be all null
2247+
// 2. minCPU, maxCPU, minMemory and maxMemory should all be null or all specified
2248+
boolean isCustomized = cmd.isCustomized();
2249+
if (isCustomized) {
2250+
// validate specs
2251+
//restricting the createserviceoffering to allow setting all or none of the dynamic parameters to null
2252+
if (cpuNumber != null || memory != null) {
2253+
throw new InvalidParameterValueException("For creating a custom compute offering cpu and memory all should be null");
2254+
}
2255+
// if any of them is null, then all of them shoull be null
2256+
if (maxCPU == null || minCPU == null || maxMemory == null || minMemory == null) {
2257+
if (maxCPU != null || minCPU != null || maxMemory != null || minMemory != null) {
2258+
throw new InvalidParameterValueException("For creating a custom compute offering min/max cpu and min/max memory should all be specified");
2259+
}
2260+
} else {
2261+
if (cpuSpeed != null && (cpuSpeed.intValue() < 0 || cpuSpeed.longValue() > Integer.MAX_VALUE)) {
2262+
throw new InvalidParameterValueException("Failed to create service offering " + offeringName + ": specify the cpu speed value between 1 and " + Integer.MAX_VALUE);
2263+
}
2264+
if ((maxCPU <= 0 || maxCPU.longValue() > Integer.MAX_VALUE) || (minCPU <= 0 || minCPU.longValue() > Integer.MAX_VALUE ) ) {
2265+
throw new InvalidParameterValueException("Failed to create service offering " + offeringName + ": specify the minimum or minimum cpu number value between 1 and " + Integer.MAX_VALUE);
2266+
}
2267+
if (minMemory < 32 || (minMemory.longValue() > Integer.MAX_VALUE) || (maxMemory.longValue() > Integer.MAX_VALUE)) {
2268+
throw new InvalidParameterValueException("Failed to create service offering " + offeringName + ": specify the memory value between 32 and " + Integer.MAX_VALUE + " MB");
2269+
}
2270+
// Persist min/max CPU and Memory parameters in the service_offering_details table
2271+
details.put(ApiConstants.MIN_MEMORY, minMemory.toString());
2272+
details.put(ApiConstants.MAX_MEMORY, maxMemory.toString());
2273+
details.put(ApiConstants.MIN_CPU_NUMBER, minCPU.toString());
2274+
details.put(ApiConstants.MAX_CPU_NUMBER, maxCPU.toString());
2275+
}
2276+
} else {
2277+
if (cpuNumber != null && (cpuNumber.intValue() <= 0 || cpuNumber.longValue() > Integer.MAX_VALUE)) {
2278+
throw new InvalidParameterValueException("Failed to create service offering " + offeringName + ": specify the cpu number value between 1 and " + Integer.MAX_VALUE);
2279+
}
2280+
if (cpuSpeed != null && (cpuSpeed.intValue() < 0 || cpuSpeed.longValue() > Integer.MAX_VALUE)) {
2281+
throw new InvalidParameterValueException("Failed to create service offering " + offeringName + ": specify the cpu speed value between 0 and " + Integer.MAX_VALUE);
2282+
}
2283+
if (memory != null && (memory.intValue() < 32 || memory.longValue() > Integer.MAX_VALUE)) {
2284+
throw new InvalidParameterValueException("Failed to create service offering " + offeringName + ": specify the memory value between 32 and " + Integer.MAX_VALUE + " MB");
22402285
}
2241-
}
2242-
2243-
if (cpuNumber != null && (cpuNumber.intValue() <= 0 || cpuNumber.longValue() > Integer.MAX_VALUE)) {
2244-
throw new InvalidParameterValueException("Failed to create service offering " + name + ": specify the cpu number value between 1 and " + Integer.MAX_VALUE);
2245-
}
2246-
if (cpuSpeed != null && (cpuSpeed.intValue() < 0 || cpuSpeed.longValue() > Integer.MAX_VALUE)) {
2247-
throw new InvalidParameterValueException("Failed to create service offering " + name + ": specify the cpu speed value between 0 and " + Integer.MAX_VALUE);
2248-
}
2249-
if (memory != null && (memory.intValue() < 32 || memory.longValue() > Integer.MAX_VALUE)) {
2250-
throw new InvalidParameterValueException("Failed to create service offering " + name + ": specify the memory value between 32 and " + Integer.MAX_VALUE + " MB");
22512286
}
22522287

22532288
// check if valid domain
@@ -2330,7 +2365,7 @@ public ServiceOffering createServiceOffering(final CreateServiceOfferingCmd cmd)
23302365

23312366
return createServiceOffering(userId, cmd.isSystem(), vmType, cmd.getServiceOfferingName(), cpuNumber, memory, cpuSpeed, cmd.getDisplayText(),
23322367
cmd.getProvisioningType(), localStorageRequired, offerHA, limitCpuUse, volatileVm, cmd.getTags(), cmd.getDomainId(), cmd.getHostTag(),
2333-
cmd.getNetworkRate(), cmd.getDeploymentPlanner(), cmd.getDetails(), isCustomizedIops, cmd.getMinIops(), cmd.getMaxIops(),
2368+
cmd.getNetworkRate(), cmd.getDeploymentPlanner(), details, isCustomizedIops, cmd.getMinIops(), cmd.getMaxIops(),
23342369
cmd.getBytesReadRate(), cmd.getBytesReadRateMax(), cmd.getBytesReadRateMaxLength(),
23352370
cmd.getBytesWriteRate(), cmd.getBytesWriteRateMax(), cmd.getBytesWriteRateMaxLength(),
23362371
cmd.getIopsReadRate(), cmd.getIopsReadRateMax(), cmd.getIopsReadRateMaxLength(),

server/src/main/java/com/cloud/vm/UserVmManagerImpl.java

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,6 +1017,7 @@ public UserVm upgradeVirtualMachine(UpgradeVMCmd cmd) throws ResourceAllocationE
10171017

10181018
@Override
10191019
public void validateCustomParameters(ServiceOfferingVO serviceOffering, Map<String, String> customParameters) {
1020+
//TODO need to validate custom cpu, and memory against min/max CPU/Memory ranges from service_offering_details table
10201021
if (customParameters.size() != 0) {
10211022
if (serviceOffering.getCpu() == null) {
10221023
String cpuNumber = customParameters.get(UsageEventVO.DynamicParameters.cpuNumber.name());
@@ -1033,7 +1034,7 @@ public void validateCustomParameters(ServiceOfferingVO serviceOffering, Map<Stri
10331034
if ((cpuSpeed == null) || (NumbersUtil.parseInt(cpuSpeed, -1) <= 0)) {
10341035
throw new InvalidParameterValueException("Invalid cpu speed value, specify a value between 1 and " + Integer.MAX_VALUE);
10351036
}
1036-
} else if (customParameters.containsKey(UsageEventVO.DynamicParameters.cpuSpeed.name())) {
1037+
} else if (!serviceOffering.isCustomCpuSpeedSupported() && customParameters.containsKey(UsageEventVO.DynamicParameters.cpuSpeed.name())) {
10371038
throw new InvalidParameterValueException("The cpu speed of this offering id:" + serviceOffering.getId()
10381039
+ " is not customizable. This is predefined in the template.");
10391040
}
@@ -3388,7 +3389,29 @@ private UserVm createVirtualMachine(DataCenter zone, ServiceOffering serviceOffe
33883389
}
33893390
size += _diskOfferingDao.findById(diskOfferingId).getDiskSize();
33903391
}
3391-
resourceLimitCheck(owner, isDisplayVm, new Long(offering.getCpu()), new Long(offering.getRamSize()));
3392+
3393+
// Check Limits from new parameters here
3394+
// Get custom offerring cpu and memory ranges frm service_offering_details Table;
3395+
Map<String, String> details = serviceOfferingDetailsDao.listDetailsKeyPairs(offering.getId());
3396+
3397+
if (details.containsKey(ApiConstants.MAX_CPU_NUMBER) && details.containsKey(ApiConstants.MIN_CPU_NUMBER)
3398+
&& details.containsKey(ApiConstants.MIN_MEMORY) && details.containsKey(ApiConstants.MAX_MEMORY)){
3399+
3400+
int cpu = NumbersUtil.parseInt(customParameters.get(UsageEventVO.DynamicParameters.cpuNumber.name()), -1);
3401+
3402+
if (cpu < NumbersUtil.parseInt(details.get(ApiConstants.MIN_CPU_NUMBER), -1) && cpu > NumbersUtil.parseInt(details.get(ApiConstants.MAX_CPU_NUMBER), -1)) {
3403+
throw new InvalidParameterValueException("The provided cpu value: " + cpu + "is out of the range supported by this custom offering");
3404+
}
3405+
3406+
int memory = NumbersUtil.parseInt(customParameters.get(UsageEventVO.DynamicParameters.memory.name()), -1);
3407+
3408+
if (memory < NumbersUtil.parseInt(details.get(ApiConstants.MIN_MEMORY), -1) && memory > NumbersUtil.parseInt(details.get(ApiConstants.MAX_MEMORY), -1)) {
3409+
throw new InvalidParameterValueException("The provided memory value: " + memory + "is out of the range supported by this custom offering");
3410+
}
3411+
3412+
} else {
3413+
resourceLimitCheck(owner, isDisplayVm, new Long(offering.getCpu()), new Long(offering.getRamSize()));
3414+
}
33923415

33933416
_resourceLimitMgr.checkResourceLimit(owner, ResourceType.volume, (isIso || diskOfferingId == null ? 1 : 2));
33943417
_resourceLimitMgr.checkResourceLimit(owner, ResourceType.primary_storage, size);

ui/css/cloudstack3.css

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6304,11 +6304,11 @@ label.error {
63046304
margin-top: 9px !important;
63056305
}
63066306

6307-
.multi-wizard.instance-wizard .custom-disk-size .select-container {
6307+
.multi-wizard.instance-wizard .custom-slider-container .select-container {
63086308
height: 279px;
63096309
}
63106310

6311-
.multi-wizard.instance-wizard .custom-disk-size .select-container {
6311+
.multi-wizard.instance-wizard .custom-slider-container .select-container {
63126312
height: 213px;
63136313
margin: -7px 6px 0 8px;
63146314
/*+border-radius:6px;*/
@@ -6393,27 +6393,35 @@ label.error {
63936393
font-size: 10px;
63946394
}
63956395

6396-
.instance-wizard .step.data-disk-offering.custom-disk-size .select-container {
6396+
.instance-wizard .step.data-disk-offering.custom-slider-container .select-container {
6397+
height: 272px;
6398+
}
6399+
6400+
.instance-wizard .step.service-offering.custom-slider-container .select-container {
63976401
height: 272px;
63986402
}
63996403

64006404
.instance-wizard .step.data-disk-offering.custom-iops-do .select-container {
64016405
height: 240px;
64026406
}
64036407

6404-
.instance-wizard .step.data-disk-offering.custom-disk-size.custom-iops-do .select-container {
6408+
.instance-wizard .step.data-disk-offering.custom-slider-container.custom-iops-do .select-container {
64056409
height: 176px;
64066410
}
64076411

6408-
.instance-wizard .step.data-disk-offering.required.custom-disk-size .select-container {
6412+
.instance-wizard .step.service-offering.required.custom-slider-container .select-container {
6413+
height: 315px;
6414+
}
6415+
6416+
.instance-wizard .step.data-disk-offering.required.custom-slider-container .select-container {
64096417
height: 315px;
64106418
}
64116419

64126420
.instance-wizard .step.data-disk-offering.required.custom-iops-do .select-container {
64136421
height: 295px;
64146422
}
64156423

6416-
.instance-wizard .step.data-disk-offering.required.custom-disk-size.custom-iops-do .select-container {
6424+
.instance-wizard .step.data-disk-offering.required.custom-slider-container.custom-iops-do .select-container {
64176425
height: 223px;
64186426
}
64196427

0 commit comments

Comments
 (0)