Skip to content

Commit 0f68a42

Browse files
Live scaling for VMs with fixed service offerings on KVM
1 parent 1e512ab commit 0f68a42

23 files changed

Lines changed: 500 additions & 256 deletions

File tree

api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public class VirtualMachineTO {
5151

5252
private long minRam;
5353
private long maxRam;
54+
private long requestedRam;
5455
private String hostName;
5556
private String arch;
5657
private String os;
@@ -207,15 +208,20 @@ public long getMinRam() {
207208
return minRam;
208209
}
209210

210-
public void setRam(long minRam, long maxRam) {
211+
public void setRam(long minRam, long maxRam, long requestedRam) {
211212
this.minRam = minRam;
212213
this.maxRam = maxRam;
214+
this.requestedRam = requestedRam;
213215
}
214216

215217
public long getMaxRam() {
216218
return maxRam;
217219
}
218220

221+
public long getRequestedRam() {
222+
return requestedRam;
223+
}
224+
219225
public String getHostName() {
220226
return hostName;
221227
}

core/src/main/java/com/cloud/agent/api/ScaleVmCommand.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public class ScaleVmCommand extends Command {
3030
Integer maxSpeed;
3131
long minRam;
3232
long maxRam;
33+
private boolean limitCpuUseChange;
3334

3435
public VirtualMachineTO getVm() {
3536
return vm;
@@ -43,7 +44,7 @@ public int getCpus() {
4344
return cpus;
4445
}
4546

46-
public ScaleVmCommand(String vmName, int cpus, Integer minSpeed, Integer maxSpeed, long minRam, long maxRam, boolean limitCpuUse) {
47+
public ScaleVmCommand(String vmName, int cpus, Integer minSpeed, Integer maxSpeed, long minRam, long maxRam, boolean limitCpuUse, Double cpuQuotaPercentage, boolean limitCpuUseChange) {
4748
super();
4849
this.vmName = vmName;
4950
this.cpus = cpus;
@@ -52,6 +53,8 @@ public ScaleVmCommand(String vmName, int cpus, Integer minSpeed, Integer maxSpee
5253
this.minRam = minRam;
5354
this.maxRam = maxRam;
5455
this.vm = new VirtualMachineTO(1L, vmName, null, cpus, minSpeed, maxSpeed, minRam, maxRam, null, null, false, limitCpuUse, null);
56+
this.vm.setCpuQuotaPercentage(cpuQuotaPercentage);
57+
this.limitCpuUseChange = limitCpuUseChange;
5558
}
5659

5760
public void setCpus(int cpus) {
@@ -102,6 +105,10 @@ public VirtualMachineTO getVirtualMachine() {
102105
return vm;
103106
}
104107

108+
public boolean getLimitCpuUseChange() {
109+
return limitCpuUseChange;
110+
}
111+
105112
@Override
106113
public boolean executeInSequence() {
107114
return true;

engine/components-api/src/main/java/com/cloud/capacity/CapacityManager.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,20 @@ public interface CapacityManager {
133133
"capacity.calculate.workers", "1",
134134
"Number of worker threads to be used for capacities calculation", true);
135135

136+
ConfigKey<Integer> KvmMemoryDynamicScalingCapacity = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED,
137+
Integer.class, "kvm.memory.dynamic.scaling.capacity", "0",
138+
"Defines the maximum memory capacity in MiB for which VMs can be dynamically scaled to with KVM. " +
139+
"The 'kvm.memory.dynamic.scaling.capacity' setting's value will be used to define the value of the " +
140+
"'<maxMemory />' element of domain XMLs. If it is set to a value less than or equal to '0', then the host's memory capacity will be considered.",
141+
true, ConfigKey.Scope.Cluster);
142+
143+
ConfigKey<Integer> KvmCpuDynamicScalingCapacity = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED,
144+
Integer.class, "kvm.cpu.dynamic.scaling.capacity", "0",
145+
"Defines the maximum vCPU capacity for which VMs can be dynamically scaled to with KVM. " +
146+
"The 'kvm.cpu.dynamic.scaling.capacity' setting's value will be used to define the value of the " +
147+
"'<vcpu />' element of domain XMLs. If it is set to a value less than or equal to '0', then the host's CPU cores capacity will be considered.",
148+
true, ConfigKey.Scope.Cluster);
149+
136150
public boolean releaseVmCapacity(VirtualMachine vm, boolean moveFromReserved, boolean moveToReservered, Long hostId);
137151

138152
void allocateVmCapacity(VirtualMachine vm, boolean fromLastHost);

engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import javax.naming.ConfigurationException;
5151
import javax.persistence.EntityExistsException;
5252

53+
import com.cloud.hypervisor.KVMGuru;
5354
import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
5455
import org.apache.cloudstack.annotation.AnnotationService;
5556
import org.apache.cloudstack.annotation.dao.AnnotationDao;
@@ -5162,7 +5163,7 @@ public VMInstanceVO reConfigureVm(final String vmUuid, final ServiceOffering old
51625163
try {
51635164
result = retrieveResultFromJobOutcomeAndThrowExceptionIfNeeded(outcome);
51645165
} catch (Exception ex) {
5165-
throw new RuntimeException("Unhandled exception", ex);
5166+
throw new RuntimeException("Unable to reconfigure VM.", ex);
51665167
}
51675168

51685169
if (result != null) {
@@ -5175,22 +5176,29 @@ public VMInstanceVO reConfigureVm(final String vmUuid, final ServiceOffering old
51755176

51765177
private VMInstanceVO orchestrateReConfigureVm(String vmUuid, ServiceOffering oldServiceOffering, ServiceOffering newServiceOffering,
51775178
boolean reconfiguringOnExistingHost) throws ResourceUnavailableException, ConcurrentOperationException {
5178-
final VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
5179+
VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
51795180

51805181
HostVO hostVo = _hostDao.findById(vm.getHostId());
51815182

5182-
Long clustedId = hostVo.getClusterId();
5183-
Float memoryOvercommitRatio = CapacityManager.MemOverprovisioningFactor.valueIn(clustedId);
5184-
Float cpuOvercommitRatio = CapacityManager.CpuOverprovisioningFactor.valueIn(clustedId);
5185-
boolean divideMemoryByOverprovisioning = HypervisorGuruBase.VmMinMemoryEqualsMemoryDividedByMemOverprovisioningFactor.valueIn(clustedId);
5186-
boolean divideCpuByOverprovisioning = HypervisorGuruBase.VmMinCpuSpeedEqualsCpuSpeedDividedByCpuOverprovisioningFactor.valueIn(clustedId);
5183+
Long clusterId = hostVo.getClusterId();
5184+
Float memoryOvercommitRatio = CapacityManager.MemOverprovisioningFactor.valueIn(clusterId);
5185+
Float cpuOvercommitRatio = CapacityManager.CpuOverprovisioningFactor.valueIn(clusterId);
5186+
boolean divideMemoryByOverprovisioning = HypervisorGuruBase.VmMinMemoryEqualsMemoryDividedByMemOverprovisioningFactor.valueIn(clusterId);
5187+
boolean divideCpuByOverprovisioning = HypervisorGuruBase.VmMinCpuSpeedEqualsCpuSpeedDividedByCpuOverprovisioningFactor.valueIn(clusterId);
51875188

51885189
int minMemory = (int)(newServiceOffering.getRamSize() / (divideMemoryByOverprovisioning ? memoryOvercommitRatio : 1));
51895190
int minSpeed = (int)(newServiceOffering.getSpeed() / (divideCpuByOverprovisioning ? cpuOvercommitRatio : 1));
51905191

5191-
ScaleVmCommand scaleVmCommand =
5192-
new ScaleVmCommand(vm.getInstanceName(), newServiceOffering.getCpu(), minSpeed,
5193-
newServiceOffering.getSpeed(), minMemory * 1024L * 1024L, newServiceOffering.getRamSize() * 1024L * 1024L, newServiceOffering.getLimitCpuUse());
5192+
Double cpuQuotaPercentage = null;
5193+
if (newServiceOffering.getLimitCpuUse() && vm.getHypervisorType().equals(HypervisorType.KVM)) {
5194+
KVMGuru kvmGuru = (KVMGuru) _hvGuruMgr.getGuru(vm.getHypervisorType());
5195+
cpuQuotaPercentage = kvmGuru.getCpuQuotaPercentage(minSpeed, hostVo.getSpeed());
5196+
}
5197+
5198+
boolean limitCpuUseChange = oldServiceOffering.getLimitCpuUse() != newServiceOffering.getLimitCpuUse();
5199+
ScaleVmCommand scaleVmCommand = new ScaleVmCommand(vm.getInstanceName(), newServiceOffering.getCpu(), minSpeed, newServiceOffering.getSpeed(),
5200+
minMemory * 1024L * 1024L, newServiceOffering.getRamSize() * 1024L * 1024L,
5201+
newServiceOffering.getLimitCpuUse(), cpuQuotaPercentage, limitCpuUseChange);
51945202

51955203
scaleVmCommand.getVirtualMachine().setId(vm.getId());
51965204
scaleVmCommand.getVirtualMachine().setUuid(vm.getUuid());
@@ -5219,16 +5227,20 @@ private VMInstanceVO orchestrateReConfigureVm(String vmUuid, ServiceOffering old
52195227
throw new CloudRuntimeException("Unable to scale vm due to " + (reconfigureAnswer == null ? "" : reconfigureAnswer.getDetails()));
52205228
}
52215229

5222-
upgradeVmDb(vm.getId(), newServiceOffering, oldServiceOffering);
5230+
if (reconfiguringOnExistingHost) {
5231+
_capacityMgr.releaseVmCapacity(vm, false, false, vm.getHostId());
5232+
}
5233+
5234+
boolean vmUpgraded = upgradeVmDb(vm.getId(), newServiceOffering, oldServiceOffering);
5235+
if (vmUpgraded) {
5236+
vm = _vmDao.findById(vm.getId());
5237+
}
52235238

52245239
if (vm.getType().equals(VirtualMachine.Type.User)) {
52255240
_userVmMgr.generateUsageEvent(vm, vm.isDisplayVm(), EventTypes.EVENT_VM_DYNAMIC_SCALE);
52265241
}
52275242

52285243
if (reconfiguringOnExistingHost) {
5229-
vm.setServiceOfferingId(oldServiceOffering.getId());
5230-
_capacityMgr.releaseVmCapacity(vm, false, false, vm.getHostId());
5231-
vm.setServiceOfferingId(newServiceOffering.getId());
52325244
_capacityMgr.allocateVmCapacity(vm, false);
52335245
}
52345246

engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,21 @@ CREATE TABLE IF NOT EXISTS `cloud_usage`.`quota_tariff_usage` (
127127
PRIMARY KEY (`id`),
128128
CONSTRAINT `fk_quota_tariff_usage__tariff_id` FOREIGN KEY (`tariff_id`) REFERENCES `cloud_usage`.`quota_tariff` (`id`),
129129
CONSTRAINT `fk_quota_tariff_usage__quota_usage_id` FOREIGN KEY (`quota_usage_id`) REFERENCES `cloud_usage`.`quota_usage` (`id`));
130+
131+
-- Creates the 'kvm.memory.dynamic.scaling.capacity' and, for already active ACS environments,
132+
-- initializes it with the value of the setting 'vm.serviceoffering.ram.size.max'
133+
INSERT INTO `cloud`.`configuration` (`category`, `instance`, `component`, `name`, `value`, `default_value`, `updated`, `scope`, `is_dynamic`, `group_id`, `subgroup_id`, `display_text`, `description`)
134+
SELECT 'Advanced', 'DEFAULT', 'CapacityManager', 'kvm.memory.dynamic.scaling.capacity', `cfg`.`value`, 0, NULL, 4, 1, 6, 27,
135+
'KVM memory dynamic scaling capacity', 'Defines the maximum memory capacity in MiB for which VMs can be dynamically scaled to with KVM. The ''kvm.memory.dynamic.scaling.capacity'' setting''s value will be used to define the value of the ''<maxMemory />'' element of domain XMLs. If it is set to a value less than or equal to ''0'', then the host''s memory capacity will be considered.'
136+
FROM `cloud`.`configuration` `cfg`
137+
WHERE NOT EXISTS (SELECT 1 FROM `cloud`.`configuration` WHERE `name` = 'kvm.memory.dynamic.scaling.capacity')
138+
AND `cfg`.`name` = 'vm.serviceoffering.ram.size.max';
139+
140+
-- Creates the 'kvm.cpu.dynamic.scaling.capacity' and, for already active ACS environments,
141+
-- initializes it with the value of the setting 'vm.serviceoffering.cpu.cores.max'
142+
INSERT INTO `cloud`.`configuration` (`category`, `instance`, `component`, `name`, `value`, `default_value`, `updated`, `scope`, `is_dynamic`, `group_id`, `subgroup_id`, `display_text`, `description`)
143+
SELECT 'Advanced', 'DEFAULT', 'CapacityManager', 'kvm.cpu.dynamic.scaling.capacity', `cfg`.`value`, 0, NULL, 4, 1, 6, 27,
144+
'KVM CPU dynamic scaling capacity', 'Defines the maximum vCPU capacity for which VMs can be dynamically scaled to with KVM. The ''kvm.cpu.dynamic.scaling.capacity'' setting''s value will be used to define the value of the ''<vcpu />'' element of domain XMLs. If it is set to a value less than or equal to ''0'', then the host''s CPU cores capacity will be considered.'
145+
FROM `cloud`.`configuration` `cfg`
146+
WHERE NOT EXISTS (SELECT 1 FROM `cloud`.`configuration` WHERE `name` = 'kvm.cpu.dynamic.scaling.capacity')
147+
AND `cfg`.`name` = 'vm.serviceoffering.cpu.cores.max';

0 commit comments

Comments
 (0)