Skip to content

Commit 7583d7d

Browse files
Live scaling for VMs with fixed service offerings on KVM
1 parent 82bfa9f commit 7583d7d

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
@@ -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;
@@ -5180,7 +5181,7 @@ public VMInstanceVO reConfigureVm(final String vmUuid, final ServiceOffering old
51805181
try {
51815182
result = retrieveResultFromJobOutcomeAndThrowExceptionIfNeeded(outcome);
51825183
} catch (Exception ex) {
5183-
throw new RuntimeException("Unhandled exception", ex);
5184+
throw new RuntimeException("Unable to reconfigure VM.", ex);
51845185
}
51855186

51865187
if (result != null) {
@@ -5193,22 +5194,29 @@ public VMInstanceVO reConfigureVm(final String vmUuid, final ServiceOffering old
51935194

51945195
private VMInstanceVO orchestrateReConfigureVm(String vmUuid, ServiceOffering oldServiceOffering, ServiceOffering newServiceOffering,
51955196
boolean reconfiguringOnExistingHost) throws ResourceUnavailableException, ConcurrentOperationException {
5196-
final VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
5197+
VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
51975198

51985199
HostVO hostVo = _hostDao.findById(vm.getHostId());
51995200

5200-
Long clustedId = hostVo.getClusterId();
5201-
Float memoryOvercommitRatio = CapacityManager.MemOverprovisioningFactor.valueIn(clustedId);
5202-
Float cpuOvercommitRatio = CapacityManager.CpuOverprovisioningFactor.valueIn(clustedId);
5203-
boolean divideMemoryByOverprovisioning = HypervisorGuruBase.VmMinMemoryEqualsMemoryDividedByMemOverprovisioningFactor.valueIn(clustedId);
5204-
boolean divideCpuByOverprovisioning = HypervisorGuruBase.VmMinCpuSpeedEqualsCpuSpeedDividedByCpuOverprovisioningFactor.valueIn(clustedId);
5201+
Long clusterId = hostVo.getClusterId();
5202+
Float memoryOvercommitRatio = CapacityManager.MemOverprovisioningFactor.valueIn(clusterId);
5203+
Float cpuOvercommitRatio = CapacityManager.CpuOverprovisioningFactor.valueIn(clusterId);
5204+
boolean divideMemoryByOverprovisioning = HypervisorGuruBase.VmMinMemoryEqualsMemoryDividedByMemOverprovisioningFactor.valueIn(clusterId);
5205+
boolean divideCpuByOverprovisioning = HypervisorGuruBase.VmMinCpuSpeedEqualsCpuSpeedDividedByCpuOverprovisioningFactor.valueIn(clusterId);
52055206

52065207
int minMemory = (int)(newServiceOffering.getRamSize() / (divideMemoryByOverprovisioning ? memoryOvercommitRatio : 1));
52075208
int minSpeed = (int)(newServiceOffering.getSpeed() / (divideCpuByOverprovisioning ? cpuOvercommitRatio : 1));
52085209

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

52135221
scaleVmCommand.getVirtualMachine().setId(vm.getId());
52145222
scaleVmCommand.getVirtualMachine().setUuid(vm.getUuid());
@@ -5237,16 +5245,20 @@ private VMInstanceVO orchestrateReConfigureVm(String vmUuid, ServiceOffering old
52375245
throw new CloudRuntimeException("Unable to scale vm due to " + (reconfigureAnswer == null ? "" : reconfigureAnswer.getDetails()));
52385246
}
52395247

5240-
upgradeVmDb(vm.getId(), newServiceOffering, oldServiceOffering);
5248+
if (reconfiguringOnExistingHost) {
5249+
_capacityMgr.releaseVmCapacity(vm, false, false, vm.getHostId());
5250+
}
5251+
5252+
boolean vmUpgraded = upgradeVmDb(vm.getId(), newServiceOffering, oldServiceOffering);
5253+
if (vmUpgraded) {
5254+
vm = _vmDao.findById(vm.getId());
5255+
}
52415256

52425257
if (vm.getType().equals(VirtualMachine.Type.User)) {
52435258
_userVmMgr.generateUsageEvent(vm, vm.isDisplayVm(), EventTypes.EVENT_VM_DYNAMIC_SCALE);
52445259
}
52455260

52465261
if (reconfiguringOnExistingHost) {
5247-
vm.setServiceOfferingId(oldServiceOffering.getId());
5248-
_capacityMgr.releaseVmCapacity(vm, false, false, vm.getHostId());
5249-
vm.setServiceOfferingId(newServiceOffering.getId());
52505262
_capacityMgr.allocateVmCapacity(vm, false);
52515263
}
52525264

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)