Skip to content

Commit 6e93c57

Browse files
Live scaling for VMs with fixed service offerings on KVM
1 parent 30dd234 commit 6e93c57

File tree

23 files changed

+499
-257
lines changed

23 files changed

+499
-257
lines changed

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
@@ -51,6 +51,7 @@
5151
import javax.persistence.EntityExistsException;
5252

5353

54+
import com.cloud.hypervisor.KVMGuru;
5455
import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
5556
import org.apache.cloudstack.annotation.AnnotationService;
5657
import org.apache.cloudstack.annotation.dao.AnnotationDao;
@@ -5183,7 +5184,7 @@ public VMInstanceVO reConfigureVm(final String vmUuid, final ServiceOffering old
51835184
try {
51845185
result = retrieveResultFromJobOutcomeAndThrowExceptionIfNeeded(outcome);
51855186
} catch (Exception ex) {
5186-
throw new RuntimeException("Unhandled exception", ex);
5187+
throw new RuntimeException("Unable to reconfigure VM.", ex);
51875188
}
51885189

51895190
if (result != null) {
@@ -5196,22 +5197,29 @@ public VMInstanceVO reConfigureVm(final String vmUuid, final ServiceOffering old
51965197

51975198
private VMInstanceVO orchestrateReConfigureVm(String vmUuid, ServiceOffering oldServiceOffering, ServiceOffering newServiceOffering,
51985199
boolean reconfiguringOnExistingHost) throws ResourceUnavailableException, ConcurrentOperationException {
5199-
final VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
5200+
VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
52005201

52015202
HostVO hostVo = _hostDao.findById(vm.getHostId());
52025203

5203-
Long clustedId = hostVo.getClusterId();
5204-
Float memoryOvercommitRatio = CapacityManager.MemOverprovisioningFactor.valueIn(clustedId);
5205-
Float cpuOvercommitRatio = CapacityManager.CpuOverprovisioningFactor.valueIn(clustedId);
5206-
boolean divideMemoryByOverprovisioning = HypervisorGuruBase.VmMinMemoryEqualsMemoryDividedByMemOverprovisioningFactor.valueIn(clustedId);
5207-
boolean divideCpuByOverprovisioning = HypervisorGuruBase.VmMinCpuSpeedEqualsCpuSpeedDividedByCpuOverprovisioningFactor.valueIn(clustedId);
5204+
Long clusterId = hostVo.getClusterId();
5205+
Float memoryOvercommitRatio = CapacityManager.MemOverprovisioningFactor.valueIn(clusterId);
5206+
Float cpuOvercommitRatio = CapacityManager.CpuOverprovisioningFactor.valueIn(clusterId);
5207+
boolean divideMemoryByOverprovisioning = HypervisorGuruBase.VmMinMemoryEqualsMemoryDividedByMemOverprovisioningFactor.valueIn(clusterId);
5208+
boolean divideCpuByOverprovisioning = HypervisorGuruBase.VmMinCpuSpeedEqualsCpuSpeedDividedByCpuOverprovisioningFactor.valueIn(clusterId);
52085209

52095210
int minMemory = (int)(newServiceOffering.getRamSize() / (divideMemoryByOverprovisioning ? memoryOvercommitRatio : 1));
52105211
int minSpeed = (int)(newServiceOffering.getSpeed() / (divideCpuByOverprovisioning ? cpuOvercommitRatio : 1));
52115212

5212-
ScaleVmCommand scaleVmCommand =
5213-
new ScaleVmCommand(vm.getInstanceName(), newServiceOffering.getCpu(), minSpeed,
5214-
newServiceOffering.getSpeed(), minMemory * 1024L * 1024L, newServiceOffering.getRamSize() * 1024L * 1024L, newServiceOffering.getLimitCpuUse());
5213+
Double cpuQuotaPercentage = null;
5214+
if (newServiceOffering.getLimitCpuUse() && vm.getHypervisorType().equals(HypervisorType.KVM)) {
5215+
KVMGuru kvmGuru = (KVMGuru) _hvGuruMgr.getGuru(vm.getHypervisorType());
5216+
cpuQuotaPercentage = kvmGuru.getCpuQuotaPercentage(minSpeed, hostVo.getSpeed());
5217+
}
5218+
5219+
boolean limitCpuUseChange = oldServiceOffering.getLimitCpuUse() != newServiceOffering.getLimitCpuUse();
5220+
ScaleVmCommand scaleVmCommand = new ScaleVmCommand(vm.getInstanceName(), newServiceOffering.getCpu(), minSpeed, newServiceOffering.getSpeed(),
5221+
minMemory * 1024L * 1024L, newServiceOffering.getRamSize() * 1024L * 1024L,
5222+
newServiceOffering.getLimitCpuUse(), cpuQuotaPercentage, limitCpuUseChange);
52155223

52165224
scaleVmCommand.getVirtualMachine().setId(vm.getId());
52175225
scaleVmCommand.getVirtualMachine().setUuid(vm.getUuid());
@@ -5240,16 +5248,20 @@ private VMInstanceVO orchestrateReConfigureVm(String vmUuid, ServiceOffering old
52405248
throw new CloudRuntimeException("Unable to scale vm due to " + (reconfigureAnswer == null ? "" : reconfigureAnswer.getDetails()));
52415249
}
52425250

5243-
upgradeVmDb(vm.getId(), newServiceOffering, oldServiceOffering);
5251+
if (reconfiguringOnExistingHost) {
5252+
_capacityMgr.releaseVmCapacity(vm, false, false, vm.getHostId());
5253+
}
5254+
5255+
boolean vmUpgraded = upgradeVmDb(vm.getId(), newServiceOffering, oldServiceOffering);
5256+
if (vmUpgraded) {
5257+
vm = _vmDao.findById(vm.getId());
5258+
}
52445259

52455260
if (vm.getType().equals(VirtualMachine.Type.User)) {
52465261
_userVmMgr.generateUsageEvent(vm, vm.isDisplayVm(), EventTypes.EVENT_VM_DYNAMIC_SCALE);
52475262
}
52485263

52495264
if (reconfiguringOnExistingHost) {
5250-
vm.setServiceOfferingId(oldServiceOffering.getId());
5251-
_capacityMgr.releaseVmCapacity(vm, false, false, vm.getHostId());
5252-
vm.setServiceOfferingId(newServiceOffering.getId());
52535265
_capacityMgr.allocateVmCapacity(vm, false);
52545266
}
52555267

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
@@ -117,3 +117,21 @@ CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','conserve_mode', 'tin
117117

118118
--- Disable/enable NICs
119119
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.nics','enabled', 'TINYINT(1) NOT NULL DEFAULT 1 COMMENT ''Indicates whether the NIC is enabled or not'' ');
120+
121+
-- Creates the 'kvm.memory.dynamic.scaling.capacity' and, for already active ACS environments,
122+
-- initializes it with the value of the setting 'vm.serviceoffering.ram.size.max'
123+
INSERT INTO `cloud`.`configuration` (`category`, `instance`, `component`, `name`, `value`, `default_value`, `updated`, `scope`, `is_dynamic`, `group_id`, `subgroup_id`, `display_text`, `description`)
124+
SELECT 'Advanced', 'DEFAULT', 'CapacityManager', 'kvm.memory.dynamic.scaling.capacity', `cfg`.`value`, 0, NULL, 4, 1, 6, 27,
125+
'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.'
126+
FROM `cloud`.`configuration` `cfg`
127+
WHERE NOT EXISTS (SELECT 1 FROM `cloud`.`configuration` WHERE `name` = 'kvm.memory.dynamic.scaling.capacity')
128+
AND `cfg`.`name` = 'vm.serviceoffering.ram.size.max';
129+
130+
-- Creates the 'kvm.cpu.dynamic.scaling.capacity' and, for already active ACS environments,
131+
-- initializes it with the value of the setting 'vm.serviceoffering.cpu.cores.max'
132+
INSERT INTO `cloud`.`configuration` (`category`, `instance`, `component`, `name`, `value`, `default_value`, `updated`, `scope`, `is_dynamic`, `group_id`, `subgroup_id`, `display_text`, `description`)
133+
SELECT 'Advanced', 'DEFAULT', 'CapacityManager', 'kvm.cpu.dynamic.scaling.capacity', `cfg`.`value`, 0, NULL, 4, 1, 6, 27,
134+
'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.'
135+
FROM `cloud`.`configuration` `cfg`
136+
WHERE NOT EXISTS (SELECT 1 FROM `cloud`.`configuration` WHERE `name` = 'kvm.cpu.dynamic.scaling.capacity')
137+
AND `cfg`.`name` = 'vm.serviceoffering.cpu.cores.max';

0 commit comments

Comments
 (0)