This is an automated email from the ASF dual-hosted git repository.

winterhazel pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/main by this push:
     new 6b831f5196c Live scaling for VMs with fixed service offerings on KVM 
(#12975)
6b831f5196c is described below

commit 6b831f5196c3cfcc2d7aaf3fc2150c069d125fdc
Author: Bernardo De Marco Gonçalves <[email protected]>
AuthorDate: Fri May 22 08:19:21 2026 -0300

    Live scaling for VMs with fixed service offerings on KVM (#12975)
---
 .../com/cloud/agent/api/to/VirtualMachineTO.java   |   8 +-
 .../java/com/cloud/agent/api/ScaleVmCommand.java   |   9 +-
 .../java/com/cloud/capacity/CapacityManager.java   |  14 ++
 .../com/cloud/vm/VirtualMachineManagerImpl.java    |  40 ++++--
 .../resources/META-INF/db/schema-42210to42300.sql  |  18 +++
 .../kvm/resource/LibvirtComputingResource.java     | 157 ++++++++++++++++-----
 .../kvm/resource/LibvirtDomainXMLParser.java       |   2 +-
 .../hypervisor/kvm/resource/LibvirtVMDef.java      |  17 +--
 .../wrapper/LibvirtScaleVmCommandWrapper.java      |   5 +-
 .../kvm/resource/LibvirtComputingResourceTest.java | 116 +++++++++++++--
 .../com/cloud/capacity/CapacityManagerImpl.java    |   2 +-
 .../main/java/com/cloud/hypervisor/KVMGuru.java    | 148 +++++++++----------
 .../src/main/java/com/cloud/vm/UserVmManager.java  |   4 +
 .../main/java/com/cloud/vm/UserVmManagerImpl.java  |  36 +++--
 .../java/com/cloud/hypervisor/KVMGuruTest.java     | 123 ++++++++--------
 .../component/test_escalations_instances.py        |   3 -
 .../component/test_escalations_ipaddresses.py      |   9 +-
 ui/public/locales/el_GR.json                       |   2 -
 ui/public/locales/en.json                          |   1 -
 ui/public/locales/ja_JP.json                       |   1 -
 ui/public/locales/pt_BR.json                       |   1 -
 ui/public/locales/te.json                          |   1 -
 ui/public/locales/zh_CN.json                       |   1 -
 ui/src/views/compute/ScaleVM.vue                   |  37 +++--
 ui/src/views/compute/wizard/ComputeSelection.vue   |  12 +-
 25 files changed, 502 insertions(+), 265 deletions(-)

diff --git a/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java 
b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java
index e26cc1e9f02..9af6c731fd2 100644
--- a/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java
+++ b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java
@@ -51,6 +51,7 @@ public class VirtualMachineTO {
 
     private long minRam;
     private long maxRam;
+    private long requestedRam;
     private String hostName;
     private String arch;
     private String os;
@@ -207,15 +208,20 @@ public class VirtualMachineTO {
         return minRam;
     }
 
-    public void setRam(long minRam, long maxRam) {
+    public void setRam(long minRam, long maxRam, long requestedRam) {
         this.minRam = minRam;
         this.maxRam = maxRam;
+        this.requestedRam = requestedRam;
     }
 
     public long getMaxRam() {
         return maxRam;
     }
 
+    public long getRequestedRam() {
+        return requestedRam;
+    }
+
     public String getHostName() {
         return hostName;
     }
diff --git a/core/src/main/java/com/cloud/agent/api/ScaleVmCommand.java 
b/core/src/main/java/com/cloud/agent/api/ScaleVmCommand.java
index dc63b1ee746..e2953e9c7ad 100644
--- a/core/src/main/java/com/cloud/agent/api/ScaleVmCommand.java
+++ b/core/src/main/java/com/cloud/agent/api/ScaleVmCommand.java
@@ -30,6 +30,7 @@ public class ScaleVmCommand extends Command {
     Integer maxSpeed;
     long minRam;
     long maxRam;
+    private boolean limitCpuUseChange;
 
     public VirtualMachineTO getVm() {
         return vm;
@@ -43,7 +44,7 @@ public class ScaleVmCommand extends Command {
         return cpus;
     }
 
-    public ScaleVmCommand(String vmName, int cpus, Integer minSpeed, Integer 
maxSpeed, long minRam, long maxRam, boolean limitCpuUse) {
+    public ScaleVmCommand(String vmName, int cpus, Integer minSpeed, Integer 
maxSpeed, long minRam, long maxRam, boolean limitCpuUse, Double 
cpuQuotaPercentage, boolean limitCpuUseChange) {
         super();
         this.vmName = vmName;
         this.cpus = cpus;
@@ -52,6 +53,8 @@ public class ScaleVmCommand extends Command {
         this.minRam = minRam;
         this.maxRam = maxRam;
         this.vm = new VirtualMachineTO(1L, vmName, null, cpus, minSpeed, 
maxSpeed, minRam, maxRam, null, null, false, limitCpuUse, null);
+        this.vm.setCpuQuotaPercentage(cpuQuotaPercentage);
+        this.limitCpuUseChange = limitCpuUseChange;
     }
 
     public void setCpus(int cpus) {
@@ -102,6 +105,10 @@ public class ScaleVmCommand extends Command {
         return vm;
     }
 
+    public boolean getLimitCpuUseChange() {
+        return limitCpuUseChange;
+    }
+
     @Override
     public boolean executeInSequence() {
         return true;
diff --git 
a/engine/components-api/src/main/java/com/cloud/capacity/CapacityManager.java 
b/engine/components-api/src/main/java/com/cloud/capacity/CapacityManager.java
index 4c81c7359f2..abaf6ea967d 100644
--- 
a/engine/components-api/src/main/java/com/cloud/capacity/CapacityManager.java
+++ 
b/engine/components-api/src/main/java/com/cloud/capacity/CapacityManager.java
@@ -133,6 +133,20 @@ public interface CapacityManager {
             "capacity.calculate.workers", "1",
             "Number of worker threads to be used for capacities calculation", 
true);
 
+    ConfigKey<Integer> KvmMemoryDynamicScalingCapacity = new 
ConfigKey<>(ConfigKey.CATEGORY_ADVANCED,
+            Integer.class, "kvm.memory.dynamic.scaling.capacity", "0",
+            "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.",
+            true, ConfigKey.Scope.Cluster);
+
+    ConfigKey<Integer> KvmCpuDynamicScalingCapacity = new 
ConfigKey<>(ConfigKey.CATEGORY_ADVANCED,
+            Integer.class, "kvm.cpu.dynamic.scaling.capacity", "0",
+            "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.",
+            true, ConfigKey.Scope.Cluster);
+
     public boolean releaseVmCapacity(VirtualMachine vm, boolean 
moveFromReserved, boolean moveToReservered, Long hostId);
 
     void allocateVmCapacity(VirtualMachine vm, boolean fromLastHost);
diff --git 
a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
 
b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
index 17ddf870670..ead990b42b8 100755
--- 
a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
+++ 
b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
@@ -50,6 +50,7 @@ import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 import javax.persistence.EntityExistsException;
 
+import com.cloud.hypervisor.KVMGuru;
 import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
 import org.apache.cloudstack.annotation.AnnotationService;
 import org.apache.cloudstack.annotation.dao.AnnotationDao;
@@ -5162,7 +5163,7 @@ public class VirtualMachineManagerImpl extends 
ManagerBase implements VirtualMac
             try {
                 result = 
retrieveResultFromJobOutcomeAndThrowExceptionIfNeeded(outcome);
             } catch (Exception ex) {
-                throw new RuntimeException("Unhandled exception", ex);
+                throw new RuntimeException("Unable to reconfigure VM.", ex);
             }
 
             if (result != null) {
@@ -5175,22 +5176,29 @@ public class VirtualMachineManagerImpl extends 
ManagerBase implements VirtualMac
 
     private VMInstanceVO orchestrateReConfigureVm(String vmUuid, 
ServiceOffering oldServiceOffering, ServiceOffering newServiceOffering,
                                                   boolean 
reconfiguringOnExistingHost) throws ResourceUnavailableException, 
ConcurrentOperationException {
-        final VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
+        VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
 
         HostVO hostVo = _hostDao.findById(vm.getHostId());
 
-        Long clustedId = hostVo.getClusterId();
-        Float memoryOvercommitRatio = 
CapacityManager.MemOverprovisioningFactor.valueIn(clustedId);
-        Float cpuOvercommitRatio = 
CapacityManager.CpuOverprovisioningFactor.valueIn(clustedId);
-        boolean divideMemoryByOverprovisioning = 
HypervisorGuruBase.VmMinMemoryEqualsMemoryDividedByMemOverprovisioningFactor.valueIn(clustedId);
-        boolean divideCpuByOverprovisioning = 
HypervisorGuruBase.VmMinCpuSpeedEqualsCpuSpeedDividedByCpuOverprovisioningFactor.valueIn(clustedId);
+        Long clusterId = hostVo.getClusterId();
+        Float memoryOvercommitRatio = 
CapacityManager.MemOverprovisioningFactor.valueIn(clusterId);
+        Float cpuOvercommitRatio = 
CapacityManager.CpuOverprovisioningFactor.valueIn(clusterId);
+        boolean divideMemoryByOverprovisioning = 
HypervisorGuruBase.VmMinMemoryEqualsMemoryDividedByMemOverprovisioningFactor.valueIn(clusterId);
+        boolean divideCpuByOverprovisioning = 
HypervisorGuruBase.VmMinCpuSpeedEqualsCpuSpeedDividedByCpuOverprovisioningFactor.valueIn(clusterId);
 
         int minMemory = (int)(newServiceOffering.getRamSize() / 
(divideMemoryByOverprovisioning ? memoryOvercommitRatio : 1));
         int minSpeed = (int)(newServiceOffering.getSpeed() / 
(divideCpuByOverprovisioning ? cpuOvercommitRatio : 1));
 
-        ScaleVmCommand scaleVmCommand =
-                new ScaleVmCommand(vm.getInstanceName(), 
newServiceOffering.getCpu(), minSpeed,
-                        newServiceOffering.getSpeed(), minMemory * 1024L * 
1024L, newServiceOffering.getRamSize() * 1024L * 1024L, 
newServiceOffering.getLimitCpuUse());
+        Double cpuQuotaPercentage = null;
+        if (newServiceOffering.getLimitCpuUse() && 
vm.getHypervisorType().equals(HypervisorType.KVM)) {
+            KVMGuru kvmGuru = (KVMGuru) 
_hvGuruMgr.getGuru(vm.getHypervisorType());
+            cpuQuotaPercentage = kvmGuru.getCpuQuotaPercentage(minSpeed, 
hostVo.getSpeed());
+        }
+
+        boolean limitCpuUseChange = oldServiceOffering.getLimitCpuUse() != 
newServiceOffering.getLimitCpuUse();
+        ScaleVmCommand scaleVmCommand = new 
ScaleVmCommand(vm.getInstanceName(), newServiceOffering.getCpu(), minSpeed, 
newServiceOffering.getSpeed(),
+                minMemory * 1024L * 1024L, newServiceOffering.getRamSize() * 
1024L * 1024L,
+                newServiceOffering.getLimitCpuUse(), cpuQuotaPercentage, 
limitCpuUseChange);
 
         scaleVmCommand.getVirtualMachine().setId(vm.getId());
         scaleVmCommand.getVirtualMachine().setUuid(vm.getUuid());
@@ -5219,16 +5227,20 @@ public class VirtualMachineManagerImpl extends 
ManagerBase implements VirtualMac
                 throw new CloudRuntimeException("Unable to scale vm due to " + 
(reconfigureAnswer == null ? "" : reconfigureAnswer.getDetails()));
             }
 
-            upgradeVmDb(vm.getId(), newServiceOffering, oldServiceOffering);
+            if (reconfiguringOnExistingHost) {
+                _capacityMgr.releaseVmCapacity(vm, false, false, 
vm.getHostId());
+            }
+
+            boolean vmUpgraded = upgradeVmDb(vm.getId(), newServiceOffering, 
oldServiceOffering);
+            if (vmUpgraded) {
+                vm = _vmDao.findById(vm.getId());
+            }
 
             if (vm.getType().equals(VirtualMachine.Type.User)) {
                 _userVmMgr.generateUsageEvent(vm, vm.isDisplayVm(), 
EventTypes.EVENT_VM_DYNAMIC_SCALE);
             }
 
             if (reconfiguringOnExistingHost) {
-                vm.setServiceOfferingId(oldServiceOffering.getId());
-                _capacityMgr.releaseVmCapacity(vm, false, false, 
vm.getHostId());
-                vm.setServiceOfferingId(newServiceOffering.getId());
                 _capacityMgr.allocateVmCapacity(vm, false);
             }
 
diff --git 
a/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql 
b/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql
index c99f798d3d5..dfa5fa8f3a1 100644
--- a/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql
+++ b/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql
@@ -131,3 +131,21 @@ CREATE TABLE IF NOT EXISTS 
`cloud_usage`.`quota_tariff_usage` (
 -- Add the 'keep_mac_address_on_public_nic' column to the 'cloud.networks' and 
'cloud.vpc' tables
 CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.networks', 
'keep_mac_address_on_public_nic', 'TINYINT(1) NOT NULL DEFAULT 1');
 CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc', 
'keep_mac_address_on_public_nic', 'TINYINT(1) NOT NULL DEFAULT 1');
+
+-- Creates the 'kvm.memory.dynamic.scaling.capacity' and, for already active 
ACS environments,
+-- initializes it with the value of the setting 
'vm.serviceoffering.ram.size.max'
+INSERT INTO `cloud`.`configuration` (`category`, `instance`, `component`, 
`name`, `value`, `default_value`, `updated`, `scope`, `is_dynamic`, `group_id`, 
`subgroup_id`, `display_text`, `description`)
+SELECT 'Advanced', 'DEFAULT', 'CapacityManager', 
'kvm.memory.dynamic.scaling.capacity', `cfg`.`value`, 0, NULL, 4, 1, 6, 27,
+       '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.'
+FROM `cloud`.`configuration` `cfg`
+WHERE NOT EXISTS (SELECT 1 FROM `cloud`.`configuration` WHERE `name` = 
'kvm.memory.dynamic.scaling.capacity')
+  AND `cfg`.`name` = 'vm.serviceoffering.ram.size.max';
+
+-- Creates the 'kvm.cpu.dynamic.scaling.capacity' and, for already active ACS 
environments,
+-- initializes it with the value of the setting 
'vm.serviceoffering.cpu.cores.max'
+INSERT INTO `cloud`.`configuration` (`category`, `instance`, `component`, 
`name`, `value`, `default_value`, `updated`, `scope`, `is_dynamic`, `group_id`, 
`subgroup_id`, `display_text`, `description`)
+SELECT 'Advanced', 'DEFAULT', 'CapacityManager', 
'kvm.cpu.dynamic.scaling.capacity', `cfg`.`value`, 0, NULL, 4, 1, 6, 27,
+       '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.'
+FROM `cloud`.`configuration` `cfg`
+WHERE NOT EXISTS (SELECT 1 FROM `cloud`.`configuration` WHERE `name` = 
'kvm.cpu.dynamic.scaling.capacity')
+  AND `cfg`.`name` = 'vm.serviceoffering.cpu.cores.max';
diff --git 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
index 64ec0ed95d2..06b668b801d 100644
--- 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
+++ 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
@@ -124,6 +124,7 @@ import org.libvirt.DomainSnapshot;
 import org.libvirt.LibvirtException;
 import org.libvirt.MemoryStatistic;
 import org.libvirt.Network;
+import org.libvirt.SchedLongParameter;
 import org.libvirt.SchedParameter;
 import org.libvirt.SchedUlongParameter;
 import org.libvirt.Secret;
@@ -883,6 +884,25 @@ public class LibvirtComputingResource extends 
ServerResourceBase implements Serv
         SUCCESS, FAILURE, IGNORE
     }
 
+    public enum CpuSchedulerParameter {
+        CPU_SHARES("cpu_shares"), PERIOD("vcpu_period"), QUOTA("vcpu_quota");
+
+        private String name;
+
+        CpuSchedulerParameter(String name) {
+            this.name = name;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        @Override
+        public String toString() {
+            return getName();
+        }
+    }
+
     protected BridgeType bridgeType;
 
     protected StorageSubsystemCommandHandler storageHandler;
@@ -2965,23 +2985,61 @@ public class LibvirtComputingResource extends 
ServerResourceBase implements Serv
     protected void setQuotaAndPeriod(VirtualMachineTO vmTO, CpuTuneDef ctd) {
         if (vmTO.isLimitCpuUse() && vmTO.getCpuQuotaPercentage() != null) {
             Double cpuQuotaPercentage = vmTO.getCpuQuotaPercentage();
-            int period = CpuTuneDef.DEFAULT_PERIOD;
-            int quota = (int) (period * cpuQuotaPercentage);
-            if (quota < CpuTuneDef.MIN_QUOTA) {
-                LOGGER.info("Calculated quota (" + quota + ") below the 
minimum (" + CpuTuneDef.MIN_QUOTA + ") for VM domain " + vmTO.getUuid() + ", 
setting it to minimum " +
-                        "and calculating period instead of using the default");
-                quota = CpuTuneDef.MIN_QUOTA;
-                period = (int) ((double) quota / cpuQuotaPercentage);
-                if (period > CpuTuneDef.MAX_PERIOD) {
-                    LOGGER.info("Calculated period (" + period + ") exceeds 
the maximum (" + CpuTuneDef.MAX_PERIOD +
-                            "), setting it to the maximum");
-                    period = CpuTuneDef.MAX_PERIOD;
-                }
+            Pair<Integer, Long> periodAndQuota = 
getPeriodAndQuota(cpuQuotaPercentage);
+            ctd.setPeriod(periodAndQuota.first());
+            ctd.setQuota(periodAndQuota.second());
+            LOGGER.info("Setting quota = [{}] and period = [{}] to VM domain 
[{}].", periodAndQuota.second(), periodAndQuota.first(), vmTO.getUuid());
+        }
+    }
+
+    /**
+     * Calculates the CPU period and quota based on the quota percentage 
defined by the Management Server
+     * @param cpuQuotaPercentage CPU quota percentage defined by the 
Management Server
+     * @return The period and quota to be defined for the VM's domain
+     */
+    protected Pair<Integer, Long> getPeriodAndQuota(double cpuQuotaPercentage) 
{
+        int period = CpuTuneDef.DEFAULT_PERIOD;
+        long quota = (long) (period * cpuQuotaPercentage);
+        if (quota < CpuTuneDef.MIN_QUOTA) {
+            LOGGER.info("Calculated quota ({}) below the minimum ({}), setting 
it to minimum and calculating period instead of using the default", quota, 
CpuTuneDef.MIN_QUOTA);
+            quota = CpuTuneDef.MIN_QUOTA;
+            period = (int) ((double) quota / cpuQuotaPercentage);
+            if (period > CpuTuneDef.MAX_PERIOD) {
+                LOGGER.info("Calculated period ({}) exceeds the maximum ({}), 
setting it to the maximum", period, CpuTuneDef.MAX_PERIOD);
+                period = CpuTuneDef.MAX_PERIOD;
             }
-            ctd.setQuota(quota);
-            ctd.setPeriod(period);
-            LOGGER.info("Setting quota=" + quota + ", period=" + period + " to 
VM domain " + vmTO.getUuid());
         }
+
+        LOGGER.info("Calculated period = [{}] and quota = [{}] given the [{}] 
quota percentage.", period, quota, cpuQuotaPercentage);
+        return new Pair<>(period, quota);
+    }
+
+    /**
+     * Dynamically updates the domain's "vcpu_quota" and "period" fields of 
the CPU tune definition.
+     * This is required because the values of the fields must change according 
to the new CPU speed of the VM.
+     * When the CPU limitation is removed from the domain, the "vcpu_quota" 
field is set to 17,592,186,044,415.
+     * @param domain VM's domain.
+     * @param vmTO VM's transfer object, which contains the required fields to 
update the "vcpu_quota" and "period" fields.
+     * @param limitCpuUseChange Indicates whether the CPU limitation for the 
VM has changed.
+     * @throws org.libvirt.LibvirtException
+     **/
+    public void updateCpuQuotaAndPeriod(Domain domain, VirtualMachineTO vmTO, 
boolean limitCpuUseChange) throws LibvirtException {
+        if (hypervisorLibvirtVersion < MIN_LIBVIRT_VERSION_FOR_GUEST_CPU_TUNE 
|| (!limitCpuUseChange && !vmTO.isLimitCpuUse())) {
+            logger.info("Not updating the [{}] and [{}] for the [{}] domain, 
because [{}].",
+                    CpuSchedulerParameter.QUOTA, CpuSchedulerParameter.PERIOD, 
domain.getName(), hypervisorLibvirtVersion < 
MIN_LIBVIRT_VERSION_FOR_GUEST_CPU_TUNE ?
+                            "the current Libvirt version does not support CPU 
tune" : "it was not requested to remove, change or apply CPU limitation for the 
instance.");
+            return;
+        }
+
+        if (limitCpuUseChange && !vmTO.isLimitCpuUse()) {
+            logger.info("Updating the [{}] of the [{}] domain to [{}], because 
CPU limitation has been removed.", CpuSchedulerParameter.QUOTA, 
domain.getName(), CpuTuneDef.MAX_CPU_QUOTA);
+            LibvirtComputingResource.setQuota(domain, 
CpuTuneDef.MAX_CPU_QUOTA);
+            return;
+        }
+
+        Pair<Integer, Long> periodAndQuota = 
getPeriodAndQuota(vmTO.getCpuQuotaPercentage());
+        LibvirtComputingResource.setPeriod(domain, periodAndQuota.first());
+        LibvirtComputingResource.setQuota(domain, periodAndQuota.second());
     }
 
     protected void enlightenWindowsVm(VirtualMachineTO vmTO, FeaturesDef 
features) {
@@ -3445,10 +3503,10 @@ public class LibvirtComputingResource extends 
ServerResourceBase implements Serv
 
         grd.setMemBalloning(!noMemBalloon);
 
-        Long maxRam = ByteScaleUtils.bytesToKibibytes(vmTO.getMaxRam());
-
-        grd.setMemorySize(maxRam);
-        grd.setCurrentMem(getCurrentMemAccordingToMemBallooning(vmTO, maxRam));
+        long requestedRam = 
ByteScaleUtils.bytesToKibibytes(vmTO.getRequestedRam());
+        long minRam = ByteScaleUtils.bytesToKibibytes(vmTO.getMinRam());
+        grd.setCurrentMem(getCurrentMemAccordingToMemBallooning(vmTO, 
requestedRam, minRam));
+        grd.setMaxMemory(ByteScaleUtils.bytesToKibibytes(vmTO.getMaxRam()));
 
         int vcpus = vmTO.getCpus();
         Integer maxVcpus = vmTO.getVcpuMaxLimit();
@@ -3459,18 +3517,19 @@ public class LibvirtComputingResource extends 
ServerResourceBase implements Serv
         return grd;
     }
 
-    protected long getCurrentMemAccordingToMemBallooning(VirtualMachineTO 
vmTO, long maxRam) {
-        long retVal = maxRam;
+    protected long getCurrentMemAccordingToMemBallooning(VirtualMachineTO 
vmTO, long requestedRam, long minRam) {
         if (noMemBalloon) {
-            LOGGER.warn(String.format("Setting VM's [%s] current memory as max 
memory [%s] due to memory ballooning is disabled. If you are using a custom 
service offering, verify if memory ballooning really should be disabled.", 
vmTO.toString(), maxRam));
-        } else if (vmTO != null && vmTO.getType() != VirtualMachine.Type.User) 
{
-            LOGGER.warn(String.format("Setting System VM's [%s] current memory 
as max memory [%s].", vmTO.toString(), maxRam));
-        } else {
-            long minRam = ByteScaleUtils.bytesToKibibytes(vmTO.getMinRam());
-            LOGGER.debug(String.format("Setting VM's [%s] current memory as 
min memory [%s] due to memory ballooning is enabled.", vmTO.toString(), 
minRam));
-            retVal = minRam;
+            LOGGER.warn("Setting VM's [{}] current memory as requested memory 
[{}] due to memory ballooning is disabled.", vmTO.toString(), requestedRam);
+            return requestedRam;
+        }
+
+        if (vmTO != null && vmTO.getType() != VirtualMachine.Type.User) {
+            LOGGER.warn("Setting System VM's [{}] current memory as requested 
memory [{}].", vmTO.toString(), requestedRam);
+            return requestedRam;
         }
-        return retVal;
+
+        LOGGER.debug("Setting VM's [{}] current memory as min memory [{}] due 
to memory ballooning is enabled.", vmTO.toString(), minRam);
+        return minRam;
     }
 
     /**
@@ -6255,29 +6314,59 @@ public class LibvirtComputingResource extends 
ServerResourceBase implements Serv
      **/
     public static Integer getCpuShares(Domain dm) throws LibvirtException {
         for (SchedParameter c : dm.getSchedulerParameters()) {
-            if (c.field.equals("cpu_shares")) {
+            if (c.field.equals(CpuSchedulerParameter.CPU_SHARES.getName())) {
                 return Integer.parseInt(c.getValueAsString());
             }
         }
-        LOGGER.warn(String.format("Could not get cpu_shares of domain: [%s]. 
Returning default value of 0. ", dm.getName()));
+        LOGGER.warn("Could not get [{}] of domain: [{}]. Returning default 
value of 0. ", CpuSchedulerParameter.CPU_SHARES.getName(), dm.getName());
         return 0;
     }
 
     /**
-     * Sets the cpu_shares (priority) of the running VM <br/>
+     * Updates the cpu_shares (priority) of the running VM.
      * @param dm domain of the VM.
      * @param cpuShares new priority of the running VM.
-     * @throws org.libvirt.LibvirtException
      **/
     public static void setCpuShares(Domain dm, Integer cpuShares) throws 
LibvirtException {
+        LOGGER.info("Dynamically updating the [{}] of the [{}] VM to [{}].", 
CpuSchedulerParameter.CPU_SHARES.getName(), dm.getName(), cpuShares);
         SchedUlongParameter[] params = new SchedUlongParameter[1];
         params[0] = new SchedUlongParameter();
-        params[0].field = "cpu_shares";
+        params[0].field = CpuSchedulerParameter.CPU_SHARES.getName();
         params[0].value = cpuShares;
 
         dm.setSchedulerParameters(params);
     }
 
+    /**
+     * Updates the period of the running VM.
+     * @param domain domain of the VM.
+     * @param period new period of the running VM.
+     **/
+    public static void setPeriod(Domain domain, int period) throws 
LibvirtException {
+        LOGGER.info("Dynamically updating the [{}] of the [{}] VM to [{}].", 
CpuSchedulerParameter.PERIOD.getName(), domain.getName(), period);
+        SchedUlongParameter[] params = new SchedUlongParameter[1];
+        params[0] = new SchedUlongParameter();
+        params[0].field = CpuSchedulerParameter.PERIOD.getName();
+        params[0].value = period;
+
+        domain.setSchedulerParameters(params);
+    }
+
+    /**
+     * Updates the quota of the running VM.
+     * @param domain domain of the VM.
+     * @param quota new quota of the running VM.
+     **/
+    public static void setQuota(Domain domain, long quota) throws 
LibvirtException {
+        LOGGER.info("Dynamically updating the [{}] of the [{}] VM to [{}].", 
CpuSchedulerParameter.QUOTA.getName(), domain.getName(), quota);
+        SchedLongParameter[] params = new SchedLongParameter[1];
+        params[0] = new SchedLongParameter();
+        params[0].field = CpuSchedulerParameter.QUOTA.getName();
+        params[0].value = quota;
+
+        domain.setSchedulerParameters(params);
+    }
+
     /**
      * Set up a libvirt secret for a volume. If Libvirt says that a secret 
already exists for this volume path, we use its uuid.
      * The UUID of the secret needs to be prescriptive such that we can 
register the same UUID on target host during live migration
diff --git 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java
 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java
index 4d823783a99..e114669b8b5 100644
--- 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java
+++ 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtDomainXMLParser.java
@@ -542,7 +542,7 @@ public class LibvirtDomainXMLParser {
 
             final String quota = getTagValue("quota", cpuTuneDefElement);
             if (StringUtils.isNotBlank(quota)) {
-                cpuTuneDef.setQuota((Integer.parseInt(quota)));
+                cpuTuneDef.setQuota((Long.parseLong(quota)));
             }
 
             final String period = getTagValue("period", cpuTuneDefElement);
diff --git 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
index 097a9b8dd32..bf8b1af6c18 100644
--- 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
+++ 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
@@ -443,15 +443,15 @@ public class LibvirtVMDef {
     }
 
     public static class GuestResourceDef {
-        private long memory;
+        private long maxMemory;
         private long currentMemory = -1;
         private int vcpu = -1;
         private int maxVcpu = -1;
         private boolean memoryBalloning = false;
         private int memoryBalloonStatsPeriod = 
AgentPropertiesFileHandler.getPropertyValue(AgentProperties.VM_MEMBALLOON_STATS_PERIOD);
 
-        public void setMemorySize(long mem) {
-            this.memory = mem;
+        public void setMaxMemory(long mem) {
+            this.maxMemory = mem;
         }
 
         public void setCurrentMem(long currMem) {
@@ -484,8 +484,8 @@ public class LibvirtVMDef {
             response.append(String.format("<memory>%s</memory>\n", 
this.currentMemory));
             
response.append(String.format("<currentMemory>%s</currentMemory>\n", 
this.currentMemory));
 
-            if (this.memory > this.currentMemory) {
-                response.append(String.format("<maxMemory slots='16' 
unit='KiB'>%s</maxMemory>\n", this.memory));
+            if (this.maxMemory > this.currentMemory) {
+                response.append(String.format("<maxMemory slots='16' 
unit='KiB'>%s</maxMemory>\n", this.maxMemory));
                 response.append(String.format("<cpu> <numa> <cell id='0' 
cpus='0-%s' memory='%s' unit='KiB'/> </numa> </cpu>\n", this.maxVcpu - 1, 
this.currentMemory));
             }
 
@@ -1920,11 +1920,12 @@ public class LibvirtVMDef {
 
     public static class CpuTuneDef {
         private int _shares = 0;
-        private int quota = 0;
+        private long quota = 0;
         private int period = 0;
         static final int DEFAULT_PERIOD = 10000;
         static final int MIN_QUOTA = 1000;
         static final int MAX_PERIOD = 1000000;
+        public static final long MAX_CPU_QUOTA = 17592186044415L;
 
         public void setShares(int shares) {
             _shares = shares;
@@ -1934,11 +1935,11 @@ public class LibvirtVMDef {
             return _shares;
         }
 
-        public int getQuota() {
+        public long getQuota() {
             return quota;
         }
 
-        public void setQuota(int quota) {
+        public void setQuota(long quota) {
             this.quota = quota;
         }
 
diff --git 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtScaleVmCommandWrapper.java
 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtScaleVmCommandWrapper.java
index 1536984f2e8..b1f64c8c6db 100644
--- 
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtScaleVmCommandWrapper.java
+++ 
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtScaleVmCommandWrapper.java
@@ -49,10 +49,11 @@ public class LibvirtScaleVmCommandWrapper extends 
CommandWrapper<ScaleVmCommand,
             conn = libvirtUtilitiesHelper.getConnectionByVmName(vmName);
             Domain dm = conn.domainLookupByName(vmName);
 
-            logger.debug(String.format("Scaling %s.", scalingDetails));
+            logger.debug("Scaling {}.", scalingDetails);
             scaleMemory(dm, newMemory, vmDefinition);
             scaleVcpus(dm, newVcpus, vmDefinition);
             updateCpuShares(dm, newCpuShares);
+            libvirtComputingResource.updateCpuQuotaAndPeriod(dm, vmSpec, 
command.getLimitCpuUseChange());
 
             return new ScaleVmAnswer(command, true, 
String.format("Successfully scaled %s.", scalingDetails));
         } catch (LibvirtException | CloudRuntimeException e) {
@@ -74,7 +75,7 @@ public class LibvirtScaleVmCommandWrapper extends 
CommandWrapper<ScaleVmCommand,
 
         if (oldCpuShares < newCpuShares) {
             LibvirtComputingResource.setCpuShares(dm, newCpuShares);
-            logger.info(String.format("Successfully increased cpu_shares of VM 
[%s] from [%s] to [%s].", dm.getName(), oldCpuShares, newCpuShares));
+            logger.info("Successfully increased cpu_shares of VM [{}] from 
[{}] to [{}].", dm.getName(), oldCpuShares, newCpuShares);
         }
     }
 
diff --git 
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
 
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
index f35230f8c6b..b3bdafb7375 100644
--- 
a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
+++ 
b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
@@ -5642,35 +5642,45 @@ public class LibvirtComputingResourceTest {
         Mockito.verify(vmDef, times(1)).addComp(any());
     }
 
-    public void 
validateGetCurrentMemAccordingToMemBallooningWithoutMemBalooning(){
+    @Test
+    public void 
getCurrentMemAccordingToMemBallooningTestValidateCurrentMemoryWithoutMemBallooning(){
         VirtualMachineTO vmTo = Mockito.mock(VirtualMachineTO.class);
-        Mockito.when(vmTo.getType()).thenReturn(Type.User);
         LibvirtComputingResource libvirtComputingResource = new 
LibvirtComputingResource();
         libvirtComputingResource.noMemBalloon = true;
-        long maxMemory = 2048;
+        long requestedMemory = 1024 * 1024;
+        long minMemory = 512 * 1024;
 
-        long currentMemory = 
libvirtComputingResource.getCurrentMemAccordingToMemBallooning(vmTo, maxMemory);
-        Assert.assertEquals(maxMemory, currentMemory);
-        Mockito.verify(vmTo, Mockito.times(0)).getMinRam();
+        long currentMemory = 
libvirtComputingResource.getCurrentMemAccordingToMemBallooning(vmTo, 
requestedMemory, minMemory);
+        Assert.assertEquals(requestedMemory, currentMemory);
     }
 
     @Test
-    public void 
validateGetCurrentMemAccordingToMemBallooningWithtMemBalooning(){
+    public void 
getCurrentMemAccordingToMemBallooningTestValidateCurrentMemoryWithMemoryBallooning(){
         LibvirtComputingResource libvirtComputingResource = new 
LibvirtComputingResource();
         libvirtComputingResource.noMemBalloon = false;
 
-        long maxMemory = 2048;
-        long minMemory = ByteScaleUtils.mebibytesToBytes(64);
-
         VirtualMachineTO vmTo = Mockito.mock(VirtualMachineTO.class);
         Mockito.when(vmTo.getType()).thenReturn(Type.User);
-        Mockito.when(vmTo.getMinRam()).thenReturn(minMemory);
+        long requestedMemory = 1024 * 1024;
+        long minMemory = 512 * 1024;
 
-        long currentMemory = 
libvirtComputingResource.getCurrentMemAccordingToMemBallooning(vmTo, maxMemory);
-        Assert.assertEquals(ByteScaleUtils.bytesToKibibytes(minMemory), 
currentMemory);
-        Mockito.verify(vmTo).getMinRam();
+        long currentMemory = 
libvirtComputingResource.getCurrentMemAccordingToMemBallooning(vmTo, 
requestedMemory, minMemory);
+        Assert.assertEquals(minMemory, currentMemory);
     }
 
+    @Test
+    public void 
getCurrentMemAccordingToMemBallooningTestValidateCurrentMemoryForSystemVms() {
+        LibvirtComputingResource libvirtComputingResource = new 
LibvirtComputingResource();
+        libvirtComputingResource.noMemBalloon = false;
+
+        VirtualMachineTO vmTo = Mockito.mock(VirtualMachineTO.class);
+        Mockito.when(vmTo.getType()).thenReturn(Type.SecondaryStorageVm);
+        long requestedMemory = 1024 * 1024;
+        long minMemory = 512 * 1024;
+
+        long currentMemory = 
libvirtComputingResource.getCurrentMemAccordingToMemBallooning(vmTo, 
requestedMemory, minMemory);
+        Assert.assertEquals(requestedMemory, currentMemory);
+    }
     @Test
     public void validateCreateGuestResourceDefWithVcpuMaxLimit(){
         LibvirtComputingResource libvirtComputingResource = new 
LibvirtComputingResource();
@@ -7234,4 +7244,82 @@ public class LibvirtComputingResourceTest {
 
         libvirtComputingResourceSpy.getInterface(connMock, vmName, 
invalidMacAddress);
     }
+
+    @Test
+    public void 
updateCpuQuotaAndPeriodTestAssertPeriodAndQuotaAreNotUpdatedWhenLibvirtVersionIsLessThanTheMinimum()
 throws LibvirtException {
+        libvirtComputingResourceSpy.hypervisorLibvirtVersion = 8999;
+        libvirtComputingResourceSpy.updateCpuQuotaAndPeriod(domainMock, null, 
false);
+        Mockito.verify(domainMock, 
Mockito.never()).setSchedulerParameters(Mockito.any());
+    }
+
+    @Test
+    public void 
updateCpuQuotaAndPeriodTestAssertPeriodAndQuotaAreNotUpdatedWhenThereIsNoCapCapChangeAndNoCpuLimitationIsApplied()
 throws LibvirtException {
+        Mockito.when(vmTO.isLimitCpuUse()).thenReturn(false);
+        libvirtComputingResourceSpy.hypervisorLibvirtVersion = 9000;
+        libvirtComputingResourceSpy.updateCpuQuotaAndPeriod(domainMock, vmTO, 
false);
+        Mockito.verify(domainMock, 
Mockito.never()).setSchedulerParameters(Mockito.any());
+    }
+
+    @Test
+    public void 
updateCpuQuotaAndPeriodTestAssertQuotaIsRemovedWhenThereIsCpuCapChangeAndNoCpuLimitationIsApplied()
 throws LibvirtException {
+        Mockito.when(vmTO.isLimitCpuUse()).thenReturn(false);
+        Mockito.when(domainMock.getName()).thenReturn("i-2-10-VM");
+        libvirtComputingResourceSpy.hypervisorLibvirtVersion = 9000;
+        libvirtComputingResourceSpy.updateCpuQuotaAndPeriod(domainMock, vmTO, 
true);
+        Mockito.verify(domainMock, 
Mockito.times(1)).setSchedulerParameters(Mockito.any());
+    }
+
+    @Test
+    public void 
updateCpuQuotaAndPeriodTestAssertPeriodAndQuotaAreUpdatedWhenThereIsNotCpuCapChangeAndCpuLimitationIsApplied()
 throws LibvirtException {
+        Mockito.when(vmTO.isLimitCpuUse()).thenReturn(true);
+        double cpuQuotaPercentage = 0.03;
+        
Mockito.when(vmTO.getCpuQuotaPercentage()).thenReturn(cpuQuotaPercentage);
+        Mockito.doReturn(new Pair<>(1000, 
300L)).when(libvirtComputingResourceSpy).getPeriodAndQuota(cpuQuotaPercentage);
+        Mockito.when(domainMock.getName()).thenReturn("i-2-10-VM");
+        libvirtComputingResourceSpy.hypervisorLibvirtVersion = 9000;
+        libvirtComputingResourceSpy.updateCpuQuotaAndPeriod(domainMock, vmTO, 
false);
+        Mockito.verify(domainMock, 
Mockito.times(2)).setSchedulerParameters(Mockito.any());
+    }
+
+    @Test
+    public void 
updateCpuQuotaAndPeriodTestAssertPeriodAndQuotaAreUpdatedWhenThereIsCpuCapChangeAndCpuLimitationIsApplied()
 throws LibvirtException {
+        Mockito.when(vmTO.isLimitCpuUse()).thenReturn(true);
+        double cpuQuotaPercentage = 0.03;
+        
Mockito.when(vmTO.getCpuQuotaPercentage()).thenReturn(cpuQuotaPercentage);
+        Mockito.doReturn(new Pair<>(1000, 
300L)).when(libvirtComputingResourceSpy).getPeriodAndQuota(cpuQuotaPercentage);
+        Mockito.when(domainMock.getName()).thenReturn("i-2-10-VM");
+        libvirtComputingResourceSpy.hypervisorLibvirtVersion = 9000;
+        libvirtComputingResourceSpy.updateCpuQuotaAndPeriod(domainMock, vmTO, 
true);
+        Mockito.verify(domainMock, 
Mockito.times(2)).setSchedulerParameters(Mockito.any());
+    }
+
+    @Test
+    public void 
getPeriodAndQuotaTestAssertQuotaIsEqualToPeriodMultipliedByQuotaPercentage() {
+        double cpuQuotaPercentage = 0.3;
+        int expectedPeriod = CpuTuneDef.DEFAULT_PERIOD;
+        long expectedQuota = (long) (expectedPeriod * cpuQuotaPercentage);
+        Pair<Integer, Long> expectedResult = new Pair<>(expectedPeriod, 
expectedQuota);
+        Pair<Integer, Long> result = 
libvirtComputingResourceSpy.getPeriodAndQuota(cpuQuotaPercentage);
+        Assert.assertEquals(expectedResult, result);
+    }
+
+    @Test
+    public void getPeriodAndQuotaTestQuotaIsEqualToMinimumWhenRequired() {
+        double cpuQuotaPercentage = 0.03;
+        long expectedQuota = CpuTuneDef.MIN_QUOTA;
+        int expectedPeriod = (int) ((double) expectedQuota / 
cpuQuotaPercentage);
+        Pair<Integer, Long> expectedResult = new Pair<>(expectedPeriod, 
expectedQuota);
+        Pair<Integer, Long> result = 
libvirtComputingResourceSpy.getPeriodAndQuota(cpuQuotaPercentage);
+        Assert.assertEquals(expectedResult, result);
+    }
+
+    @Test
+    public void getPeriodAndQuotaTestPeriodIsEqualToMaximumWhenRequired() {
+        double cpuQuotaPercentage = 0.0003;
+        long expectedQuota = CpuTuneDef.MIN_QUOTA;
+        int expectedPeriod = CpuTuneDef.MAX_PERIOD;
+        Pair<Integer, Long> expectedResult = new Pair<>(expectedPeriod, 
expectedQuota);
+        Pair<Integer, Long> result = 
libvirtComputingResourceSpy.getPeriodAndQuota(cpuQuotaPercentage);
+        Assert.assertEquals(expectedResult, result);
+    }
 }
diff --git a/server/src/main/java/com/cloud/capacity/CapacityManagerImpl.java 
b/server/src/main/java/com/cloud/capacity/CapacityManagerImpl.java
index 2940f900b08..3be7384ffb7 100644
--- a/server/src/main/java/com/cloud/capacity/CapacityManagerImpl.java
+++ b/server/src/main/java/com/cloud/capacity/CapacityManagerImpl.java
@@ -1214,6 +1214,6 @@ public class CapacityManagerImpl extends ManagerBase 
implements CapacityManager,
     public ConfigKey<?>[] getConfigKeys() {
         return new ConfigKey<?>[] {CpuOverprovisioningFactor, 
MemOverprovisioningFactor, StorageCapacityDisableThreshold, 
StorageOverprovisioningFactor,
                 StorageAllocatedCapacityDisableThreshold, 
StorageOperationsExcludeCluster, ImageStoreNFSVersion, 
SecondaryStorageCapacityThreshold,
-                StorageAllocatedCapacityDisableThresholdForVolumeSize, 
CapacityCalculateWorkers };
+                StorageAllocatedCapacityDisableThresholdForVolumeSize, 
CapacityCalculateWorkers, KvmMemoryDynamicScalingCapacity, 
KvmCpuDynamicScalingCapacity };
     }
 }
diff --git a/server/src/main/java/com/cloud/hypervisor/KVMGuru.java 
b/server/src/main/java/com/cloud/hypervisor/KVMGuru.java
index 3303bc02933..6c1c3424b1b 100644
--- a/server/src/main/java/com/cloud/hypervisor/KVMGuru.java
+++ b/server/src/main/java/com/cloud/hypervisor/KVMGuru.java
@@ -20,7 +20,7 @@ import com.cloud.agent.api.Command;
 import com.cloud.agent.api.to.DataObjectType;
 import com.cloud.agent.api.to.NicTO;
 import com.cloud.agent.api.to.VirtualMachineTO;
-import com.cloud.configuration.ConfigurationManagerImpl;
+import com.cloud.capacity.CapacityManager;
 import com.cloud.event.EventTypes;
 import com.cloud.event.UsageEventUtils;
 import com.cloud.host.HostVO;
@@ -44,6 +44,7 @@ import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.VirtualMachineProfile;
 import com.cloud.vm.dao.VMInstanceDao;
 import org.apache.cloudstack.backup.Backup;
+import org.apache.cloudstack.framework.config.ConfigKey;
 import org.apache.cloudstack.storage.command.CopyCommand;
 import org.apache.cloudstack.storage.command.StorageSubSystemCommand;
 import 
org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
@@ -54,9 +55,7 @@ import java.math.RoundingMode;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
-import org.apache.cloudstack.api.ApiConstants;
 import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
-import org.apache.commons.lang3.math.NumberUtils;
 
 public class KVMGuru extends HypervisorGuruBase implements HypervisorGuru {
     @Inject
@@ -130,30 +129,47 @@ public class KVMGuru extends HypervisorGuruBase 
implements HypervisorGuru {
      * @param vmProfile vm profile
      */
     protected void setVmQuotaPercentage(VirtualMachineTO to, 
VirtualMachineProfile vmProfile) {
-        if (to.isLimitCpuUse()) {
-            VirtualMachine vm = vmProfile.getVirtualMachine();
-            HostVO host = hostDao.findById(vm.getHostId());
-            if (host == null) {
-                logger.warn("Host is not available. Skipping setting CPU quota 
percentage for VM: {}", vm);
-                return;
-            }
-            logger.debug("Limiting CPU usage for VM: {} on host: {}", vm, 
host);
-            double hostMaxSpeed = getHostCPUSpeed(host);
-            double maxSpeed = getVmSpeed(to);
-            try {
-                BigDecimal percent = new BigDecimal(maxSpeed / hostMaxSpeed);
-                percent = percent.setScale(2, RoundingMode.HALF_DOWN);
-                if (percent.compareTo(new BigDecimal(1)) == 1) {
-                    logger.debug("VM {} CPU MHz exceeded host {} CPU MHz, 
limiting VM CPU to the host maximum", vm, host);
-                    percent = new BigDecimal(1);
-                }
-                to.setCpuQuotaPercentage(percent.doubleValue());
-                logger.debug("Host: {} max CPU speed = {} MHz, VM: {} max CPU 
speed = {} MHz. " +
-                        "Setting CPU quota percentage as: {}",
-                        host, hostMaxSpeed, vm, maxSpeed, 
percent.doubleValue());
-            } catch (NumberFormatException e) {
-                logger.error("Error calculating VM: {} quota percentage, it 
will not be set. Error: {}", vm, e.getMessage(), e);
+        if (!to.isLimitCpuUse()) {
+            return;
+        }
+
+        VirtualMachine vm = vmProfile.getVirtualMachine();
+        HostVO host = hostDao.findById(vm.getHostId());
+        if (host == null) {
+            logger.warn("Host is not available. Skipping setting CPU quota 
percentage for VM: [{}].", vm);
+            return;
+        }
+
+        logger.debug("Limiting CPU usage for VM: [{}] on host: [{}].", vm, 
host);
+        double maxSpeed = getVmSpeed(to);
+        double hostMaxSpeed = getHostCPUSpeed(host);
+        Double cpuQuotaPercentage = getCpuQuotaPercentage(maxSpeed, 
hostMaxSpeed);
+        if (cpuQuotaPercentage != null) {
+            to.setCpuQuotaPercentage(cpuQuotaPercentage);
+        }
+    }
+
+    /**
+     * Calculates the VM quota percentage based on the VM and host CPU speeds.
+     * @param vmSpeeed Speed of the VM.
+     * @param hostSpeed Speed of the host.
+     * @return The VM quota percentage.
+     */
+    public Double getCpuQuotaPercentage(double vmSpeeed, double hostSpeed) {
+        logger.debug("Calculating CPU quota percentage for VM with speed [{}] 
on host with speed [{}].", vmSpeeed, hostSpeed);
+        try {
+            BigDecimal percent = new BigDecimal(vmSpeeed / hostSpeed);
+            percent = percent.setScale(2, RoundingMode.HALF_DOWN);
+            if (percent.compareTo(new BigDecimal(1)) > 0) {
+                logger.debug("VM CPU speed exceeded host CPU speed and, 
therefore, limiting VM CPU quota to the host maximum.");
+                percent = new BigDecimal(1);
             }
+            double quotaPercentage = percent.doubleValue();
+            logger.info("Calculated CPU quota percentage for VM with speed 
[{}] on host with speed [{}] is [{}].", vmSpeeed, hostSpeed, quotaPercentage);
+            return quotaPercentage;
+        } catch (NumberFormatException e) {
+            logger.info("Could not calculate CPU quota percentage for VM with 
speed [{}] on host with speed [{}]. Therefore, CPU limitation will not be set 
for the domain.", vmSpeeed, hostSpeed);
+            return null;
         }
     }
 
@@ -214,28 +230,31 @@ public class KVMGuru extends HypervisorGuruBase 
implements HypervisorGuru {
         Pair<Long, Integer> max = getHostMaxMemoryAndCpuCores(hostVo, 
virtualMachine, vmDescription);
 
         Long maxHostMemory = max.first();
-        Integer maxHostCpuCore = max.second();
+        Integer maxHostCpuCores = max.second();
 
         long minMemory = virtualMachineTo.getMinRam();
         Long maxMemory = virtualMachineTo.getMaxRam();
+        long requestedMemory = maxMemory;
+
         int minCpuCores = virtualMachineTo.getCpus();
-        Integer maxCpuCores = minCpuCores;
+        int maxCpuCores = minCpuCores;
 
-        ServiceOfferingVO serviceOfferingVO = 
serviceOfferingDao.findById(virtualMachineProfile.getId(), 
virtualMachineProfile.getServiceOfferingId());
-        if (isVmDynamicScalable(serviceOfferingVO, virtualMachineTo, 
virtualMachine)) {
+        if (isVmDynamicScalable(virtualMachineTo, virtualMachine)) {
+            ServiceOfferingVO serviceOfferingVO = 
serviceOfferingDao.findById(virtualMachineProfile.getId(), 
virtualMachineProfile.getServiceOfferingId());
             serviceOfferingDao.loadDetails(serviceOfferingVO);
 
-            maxMemory = getVmMaxMemory(serviceOfferingVO, vmDescription, 
maxHostMemory);
-            maxCpuCores = getVmMaxCpuCores(serviceOfferingVO, vmDescription, 
maxHostCpuCore);
+            Long clusterId = hostVo != null ? hostVo.getClusterId() : null;
+            maxMemory = getVmMaxMemory(serviceOfferingVO, vmDescription, 
maxHostMemory, clusterId);
+            maxCpuCores = getVmMaxCpuCores(serviceOfferingVO, vmDescription, 
maxHostCpuCores, clusterId);
         }
 
-        virtualMachineTo.setRam(minMemory, maxMemory);
+        virtualMachineTo.setRam(minMemory, maxMemory, requestedMemory);
         virtualMachineTo.setCpus(minCpuCores);
         virtualMachineTo.setVcpuMaxLimit(maxCpuCores);
     }
 
-    protected boolean isVmDynamicScalable(ServiceOfferingVO serviceOfferingVO, 
VirtualMachineTO virtualMachineTo, VirtualMachine virtualMachine) {
-        return serviceOfferingVO.isDynamic() && 
virtualMachineTo.isEnableDynamicallyScaleVm() && 
UserVmManager.EnableDynamicallyScaleVm.valueIn(virtualMachine.getDataCenterId());
+    protected boolean isVmDynamicScalable(VirtualMachineTO virtualMachineTo, 
VirtualMachine virtualMachine) {
+        return virtualMachineTo.isEnableDynamicallyScaleVm() && 
UserVmManager.EnableDynamicallyScaleVm.valueIn(virtualMachine.getDataCenterId());
     }
 
     protected Pair<Long, Integer> getHostMaxMemoryAndCpuCores(HostVO host, 
VirtualMachine virtualMachine, String vmDescription){
@@ -263,53 +282,34 @@ public class KVMGuru extends HypervisorGuruBase 
implements HypervisorGuru {
         return new Pair<>(maxHostMemory, maxHostCpuCore);
     }
 
-    protected Long getVmMaxMemory(ServiceOfferingVO serviceOfferingVO, String 
vmDescription, Long maxHostMemory) {
-        String serviceOfferingDescription = serviceOfferingVO.toString();
-
+    protected Long getVmMaxMemory(ServiceOfferingVO serviceOfferingVO, String 
vmDescription, Long maxHostMemory, Long clusterId) {
         Long maxMemory;
-        Integer customOfferingMaxMemory = 
NumberUtils.createInteger(serviceOfferingVO.getDetail(ApiConstants.MAX_MEMORY));
-        Integer maxMemoryConfig = 
ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE.value();
-        if (customOfferingMaxMemory != null) {
-            logger.debug(String.format("Using 'Custom unconstrained' %s max 
memory value [%sMb] as %s memory.", serviceOfferingDescription, 
customOfferingMaxMemory, vmDescription));
-            maxMemory = 
ByteScaleUtils.mebibytesToBytes(customOfferingMaxMemory);
+        ConfigKey<Integer> maxMemoryConfig = 
CapacityManager.KvmMemoryDynamicScalingCapacity;
+        Integer maxMemoryConfigValue = maxMemoryConfig.valueIn(clusterId);
+        logger.info("[{}] is a dynamically scalable service offering. Using 
config [{}] value [{}] in cluster [ID: {}] as max [{}] memory.",
+                serviceOfferingVO.toString(), maxMemoryConfig.key(), 
maxMemoryConfigValue, clusterId, vmDescription);
+        if (maxMemoryConfigValue > 0) {
+            maxMemory = ByteScaleUtils.mebibytesToBytes(maxMemoryConfigValue);
         } else {
-            String maxMemoryConfigKey = 
ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE.key();
-
-            logger.info(String.format("%s is a 'Custom unconstrained' service 
offering. Using config [%s] value [%s] as max %s memory.",
-                    serviceOfferingDescription, maxMemoryConfigKey, 
maxMemoryConfig, vmDescription));
-
-            if (maxMemoryConfig > 0) {
-                maxMemory = ByteScaleUtils.mebibytesToBytes(maxMemoryConfig);
-            } else {
-                logger.info(String.format("Config [%s] has value less or equal 
'0'. Using %s host or last host max memory [%s] as VM max memory in the 
hypervisor.", maxMemoryConfigKey, vmDescription, maxHostMemory));
-                maxMemory = maxHostMemory;
-            }
+            logger.info("Config [{}] in cluster [ID: {}] has value less or 
equal '0'. Using [{}] host or last host max memory [{}] as VM max memory in the 
hypervisor.",
+                    maxMemoryConfig.key(), clusterId, vmDescription, 
maxHostMemory);
+            maxMemory = maxHostMemory;
         }
         return maxMemory;
     }
 
-    protected Integer getVmMaxCpuCores(ServiceOfferingVO serviceOfferingVO, 
String vmDescription, Integer maxHostCpuCore) {
-        String serviceOfferingDescription = serviceOfferingVO.toString();
-
+    protected Integer getVmMaxCpuCores(ServiceOfferingVO serviceOfferingVO, 
String vmDescription, Integer maxHostCpuCores, Long clusterId) {
         Integer maxCpuCores;
-        Integer customOfferingMaxCpuCores = 
NumberUtils.createInteger(serviceOfferingVO.getDetail(ApiConstants.MAX_CPU_NUMBER));
-        Integer maxCpuCoresConfig = 
ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES.value();
-
-        if (customOfferingMaxCpuCores != null) {
-            logger.debug(String.format("Using 'Custom unconstrained' %s max 
cpu cores [%s] as %s cpu cores.", serviceOfferingDescription, 
customOfferingMaxCpuCores, vmDescription));
-            maxCpuCores = customOfferingMaxCpuCores;
+        ConfigKey<Integer> maxCpuCoresConfig = 
CapacityManager.KvmCpuDynamicScalingCapacity;
+        Integer maxCpuCoresConfigValue = maxCpuCoresConfig.valueIn(clusterId);
+        logger.info("[{}] is a dynamically scalable service offering. Using 
config [{}] value [{}] in cluster [ID: {}] as max [{}] CPU cores.",
+                serviceOfferingVO.toString(), maxCpuCoresConfig.key(), 
maxCpuCoresConfigValue, clusterId, vmDescription);
+        if (maxCpuCoresConfigValue > 0) {
+            maxCpuCores = maxCpuCoresConfigValue;
         } else {
-            String maxCpuCoreConfigKey = 
ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES.key();
-
-            logger.info(String.format("%s is a 'Custom unconstrained' service 
offering. Using config [%s] value [%s] as max %s cpu cores.",
-                    serviceOfferingDescription, maxCpuCoreConfigKey, 
maxCpuCoresConfig, vmDescription));
-
-            if (maxCpuCoresConfig > 0) {
-                maxCpuCores = maxCpuCoresConfig;
-            } else {
-                logger.info(String.format("Config [%s] has value less or equal 
'0'. Using %s host or last host max cpu cores [%s] as VM cpu cores in the 
hypervisor.", maxCpuCoreConfigKey, vmDescription, maxHostCpuCore));
-                maxCpuCores = maxHostCpuCore;
-            }
+            logger.info("Config [{}] in cluster [ID: {}] has value less or 
equal '0'. Using [{}] host or last host max CPU cores [{}] as VM CPU cores in 
the hypervisor.",
+                    maxCpuCoresConfig.key(), clusterId, vmDescription, 
maxHostCpuCores);
+            maxCpuCores = maxHostCpuCores;
         }
         return maxCpuCores;
     }
diff --git a/server/src/main/java/com/cloud/vm/UserVmManager.java 
b/server/src/main/java/com/cloud/vm/UserVmManager.java
index 38cb6d2db46..300803bfa98 100644
--- a/server/src/main/java/com/cloud/vm/UserVmManager.java
+++ b/server/src/main/java/com/cloud/vm/UserVmManager.java
@@ -111,6 +111,10 @@ public interface UserVmManager extends UserVmService {
     ConfigKey<Boolean> AllowDifferentHostTagsOfferingsForVmScale = new 
ConfigKey<>("Advanced", Boolean.class, 
"allow.different.host.tags.offerings.for.vm.scale", "false",
             "Enables/Disable allowing to change a VM offering to offerings 
with different host tags", true);
 
+    ConfigKey<Boolean> AutoMigrateVmOnLiveScaleInsufficientCapacity = new 
ConfigKey<>("Advanced", Boolean.class, 
"auto.migrate.vm.on.live.scale.insufficient.capacity",
+            "true", "Defines whether a VM should be automatically migrated to 
a suitable host when the current host " +
+                    "lacks sufficient compute capacity to live scale the 
instance. Defaults to true.", true, ConfigKey.Scope.Cluster);
+
     static final int MAX_USER_DATA_LENGTH_BYTES = 2048;
 
     public  static  final String CKS_NODE = "cksnode";
diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java 
b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
index 60482c431eb..0a2f3b902e5 100644
--- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
@@ -2116,25 +2116,26 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
         int newCpu = newServiceOffering.getCpu();
         int newMemory = newServiceOffering.getRamSize();
         int newSpeed = newServiceOffering.getSpeed();
+        boolean cpuCapEnabledForTheNewOffering = 
newServiceOffering.getLimitCpuUse();
         int currentCpu = currentServiceOffering.getCpu();
         int currentMemory = currentServiceOffering.getRamSize();
         int currentSpeed = currentServiceOffering.getSpeed();
+        boolean cpuCapEnabledForTheCurrentOffering = 
currentServiceOffering.getLimitCpuUse();
         int memoryDiff = newMemory - currentMemory;
         int cpuDiff = newCpu * newSpeed - currentCpu * currentSpeed;
 
-        // Don't allow to scale when (Any of the new values less than current 
values) OR (All current and new values are same)
-        if ((newSpeed < currentSpeed || newMemory < currentMemory || newCpu < 
currentCpu) || (newSpeed == currentSpeed && newMemory == currentMemory && 
newCpu == currentCpu)) {
-            String message = String.format("While the VM is running, only 
scalling up it is supported. New service offering {\"memory\": %s, \"speed\": 
%s, \"cpu\": %s} should"
-              + " have at least one value (ram, speed or cpu) greater than the 
current values {\"memory\": %s, \"speed\": %s, \"cpu\": %s}.", newMemory, 
newSpeed, newCpu,
-              currentMemory, currentSpeed, currentCpu);
-
-            throw new InvalidParameterValueException(message);
+        boolean scalingDown = newSpeed < currentSpeed || newMemory < 
currentMemory || newCpu < currentCpu;
+        if (scalingDown) {
+            throw new InvalidParameterValueException(String.format("Scaling 
down is not supported while the VM is running. The new service offering 
attributes " +
+                    "{\"memory\": %s, \"CPU speed\": %s, \"vCPUs\": %s} must 
not be lower than the current values {\"memory\": %s, \"CPU speed\": %s, 
\"vCPUs\": %s}.",
+                    newMemory, newSpeed, newCpu, currentMemory, currentSpeed, 
currentCpu));
         }
 
-        if (vmHypervisorType.equals(HypervisorType.KVM) && 
!currentServiceOffering.isDynamic()) {
-            String message = String.format("Unable to live scale VM on KVM 
when current service offering is a \"Fixed Offering\". KVM needs the tag 
\"maxMemory\" to live scale and it is only configured when VM is deployed with 
a custom service offering and \"Dynamic Scalable\" is enabled.");
-            logger.info(message);
-            throw new InvalidParameterValueException(message);
+        boolean sameAmountOfResourcesAsThePreviousOffering = newSpeed == 
currentSpeed && newMemory == currentMemory && newCpu == currentCpu;
+        boolean cpuCapChange = cpuCapEnabledForTheCurrentOffering != 
cpuCapEnabledForTheNewOffering;
+        if (sameAmountOfResourcesAsThePreviousOffering && (vmHypervisorType != 
HypervisorType.KVM || !cpuCapChange)) {
+            throw new InvalidParameterValueException("While the VM is running, 
scaling to a service offering with the same attributes (memory, CPU speed and 
vCPUs) " +
+                    "is only allowed when the CPU cap is changed.");
         }
 
         serviceOfferingDao.loadDetails(currentServiceOffering);
@@ -2200,8 +2201,19 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
                         excludes.addHost(vmInstance.getHostId());
                     }
 
+                    boolean autoMigrateVmToASuitableHost = 
AutoMigrateVmOnLiveScaleInsufficientCapacity.valueIn(host.getClusterId());
+                    if (!existingHostHasCapacity && 
!autoMigrateVmToASuitableHost) {
+                        logger.error("Unable to scale the VM [{}] because the 
host [{}] in which it is currently allocated does not " +
+                                "have enough compute capacity to scale the 
instance and the VM should not be automatically migrated to another host " +
+                                "([{}] setting is [false]).", 
vmInstance.getInstanceName(), host.getName(), 
AutoMigrateVmOnLiveScaleInsufficientCapacity.key());
+                        return false;
+                    }
+
                     // #2 migrate the vm if host doesn't have capacity or is 
in avoid set
                     if (!existingHostHasCapacity) {
+                        logger.info("Host [{}] does not have enough compute 
capacity to scale the instance [{}]. Since the [{}] setting is " +
+                                        "[true], the VM will be migrated to a 
suitable host and, if succeeded, the VM will be live scaled to the requested " +
+                                        "compute offering.", host.getName(), 
vmInstance.getInstanceName(), 
AutoMigrateVmOnLiveScaleInsufficientCapacity.key());
                         _itMgr.findHostAndMigrate(vmInstance.getUuid(), 
newServiceOfferingId, customParameters, excludes);
                     }
 
@@ -9458,7 +9470,7 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
                 VmIpFetchThreadPoolMax, VmIpFetchTaskWorkers, 
AllowDeployVmIfGivenHostFails, EnableAdditionalVmConfig, DisplayVMOVFProperties,
                 KvmAdditionalConfigAllowList, 
XenServerAdditionalConfigAllowList, VmwareAdditionalConfigAllowList, 
DestroyRootVolumeOnVmDestruction,
                 EnforceStrictResourceLimitHostTagCheck, StrictHostTags, 
AllowUserForceStopVm, VmDistinctHostNameScope,
-                VmwareAdditionalDetailsFromOvaEnabled, 
VmwareAllowedAdditionalDetailsFromOva, 
AllowDifferentHostTagsOfferingsForVmScale};
+                VmwareAdditionalDetailsFromOvaEnabled, 
VmwareAllowedAdditionalDetailsFromOva, 
AllowDifferentHostTagsOfferingsForVmScale, 
AutoMigrateVmOnLiveScaleInsufficientCapacity};
     }
 
     @Override
diff --git a/server/src/test/java/com/cloud/hypervisor/KVMGuruTest.java 
b/server/src/test/java/com/cloud/hypervisor/KVMGuruTest.java
index d94f9db0c99..e5239a65123 100644
--- a/server/src/test/java/com/cloud/hypervisor/KVMGuruTest.java
+++ b/server/src/test/java/com/cloud/hypervisor/KVMGuruTest.java
@@ -53,6 +53,10 @@ import com.cloud.storage.dao.GuestOSHypervisorDao;
 import com.cloud.utils.Pair;
 import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.VirtualMachineProfile;
+import org.apache.cloudstack.framework.config.impl.ConfigDepotImpl;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
 
 @RunWith(MockitoJUnitRunner.class)
 public class KVMGuruTest {
@@ -186,79 +190,45 @@ public class KVMGuruTest {
     }
 
     @Test
-    public void validateGetVmMaxMemoryReturnCustomOfferingMaxMemory(){
-        int maxCustomOfferingMemory = 64;
-        
Mockito.when(serviceOfferingVoMock.getDetail(ApiConstants.MAX_MEMORY)).thenReturn(String.valueOf(maxCustomOfferingMemory));
-
-        long result = guru.getVmMaxMemory(serviceOfferingVoMock, "Vm 
description", 1l);
-
-        
Assert.assertEquals(ByteScaleUtils.mebibytesToBytes(maxCustomOfferingMemory), 
result);
-    }
-
-    @Test
-    public void validateGetVmMaxMemoryReturnVmServiceOfferingMaxRAMSize(){
-        int maxMemoryConfig = 64;
-        
Mockito.when(serviceOfferingVoMock.getDetail(ApiConstants.MAX_MEMORY)).thenReturn(null);
+    public void 
getVmMaxMemoryTestConsiderKvmMemoryDynamicScalingCapacitySettingWhenItIsGreaterThanZero()
 {
+        int maxMemoryConfigValue = 64;
 
-        ConfigKey<Integer> vmServiceOfferingMaxRAMSize = 
Mockito.mock(ConfigKey.class);
-        ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE = 
vmServiceOfferingMaxRAMSize;
+        ConfigDepotImpl configDepotMock = Mockito.mock(ConfigDepotImpl.class);
+        ConfigKey.init(configDepotMock);
+        Mockito.when(configDepotMock.getConfigStringValue(Mockito.any(), 
Mockito.any(), Mockito.any()))
+                .thenReturn(String.valueOf(maxMemoryConfigValue));
 
-        
Mockito.when(vmServiceOfferingMaxRAMSize.value()).thenReturn(maxMemoryConfig);
-        long result = guru.getVmMaxMemory(serviceOfferingVoMock, "Vm 
description", 1l);
-
-        Assert.assertEquals(ByteScaleUtils.mebibytesToBytes(maxMemoryConfig), 
result);
+        long result = guru.getVmMaxMemory(serviceOfferingVoMock, "Vm 
description", 1L, 1L);
+        ConfigKey.init(null);
+        
Assert.assertEquals(ByteScaleUtils.mebibytesToBytes(maxMemoryConfigValue), 
result);
     }
 
     @Test
-    public void validateGetVmMaxMemoryReturnMaxHostMemory(){
+    public void 
getVmMaxMemoryTestConsiderHostMaxMemoryWhenKvmMemoryDynamicScalingCapacitySettingIsEqualToZero()
 {
         long maxHostMemory = ByteScaleUtils.mebibytesToBytes(2000);
-        
Mockito.when(serviceOfferingVoMock.getDetail(ApiConstants.MAX_MEMORY)).thenReturn(null);
-
-        ConfigKey<Integer> vmServiceOfferingMaxRAMSize = 
Mockito.mock(ConfigKey.class);
-        ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE = 
vmServiceOfferingMaxRAMSize;
-
-        Mockito.when(vmServiceOfferingMaxRAMSize.value()).thenReturn(0);
-
-        long result = guru.getVmMaxMemory(serviceOfferingVoMock, "Vm 
description", maxHostMemory);
+        long result = guru.getVmMaxMemory(serviceOfferingVoMock, "Vm 
description", maxHostMemory, 1L);
 
         Assert.assertEquals(maxHostMemory, result);
     }
 
     @Test
-    public void validateGetVmMaxCpuCoresReturnCustomOfferingMaxCpuCores(){
-        int maxCustomOfferingCpuCores = 16;
-        
Mockito.when(serviceOfferingVoMock.getDetail(ApiConstants.MAX_CPU_NUMBER)).thenReturn(String.valueOf(maxCustomOfferingCpuCores));
-
-        long result = guru.getVmMaxCpuCores(serviceOfferingVoMock, "Vm 
description", 1);
-
-        Assert.assertEquals(maxCustomOfferingCpuCores, result);
-    }
-
-    @Test
-    public void validateGetVmMaxCpuCoresVmServiceOfferingMaxCPUCores(){
+    public void 
getVmMaxCpuCoresTestConsiderKvmCpuDynamicScalingCapacitySettingWhenItIsGreaterThanZero()
 {
         int maxCpuCoresConfig = 16;
-        
Mockito.when(serviceOfferingVoMock.getDetail(ApiConstants.MAX_CPU_NUMBER)).thenReturn(null);
+        ConfigDepotImpl configDepotMock = Mockito.mock(ConfigDepotImpl.class);
+        ConfigKey.init(configDepotMock);
+        Mockito.when(configDepotMock.getConfigStringValue(Mockito.any(), 
Mockito.any(), Mockito.any()))
+                .thenReturn(String.valueOf(maxCpuCoresConfig));
 
-        ConfigKey<Integer> vmServiceOfferingMaxCPUCores = 
Mockito.mock(ConfigKey.class);
-        ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES = 
vmServiceOfferingMaxCPUCores;
-
-        
Mockito.when(vmServiceOfferingMaxCPUCores.value()).thenReturn(maxCpuCoresConfig);
-        long result = guru.getVmMaxCpuCores(serviceOfferingVoMock, "Vm 
description", 1);
+        long result = guru.getVmMaxCpuCores(serviceOfferingVoMock, "Vm 
description", 1, 1L);
+        ConfigKey.init(null);
 
         Assert.assertEquals(maxCpuCoresConfig, result);
     }
 
     @Test
-    public void validateGetVmMaxCpuCoresReturnMaxHostMemory(){
+    public void 
getVmMaxCpuCoresTestConsiderHostMaxCpuWhenKvmCpuDynamicScalingCapacitySettingIsEqualToZero()
 {
         int maxHostCpuCores = 64;
-        
Mockito.when(serviceOfferingVoMock.getDetail(ApiConstants.MAX_CPU_NUMBER)).thenReturn(null);
-
-        ConfigKey<Integer> vmServiceOfferingMaxCPUCores = 
Mockito.mock(ConfigKey.class);
-        ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES = 
vmServiceOfferingMaxCPUCores;
-
-        Mockito.when(vmServiceOfferingMaxCPUCores.value()).thenReturn(0);
-
-        long result = guru.getVmMaxCpuCores(serviceOfferingVoMock, "Vm 
description", maxHostCpuCores);
+        long result = guru.getVmMaxCpuCores(serviceOfferingVoMock, "Vm 
description", maxHostCpuCores, 1L);
 
         Assert.assertEquals(maxHostCpuCores, result);
     }
@@ -321,39 +291,36 @@ public class KVMGuruTest {
         guru.serviceOfferingDao = serviceOfferingDaoMock;
 
         
Mockito.doReturn(serviceOfferingVoMock).when(serviceOfferingDaoMock).findById(Mockito.anyLong(),
 Mockito.anyLong());
-        Mockito.doReturn(true).when(guru).isVmDynamicScalable(Mockito.any(), 
Mockito.any(), Mockito.any());
+        Mockito.doReturn(true).when(guru).isVmDynamicScalable(Mockito.any(), 
Mockito.any());
 
         guru.configureVmMemoryAndCpuCores(vmTO, host, virtualMachineMock, 
vmProfile);
 
-        
Mockito.verify(guru).getVmMaxMemory(Mockito.any(ServiceOfferingVO.class), 
Mockito.anyString(), Mockito.anyLong());
-        
Mockito.verify(guru).getVmMaxCpuCores(Mockito.any(ServiceOfferingVO.class), 
Mockito.anyString(), Mockito.anyInt());
+        
Mockito.verify(guru).getVmMaxMemory(Mockito.any(ServiceOfferingVO.class), 
Mockito.anyString(), Mockito.anyLong(), Mockito.anyLong());
+        
Mockito.verify(guru).getVmMaxCpuCores(Mockito.any(ServiceOfferingVO.class), 
Mockito.anyString(), Mockito.anyInt(), Mockito.anyLong());
     }
 
     @Test
     public void 
validateConfigureVmMemoryAndCpuCoresServiceOfferingIsNotDynamicAndVmIsDynamicDoNotCallGetMethods(){
         guru.serviceOfferingDao = serviceOfferingDaoMock;
 
-        
Mockito.doReturn(serviceOfferingVoMock).when(serviceOfferingDaoMock).findById(Mockito.anyLong(),
 Mockito.anyLong());
-        Mockito.doReturn(false).when(guru).isVmDynamicScalable(Mockito.any(), 
Mockito.any(), Mockito.any());
+        Mockito.doReturn(false).when(guru).isVmDynamicScalable(Mockito.any(), 
Mockito.any());
 
         guru.configureVmMemoryAndCpuCores(vmTO, host, virtualMachineMock, 
vmProfile);
 
-        Mockito.verify(guru, 
Mockito.never()).getVmMaxMemory(Mockito.any(ServiceOfferingVO.class), 
Mockito.anyString(), Mockito.anyLong());
-        Mockito.verify(guru, 
Mockito.never()).getVmMaxCpuCores(Mockito.any(ServiceOfferingVO.class), 
Mockito.anyString(), Mockito.anyInt());
+        Mockito.verify(guru, 
Mockito.never()).getVmMaxMemory(Mockito.any(ServiceOfferingVO.class), 
Mockito.anyString(), Mockito.anyLong(), Mockito.anyLong());
+        Mockito.verify(guru, 
Mockito.never()).getVmMaxCpuCores(Mockito.any(ServiceOfferingVO.class), 
Mockito.anyString(), Mockito.anyInt(), Mockito.anyLong());
     }
 
     @Test
     public void 
validateConfigureVmMemoryAndCpuCoresServiceOfferingIsDynamicAndVmIsNotDynamicDoNotCallGetMethods(){
         guru.serviceOfferingDao = serviceOfferingDaoMock;
 
-        
Mockito.doReturn(serviceOfferingVoMock).when(serviceOfferingDaoMock).findById(Mockito.anyLong(),
 Mockito.anyLong());
-        Mockito.doReturn(true).when(serviceOfferingVoMock).isDynamic();
         Mockito.doReturn(false).when(vmTO).isEnableDynamicallyScaleVm();
 
         guru.configureVmMemoryAndCpuCores(vmTO, host, virtualMachineMock, 
vmProfile);
 
-        Mockito.verify(guru, 
Mockito.never()).getVmMaxMemory(Mockito.any(ServiceOfferingVO.class), 
Mockito.anyString(), Mockito.anyLong());
-        Mockito.verify(guru, 
Mockito.never()).getVmMaxCpuCores(Mockito.any(ServiceOfferingVO.class), 
Mockito.anyString(), Mockito.anyInt());
+        Mockito.verify(guru, 
Mockito.never()).getVmMaxMemory(Mockito.any(ServiceOfferingVO.class), 
Mockito.anyString(), Mockito.anyLong(), Mockito.anyLong());
+        Mockito.verify(guru, 
Mockito.never()).getVmMaxCpuCores(Mockito.any(ServiceOfferingVO.class), 
Mockito.anyString(), Mockito.anyInt(), Mockito.anyLong());
     }
 
     @Test
@@ -506,4 +473,30 @@ public class KVMGuruTest {
 
         Assert.assertNull(clusterId);
     }
+
+    @Test
+    public void 
getCpuQuotaPercentageTestAssertQuotaEqualsVmSpeedDividedByHostSpeed() {
+        double hostSpeed = 3000;
+        double vmSpeed = 500;
+        double expectedQuota = new BigDecimal(vmSpeed / hostSpeed).setScale(2, 
RoundingMode.HALF_DOWN).doubleValue();
+        double actualQuota = guru.getCpuQuotaPercentage(vmSpeed, hostSpeed);
+        Assert.assertEquals(expectedQuota, actualQuota, 0.0001);
+    }
+
+    @Test
+    public void 
getCpuQuotaPercentageTestAssertQuotaEqualsOneWhenVmSpeedIsGreaterThanHostSpeed()
 {
+        double hostSpeed = 3000;
+        double vmSpeed = 6000;
+        double expectedQuota = 1;
+        double actualQuota = guru.getCpuQuotaPercentage(vmSpeed, hostSpeed);
+        Assert.assertEquals(expectedQuota, actualQuota, 0.0001);
+    }
+
+    @Test
+    public void 
getCpuQuotaPercentageTestReturnNullWhenANumberFormatExceptionIsThrown() {
+        double hostSpeed = 0;
+        double vmSpeed = 6000;
+        Double actualQuota = guru.getCpuQuotaPercentage(vmSpeed, hostSpeed);
+        Assert.assertNull(actualQuota);
+    }
 }
diff --git a/test/integration/component/test_escalations_instances.py 
b/test/integration/component/test_escalations_instances.py
index 89c4f4ce2a6..4aea35850d0 100644
--- a/test/integration/component/test_escalations_instances.py
+++ b/test/integration/component/test_escalations_instances.py
@@ -3341,9 +3341,6 @@ class TestInstances(cloudstackTestCase):
                deployed in step1
         Step6: Verifying that VM's service offerings is changed
         """
-        if self.hypervisor.lower() == 'kvm':
-            self.skipTest(
-                "ScaleVM is not supported on KVM. Hence, skipping the test")
         # Checking if Dynamic scaling of VM is supported or not
         list_config = Configurations.list(
             self.apiClient,
diff --git a/test/integration/component/test_escalations_ipaddresses.py 
b/test/integration/component/test_escalations_ipaddresses.py
index b91b67c16c3..d9d760c4901 100644
--- a/test/integration/component/test_escalations_ipaddresses.py
+++ b/test/integration/component/test_escalations_ipaddresses.py
@@ -3455,8 +3455,7 @@ class TestIpAddresses(cloudstackTestCase):
         Step18: Verifying Autoscale policy is updated with condition2
         """
         if self.hypervisor.lower() == 'kvm':
-            self.skipTest(
-                "ScaleVM is not supported on KVM. Hence, skipping the test")
+            self.skipTest("Test not supported on KVM. Skipping it")
 
         list_physical_networks = PhysicalNetwork.list(
             self.apiClient,
@@ -3734,8 +3733,7 @@ class TestIpAddresses(cloudstackTestCase):
         Step16: Verifying that Autoscale VM is updated
         """
         if self.hypervisor.lower() == 'kvm':
-            self.skipTest(
-                "ScaleVM is not supported on KVM. Hence, skipping the test")
+            self.skipTest("Test not supported on KVM. Skipping it")
 
         list_physical_networks = PhysicalNetwork.list(
             self.apiClient,
@@ -4061,8 +4059,7 @@ class TestIpAddresses(cloudstackTestCase):
         Step14: Enabling Autoscale VM group and verifying it was enabled
         """
         if self.hypervisor.lower() == 'kvm':
-            self.skipTest(
-                "ScaleVM is not supported on KVM. Hence, skipping the test")
+            self.skipTest("Test not supported on KVM. Skipping it.")
 
         list_physical_networks = PhysicalNetwork.list(
             self.apiClient,
diff --git a/ui/public/locales/el_GR.json b/ui/public/locales/el_GR.json
index bb90661d9d3..566e182b8fa 100644
--- a/ui/public/locales/el_GR.json
+++ b/ui/public/locales/el_GR.json
@@ -2270,8 +2270,6 @@
 "message.error.enable.saml": "Δεν είναι δυνατή η εύρεση των id χρηστών για την 
ενεργοποίηση του Saml σύνδεση μιας φοράς, παρακαλούμε να το ενεργοποιήσετε με 
μη αυτόματο τρόπο.",
 "message.error.end.date.and.time": "Παρακαλώ, εισάγετε την τελική ημερομηνία 
και ώρα!",
 "message.error.endip": "Πληκτρολογήστε End IP",
-"message.error.fixed.offering.kvm": "Δεν είναι δυνατό να κλιμωκαθούν οι 
εικονές μηχανές που χρησιμοποιούν τον επόπτη KVM με ένα σταθερό υπολογισμό 
προσφοράς υπηρεσίας.",
-"message.error.fixed.offering.kvm": "Δεν είναι εφικτή η κλιμάκωση προς τα πάνω 
της εικονικής μηχανής ενώ ανήκει σε μία σταθερή προσφορά νέφους.",
 "message.error.gateway": "Πληκτρολογήστε Πύλη",
 "message.error.host.name": "Πληκτρολογήστε το όνομα του κεντρικού υπολογιστή",
 "message.error.host.password": "Πληκτρολογήστε τον κωδικό πρόσβασης κεντρικού 
υπολογιστή",
diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index 1187b3e62b4..83422cd4101 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -3599,7 +3599,6 @@
 "message.error.zone.name": "Please enter Zone name.",
 "message.error.zone.type": "Please select Zone type.",
 "message.error.linstor.resourcegroup": "Please enter the Linstor 
Resource-Group.",
-"message.error.fixed.offering.kvm": "It's not possible to scale up Instances 
that utilize KVM hypervisor with a fixed compute offering.",
 "message.error.create.webhook.local.account": "Account must be provided for 
creating a Webhook with Local scope.",
 "message.error.create.webhook.name": "Name must be provided for creating a 
Webhook.",
 "message.error.create.webhook.payloadurl": "Payload URL must be provided for 
creating a Webhook.",
diff --git a/ui/public/locales/ja_JP.json b/ui/public/locales/ja_JP.json
index 850264fb341..b7f32d7a2e1 100644
--- a/ui/public/locales/ja_JP.json
+++ b/ui/public/locales/ja_JP.json
@@ -2932,7 +2932,6 @@
   "message.error.domain": "ドメインを入力し、ROOTドメインは空のままにします",
   "message.error.enable.saml": "SAML SSOを有効にするユーザーIDが見つかりません。手動で有効にしてください。",
   "message.error.endip": "終了IPを入力してください",
-  "message.error.fixed.offering.kvm": 
"固定コンピューティングオファリングでKVMハイパーバイザーを利用するVMをスケールアップすることはできません。",
   "message.error.gateway": "ゲートウェイに入ってください",
   "message.error.host.name": "ホスト名を入力してください",
   "message.error.host.password": "ホストパスワードを入力してください",
diff --git a/ui/public/locales/pt_BR.json b/ui/public/locales/pt_BR.json
index 4b446eccff3..dee353ec589 100644
--- a/ui/public/locales/pt_BR.json
+++ b/ui/public/locales/pt_BR.json
@@ -3229,7 +3229,6 @@
 "message.error.zone.name": "Por favor, insira o nome da zona",
 "message.error.zone.type": "Por favor, selecione o tipo de zona",
 "message.error.linstor.resourcegroup": "Por favor, insira o Linstor 
Resource-Group",
-"message.error.fixed.offering.kvm": "N\u00e3o \u00e9 poss\u00edvel escalar VMs 
que utilizam o virtualizador KVM com uma oferta de computa\u00e7\u00e3o fixa.",
 "message.fail.to.delete": "Falha ao deletar.",
 "message.failed.to.add": "Falha ao adicionar",
 "message.failed.to.assign.vms": "Falha ao atribuir VMs",
diff --git a/ui/public/locales/te.json b/ui/public/locales/te.json
index ed72626e0a6..f3b4c70ca2e 100644
--- a/ui/public/locales/te.json
+++ b/ui/public/locales/te.json
@@ -3169,7 +3169,6 @@
   "message.error.zone.name": "దయచేసి జోన్ పేరును నమోదు చేయండి.",
   "message.error.zone.type": "దయచేసి జోన్ రకాన్ని ఎంచుకోండి.",
   "message.error.linstor.resourcegroup": "దయచేసి Linstor Resource-Groupని 
నమోదు చేయండి.",
-  "message.error.fixed.offering.kvm": "ఫిక్స్‌డ్ కంప్యూట్ ఆఫర్‌తో KVM 
హైపర్‌వైజర్‌ను ఉపయోగించుకునే సందర్భాలను స్కేల్ చేయడం సాధ్యం కాదు.",
   "message.error.create.webhook.local.account": "స్థానిక స్కోప్‌తో 
వెబ్‌హుక్‌ని సృష్టించడానికి తప్పనిసరిగా ఖాతా అందించబడాలి.",
   "message.error.create.webhook.name": "వెబ్‌హుక్‌ని సృష్టించడానికి 
తప్పనిసరిగా పేరు అందించాలి.",
   "message.error.create.webhook.payloadurl": "Webhookని సృష్టించడానికి పేలోడ్ 
URL తప్పనిసరిగా అందించబడాలి.",
diff --git a/ui/public/locales/zh_CN.json b/ui/public/locales/zh_CN.json
index 4b68a2ef6d7..9536bab7ee4 100644
--- a/ui/public/locales/zh_CN.json
+++ b/ui/public/locales/zh_CN.json
@@ -3462,7 +3462,6 @@
   "message.error.zone.name": "\u8BF7\u8F93\u5165\u533A\u57DF\u540D\u79F0",
   "message.error.zone.type": "\u8BF7\u9009\u62E9\u533A\u57DF\u7C7B\u578B",
   "message.error.linstor.resourcegroup": "\u8BF7\u8F93\u5165 Linstor 
\u8D44\u6E90\u7EC4",
-  "message.error.fixed.offering.kvm": 
"\u4E0D\u53EF\u80FD\u901A\u8FC7\u56FA\u5B9A\u7684\u8BA1\u7B97\u65B9\u6848\u6765\u6269\u5C55KVM\u865A\u62DF\u673A\u3002",
   "message.fail.to.delete": "\u5220\u9664\u5931\u8D25\u3002",
   "message.failed.to.add": "\u6DFB\u52A0\u5931\u8D25",
   "message.failed.to.assign.vms": "\u672A\u80FD\u5206\u914D\u865A\u62DF\u673A",
diff --git a/ui/src/views/compute/ScaleVM.vue b/ui/src/views/compute/ScaleVM.vue
index ce8dca5b43a..b810e1327c6 100644
--- a/ui/src/views/compute/ScaleVM.vue
+++ b/ui/src/views/compute/ScaleVM.vue
@@ -23,10 +23,6 @@
       <loading-outlined style="color: #1890ff;" />
     </div>
 
-    <a-alert v-if="fixedOfferingKvm" type="error" show-icon>
-      <template #message><span style="margin-bottom: 5px" 
v-html="$t('message.error.fixed.offering.kvm')" /></template>
-    </a-alert>
-
     <compute-offering-selection
       :compute-items="offerings"
       :loading="loading"
@@ -42,9 +38,11 @@
       :memory-input-decorator="memoryKey"
       :computeOfferingId="selectedOffering.id"
       :isConstrained="'serviceofferingdetails' in selectedOffering"
+      :initialCpuValue="getInitialCpuValue()"
       :minCpu="getMinCpu()"
       :maxCpu="'serviceofferingdetails' in selectedOffering ? 
selectedOffering.serviceofferingdetails.maxcpunumber*1 : 
Number.MAX_SAFE_INTEGER"
       :cpuSpeed="getCPUSpeed()"
+      :initialMemoryValue="getInitialMemoryValue()"
       :minMemory="getMinMemory()"
       :maxMemory="'serviceofferingdetails' in selectedOffering ? 
selectedOffering.serviceofferingdetails.maxmemory*1 : Number.MAX_SAFE_INTEGER"
       :isCustomized="selectedOffering.iscustomized"
@@ -151,32 +149,31 @@ export default {
           return
         }
         this.offerings = response.listserviceofferingsresponse.serviceoffering 
|| []
-        if (this.resource.state === 'Running' && this.resource.hypervisor === 
'KVM') {
-          this.offerings = this.offerings.filter(offering => offering.id === 
this.resource.serviceofferingid)
-          this.currentOffer = this.offerings[0]
-          if (this.currentOffer === undefined) {
-            this.fixedOfferingKvm = true
-          }
-        }
-        this.offerings.map(i => { this.offeringsMap[i.id] = i })
+        this.offerings.forEach(offering => { this.offeringsMap[offering.id] = 
offering })
       }).finally(() => {
         this.loading = false
       })
     },
     getMinCpu () {
-      // We can only scale up while a VM is running
-      if (this.resource.state === 'Running') {
-        return this.resource.cpunumber
-      }
       return this.selectedOffering?.serviceofferingdetails?.mincpunumber * 1 
|| 1
     },
-    getMinMemory () {
-      // We can only scale up while a VM is running
-      if (this.resource.state === 'Running') {
-        return this.resource.memory
+    getInitialCpuValue () {
+      const offeringMinCpu = this.getMinCpu()
+      if (this.resource.cpunumber < offeringMinCpu) {
+        return offeringMinCpu
       }
+      return this.resource.cpunumber
+    },
+    getMinMemory () {
       return this.selectedOffering?.serviceofferingdetails?.minmemory * 1 || 32
     },
+    getInitialMemoryValue () {
+      const offeringMinMemory = this.getMinMemory()
+      if (this.resource.memory < offeringMinMemory) {
+        return offeringMinMemory
+      }
+      return this.resource.memory
+    },
     getCPUSpeed () {
       // We can only scale up while a VM is running
       if (this.resource.state === 'Running') {
diff --git a/ui/src/views/compute/wizard/ComputeSelection.vue 
b/ui/src/views/compute/wizard/ComputeSelection.vue
index 563e17984e3..9cf36153f5c 100644
--- a/ui/src/views/compute/wizard/ComputeSelection.vue
+++ b/ui/src/views/compute/wizard/ComputeSelection.vue
@@ -111,6 +111,10 @@ export default {
       type: Number,
       default: 0
     },
+    initialCpuValue: {
+      type: Number,
+      default: 0
+    },
     minCpu: {
       type: Number,
       default: 0
@@ -119,6 +123,10 @@ export default {
       type: Number,
       default: 2
     },
+    initialMemoryValue: {
+      type: Number,
+      default: 0
+    },
     minMemory: {
       type: Number,
       default: 0
@@ -200,8 +208,8 @@ export default {
   },
   methods: {
     fillValue () {
-      this.cpuNumberInputValue = this.minCpu
-      this.memoryInputValue = this.minMemory
+      this.cpuNumberInputValue = this.initialCpuValue > 0 ? 
this.initialCpuValue : this.minCpu
+      this.memoryInputValue = this.initialMemoryValue > 0 ? 
this.initialMemoryValue : this.minMemory
       this.cpuSpeedInputValue = this.cpuSpeed
 
       if (!this.preFillContent) {

Reply via email to