Skip to content

Commit c693688

Browse files
authored
server: prevent adding vm compute details when not applicable (#12637)
1 parent 8eb162c commit c693688

File tree

3 files changed

+202
-85
lines changed

3 files changed

+202
-85
lines changed

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

Lines changed: 46 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@
5454
import javax.xml.parsers.DocumentBuilder;
5555
import javax.xml.parsers.ParserConfigurationException;
5656

57-
import com.cloud.resourcelimit.ReservationHelper;
5857
import org.apache.cloudstack.acl.ControlledEntity;
5958
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
6059
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
@@ -135,8 +134,8 @@
135134
import org.apache.cloudstack.userdata.UserDataManager;
136135
import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
137136
import org.apache.cloudstack.utils.security.ParserUtils;
138-
import org.apache.cloudstack.vm.schedule.VMScheduleManager;
139137
import org.apache.cloudstack.vm.UnmanagedVMsManager;
138+
import org.apache.cloudstack.vm.schedule.VMScheduleManager;
140139
import org.apache.commons.collections.CollectionUtils;
141140
import org.apache.commons.collections.MapUtils;
142141
import org.apache.commons.lang.math.NumberUtils;
@@ -297,6 +296,7 @@
297296
import com.cloud.resource.ResourceManager;
298297
import com.cloud.resource.ResourceState;
299298
import com.cloud.resourcelimit.CheckedReservation;
299+
import com.cloud.resourcelimit.ReservationHelper;
300300
import com.cloud.server.ManagementService;
301301
import com.cloud.server.ResourceTag;
302302
import com.cloud.server.StatsCollector;
@@ -1285,46 +1285,45 @@ private void validateOfferingMaxResource(ServiceOfferingVO offering) {
12851285

12861286
@Override
12871287
public void validateCustomParameters(ServiceOfferingVO serviceOffering, Map<String, String> customParameters) {
1288-
//TODO need to validate custom cpu, and memory against min/max CPU/Memory ranges from service_offering_details table
1289-
if (customParameters.size() != 0) {
1290-
Map<String, String> offeringDetails = serviceOfferingDetailsDao.listDetailsKeyPairs(serviceOffering.getId());
1291-
if (serviceOffering.getCpu() == null) {
1292-
int minCPU = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MIN_CPU_NUMBER), 1);
1293-
int maxCPU = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MAX_CPU_NUMBER), Integer.MAX_VALUE);
1294-
int cpuNumber = NumbersUtil.parseInt(customParameters.get(UsageEventVO.DynamicParameters.cpuNumber.name()), -1);
1295-
Integer maxCPUCores = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES.value() == 0 ? Integer.MAX_VALUE: ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES.value();
1296-
if (cpuNumber < minCPU || cpuNumber > maxCPU || cpuNumber > maxCPUCores) {
1297-
throw new InvalidParameterValueException(String.format("Invalid CPU cores value, specify a value between %d and %d", minCPU, Math.min(maxCPUCores, maxCPU)));
1298-
}
1299-
} else if (customParameters.containsKey(UsageEventVO.DynamicParameters.cpuNumber.name())) {
1300-
throw new InvalidParameterValueException("The CPU cores of this offering id:" + serviceOffering.getUuid()
1301-
+ " is not customizable. This is predefined in the Template.");
1302-
}
1303-
1304-
if (serviceOffering.getSpeed() == null) {
1305-
String cpuSpeed = customParameters.get(UsageEventVO.DynamicParameters.cpuSpeed.name());
1306-
if ((cpuSpeed == null) || (NumbersUtil.parseInt(cpuSpeed, -1) <= 0)) {
1307-
throw new InvalidParameterValueException("Invalid CPU speed value, specify a value between 1 and " + Integer.MAX_VALUE);
1308-
}
1309-
} else if (!serviceOffering.isCustomCpuSpeedSupported() && customParameters.containsKey(UsageEventVO.DynamicParameters.cpuSpeed.name())) {
1310-
throw new InvalidParameterValueException("The CPU speed of this offering id:" + serviceOffering.getUuid()
1311-
+ " is not customizable. This is predefined in the Template.");
1312-
}
1313-
1314-
if (serviceOffering.getRamSize() == null) {
1315-
int minMemory = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MIN_MEMORY), 32);
1316-
int maxMemory = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MAX_MEMORY), Integer.MAX_VALUE);
1317-
int memory = NumbersUtil.parseInt(customParameters.get(UsageEventVO.DynamicParameters.memory.name()), -1);
1318-
Integer maxRAMSize = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE.value() == 0 ? Integer.MAX_VALUE: ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE.value();
1319-
if (memory < minMemory || memory > maxMemory || memory > maxRAMSize) {
1320-
throw new InvalidParameterValueException(String.format("Invalid memory value, specify a value between %d and %d", minMemory, Math.min(maxRAMSize, maxMemory)));
1321-
}
1322-
} else if (customParameters.containsKey(UsageEventVO.DynamicParameters.memory.name())) {
1323-
throw new InvalidParameterValueException("The memory of this offering id:" + serviceOffering.getUuid() + " is not customizable. This is predefined in the Template.");
1324-
}
1325-
} else {
1288+
if (MapUtils.isEmpty(customParameters) && serviceOffering.isDynamic()) {
13261289
throw new InvalidParameterValueException("Need to specify custom parameter values cpu, cpu speed and memory when using custom offering");
13271290
}
1291+
Map<String, String> offeringDetails = serviceOfferingDetailsDao.listDetailsKeyPairs(serviceOffering.getId());
1292+
if (serviceOffering.getCpu() == null) {
1293+
int minCPU = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MIN_CPU_NUMBER), 1);
1294+
int maxCPU = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MAX_CPU_NUMBER), Integer.MAX_VALUE);
1295+
int cpuNumber = NumbersUtil.parseInt(customParameters.get(UsageEventVO.DynamicParameters.cpuNumber.name()), -1);
1296+
int maxCPUCores = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES.value() == 0 ? Integer.MAX_VALUE: ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES.value();
1297+
if (cpuNumber < minCPU || cpuNumber > maxCPU || cpuNumber > maxCPUCores) {
1298+
throw new InvalidParameterValueException(String.format("Invalid CPU cores value, specify a value between %d and %d", minCPU, Math.min(maxCPUCores, maxCPU)));
1299+
}
1300+
} else if (customParameters.containsKey(UsageEventVO.DynamicParameters.cpuNumber.name())) {
1301+
throw new InvalidParameterValueException("The CPU cores of this offering id:" + serviceOffering.getUuid()
1302+
+ " is not customizable. This is predefined in the Template.");
1303+
}
1304+
1305+
if (serviceOffering.getSpeed() == null) {
1306+
String cpuSpeed = customParameters.get(UsageEventVO.DynamicParameters.cpuSpeed.name());
1307+
if ((cpuSpeed == null) || (NumbersUtil.parseInt(cpuSpeed, -1) <= 0)) {
1308+
throw new InvalidParameterValueException("Invalid CPU speed value, specify a value between 1 and " + Integer.MAX_VALUE);
1309+
}
1310+
} else if (!serviceOffering.isCustomCpuSpeedSupported() && customParameters.containsKey(UsageEventVO.DynamicParameters.cpuSpeed.name())) {
1311+
throw new InvalidParameterValueException(String.format("The CPU speed of this offering id:%s"
1312+
+ " is not customizable. This is predefined as %d MHz.",
1313+
serviceOffering.getUuid(), serviceOffering.getSpeed()));
1314+
}
1315+
1316+
if (serviceOffering.getRamSize() == null) {
1317+
int minMemory = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MIN_MEMORY), 32);
1318+
int maxMemory = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MAX_MEMORY), Integer.MAX_VALUE);
1319+
int memory = NumbersUtil.parseInt(customParameters.get(UsageEventVO.DynamicParameters.memory.name()), -1);
1320+
int maxRAMSize = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE.value() == 0 ? Integer.MAX_VALUE: ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE.value();
1321+
if (memory < minMemory || memory > maxMemory || memory > maxRAMSize) {
1322+
throw new InvalidParameterValueException(String.format("Invalid memory value, specify a value between %d and %d", minMemory, Math.min(maxRAMSize, maxMemory)));
1323+
}
1324+
} else if (customParameters.containsKey(UsageEventVO.DynamicParameters.memory.name())) {
1325+
throw new InvalidParameterValueException("The memory of this offering id:" + serviceOffering.getUuid() + " is not customizable. This is predefined in the Template.");
1326+
}
13281327
}
13291328

13301329
private UserVm upgradeStoppedVirtualMachine(Long vmId, Long svcOffId, Map<String, String> customParameters) throws ResourceAllocationException {
@@ -2785,10 +2784,16 @@ protected void verifyVmLimits(UserVmVO vmInstance, Map<String, String> details)
27852784
Map<String, String> customParameters = new HashMap<>();
27862785
customParameters.put(VmDetailConstants.CPU_NUMBER, String.valueOf(newCpu));
27872786
customParameters.put(VmDetailConstants.MEMORY, String.valueOf(newMemory));
2788-
if (svcOffering.isCustomCpuSpeedSupported()) {
2787+
if (details.containsKey(VmDetailConstants.CPU_SPEED)) {
27892788
customParameters.put(VmDetailConstants.CPU_SPEED, details.get(VmDetailConstants.CPU_SPEED));
27902789
}
27912790
validateCustomParameters(svcOffering, customParameters);
2791+
} else {
2792+
if (details.containsKey(VmDetailConstants.CPU_NUMBER) || details.containsKey(VmDetailConstants.MEMORY) ||
2793+
details.containsKey(VmDetailConstants.CPU_SPEED)) {
2794+
throw new InvalidParameterValueException("CPU number, Memory and CPU speed cannot be updated for a " +
2795+
"non-dynamic offering");
2796+
}
27922797
}
27932798
if (VirtualMachineManager.ResourceCountRunningVMsonly.value()) {
27942799
return;

server/src/test/java/com/cloud/hypervisor/KVMGuruTest.java

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,25 @@
1616
// under the License.
1717
package com.cloud.hypervisor;
1818

19+
import java.io.UnsupportedEncodingException;
20+
import java.util.Arrays;
21+
import java.util.HashMap;
22+
import java.util.Map;
23+
24+
import org.apache.cloudstack.api.ApiConstants;
25+
import org.apache.cloudstack.framework.config.ConfigKey;
26+
import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
27+
import org.junit.After;
28+
import org.junit.Assert;
29+
import org.junit.Before;
30+
import org.junit.Test;
31+
import org.junit.runner.RunWith;
32+
import org.mockito.InjectMocks;
33+
import org.mockito.Mock;
34+
import org.mockito.Mockito;
35+
import org.mockito.Spy;
36+
import org.mockito.junit.MockitoJUnitRunner;
37+
1938
import com.cloud.agent.api.to.NicTO;
2039
import com.cloud.agent.api.to.VirtualMachineTO;
2140
import com.cloud.configuration.ConfigurationManagerImpl;
@@ -34,23 +53,6 @@
3453
import com.cloud.utils.Pair;
3554
import com.cloud.vm.VirtualMachine;
3655
import com.cloud.vm.VirtualMachineProfile;
37-
import org.apache.cloudstack.api.ApiConstants;
38-
import org.apache.cloudstack.framework.config.ConfigKey;
39-
import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
40-
import org.junit.Assert;
41-
import org.junit.Before;
42-
import org.junit.Test;
43-
import org.junit.runner.RunWith;
44-
import org.mockito.InjectMocks;
45-
import org.mockito.Mock;
46-
import org.mockito.Mockito;
47-
import org.mockito.Spy;
48-
import org.mockito.junit.MockitoJUnitRunner;
49-
50-
import java.io.UnsupportedEncodingException;
51-
import java.util.Arrays;
52-
import java.util.HashMap;
53-
import java.util.Map;
5456

5557
@RunWith(MockitoJUnitRunner.class)
5658
public class KVMGuruTest {
@@ -111,8 +113,15 @@ public class KVMGuruTest {
111113
private static final String detail2Key = "detail2";
112114
private static final String detail2Value = "value2";
113115

116+
private ConfigKey<Integer> originalVmServiceOfferingMaxCpuCores;
117+
private ConfigKey<Integer> originalVmServiceOfferingMaxRAMSize;
118+
114119
@Before
115120
public void setup() throws UnsupportedEncodingException {
121+
// Preserve the original value for restoration in tearDown
122+
originalVmServiceOfferingMaxCpuCores = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES;
123+
originalVmServiceOfferingMaxRAMSize = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE;
124+
116125
Mockito.when(vmTO.getLimitCpuUse()).thenReturn(true);
117126
Mockito.when(vmProfile.getVirtualMachine()).thenReturn(vm);
118127
Mockito.when(vm.getHostId()).thenReturn(hostId);
@@ -134,6 +143,13 @@ public void setup() throws UnsupportedEncodingException {
134143
Arrays.asList(detail1, detail2));
135144
}
136145

146+
@After
147+
public void tearDown() {
148+
// Restore the original value
149+
ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES = originalVmServiceOfferingMaxCpuCores;
150+
ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE = originalVmServiceOfferingMaxRAMSize;
151+
}
152+
137153
@Test
138154
public void testSetVmQuotaPercentage() {
139155
guru.setVmQuotaPercentage(vmTO, vmProfile);

0 commit comments

Comments
 (0)