You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by da...@apache.org on 2021/08/21 07:29:32 UTC

[cloudstack] branch main updated: Support vm dynamic scaling with kvm (#4878)

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

dahn 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 8a16729  Support vm dynamic scaling with kvm (#4878)
8a16729 is described below

commit 8a16729fcf10d9e6952fcccc07d8d17952fa6312
Author: Daniel Augusto Veronezi Salvador <38...@users.noreply.github.com>
AuthorDate: Sat Aug 21 04:29:02 2021 -0300

    Support vm dynamic scaling with kvm (#4878)
    
    * Create utility to centralize byte convertions
    
    * Add/change toString definitions
    
    * Create Libvirt handler to ScaleVmCommand
    
    * Enable dynamic scalling VM with KVM
    
    * Move config from interface to class and rename it
    
    As every variable declared in interfaces are already final,
    this moving will be needed to mock tests in nexts commits
    
    * Configure VM max memory and cpu cores
    
    The values are according to service offering or global configs
    
    * Extract dpdk configuration to a method and test it
    
    * Extract OS desc config to a method and test it
    
    * Extract guest resource def to a method and test it
    
    Improve libvirt def
    
    * Refactor LibvirtVMDef.GuestResourceDef
    
    * Refactor ScaleVmCommand
    
    * Improve VMInstaVO toString()
    
    * Refactor upgradeRunningVirtualMachine method
    
    * Turn int variables into long on utility
    
    * Verify if VM is scalable on KVMGuru
    
    * Rename some KVMGuruTest's methods
    
    * Change vm's xml to work with max memory
    
    * Verify if service offering is dynamic before scale
    
    * Create methods to retrieve data from domain
    
    * Create def to hotplug memory
    
    * Adjust the way command was scaling the VM
    
    * Fix database persistence before executing command
    
    * Send more info to host to improve log
    
    * Fix var name
    
    * Fix missing "}"
    
    * Undo unnecessary changes
    
    * Address review
    
    * Fix scale validation
    
    * Add VM prepared for dynamic scaling validation
    
    * Refactor LibvirtScaleVmCommandWrapper and improve unit tests
    
    * Remove duplicated method
    
    * Add RuntimeException check
    
    * Remove copyright from header
    
    * Remove copyright from header
    
    * Remove copyright from header
    
    * Remove copyright from header
    
    * Remove copyright from header
    
    * Update ByteScaleUtilsTest.java
    
    Co-authored-by: Daniel Augusto Veronezi Salvador <da...@scclouds.com.br>
---
 .../com/cloud/agent/api/to/VirtualMachineTO.java   |   4 +
 .../java/com/cloud/agent/api/ScaleVmCommand.java   |   3 -
 .../java/com/cloud/resource/CommandWrapper.java    |   2 +
 .../java/com/cloud/vm/VirtualMachineManager.java   |  16 -
 .../com/cloud/vm/VirtualMachineManagerImpl.java    |  47 ++-
 .../src/main/java/com/cloud/host/HostVO.java       |   2 +-
 .../java/com/cloud/service/ServiceOfferingVO.java  |   4 +
 .../kvm/resource/LibvirtComputingResource.java     |  69 ++++-
 .../hypervisor/kvm/resource/LibvirtVMDef.java      |  61 ++--
 .../kvm/resource/LibvirtVmMemoryDeviceDef.java     |  43 +++
 .../wrapper/LibvirtScaleVmCommandWrapper.java      | 103 +++++++
 .../kvm/resource/LibvirtComputingResourceTest.java |  96 +++++-
 .../kvm/resource/LibvirtVmMemoryDeviceDefTest.java |  41 +++
 .../wrapper/LibvirtScaleVmCommandWrapperTest.java  | 244 ++++++++++++++++
 .../configuration/ConfigurationManagerImpl.java    |  15 +-
 .../main/java/com/cloud/hypervisor/KVMGuru.java    | 159 +++++++++-
 .../main/java/com/cloud/vm/UserVmManagerImpl.java  |  63 ++--
 .../java/com/cloud/hypervisor/KVMGuruTest.java     | 323 +++++++++++++++++++++
 .../cloudstack/utils/bytescale/ByteScaleUtils.java |  47 +++
 .../utils/bytescale/ByteScaleUtilsTest.java        |  51 ++++
 20 files changed, 1261 insertions(+), 132 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 8a30b5e..5fc2483 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
@@ -146,6 +146,10 @@ public class VirtualMachineTO {
         return type;
     }
 
+    public void setType(Type type) {
+        this.type = type;
+    }
+
     public BootloaderType getBootloader() {
         return bootloader;
     }
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 1c763dd..dc63b1e 100644
--- a/core/src/main/java/com/cloud/agent/api/ScaleVmCommand.java
+++ b/core/src/main/java/com/cloud/agent/api/ScaleVmCommand.java
@@ -52,9 +52,6 @@ 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);
-        /*vm.setName(vmName);
-        vm.setCpus(cpus);
-        vm.setRam(minRam, maxRam);*/
     }
 
     public void setCpus(int cpus) {
diff --git a/core/src/main/java/com/cloud/resource/CommandWrapper.java b/core/src/main/java/com/cloud/resource/CommandWrapper.java
index f859699..2145750 100644
--- a/core/src/main/java/com/cloud/resource/CommandWrapper.java
+++ b/core/src/main/java/com/cloud/resource/CommandWrapper.java
@@ -21,8 +21,10 @@ package com.cloud.resource;
 
 import com.cloud.agent.api.Answer;
 import com.cloud.agent.api.Command;
+import org.apache.log4j.Logger;
 
 public abstract class CommandWrapper<T extends Command, A extends Answer, R extends ServerResource> {
+    protected Logger logger = Logger.getLogger(getClass());
 
     /**
      * @param T is the command to be used.
diff --git a/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java b/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java
index e7828fa..68183ad 100644
--- a/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java
+++ b/engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java
@@ -76,22 +76,6 @@ public interface VirtualMachineManager extends Manager {
     ConfigKey<Boolean> AllowExposeHypervisorHostname = new ConfigKey<Boolean>("Advanced", Boolean.class, "global.allow.expose.host.hostname",
             "false", "If set to true, it allows the hypervisor host name on which the VM is spawned on to be exposed to the VM", true, ConfigKey.Scope.Global);
 
-    static final ConfigKey<Integer> VmServiceOfferingMaxCPUCores = new ConfigKey<Integer>("Advanced",
-            Integer.class,
-            "vm.serviceoffering.cpu.cores.max",
-            "0",
-            "Maximum CPU cores for vm service offering. If 0 - no limitation",
-            true
-    );
-
-    static final ConfigKey<Integer> VmServiceOfferingMaxRAMSize = new ConfigKey<Integer>("Advanced",
-            Integer.class,
-            "vm.serviceoffering.ram.size.max",
-            "0",
-            "Maximum RAM size in MB for vm service offering. If 0 - no limitation",
-            true
-    );
-
     interface Topics {
         String VM_POWER_STATE = "vm.powerstate";
     }
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 eca0852..1c77295 100755
--- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
+++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
@@ -4665,6 +4665,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
                     throw (ConcurrentOperationException)jobResult;
                 } else if (jobResult instanceof InsufficientServerCapacityException) {
                     throw (InsufficientServerCapacityException)jobResult;
+                } else if (jobResult instanceof RuntimeException) {
+                    throw (RuntimeException)jobResult;
                 } else if (jobResult instanceof Throwable) {
                     s_logger.error("Unhandled exception", (Throwable)jobResult);
                     throw new RuntimeException("Unhandled exception", (Throwable)jobResult);
@@ -4677,8 +4679,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
 
     private VMInstanceVO orchestrateReConfigureVm(String vmUuid, ServiceOffering oldServiceOffering, ServiceOffering newServiceOffering,
                                                   boolean reconfiguringOnExistingHost) throws ResourceUnavailableException, ConcurrentOperationException {
-        VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
-        upgradeVmDb(vm.getId(), newServiceOffering, oldServiceOffering);
+        final VMInstanceVO vm = _vmDao.findByUuid(vmUuid);
 
         HostVO hostVo = _hostDao.findById(vm.getHostId());
 
@@ -4695,6 +4696,10 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
                 new ScaleVmCommand(vm.getInstanceName(), newServiceOffering.getCpu(), minSpeed,
                         newServiceOffering.getSpeed(), minMemory * 1024L * 1024L, newServiceOffering.getRamSize() * 1024L * 1024L, newServiceOffering.getLimitCpuUse());
 
+        scaleVmCommand.getVirtualMachine().setId(vm.getId());
+        scaleVmCommand.getVirtualMachine().setUuid(vm.getUuid());
+        scaleVmCommand.getVirtualMachine().setType(vm.getType());
+
         Long dstHostId = vm.getHostId();
 
         if (vm.getHypervisorType().equals(HypervisorType.VMware)) {
@@ -4710,9 +4715,20 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
         work.setResourceId(vm.getHostId());
         _workDao.persist(work);
 
-        boolean success = false;
-
         try {
+            Answer reconfigureAnswer = _agentMgr.send(vm.getHostId(), scaleVmCommand);
+
+            if (reconfigureAnswer == null || !reconfigureAnswer.getResult()) {
+                s_logger.error("Unable to scale vm due to " + (reconfigureAnswer == null ? "" : reconfigureAnswer.getDetails()));
+                throw new CloudRuntimeException("Unable to scale vm due to " + (reconfigureAnswer == null ? "" : reconfigureAnswer.getDetails()));
+            }
+
+            if (vm.getType().equals(VirtualMachine.Type.User)) {
+                _userVmMgr.generateUsageEvent(vm, vm.isDisplayVm(), EventTypes.EVENT_VM_DYNAMIC_SCALE);
+            }
+
+            upgradeVmDb(vm.getId(), newServiceOffering, oldServiceOffering);
+
             if (reconfiguringOnExistingHost) {
                 vm.setServiceOfferingId(oldServiceOffering.getId());
                 _capacityMgr.releaseVmCapacity(vm, false, false, vm.getHostId()); //release the old capacity
@@ -4720,26 +4736,10 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
                 _capacityMgr.allocateVmCapacity(vm, false); // lock the new capacity
             }
 
-            Answer scaleVmAnswer = _agentMgr.send(vm.getHostId(), scaleVmCommand);
-            if (scaleVmAnswer == null || !scaleVmAnswer.getResult()) {
-                String msg = String.format("Unable to scale %s due to [%s].", vm.toString(), (scaleVmAnswer == null ? "" : scaleVmAnswer.getDetails()));
-                s_logger.error(msg);
-                throw new CloudRuntimeException(msg);
-            }
-            if (vm.getType().equals(VirtualMachine.Type.User)) {
-                _userVmMgr.generateUsageEvent(vm, vm.isDisplayVm(), EventTypes.EVENT_VM_DYNAMIC_SCALE);
-            }
-            success = true;
-        } catch (OperationTimedoutException e) {
-            throw new AgentUnavailableException(String.format("Unable to scale %s due to [%s].", vm.toString(), e.getMessage()), dstHostId, e);
+        } catch (final OperationTimedoutException e) {
+            throw new AgentUnavailableException("Operation timed out on reconfiguring " + vm, dstHostId);
         } catch (final AgentUnavailableException e) {
             throw e;
-        } finally {
-            if (!success) {
-                _capacityMgr.releaseVmCapacity(vm, false, false, vm.getHostId()); // release the new capacity
-                upgradeVmDb(vm.getId(), oldServiceOffering, newServiceOffering); // rollback
-                _capacityMgr.allocateVmCapacity(vm, false); // allocate the old capacity
-            }
         }
 
         return vm;
@@ -4783,8 +4783,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
         return new ConfigKey<?>[] { ClusterDeltaSyncInterval, StartRetry, VmDestroyForcestop, VmOpCancelInterval, VmOpCleanupInterval, VmOpCleanupWait,
                 VmOpLockStateRetry, VmOpWaitInterval, ExecuteInSequence, VmJobCheckInterval, VmJobTimeout, VmJobStateReportInterval,
                 VmConfigDriveLabel, VmConfigDriveOnPrimaryPool, VmConfigDriveForceHostCacheUse, VmConfigDriveUseHostCacheOnUnsupportedPool,
-                HaVmRestartHostUp, ResoureCountRunningVMsonly, AllowExposeHypervisorHostname, AllowExposeHypervisorHostnameAccountLevel,
-                VmServiceOfferingMaxCPUCores, VmServiceOfferingMaxRAMSize };
+                HaVmRestartHostUp, ResoureCountRunningVMsonly, AllowExposeHypervisorHostname, AllowExposeHypervisorHostnameAccountLevel };
     }
 
     public List<StoragePoolAllocator> getStoragePoolAllocators() {
diff --git a/engine/schema/src/main/java/com/cloud/host/HostVO.java b/engine/schema/src/main/java/com/cloud/host/HostVO.java
index 1931318..776e48c 100644
--- a/engine/schema/src/main/java/com/cloud/host/HostVO.java
+++ b/engine/schema/src/main/java/com/cloud/host/HostVO.java
@@ -680,7 +680,7 @@ public class HostVO implements Host {
 
     @Override
     public String toString() {
-        return String.format("Host [{id: \"%s\", name: \"%s\", uuid: \"%s\", type=\"%s\"}]", id, name, uuid, type);
+        return String.format("Host {\"id\": \"%s\", \"name\": \"%s\", \"uuid\": \"%s\", \"type\"=\"%s\"}", id, name, uuid, type);
     }
 
     public void setHypervisorType(HypervisorType hypervisorType) {
diff --git a/engine/schema/src/main/java/com/cloud/service/ServiceOfferingVO.java b/engine/schema/src/main/java/com/cloud/service/ServiceOfferingVO.java
index 44f39a1..a236231 100644
--- a/engine/schema/src/main/java/com/cloud/service/ServiceOfferingVO.java
+++ b/engine/schema/src/main/java/com/cloud/service/ServiceOfferingVO.java
@@ -298,6 +298,10 @@ public class ServiceOfferingVO extends DiskOfferingVO implements ServiceOffering
     }
 
     @Override
+    public String toString() {
+        return String.format("Service offering {\"id\": %s, \"name\": \"%s\", \"uuid\": \"%s\"}", getId(), getName(), getUuid());
+    }
+
     public boolean isDynamicScalingEnabled() {
         return dynamicScalingEnabled;
     }
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 df729cb..2394307 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
@@ -185,6 +185,8 @@ import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.VirtualMachine.PowerState;
 import com.cloud.vm.VmDetailConstants;
 import com.google.common.base.Strings;
+import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
+import org.libvirt.VcpuInfo;
 
 /**
  * LibvirtComputingResource execute requests on the computing/routing host using
@@ -2534,21 +2536,6 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
         return cmd;
     }
 
-    /**
-     * Creates guest resources based in VM specification.
-     */
-    protected GuestResourceDef createGuestResourceDef(VirtualMachineTO vmTO) {
-        GuestResourceDef grd = new GuestResourceDef();
-
-        grd.setMemorySize(vmTO.getMaxRam() / 1024);
-        if (vmTO.getMinRam() != vmTO.getMaxRam() && !_noMemBalloon) {
-            grd.setMemBalloning(true);
-            grd.setCurrentMem(vmTO.getMinRam() / 1024);
-        }
-        grd.setVcpuNum(vmTO.getCpus());
-        return grd;
-    }
-
     private void configureGuestIfUefiEnabled(boolean isSecureBoot, String bootMode, GuestDef guest) {
         setGuestLoader(bootMode, SECURE, guest, GuestDef.GUEST_LOADER_SECURE);
         setGuestLoader(bootMode, LEGACY, guest, GuestDef.GUEST_LOADER_LEGACY);
@@ -2629,6 +2616,37 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
     }
 
     /**
+     * Creates guest resources based in VM specification.
+     */
+    protected GuestResourceDef createGuestResourceDef(VirtualMachineTO vmTO){
+        GuestResourceDef grd = new GuestResourceDef();
+
+        grd.setMemBalloning(!_noMemBalloon);
+
+        Long maxRam = ByteScaleUtils.bytesToKib(vmTO.getMaxRam());
+
+        grd.setMemorySize(maxRam);
+        grd.setCurrentMem(getCurrentMemAccordingToMemBallooning(vmTO, maxRam));
+
+        int vcpus = vmTO.getCpus();
+        Integer maxVcpus = vmTO.getVcpuMaxLimit();
+
+        grd.setVcpuNum(vcpus);
+        grd.setMaxVcpuNum(maxVcpus == null ? vcpus : maxVcpus);
+
+        return grd;
+    }
+
+    protected long getCurrentMemAccordingToMemBallooning(VirtualMachineTO vmTO, long maxRam) {
+        if (_noMemBalloon) {
+            s_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));
+            return maxRam;
+        } else {
+            return ByteScaleUtils.bytesToKib(vmTO.getMinRam());
+        }
+    }
+
+    /**
      * Adds extra configurations (if any) as a String component to the domain XML
      */
     protected void addExtraConfigComponent(Map<String, String> extraConfig, LibvirtVMDef vm) {
@@ -4556,4 +4574,25 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
         }
     }
 
+    /**
+     * Retrieves the memory of the running VM. <br/>
+     * The libvirt (see <a href="https://github.com/libvirt/libvirt/blob/master/src/conf/domain_conf.c">https://github.com/libvirt/libvirt/blob/master/src/conf/domain_conf.c</a>, function <b>virDomainDefParseMemory</b>) uses <b>total memory</b> as the tag <b>memory</b>, in VM's XML.
+     * @param dm domain of the VM.
+     * @return the memory of the VM.
+     * @throws org.libvirt.LibvirtException
+     **/
+    public static long getDomainMemory(Domain dm) throws LibvirtException {
+        return dm.getMaxMemory();
+    }
+
+    /**
+     * Retrieves the quantity of running VCPUs of the running VM. <br/>
+     * @param dm domain of the VM.
+     * @return the quantity of running VCPUs of the running VM.
+     * @throws org.libvirt.LibvirtException
+     **/
+    public static long countDomainRunningVcpus(Domain dm) throws LibvirtException {
+        VcpuInfo vcpus[] = dm.getVcpusInfo();
+        return Arrays.stream(vcpus).filter(vcpu -> vcpu.state.equals(VcpuInfo.VcpuState.VIR_VCPU_RUNNING)).count();
+    }
 }
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 c762cbf..78d1744 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
@@ -230,51 +230,54 @@ public class LibvirtVMDef {
     }
 
     public static class GuestResourceDef {
-        private long _mem;
-        private long _currentMem = -1;
-        private String _memBacking;
-        private int _vcpu = -1;
-        private boolean _memBalloning = false;
+        private long memory;
+        private long currentMemory = -1;
+        private int vcpu = -1;
+        private int maxVcpu = -1;
+        private boolean memoryBalloning = false;
 
         public void setMemorySize(long mem) {
-            _mem = mem;
+            this.memory = mem;
         }
 
         public void setCurrentMem(long currMem) {
-            _currentMem = currMem;
+            this.currentMemory = currMem;
         }
 
-        public void setMemBacking(String memBacking) {
-            _memBacking = memBacking;
+        public void setVcpuNum(int vcpu) {
+            this.vcpu = vcpu;
         }
 
-        public void setVcpuNum(int vcpu) {
-            _vcpu = vcpu;
+        public void setMaxVcpuNum(int maxVcpu) {
+            this.maxVcpu = maxVcpu;
+        }
+
+        public int getVcpu() {
+            return vcpu;
+        }
+
+        public int getMaxVcpu() {
+            return maxVcpu;
         }
 
-        public void setMemBalloning(boolean turnon) {
-            _memBalloning = turnon;
+        public void setMemBalloning(boolean memoryBalloning) {
+            this.memoryBalloning = memoryBalloning;
         }
 
         @Override
         public String toString() {
-            StringBuilder resBuidler = new StringBuilder();
-            resBuidler.append("<memory>" + _mem + "</memory>\n");
-            if (_currentMem != -1) {
-                resBuidler.append("<currentMemory>" + _currentMem + "</currentMemory>\n");
-            }
-            if (_memBacking != null) {
-                resBuidler.append("<memoryBacking>" + "<" + _memBacking + "/>" + "</memoryBacking>\n");
-            }
-            if (_memBalloning) {
-                resBuidler.append("<devices>\n" + "<memballoon model='virtio'/>\n" + "</devices>\n");
-            } else {
-                resBuidler.append("<devices>\n" + "<memballoon model='none'/>\n" + "</devices>\n");
-            }
-            if (_vcpu != -1) {
-                resBuidler.append("<vcpu>" + _vcpu + "</vcpu>\n");
+            StringBuilder response = new StringBuilder();
+            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));
+                response.append(String.format("<cpu> <numa> <cell id='0' cpus='0-%s' memory='%s' unit='KiB'/> </numa> </cpu>\n", this.maxVcpu - 1, this.currentMemory));
             }
-            return resBuidler.toString();
+
+            response.append(String.format("<devices>\n<memballoon model='%s'/>\n</devices>\n", this.memoryBalloning ? "virtio" : "none"));
+            response.append(String.format("<vcpu current=\"%s\">%s</vcpu>\n", this.vcpu, this.maxVcpu));
+            return response.toString();
         }
     }
 
diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVmMemoryDeviceDef.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVmMemoryDeviceDef.java
new file mode 100644
index 0000000..1865a6d
--- /dev/null
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtVmMemoryDeviceDef.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.cloud.hypervisor.kvm.resource;
+
+/**
+ * Provides the XML definition to a memory device which can be hotpluged to the VM.<br/>
+ * Memory is provided in KiB.
+ *
+ */
+public class LibvirtVmMemoryDeviceDef {
+
+    private final long memorySize;
+
+    public LibvirtVmMemoryDeviceDef(long memorySize) {
+        this.memorySize = memorySize;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder response = new StringBuilder();
+        response.append("<memory model='dimm'>");
+        response.append("<target>");
+        response.append(String.format("<size unit='KiB'>%s</size>", memorySize));
+        response.append("<node>0</node>");
+        response.append("</target>");
+        response.append("</memory>");
+
+        return response.toString();
+    }
+
+}
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
new file mode 100644
index 0000000..384d5cc
--- /dev/null
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtScaleVmCommandWrapper.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.cloud.hypervisor.kvm.resource.wrapper;
+
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.ScaleVmAnswer;
+import com.cloud.agent.api.ScaleVmCommand;
+import com.cloud.agent.api.to.VirtualMachineTO;
+import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
+import com.cloud.hypervisor.kvm.resource.LibvirtVmMemoryDeviceDef;
+import com.cloud.resource.CommandWrapper;
+import com.cloud.resource.ResourceWrapper;
+import com.cloud.utils.exception.CloudRuntimeException;
+import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
+import org.libvirt.Connect;
+import org.libvirt.Domain;
+import org.libvirt.LibvirtException;
+
+@ResourceWrapper(handles = ScaleVmCommand.class)
+public class LibvirtScaleVmCommandWrapper extends CommandWrapper<ScaleVmCommand, Answer, LibvirtComputingResource> {
+
+    @Override
+    public Answer execute(ScaleVmCommand command, LibvirtComputingResource libvirtComputingResource) {
+        VirtualMachineTO vmSpec = command.getVirtualMachine();
+        String vmName = vmSpec.getName();
+        Connect conn = null;
+
+        long newMemory = ByteScaleUtils.bytesToKib(vmSpec.getMaxRam());
+        int newVcpus = vmSpec.getCpus();
+        String vmDefinition = vmSpec.toString();
+        String scalingDetails = String.format("%s memory to [%s KiB] and CPU cores to [%s]", vmDefinition, newMemory, newVcpus);
+
+        try {
+            LibvirtUtilitiesHelper libvirtUtilitiesHelper = libvirtComputingResource.getLibvirtUtilitiesHelper();
+
+            conn = libvirtUtilitiesHelper.getConnectionByVmName(vmName);
+            Domain dm = conn.domainLookupByName(vmName);
+
+            logger.debug(String.format("Scaling %s.", scalingDetails));
+            scaleMemory(dm, newMemory, vmDefinition);
+            scaleVcpus(dm, newVcpus, vmDefinition);
+
+            return new ScaleVmAnswer(command, true, String.format("Successfully scaled %s.", scalingDetails));
+        } catch (LibvirtException | CloudRuntimeException e) {
+            String message = String.format("Unable to scale %s due to [%s].", scalingDetails, e.getMessage());
+            logger.error(message, e);
+            return new ScaleVmAnswer(command, false, message);
+        } finally {
+            if (conn != null) {
+                try {
+                    conn.close();
+                } catch (LibvirtException ex) {
+                    logger.warn(String.format("Error trying to close libvirt connection [%s]", ex.getMessage()), ex);
+                }
+            }
+        }
+    }
+
+    protected void scaleVcpus(Domain dm, int newVcpus, String vmDefinition) throws LibvirtException {
+        long runningVcpus = LibvirtComputingResource.countDomainRunningVcpus(dm);
+
+        if (runningVcpus < newVcpus) {
+            dm.setVcpus(newVcpus);
+            return;
+        }
+
+        logger.info(String.format("Not scaling the CPU cores. To scale the CPU cores of the %s, the new CPU count [%s] must be higher than the current CPU count [%s].",
+            vmDefinition, newVcpus, runningVcpus));
+    }
+
+    protected void scaleMemory(Domain dm, long newMemory, String vmDefinition) throws LibvirtException, CloudRuntimeException {
+        long currentMemory = LibvirtComputingResource.getDomainMemory(dm);
+        long memoryToAttach = newMemory - currentMemory;
+
+        if (memoryToAttach <= 0) {
+            logger.info(String.format("Not scaling the memory. To scale the memory of the %s, the new memory [%s] must be higher than the current memory [%s]. The current "
+              + "difference is [%s].", vmDefinition, newMemory, currentMemory, memoryToAttach));
+            return;
+        }
+
+        if (!dm.getXMLDesc(0).contains("<maxMemory slots='16' unit='KiB'>")) {
+            throw new CloudRuntimeException(String.format("The %s is not prepared for dynamic scaling. To be prepared, the VM must be deployed with a dynamic service offering,"
+              + " VM dynamic scale enabled and global setting \"enable.dynamic.scale.vm\" as \"true\". If you changed one of these settings after deploying the VM,"
+              + " consider stopping and starting it again to prepared it to dynamic scaling.", vmDefinition));
+        }
+
+        String memoryDevice = new LibvirtVmMemoryDeviceDef(memoryToAttach).toString();
+        logger.debug(String.format("Attaching memory device [%s] to %s.", memoryDevice, vmDefinition));
+        dm.attachDevice(memoryDevice);
+    }
+}
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 86f30bf..fbf1ec6 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
@@ -206,6 +206,8 @@ import com.cloud.vm.DiskProfile;
 import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.VirtualMachine.PowerState;
 import com.cloud.vm.VirtualMachine.Type;
+import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
+import org.libvirt.VcpuInfo;
 
 @RunWith(PowerMockRunner.class)
 @PrepareForTest(value = {MemStat.class})
@@ -221,6 +223,9 @@ public class LibvirtComputingResourceTest {
     @Spy
     private LibvirtComputingResource libvirtComputingResourceSpy = Mockito.spy(LibvirtComputingResource.class);
 
+    @Mock
+    Domain domainMock;
+
     private final static long HYPERVISOR_LIBVIRT_VERSION_SUPPORTS_IOURING = 6003000;
     private final static long HYPERVISOR_QEMU_VERSION_SUPPORTS_IOURING = 5000000;
 
@@ -328,6 +333,7 @@ public class LibvirtComputingResourceTest {
         final VirtualMachineTO to = new VirtualMachineTO(id, name, VirtualMachine.Type.User, cpus, minSpeed, maxSpeed, minRam, maxRam, BootloaderType.HVM, os, false, false, vncPassword);
         to.setVncAddr(vncAddr);
         to.setUuid("b0f0a72d-7efb-3cad-a8ff-70ebf30b3af9");
+        to.setVcpuMaxLimit(cpus + 1);
 
         LibvirtVMDef vm = libvirtComputingResourceSpy.createVMFromSpec(to);
         vm.setHvsType(hyperVisorType);
@@ -710,11 +716,15 @@ public class LibvirtComputingResourceTest {
     }
 
     private void verifyVcpu(VirtualMachineTO to, Document domainDoc) {
-        assertXpath(domainDoc, "/domain/vcpu/text()", String.valueOf(to.getCpus()));
+        assertXpath(domainDoc, "/domain/cpu/numa/cell/@cpus", String.format("0-%s", to.getVcpuMaxLimit() - 1));
+        assertXpath(domainDoc, "/domain/vcpu/@current", String.valueOf(to.getCpus()));
+        assertXpath(domainDoc, "/domain/vcpu/text()", String.valueOf(to.getVcpuMaxLimit()));
     }
 
     private void verifyMemory(VirtualMachineTO to, Document domainDoc, String minRam) {
-        assertXpath(domainDoc, "/domain/memory/text()", String.valueOf(to.getMaxRam() / 1024));
+        assertXpath(domainDoc, "/domain/maxMemory/text()", String.valueOf( to.getMaxRam() / 1024 ));
+        assertXpath(domainDoc, "/domain/memory/text()",minRam);
+        assertXpath(domainDoc, "/domain/cpu/numa/cell/@memory", minRam);
         assertXpath(domainDoc, "/domain/currentMemory/text()", minRam);
     }
 
@@ -5620,7 +5630,89 @@ public class LibvirtComputingResourceTest {
         Mockito.verify(vmDef, times(1)).addComp(any());
     }
 
+    public void validateGetCurrentMemAccordingToMemBallooningWithoutMemBalooning(){
+        VirtualMachineTO vmTo = Mockito.mock(VirtualMachineTO.class);
+        LibvirtComputingResource libvirtComputingResource = new LibvirtComputingResource();
+        libvirtComputingResource._noMemBalloon = true;
+        long maxMemory = 2048;
+
+        long currentMemory = libvirtComputingResource.getCurrentMemAccordingToMemBallooning(vmTo, maxMemory);
+        Assert.assertEquals(maxMemory, currentMemory);
+        Mockito.verify(vmTo, Mockito.times(0)).getMinRam();
+    }
+
     @Test
+    public void validateGetCurrentMemAccordingToMemBallooningWithtMemBalooning(){
+        LibvirtComputingResource libvirtComputingResource = new LibvirtComputingResource();
+        libvirtComputingResource._noMemBalloon = false;
+
+        long maxMemory = 2048;
+        long minMemory = ByteScaleUtils.mibToBytes(64);
+
+        VirtualMachineTO vmTo = Mockito.mock(VirtualMachineTO.class);
+        Mockito.when(vmTo.getMinRam()).thenReturn(minMemory);
+
+        long currentMemory = libvirtComputingResource.getCurrentMemAccordingToMemBallooning(vmTo, maxMemory);
+        Assert.assertEquals(ByteScaleUtils.bytesToKib(minMemory), currentMemory);
+        Mockito.verify(vmTo).getMinRam();
+    }
+
+    @Test
+    public void validateCreateGuestResourceDefWithVcpuMaxLimit(){
+        LibvirtComputingResource libvirtComputingResource = new LibvirtComputingResource();
+        VirtualMachineTO vmTo = Mockito.mock(VirtualMachineTO.class);
+        int maxCpu = 16;
+
+        Mockito.when(vmTo.getVcpuMaxLimit()).thenReturn(maxCpu);
+
+        LibvirtVMDef.GuestResourceDef grd = libvirtComputingResource.createGuestResourceDef(vmTo);
+        Assert.assertEquals(maxCpu, grd.getMaxVcpu());
+    }
+
+    @Test
+    public void validateCreateGuestResourceDefWithVcpuMaxLimitAsNull(){
+        LibvirtComputingResource libvirtComputingResource = new LibvirtComputingResource();
+        VirtualMachineTO vmTo = Mockito.mock(VirtualMachineTO.class);
+        int min = 1;
+
+        Mockito.when(vmTo.getCpus()).thenReturn(min);
+        Mockito.when(vmTo.getVcpuMaxLimit()).thenReturn(null);
+
+        LibvirtVMDef.GuestResourceDef grd = libvirtComputingResource.createGuestResourceDef(vmTo);
+        Assert.assertEquals(min, grd.getMaxVcpu());
+    }
+
+    @Test
+    public void validateGetDomainMemory() throws LibvirtException{
+        long valueExpected = ByteScaleUtils.KiB;
+
+        Mockito.doReturn(valueExpected).when(domainMock).getMaxMemory();
+        Assert.assertEquals(valueExpected, LibvirtComputingResource.getDomainMemory(domainMock));
+    }
+
+    private VcpuInfo createVcpuInfoWithState(VcpuInfo.VcpuState state) {
+        VcpuInfo vcpu = new VcpuInfo();
+        vcpu.state = state;
+        return vcpu;
+    }
+
+    @Test
+    public void validateCountDomainRunningVcpus() throws LibvirtException{
+        VcpuInfo vcpus[] = new VcpuInfo[5];
+        long valueExpected = 3; // 3 vcpus with state VIR_VCPU_RUNNING
+
+        vcpus[0] = createVcpuInfoWithState(VcpuInfo.VcpuState.VIR_VCPU_BLOCKED);
+        vcpus[1] = createVcpuInfoWithState(VcpuInfo.VcpuState.VIR_VCPU_OFFLINE);
+        vcpus[2] = createVcpuInfoWithState(VcpuInfo.VcpuState.VIR_VCPU_RUNNING);
+        vcpus[3] = createVcpuInfoWithState(VcpuInfo.VcpuState.VIR_VCPU_RUNNING);
+        vcpus[4] = createVcpuInfoWithState(VcpuInfo.VcpuState.VIR_VCPU_RUNNING);
+
+        Mockito.doReturn(vcpus).when(domainMock).getVcpusInfo();
+        long result =  LibvirtComputingResource.countDomainRunningVcpus(domainMock);
+
+        Assert.assertEquals(valueExpected, result);
+    }
+
     public void setDiskIoDriverTestIoUring() {
         DiskDef diskDef = configureAndTestSetDiskIoDriverTest(HYPERVISOR_LIBVIRT_VERSION_SUPPORTS_IOURING, HYPERVISOR_QEMU_VERSION_SUPPORTS_IOURING);
         Assert.assertEquals(DiskDef.IoDriver.IOURING, diskDef.getIoDriver());
diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtVmMemoryDeviceDefTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtVmMemoryDeviceDefTest.java
new file mode 100644
index 0000000..aee4f36
--- /dev/null
+++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/LibvirtVmMemoryDeviceDefTest.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.cloud.hypervisor.kvm.resource;
+
+import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class LibvirtVmMemoryDeviceDefTest {
+
+    @Test
+    public void validateToString(){
+        long memorySize = ByteScaleUtils.KiB;
+
+        StringBuilder expectedToString = new StringBuilder();
+        expectedToString.append("<memory model='dimm'>");
+        expectedToString.append("<target>");
+        expectedToString.append(String.format("<size unit='KiB'>%s</size>", memorySize));
+        expectedToString.append("<node>0</node>");
+        expectedToString.append("</target>");
+        expectedToString.append("</memory>");
+
+        Assert.assertEquals(expectedToString.toString(), new LibvirtVmMemoryDeviceDef(memorySize).toString());
+    }
+
+}
diff --git a/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtScaleVmCommandWrapperTest.java b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtScaleVmCommandWrapperTest.java
new file mode 100644
index 0000000..a0851e7
--- /dev/null
+++ b/plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtScaleVmCommandWrapperTest.java
@@ -0,0 +1,244 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.cloud.hypervisor.kvm.resource.wrapper;
+
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.ScaleVmCommand;
+import com.cloud.agent.api.to.VirtualMachineTO;
+import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
+import com.cloud.template.VirtualMachineTemplate;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.VirtualMachine;
+import junit.framework.TestCase;
+import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.libvirt.Connect;
+import org.libvirt.Domain;
+import org.libvirt.LibvirtException;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.Spy;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest(LibvirtComputingResource.class)
+public class LibvirtScaleVmCommandWrapperTest extends TestCase {
+
+    @Spy
+    LibvirtScaleVmCommandWrapper libvirtScaleVmCommandWrapperSpy = Mockito.spy(LibvirtScaleVmCommandWrapper.class);
+
+    @Mock
+    LibvirtComputingResource libvirtComputingResourceMock;
+
+    @Mock
+    ScaleVmCommand scaleVmCommandMock;
+
+    @Mock
+    LibvirtUtilitiesHelper libvirtUtilitiesHelperMock;
+
+    @Mock
+    Domain domainMock;
+
+    @Mock
+    Connect connectMock;
+
+    @Mock
+    LibvirtException libvirtException;
+
+    @Mock
+    Exception exception;
+
+    LibvirtRequestWrapper wrapper;
+    VirtualMachineTO vmTo;
+
+    String scalingDetails;
+
+    @Before
+    public void init() {
+        wrapper = LibvirtRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        vmTo = new VirtualMachineTO(1, "Test 1", VirtualMachine.Type.User, 2, 1000, 67108864, 67108864, VirtualMachineTemplate.BootloaderType.External, "Other Linux (64x)", true, true, "test123");
+
+        long memory = ByteScaleUtils.bytesToKib(vmTo.getMaxRam());
+        int vcpus = vmTo.getCpus();
+        scalingDetails = String.format("%s memory to [%s KiB] and CPU cores to [%s]", vmTo.toString(), memory, vcpus);
+
+        PowerMockito.mockStatic(LibvirtComputingResource.class);
+    }
+
+    @Test
+    public void validateScaleVcpusRunningVcpusLessThanNewVcpusSetNewVcpu() throws LibvirtException{
+        long runningVcpus = 1;
+        int newVcpus = 2;
+
+        PowerMockito.when(LibvirtComputingResource.countDomainRunningVcpus(Mockito.any())).thenReturn(runningVcpus);
+        Mockito.doNothing().when(domainMock).setVcpus(Mockito.anyInt());
+
+        libvirtScaleVmCommandWrapperSpy.scaleVcpus(domainMock, newVcpus, scalingDetails);
+
+        Mockito.verify(domainMock).setVcpus(Mockito.anyInt());
+    }
+
+    @Test
+    public void validateScaleVcpusRunningVcpusEqualThanNewVcpusDoNothing() throws LibvirtException{
+        long runningVcpus = 2;
+        int newVcpus = 2;
+
+        PowerMockito.when(LibvirtComputingResource.countDomainRunningVcpus(Mockito.any())).thenReturn(runningVcpus);
+
+        libvirtScaleVmCommandWrapperSpy.scaleVcpus(domainMock, newVcpus, scalingDetails);
+
+        Mockito.verify(domainMock, Mockito.never()).setVcpus(Mockito.anyInt());
+    }
+
+    @Test
+    public void validateScaleVcpusRunningVcpusHigherThanNewVcpusDoNothing() throws LibvirtException{
+        long runningVcpus = 2;
+        int newVcpus = 1;
+
+        PowerMockito.when(LibvirtComputingResource.countDomainRunningVcpus(Mockito.any())).thenReturn(runningVcpus);
+
+        libvirtScaleVmCommandWrapperSpy.scaleVcpus(domainMock, newVcpus, scalingDetails);
+
+        Mockito.verify(domainMock, Mockito.never()).setVcpus(Mockito.anyInt());
+    }
+
+    @Test (expected = LibvirtException.class)
+    public void validateScaleVcpusSetVcpusThrowLibvirtException() throws LibvirtException{
+        long runningVcpus = 1;
+        int newVcpus = 2;
+
+        PowerMockito.when(LibvirtComputingResource.countDomainRunningVcpus(Mockito.any())).thenReturn(runningVcpus);
+        Mockito.doThrow(LibvirtException.class).when(domainMock).setVcpus(Mockito.anyInt());
+
+        libvirtScaleVmCommandWrapperSpy.scaleVcpus(domainMock, newVcpus, scalingDetails);
+
+        Mockito.verify(domainMock, Mockito.never()).setVcpus(Mockito.anyInt());
+    }
+
+    @Test
+    public void validateScaleMemoryMemoryLessThanZeroDoNothing() throws LibvirtException {
+        long currentMemory = 1l;
+        long newMemory = 0l;
+
+        PowerMockito.when(LibvirtComputingResource.getDomainMemory(Mockito.any())).thenReturn(currentMemory);
+
+        libvirtScaleVmCommandWrapperSpy.scaleMemory(domainMock, newMemory, scalingDetails);
+
+        Mockito.verify(domainMock, Mockito.never()).getXMLDesc(Mockito.anyInt());
+        Mockito.verify(domainMock, Mockito.never()).attachDevice(Mockito.anyString());
+    }
+
+    @Test
+    public void validateScaleMemoryMemoryEqualToZeroDoNothing() throws LibvirtException {
+        long currentMemory = 1l;
+        long newMemory = 1l;
+
+        PowerMockito.when(LibvirtComputingResource.getDomainMemory(Mockito.any())).thenReturn(currentMemory);
+
+        libvirtScaleVmCommandWrapperSpy.scaleMemory(domainMock, newMemory, scalingDetails);
+
+        Mockito.verify(domainMock, Mockito.never()).getXMLDesc(Mockito.anyInt());
+        Mockito.verify(domainMock, Mockito.never()).attachDevice(Mockito.anyString());
+    }
+
+    @Test (expected = CloudRuntimeException.class)
+    public void validateScaleMemoryDomainXmlDoesNotContainsMaxMemory() throws LibvirtException {
+        long currentMemory = 1l;
+        long newMemory = 2l;
+
+        PowerMockito.when(LibvirtComputingResource.getDomainMemory(Mockito.any())).thenReturn(currentMemory);
+        Mockito.doReturn("").when(domainMock).getXMLDesc(Mockito.anyInt());
+
+        libvirtScaleVmCommandWrapperSpy.scaleMemory(domainMock, newMemory, scalingDetails);
+
+        Mockito.verify(domainMock).getXMLDesc(Mockito.anyInt());
+        Mockito.verify(domainMock, Mockito.never()).attachDevice(Mockito.anyString());
+    }
+
+    @Test (expected = LibvirtException.class)
+    public void validateScaleMemoryAttachDeviceThrowsLibvirtException() throws LibvirtException {
+        long currentMemory = 1l;
+        long newMemory = 2l;
+
+        PowerMockito.when(LibvirtComputingResource.getDomainMemory(Mockito.any())).thenReturn(currentMemory);
+        Mockito.doReturn("<maxMemory slots='16' unit='KiB'>").when(domainMock).getXMLDesc(Mockito.anyInt());
+        Mockito.doThrow(LibvirtException.class).when(domainMock).attachDevice(Mockito.anyString());
+
+        libvirtScaleVmCommandWrapperSpy.scaleMemory(domainMock, newMemory, scalingDetails);
+
+        Mockito.verify(domainMock).getXMLDesc(Mockito.anyInt());
+        Mockito.verify(domainMock).attachDevice(Mockito.anyString());
+    }
+
+    @Test
+    public void validateScaleMemory() throws LibvirtException {
+        long currentMemory = 1l;
+        long newMemory = 2l;
+
+        PowerMockito.when(LibvirtComputingResource.getDomainMemory(Mockito.any())).thenReturn(currentMemory);
+        Mockito.doReturn("<maxMemory slots='16' unit='KiB'>").when(domainMock).getXMLDesc(Mockito.anyInt());
+        Mockito.doNothing().when(domainMock).attachDevice(Mockito.anyString());
+
+        libvirtScaleVmCommandWrapperSpy.scaleMemory(domainMock, newMemory, scalingDetails);
+
+        Mockito.verify(domainMock).getXMLDesc(Mockito.anyInt());
+        Mockito.verify(domainMock).attachDevice(Mockito.anyString());
+    }
+
+    @Test
+    public void validateExecuteHandleLibvirtException() throws LibvirtException {
+        String errorMessage = "";
+
+        Mockito.doReturn(vmTo).when(scaleVmCommandMock).getVirtualMachine();
+        Mockito.doReturn(libvirtUtilitiesHelperMock).when(libvirtComputingResourceMock).getLibvirtUtilitiesHelper();
+        Mockito.doThrow(libvirtException).when(libvirtUtilitiesHelperMock).getConnectionByVmName(Mockito.anyString());
+        Mockito.doReturn(errorMessage).when(libvirtException).getMessage();
+
+        Answer answer = libvirtScaleVmCommandWrapperSpy.execute(scaleVmCommandMock, libvirtComputingResourceMock);
+
+        String details = String.format("Unable to scale %s due to [%s].", scalingDetails, errorMessage);
+        assertFalse(answer.getResult());
+        assertEquals(details, answer.getDetails());
+    }
+
+    @Test
+    public void validateExecuteSuccessfully() throws LibvirtException {
+        Mockito.doReturn(vmTo).when(scaleVmCommandMock).getVirtualMachine();
+        Mockito.doReturn(libvirtUtilitiesHelperMock).when(libvirtComputingResourceMock).getLibvirtUtilitiesHelper();
+        Mockito.doReturn(connectMock).when(libvirtUtilitiesHelperMock).getConnectionByVmName(Mockito.anyString());
+        Mockito.doReturn(domainMock).when(connectMock).domainLookupByName(Mockito.anyString());
+        Mockito.doNothing().when(libvirtScaleVmCommandWrapperSpy).scaleMemory(Mockito.any(), Mockito.anyLong(), Mockito.anyString());
+        Mockito.doNothing().when(libvirtScaleVmCommandWrapperSpy).scaleVcpus(Mockito.any(), Mockito.anyInt(), Mockito.anyString());
+
+        Answer answer = libvirtScaleVmCommandWrapperSpy.execute(scaleVmCommandMock, libvirtComputingResourceMock);
+
+        String details = String.format("Successfully scaled %s.", scalingDetails);
+        assertTrue(answer.getResult());
+        assertEquals(details, answer.getDetails());
+    }
+
+    @Test(expected = Exception.class)
+    public void validateExecuteThrowAnyOtherException() {
+        Mockito.doThrow(Exception.class).when(libvirtComputingResourceMock).getLibvirtUtilitiesHelper();
+
+        libvirtScaleVmCommandWrapperSpy.execute(scaleVmCommandMock, libvirtComputingResourceMock);
+    }
+}
diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
index 13761b5..afabf1b 100755
--- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
@@ -247,7 +247,6 @@ import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.utils.net.NetUtils;
 import com.cloud.vm.NicIpAlias;
 import com.cloud.vm.VirtualMachine;
-import com.cloud.vm.VirtualMachineManager;
 import com.cloud.vm.dao.NicDao;
 import com.cloud.vm.dao.NicIpAliasDao;
 import com.cloud.vm.dao.NicIpAliasVO;
@@ -432,6 +431,12 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
                         "Indicates whether the host in down state can be put into maintenance state so thats its not enabled after it comes back.",
                         true, ConfigKey.Scope.Zone, null);
 
+    public static ConfigKey<Integer> VM_SERVICE_OFFERING_MAX_CPU_CORES = new ConfigKey<Integer>("Advanced", Integer.class, "vm.serviceoffering.cpu.cores.max", "0", "Maximum CPU cores "
+      + "for vm service offering. If 0 - no limitation", true);
+
+    public static ConfigKey<Integer> VM_SERVICE_OFFERING_MAX_RAM_SIZE = new ConfigKey<Integer>("Advanced", Integer.class, "vm.serviceoffering.ram.size.max", "0", "Maximum RAM size in "
+      + "MB for vm service offering. If 0 - no limitation", true);
+
     public static final ConfigKey<Integer> VM_USERDATA_MAX_LENGTH = new ConfigKey<Integer>("Advanced", Integer.class, VM_USERDATA_MAX_LENGTH_STRING, "32768",
             "Max length of vm userdata after base64 decoding. Default is 32768 and maximum is 1048576", true);
 
@@ -2383,8 +2388,8 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
                 details.put(ApiConstants.MAX_CPU_NUMBER, maxCPU.toString());
             }
         } else {
-            Integer maxCPUCores = VirtualMachineManager.VmServiceOfferingMaxCPUCores.value() == 0 ? Integer.MAX_VALUE: VirtualMachineManager.VmServiceOfferingMaxCPUCores.value();
-            Integer maxRAMSize = VirtualMachineManager.VmServiceOfferingMaxRAMSize.value() == 0 ? Integer.MAX_VALUE: VirtualMachineManager.VmServiceOfferingMaxRAMSize.value();
+            Integer maxCPUCores = VM_SERVICE_OFFERING_MAX_CPU_CORES.value() == 0 ? Integer.MAX_VALUE: VM_SERVICE_OFFERING_MAX_CPU_CORES.value();
+            Integer maxRAMSize = VM_SERVICE_OFFERING_MAX_RAM_SIZE.value() == 0 ? Integer.MAX_VALUE: VM_SERVICE_OFFERING_MAX_RAM_SIZE.value();
             if (cpuNumber != null && (cpuNumber.intValue() <= 0 || cpuNumber.longValue() > maxCPUCores)) {
                 throw new InvalidParameterValueException("Failed to create service offering " + offeringName + ": specify the cpu number value between 1 and " + maxCPUCores);
             }
@@ -6529,7 +6534,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
     @Override
     public ConfigKey<?>[] getConfigKeys() {
         return new ConfigKey<?>[] {SystemVMUseLocalStorage, IOPS_MAX_READ_LENGTH, IOPS_MAX_WRITE_LENGTH,
-                BYTES_MAX_READ_LENGTH, BYTES_MAX_WRITE_LENGTH, ADD_HOST_ON_SERVICE_RESTART_KVM, SET_HOST_DOWN_TO_MAINTENANCE,
-                VM_USERDATA_MAX_LENGTH};
+                BYTES_MAX_READ_LENGTH, BYTES_MAX_WRITE_LENGTH, ADD_HOST_ON_SERVICE_RESTART_KVM, SET_HOST_DOWN_TO_MAINTENANCE, VM_SERVICE_OFFERING_MAX_CPU_CORES,
+                VM_SERVICE_OFFERING_MAX_RAM_SIZE, VM_USERDATA_MAX_LENGTH};
     }
 }
diff --git a/server/src/main/java/com/cloud/hypervisor/KVMGuru.java b/server/src/main/java/com/cloud/hypervisor/KVMGuru.java
index cf29a1a..c18f39a 100644
--- a/server/src/main/java/com/cloud/hypervisor/KVMGuru.java
+++ b/server/src/main/java/com/cloud/hypervisor/KVMGuru.java
@@ -20,10 +20,13 @@ 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.host.HostVO;
 import com.cloud.host.dao.HostDao;
 import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.hypervisor.kvm.dpdk.DpdkHelper;
+import com.cloud.service.ServiceOfferingVO;
+import com.cloud.service.dao.ServiceOfferingDao;
 import com.cloud.storage.DataStoreRole;
 import com.cloud.storage.GuestOSHypervisorVO;
 import com.cloud.storage.GuestOSVO;
@@ -31,6 +34,7 @@ import com.cloud.storage.dao.GuestOSDao;
 import com.cloud.storage.dao.GuestOSHypervisorDao;
 import com.cloud.utils.Pair;
 import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.UserVmManager;
 import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.VirtualMachineProfile;
 import org.apache.cloudstack.storage.command.CopyCommand;
@@ -42,6 +46,9 @@ import javax.inject.Inject;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 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
@@ -53,6 +60,9 @@ public class KVMGuru extends HypervisorGuruBase implements HypervisorGuru {
     @Inject
     DpdkHelper dpdkHelper;
 
+    @Inject
+    ServiceOfferingDao serviceOfferingDao;
+
     public static final Logger s_logger = Logger.getLogger(KVMGuru.class);
 
     @Override
@@ -112,32 +122,151 @@ public class KVMGuru extends HypervisorGuruBase implements HypervisorGuru {
         VirtualMachineTO to = toVirtualMachineTO(vm);
         setVmQuotaPercentage(to, vm);
 
-        if (dpdkHelper.isDpdkvHostUserModeSettingOnServiceOffering(vm)) {
-            dpdkHelper.setDpdkVhostUserMode(to, vm);
+        enableDpdkIfNeeded(vm, to);
+
+        VirtualMachine virtualMachine = vm.getVirtualMachine();
+        Long hostId = virtualMachine.getHostId();
+        HostVO host = hostId == null ? null : _hostDao.findById(hostId);
+
+        // Determine the VM's OS description
+        configureVmOsDescription(virtualMachine, to, host);
+
+        configureVmMemoryAndCpuCores(to, host, virtualMachine, vm);
+        return to;
+    }
+
+    protected void configureVmOsDescription(VirtualMachine virtualMachine, VirtualMachineTO virtualMachineTo, HostVO hostVo) {
+        GuestOSVO guestOS = _guestOsDao.findByIdIncludingRemoved(virtualMachine.getGuestOSId());
+        String guestOsDisplayName = guestOS.getDisplayName();
+        virtualMachineTo.setOs(guestOsDisplayName);
+        GuestOSHypervisorVO guestOsMapping = null;
+
+        if (hostVo != null) {
+            guestOsMapping = _guestOsHypervisorDao.findByOsIdAndHypervisor(guestOS.getId(), getHypervisorType().toString(), hostVo.getHypervisorVersion());
+        }
+
+        if (guestOsMapping == null || hostVo == null) {
+            virtualMachineTo.setPlatformEmulator(guestOsDisplayName == null ? "Other" : guestOsDisplayName);
+        } else {
+            virtualMachineTo.setPlatformEmulator(guestOsMapping.getGuestOsName());
+        }
+    }
+
+    protected void enableDpdkIfNeeded(VirtualMachineProfile virtualMachineProfile, VirtualMachineTO virtualMachineTo) {
+        if (dpdkHelper.isDpdkvHostUserModeSettingOnServiceOffering(virtualMachineProfile)) {
+            dpdkHelper.setDpdkVhostUserMode(virtualMachineTo, virtualMachineProfile);
         }
 
-        if (to.getType() == VirtualMachine.Type.User && MapUtils.isNotEmpty(to.getExtraConfig()) &&
-                to.getExtraConfig().containsKey(DpdkHelper.DPDK_NUMA) && to.getExtraConfig().containsKey(DpdkHelper.DPDK_HUGE_PAGES)) {
-            for (final NicTO nic : to.getNics()) {
+        if (virtualMachineTo.getType() == VirtualMachine.Type.User && MapUtils.isNotEmpty(virtualMachineTo.getExtraConfig()) &&
+          virtualMachineTo.getExtraConfig().containsKey(DpdkHelper.DPDK_NUMA) && virtualMachineTo.getExtraConfig().containsKey(DpdkHelper.DPDK_HUGE_PAGES)) {
+            for (final NicTO nic : virtualMachineTo.getNics()) {
                 nic.setDpdkEnabled(true);
             }
         }
+    }
+
+    protected void configureVmMemoryAndCpuCores(VirtualMachineTO virtualMachineTo, HostVO hostVo, VirtualMachine virtualMachine, VirtualMachineProfile virtualMachineProfile) {
+        String vmDescription = virtualMachineTo.toString();
+
+        Pair<Long, Integer> max = getHostMaxMemoryAndCpuCores(hostVo, virtualMachine, vmDescription);
+
+        Long maxHostMemory = max.first();
+        Integer maxHostCpuCore = max.second();
+
+        Long minMemory = virtualMachineTo.getMinRam();
+        Long maxMemory = minMemory;
+        Integer minCpuCores = virtualMachineTo.getCpus();
+        Integer maxCpuCores = minCpuCores;
+
+        ServiceOfferingVO serviceOfferingVO = serviceOfferingDao.findById(virtualMachineProfile.getId(), virtualMachineProfile.getServiceOfferingId());
+        if (isVmDynamicScalable(serviceOfferingVO, virtualMachineTo, virtualMachine)) {
+            serviceOfferingDao.loadDetails(serviceOfferingVO);
+
+            maxMemory = getVmMaxMemory(serviceOfferingVO, vmDescription, maxHostMemory);
+            maxCpuCores = getVmMaxCpuCores(serviceOfferingVO, vmDescription, maxHostCpuCore);
+        }
+
+        virtualMachineTo.setRam(minMemory, maxMemory);
+        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 Pair<Long, Integer> getHostMaxMemoryAndCpuCores(HostVO host, VirtualMachine virtualMachine, String vmDescription){
+        Long maxHostMemory = Long.MAX_VALUE;
+        Integer maxHostCpuCore = Integer.MAX_VALUE;
 
-        // Determine the VM's OS description
-        GuestOSVO guestOS = _guestOsDao.findByIdIncludingRemoved(vm.getVirtualMachine().getGuestOSId());
-        to.setOs(guestOS.getDisplayName());
-        HostVO host = _hostDao.findById(vm.getVirtualMachine().getHostId());
-        GuestOSHypervisorVO guestOsMapping = null;
         if (host != null) {
-            guestOsMapping = _guestOsHypervisorDao.findByOsIdAndHypervisor(guestOS.getId(), getHypervisorType().toString(), host.getHypervisorVersion());
+            return new Pair<>(host.getTotalMemory(), host.getCpus());
         }
-        if (guestOsMapping == null || host == null) {
-            to.setPlatformEmulator(guestOS.getDisplayName() == null ? "Other" : guestOS.getDisplayName());
+
+        Long lastHostId = virtualMachine.getLastHostId();
+        s_logger.info(String.format("%s is not running; therefore, we use the last host [%s] that the VM was running on to derive the unconstrained service offering max CPU and memory.", vmDescription, lastHostId));
+
+        HostVO lastHost = lastHostId == null ? null : _hostDao.findById(lastHostId);
+        if (lastHost != null) {
+            maxHostMemory = lastHost.getTotalMemory();
+            maxHostCpuCore = lastHost.getCpus();
+            s_logger.debug(String.format("Retrieved memory and cpu max values {\"memory\": %s, \"cpu\": %s} from %s last %s.", maxHostMemory, maxHostCpuCore, vmDescription, lastHost.toString()));
         } else {
-            to.setPlatformEmulator(guestOsMapping.getGuestOsName());
+            s_logger.warn(String.format("%s host [%s] and last host [%s] are null. Using 'Long.MAX_VALUE' [%s] and 'Integer.MAX_VALUE' [%s] as max memory and cpu cores.", vmDescription, virtualMachine.getHostId(), lastHostId, maxHostMemory, maxHostCpuCore));
         }
 
-        return to;
+        return new Pair<>(maxHostMemory, maxHostCpuCore);
+    }
+
+    protected Long getVmMaxMemory(ServiceOfferingVO serviceOfferingVO, String vmDescription, Long maxHostMemory) {
+        String serviceOfferingDescription = serviceOfferingVO.toString();
+
+        Long maxMemory;
+        Integer customOfferingMaxMemory = NumberUtils.createInteger(serviceOfferingVO.getDetail(ApiConstants.MAX_MEMORY));
+        Integer maxMemoryConfig = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE.value();
+        if (customOfferingMaxMemory != null) {
+            s_logger.debug(String.format("Using 'Custom unconstrained' %s max memory value [%sMb] as %s memory.", serviceOfferingDescription, customOfferingMaxMemory, vmDescription));
+            maxMemory = ByteScaleUtils.mibToBytes(customOfferingMaxMemory);
+        } else {
+            String maxMemoryConfigKey = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE.key();
+
+            s_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.mibToBytes(maxMemoryConfig);
+            } else {
+                s_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;
+            }
+        }
+        return maxMemory;
+    }
+
+    protected Integer getVmMaxCpuCores(ServiceOfferingVO serviceOfferingVO, String vmDescription, Integer maxHostCpuCore) {
+        String serviceOfferingDescription = serviceOfferingVO.toString();
+
+        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) {
+            s_logger.debug(String.format("Using 'Custom unconstrained' %s max cpu cores [%s] as %s cpu cores.", serviceOfferingDescription, customOfferingMaxCpuCores, vmDescription));
+            maxCpuCores = customOfferingMaxCpuCores;
+        } else {
+            String maxCpuCoreConfigKey = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES.key();
+
+            s_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 {
+                s_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;
+            }
+        }
+        return maxCpuCores;
     }
 
     @Override
diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
index 00c8d98..44912a0 100644
--- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
@@ -155,6 +155,7 @@ import com.cloud.capacity.Capacity;
 import com.cloud.capacity.CapacityManager;
 import com.cloud.configuration.Config;
 import com.cloud.configuration.ConfigurationManager;
+import com.cloud.configuration.ConfigurationManagerImpl;
 import com.cloud.configuration.Resource.ResourceType;
 import com.cloud.dc.DataCenter;
 import com.cloud.dc.DataCenter.NetworkType;
@@ -342,6 +343,8 @@ import com.cloud.vm.dao.VMInstanceDao;
 import com.cloud.vm.snapshot.VMSnapshotManager;
 import com.cloud.vm.snapshot.VMSnapshotVO;
 import com.cloud.vm.snapshot.dao.VMSnapshotDao;
+import java.util.HashSet;
+import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
 
 import static com.cloud.configuration.ConfigurationManagerImpl.VM_USERDATA_MAX_LENGTH;
 
@@ -1137,11 +1140,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
     }
 
     private void validateOfferingMaxResource(ServiceOfferingVO offering) {
-        Integer maxCPUCores = VirtualMachineManager.VmServiceOfferingMaxCPUCores.value() == 0 ? Integer.MAX_VALUE: VirtualMachineManager.VmServiceOfferingMaxCPUCores.value();
+        Integer maxCPUCores = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES.value() == 0 ? Integer.MAX_VALUE: ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES.value();
         if (offering.getCpu() > maxCPUCores) {
             throw new InvalidParameterValueException("Invalid cpu cores value, please choose another service offering with cpu cores between 1 and " + maxCPUCores);
         }
-        Integer maxRAMSize = VirtualMachineManager.VmServiceOfferingMaxRAMSize.value() == 0 ? Integer.MAX_VALUE: VirtualMachineManager.VmServiceOfferingMaxRAMSize.value();
+        Integer maxRAMSize = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE.value() == 0 ? Integer.MAX_VALUE: ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE.value();
         if (offering.getRamSize() > maxRAMSize) {
             throw new InvalidParameterValueException("Invalid memory value, please choose another service offering with memory between 32 and " + maxRAMSize + " MB");
         }
@@ -1156,7 +1159,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
                 int minCPU = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MIN_CPU_NUMBER), 1);
                 int maxCPU = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MAX_CPU_NUMBER), Integer.MAX_VALUE);
                 int cpuNumber = NumbersUtil.parseInt(customParameters.get(UsageEventVO.DynamicParameters.cpuNumber.name()), -1);
-                Integer maxCPUCores = VirtualMachineManager.VmServiceOfferingMaxCPUCores.value() == 0 ? Integer.MAX_VALUE: VirtualMachineManager.VmServiceOfferingMaxCPUCores.value();
+                Integer maxCPUCores = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES.value() == 0 ? Integer.MAX_VALUE: ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_CPU_CORES.value();
                 if (cpuNumber < minCPU || cpuNumber > maxCPU || cpuNumber > maxCPUCores) {
                     throw new InvalidParameterValueException(String.format("Invalid cpu cores value, specify a value between %d and %d", minCPU, Math.min(maxCPUCores, maxCPU)));
                 }
@@ -1179,7 +1182,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
                 int minMemory = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MIN_MEMORY), 32);
                 int maxMemory = NumbersUtil.parseInt(offeringDetails.get(ApiConstants.MAX_MEMORY), Integer.MAX_VALUE);
                 int memory = NumbersUtil.parseInt(customParameters.get(UsageEventVO.DynamicParameters.memory.name()), -1);
-                Integer maxRAMSize = VirtualMachineManager.VmServiceOfferingMaxRAMSize.value() == 0 ? Integer.MAX_VALUE: VirtualMachineManager.VmServiceOfferingMaxRAMSize.value();
+                Integer maxRAMSize = ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE.value() == 0 ? Integer.MAX_VALUE: ConfigurationManagerImpl.VM_SERVICE_OFFERING_MAX_RAM_SIZE.value();
                 if (memory < minMemory || memory > maxMemory || memory > maxRAMSize) {
                     throw new InvalidParameterValueException(String.format("Invalid memory value, specify a value between %d and %d", minMemory, Math.min(maxRAMSize, maxMemory)));
                 }
@@ -1873,9 +1876,19 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
 
         Account caller = CallContext.current().getCallingAccount();
         VMInstanceVO vmInstance = _vmInstanceDao.findById(vmId);
-        if (vmInstance.getHypervisorType() != HypervisorType.XenServer && vmInstance.getHypervisorType() != HypervisorType.VMware && vmInstance.getHypervisorType() != HypervisorType.Simulator) {
-            s_logger.info("Scaling the VM dynamically is not supported for VMs running on Hypervisor "+vmInstance.getHypervisorType());
-            throw new InvalidParameterValueException("Scaling the VM dynamically is not supported for VMs running on Hypervisor "+vmInstance.getHypervisorType());
+
+        Set<HypervisorType> supportedHypervisorTypes = new HashSet<>();
+        supportedHypervisorTypes.add(HypervisorType.XenServer);
+        supportedHypervisorTypes.add(HypervisorType.VMware);
+        supportedHypervisorTypes.add(HypervisorType.Simulator);
+        supportedHypervisorTypes.add(HypervisorType.KVM);
+
+        HypervisorType vmHypervisorType = vmInstance.getHypervisorType();
+
+        if (!supportedHypervisorTypes.contains(vmHypervisorType)) {
+            String message = String.format("Scaling the VM dynamically is not supported for VMs running on Hypervisor [%s].", vmInstance.getHypervisorType());
+            s_logger.info(message);
+            throw new InvalidParameterValueException(message);
         }
 
         _accountMgr.checkAccess(caller, null, true, vmInstance);
@@ -1907,9 +1920,17 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
 
         // 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)) {
-            throw new InvalidParameterValueException("Only scaling up the vm is supported, new service offering(speed=" + newSpeed + ",cpu=" + newCpu + ",memory=," + newMemory
-                    + ")" + " should have at least one value(cpu/ram) greater than old value and no resource value less than older(speed=" + currentSpeed + ",cpu=" + currentCpu
-                    + ",memory=," + currentMemory + ")");
+            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);
+        }
+
+        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.");
+            s_logger.info(message);
+            throw new InvalidParameterValueException(message);
         }
 
         _offeringDao.loadDetails(currentServiceOffering);
@@ -1919,18 +1940,18 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
         Map<String, String> newDetails = newServiceOffering.getDetails();
         String currentVgpuType = currentDetails.get("vgpuType");
         String newVgpuType = newDetails.get("vgpuType");
-        if(currentVgpuType != null) {
-            if(newVgpuType == null || !newVgpuType.equalsIgnoreCase(currentVgpuType)) {
-                throw new InvalidParameterValueException("Dynamic scaling of vGPU type is not supported. VM has vGPU Type: " + currentVgpuType);
-            }
+
+        if (currentVgpuType != null && (newVgpuType == null || !newVgpuType.equalsIgnoreCase(currentVgpuType))) {
+            throw new InvalidParameterValueException(String.format("Dynamic scaling of vGPU type is not supported. VM has vGPU Type: [%s].", currentVgpuType));
         }
 
         // Check resource limits
         if (newCpu > currentCpu) {
             _resourceLimitMgr.checkResourceLimit(caller, ResourceType.cpu, newCpu - currentCpu);
         }
+
         if (newMemory > currentMemory) {
-            _resourceLimitMgr.checkResourceLimit(caller, ResourceType.memory, newMemory - currentMemory);
+            _resourceLimitMgr.checkResourceLimit(caller, ResourceType.memory, memoryDiff);
         }
 
         // Dynamically upgrade the running vms
@@ -1942,18 +1963,18 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
             // Check zone wide flag
             boolean enableDynamicallyScaleVm = EnableDynamicallyScaleVm.valueIn(vmInstance.getDataCenterId());
             if (!enableDynamicallyScaleVm) {
-                throw new PermissionDeniedException("Dynamically scaling virtual machines is disabled for this zone, please contact your admin");
+                throw new PermissionDeniedException("Dynamically scaling virtual machines is disabled for this zone, please contact your admin.");
             }
 
             // Check vm flag
             if (!vmInstance.isDynamicallyScalable()) {
-                throw new CloudRuntimeException("Unable to Scale the VM: " + vmInstance.getUuid() + " as VM is not configured to be dynamically scalable");
+                throw new CloudRuntimeException(String.format("Unable to scale %s as it does not have tools to support dynamic scaling.", vmInstance.toString()));
             }
 
             // Check disable threshold for cluster is not crossed
             HostVO host = _hostDao.findById(vmInstance.getHostId());
             if (_capacityMgr.checkIfClusterCrossesThreshold(host.getClusterId(), cpuDiff, memoryDiff)) {
-                throw new CloudRuntimeException("Unable to scale vm: " + vmInstance.getUuid() + " due to insufficient resources");
+                throw new CloudRuntimeException(String.format("Unable to scale %s due to insufficient resources.", vmInstance.toString()));
             }
 
             while (retry-- != 0) { // It's != so that it can match -1.
@@ -1972,7 +1993,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
                     // #1 Check existing host has capacity
                     if (!excludes.shouldAvoid(ApiDBUtils.findHostById(vmInstance.getHostId()))) {
                         existingHostHasCapacity = _capacityMgr.checkIfHostHasCpuCapability(vmInstance.getHostId(), newCpu, newSpeed)
-                                && _capacityMgr.checkIfHostHasCapacity(vmInstance.getHostId(), cpuDiff, (memoryDiff) * 1024L * 1024L, false,
+                                && _capacityMgr.checkIfHostHasCapacity(vmInstance.getHostId(), cpuDiff, ByteScaleUtils.mibToBytes(memoryDiff), false,
                                         _capacityMgr.getClusterOverProvisioningFactor(host.getClusterId(), Capacity.CAPACITY_TYPE_CPU),
                                         _capacityMgr.getClusterOverProvisioningFactor(host.getClusterId(), Capacity.CAPACITY_TYPE_MEMORY), false);
                         excludes.addHost(vmInstance.getHostId());
@@ -1989,9 +2010,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
                     success = true;
                     return success;
                 } catch (InsufficientCapacityException | ResourceUnavailableException | ConcurrentOperationException e) {
-                    s_logger.warn("Received exception while scaling ", e);
-                } catch (Exception e) {
-                    s_logger.warn("Scaling failed with exception: ", e);
+                    s_logger.error(String.format("Unable to scale %s due to [%s].", vmInstance.toString(), e.getMessage()), e);
                 } finally {
                     if (!success) {
                         // Decrement CPU and Memory count accordingly.
diff --git a/server/src/test/java/com/cloud/hypervisor/KVMGuruTest.java b/server/src/test/java/com/cloud/hypervisor/KVMGuruTest.java
index a10e937..22f54d0 100644
--- a/server/src/test/java/com/cloud/hypervisor/KVMGuruTest.java
+++ b/server/src/test/java/com/cloud/hypervisor/KVMGuruTest.java
@@ -16,12 +16,22 @@
 // under the License.
 package com.cloud.hypervisor;
 
+import com.cloud.agent.api.to.NicTO;
 import com.cloud.agent.api.to.VirtualMachineTO;
+import com.cloud.configuration.ConfigurationManagerImpl;
 import com.cloud.host.HostVO;
 import com.cloud.host.dao.HostDao;
+import com.cloud.hypervisor.kvm.dpdk.DpdkHelper;
 import com.cloud.offering.ServiceOffering;
 import com.cloud.service.ServiceOfferingDetailsVO;
+import com.cloud.service.ServiceOfferingVO;
+import com.cloud.service.dao.ServiceOfferingDao;
 import com.cloud.service.dao.ServiceOfferingDetailsDao;
+import com.cloud.storage.GuestOSHypervisorVO;
+import com.cloud.storage.GuestOSVO;
+import com.cloud.storage.dao.GuestOSDao;
+import com.cloud.storage.dao.GuestOSHypervisorDao;
+import com.cloud.utils.Pair;
 import com.cloud.utils.exception.CloudRuntimeException;
 import com.cloud.vm.VirtualMachine;
 import com.cloud.vm.VirtualMachineProfile;
@@ -37,6 +47,11 @@ import org.mockito.runners.MockitoJUnitRunner;
 
 import java.io.UnsupportedEncodingException;
 import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.utils.bytescale.ByteScaleUtils;
+import org.junit.Assert;
 
 @RunWith(MockitoJUnitRunner.class)
 public class KVMGuruTest {
@@ -65,6 +80,30 @@ public class KVMGuruTest {
     @Mock
     ServiceOfferingDetailsVO detail2;
 
+    @Mock
+    ServiceOfferingVO serviceOfferingVoMock;
+
+    @Mock
+    VirtualMachine virtualMachineMock;
+
+    @Mock
+    ServiceOfferingDao serviceOfferingDaoMock;
+
+    @Mock
+    DpdkHelper dpdkHelperMock;
+
+    @Mock
+    GuestOSVO guestOsVoMock;
+
+    @Mock
+    GuestOSHypervisorVO guestOsMappingMock;
+
+    @Mock
+    GuestOSHypervisorDao guestOSHypervisorDaoMock;
+
+    @Mock
+    GuestOSDao guestOsDaoMock;
+
     private static final long hostId = 1L;
     private static final Long offeringId = 1L;
 
@@ -129,4 +168,288 @@ public class KVMGuruTest {
         guru.setVmQuotaPercentage(vmTO, vmProfile);
         Mockito.verify(vmTO).setCpuQuotaPercentage(1d);
     }
+
+    @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.mibToBytes(maxCustomOfferingMemory), result);
+    }
+
+    @Test
+    public void validateGetVmMaxMemoryReturnVmServiceOfferingMaxRAMSize(){
+        int maxMemoryConfig = 64;
+        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(maxMemoryConfig);
+        long result = guru.getVmMaxMemory(serviceOfferingVoMock, "Vm description", 1l);
+
+        Assert.assertEquals(ByteScaleUtils.mibToBytes(maxMemoryConfig), result);
+    }
+
+    @Test
+    public void validateGetVmMaxMemoryReturnMaxHostMemory(){
+        long maxHostMemory = ByteScaleUtils.mibToBytes(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);
+
+        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(){
+        int maxCpuCoresConfig = 16;
+        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(maxCpuCoresConfig);
+        long result = guru.getVmMaxCpuCores(serviceOfferingVoMock, "Vm description", 1);
+
+        Assert.assertEquals(maxCpuCoresConfig, result);
+    }
+
+    @Test
+    public void validateGetVmMaxCpuCoresReturnMaxHostMemory(){
+        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);
+
+        Assert.assertEquals(maxHostCpuCores, result);
+    }
+
+    @Test
+    public void validateGetHostMaxMemoryAndCpuCoresHostNotNull(){
+        Long maxMemory = 2048l;
+        Integer maxCpuCores = 16;
+
+        Mockito.when(host.getTotalMemory()).thenReturn(maxMemory);
+        Mockito.when(host.getCpus()).thenReturn(maxCpuCores);
+
+        Pair<Long, Integer> result = guru.getHostMaxMemoryAndCpuCores(host, virtualMachineMock, "Vm description");
+
+        Assert.assertEquals(new Pair<>(maxMemory, maxCpuCores), result);
+    }
+
+    @Test
+    public void validateGetHostMaxMemoryAndCpuCoresHostNullAndLastHostIdNull(){
+        Long maxMemory = Long.MAX_VALUE;
+        Integer maxCpuCores = Integer.MAX_VALUE;
+
+        Pair<Long, Integer> result = guru.getHostMaxMemoryAndCpuCores(null, virtualMachineMock, "Vm description");
+
+        Assert.assertEquals(new Pair<>(maxMemory, maxCpuCores), result);
+    }
+
+    @Test
+    public void validateGetHostMaxMemoryAndCpuCoresHostNullAndLastHostIdNotNullAndLastHostNull(){
+        Long maxMemory = Long.MAX_VALUE;
+        Integer maxCpuCores = Integer.MAX_VALUE;
+        guru._hostDao = hostDao;
+
+        Mockito.when(virtualMachineMock.getLastHostId()).thenReturn(1l);
+        Mockito.doReturn(null).when(hostDao).findById(Mockito.any());
+
+        Pair<Long, Integer> result = guru.getHostMaxMemoryAndCpuCores(null, virtualMachineMock, "Vm description");
+
+        Assert.assertEquals(new Pair<>(maxMemory, maxCpuCores), result);
+    }
+
+    @Test
+    public void validateGetHostMaxMemoryAndCpuCoresHostNullAndLastHostIdNotNullAndLastHostNotNull(){
+        Long maxMemory = 2048l;
+        Integer maxCpuCores = 16;
+        guru._hostDao = hostDao;
+
+        Mockito.when(virtualMachineMock.getLastHostId()).thenReturn(1l);
+        Mockito.doReturn(host).when(hostDao).findById(Mockito.any());
+        Mockito.when(host.getTotalMemory()).thenReturn(maxMemory);
+        Mockito.when(host.getCpus()).thenReturn(maxCpuCores);
+
+        Pair<Long, Integer> result = guru.getHostMaxMemoryAndCpuCores(null, virtualMachineMock, "Vm description");
+
+        Assert.assertEquals(new Pair<>(maxMemory, maxCpuCores), result);
+    }
+
+    @Test
+    public void validateConfigureVmMemoryAndCpuCoresServiceOfferingIsDynamicAndVmIsDynamicCallGetMethods(){
+        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());
+
+        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());
+    }
+
+    @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());
+
+        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());
+    }
+
+    @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());
+    }
+
+    @Test
+    public void validateEnableDpdkIfNeededCallDpdkHelperSetDpdkVhostUserMode() {
+        Mockito.when(dpdkHelperMock.isDpdkvHostUserModeSettingOnServiceOffering(vmProfile)).thenReturn(Boolean.TRUE);
+        guru.enableDpdkIfNeeded(vmProfile, vmTO);
+        Mockito.verify(dpdkHelperMock).setDpdkVhostUserMode(vmTO, vmProfile);
+    }
+
+    @Test
+    public void validateEnableDpdkIfNeededDoNotCallDpdkHelperSetDpdkVhostUserMode() {
+        Mockito.when(dpdkHelperMock.isDpdkvHostUserModeSettingOnServiceOffering(vmProfile)).thenReturn(Boolean.FALSE);
+        guru.enableDpdkIfNeeded(vmProfile, vmTO);
+        Mockito.verify(dpdkHelperMock, Mockito.times(0)).setDpdkVhostUserMode(vmTO, vmProfile);
+    }
+
+    @Test
+    public void validateEnableDpdkIfNeededNicSetDpdkEnabledTrue() {
+        Map<String, String> map = new HashMap<>();
+        map.put(DpdkHelper.DPDK_NUMA, "test1");
+        map.put(DpdkHelper.DPDK_HUGE_PAGES, "test2");
+
+        NicTO nicTo1 = Mockito.mock(NicTO.class);
+        NicTO nicTo2 = Mockito.mock(NicTO.class);
+        NicTO nicTo3 = Mockito.mock(NicTO.class);
+
+        NicTO[] nics = {nicTo1, nicTo2, nicTo3};
+
+        Mockito.when(vmTO.getType()).thenReturn(VirtualMachine.Type.User);
+        Mockito.when(vmTO.getExtraConfig()).thenReturn(map);
+        Mockito.when(vmTO.getNics()).thenReturn(nics);
+
+        guru.enableDpdkIfNeeded(vmProfile, vmTO);
+
+        for (NicTO nic : nics) {
+            Mockito.verify(nic).setDpdkEnabled(true);
+        }
+    }
+
+    @Test
+    public void validateConfigureVmOsDescriptionHostNotNullAndGuestOsMappingNotNullAndGuestOsDisplayNameNotNull(){
+        guru._guestOsDao = guestOsDaoMock;
+        guru._guestOsHypervisorDao = guestOSHypervisorDaoMock;
+
+        VirtualMachineTO virtualMachineTo = new VirtualMachineTO() {};
+        String platformEmulator = "Ubuntu";
+
+        Mockito.doReturn(guestOsVoMock).when(guestOsDaoMock).findByIdIncludingRemoved(Mockito.any());
+        Mockito.doReturn(guestOsMappingMock).when(guestOSHypervisorDaoMock).findByOsIdAndHypervisor(Mockito.anyLong(), Mockito.anyString(), Mockito.any());
+        Mockito.doReturn(platformEmulator).when(guestOsMappingMock).getGuestOsName();
+
+        guru.configureVmOsDescription(virtualMachineMock, virtualMachineTo, host);
+
+        Assert.assertEquals(platformEmulator, virtualMachineTo.getPlatformEmulator());
+    }
+
+    @Test
+    public void validateConfigureVmOsDescriptionHostNotNullAndGuestOsMappingNullAndGuestOsDisplayNameNull(){
+        guru._guestOsDao = guestOsDaoMock;
+        guru._guestOsHypervisorDao = guestOSHypervisorDaoMock;
+
+        VirtualMachineTO virtualMachineTo = new VirtualMachineTO() {};
+
+        Mockito.doReturn(guestOsVoMock).when(guestOsDaoMock).findByIdIncludingRemoved(Mockito.any());
+        Mockito.doReturn(null).when(guestOSHypervisorDaoMock).findByOsIdAndHypervisor(Mockito.anyLong(), Mockito.anyString(), Mockito.any());
+
+        guru.configureVmOsDescription(virtualMachineMock, virtualMachineTo, host);
+
+        Assert.assertEquals("Other", virtualMachineTo.getPlatformEmulator());
+    }
+
+    @Test
+    public void validateConfigureVmOsDescriptionHostNotNullAndGuestOsMappingNullAndGuestOsDisplayNameNotNull(){
+        guru._guestOsDao = guestOsDaoMock;
+        guru._guestOsHypervisorDao = guestOSHypervisorDaoMock;
+
+        VirtualMachineTO virtualMachineTo = new VirtualMachineTO() {};
+        String platformEmulator = "Ubuntu";
+
+        Mockito.doReturn(guestOsVoMock).when(guestOsDaoMock).findByIdIncludingRemoved(Mockito.any());
+        Mockito.doReturn(null).when(guestOSHypervisorDaoMock).findByOsIdAndHypervisor(Mockito.anyLong(), Mockito.anyString(), Mockito.any());
+        Mockito.doReturn(platformEmulator).when(guestOsVoMock).getDisplayName();
+
+        guru.configureVmOsDescription(virtualMachineMock, virtualMachineTo, host);
+
+        Assert.assertEquals(platformEmulator, virtualMachineTo.getPlatformEmulator());
+    }
+
+    @Test
+    public void validateConfigureVmOsDescriptionHostNullAndGuestOsMappingNullAndGuestOsDisplayNameNull(){
+        guru._guestOsDao = guestOsDaoMock;
+        VirtualMachineTO virtualMachineTo = new VirtualMachineTO() {};
+
+        Mockito.doReturn(guestOsVoMock).when(guestOsDaoMock).findByIdIncludingRemoved(Mockito.any());
+        guru.configureVmOsDescription(virtualMachineMock, virtualMachineTo, host);
+
+        Assert.assertEquals("Other", virtualMachineTo.getPlatformEmulator());
+    }
+
+    @Test
+    public void validateConfigureVmOsDescriptionHostNullAndGuestOsMappingNullAndGuestOsDisplayNameNotNull(){
+        guru._guestOsDao = guestOsDaoMock;
+
+        VirtualMachineTO virtualMachineTo = new VirtualMachineTO() {};
+        String platformEmulator = "Ubuntu";
+
+        Mockito.doReturn(guestOsVoMock).when(guestOsDaoMock).findByIdIncludingRemoved(Mockito.any());
+        Mockito.doReturn(platformEmulator).when(guestOsVoMock).getDisplayName();
+
+        guru.configureVmOsDescription(virtualMachineMock, virtualMachineTo, host);
+
+        Assert.assertEquals(platformEmulator, virtualMachineTo.getPlatformEmulator());
+    }
+
 }
\ No newline at end of file
diff --git a/utils/src/main/java/org/apache/cloudstack/utils/bytescale/ByteScaleUtils.java b/utils/src/main/java/org/apache/cloudstack/utils/bytescale/ByteScaleUtils.java
new file mode 100644
index 0000000..95d455f
--- /dev/null
+++ b/utils/src/main/java/org/apache/cloudstack/utils/bytescale/ByteScaleUtils.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cloudstack.utils.bytescale;
+
+/**
+ * This class provides a facility to convert bytes through his scales (b, Kib, Kb, Mib, Mb...).
+ *
+ */
+public class ByteScaleUtils {
+
+    public static final long KiB = 1024;
+    public static final long MiB = KiB * 1024;
+
+    private ByteScaleUtils() {}
+
+    /**
+     * Converts mebibytes to bytes.
+     *
+     * @param mib The value to convert to bytes (eq: 1, 2, 3, ..., 42,...).
+     * @return The parameter multiplied by 1048576 (1024 * 1024, 1 MiB).
+     */
+    public static long mibToBytes(long mib) {
+        return mib * MiB;
+    }
+
+    /**
+     * Converts bytes to kibibytes.
+     *
+     * @param b The value in bytes to convert to kibibytes.
+     * @return The parameter divided by 1024 (1 KiB).
+     */
+    public static long bytesToKib(long b) {
+        return b / KiB;
+    }
+}
diff --git a/utils/src/test/java/org/apache/cloudstack/utils/bytescale/ByteScaleUtilsTest.java b/utils/src/test/java/org/apache/cloudstack/utils/bytescale/ByteScaleUtilsTest.java
new file mode 100644
index 0000000..b348764
--- /dev/null
+++ b/utils/src/test/java/org/apache/cloudstack/utils/bytescale/ByteScaleUtilsTest.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cloudstack.utils.bytescale;
+
+import junit.framework.TestCase;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.junit.MockitoJUnitRunner;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ByteScaleUtilsTest extends TestCase {
+
+    @Test
+    public void validateMibToBytes() {
+        long mib = 3000L;
+        long b = 1024L * 1024L * mib;
+        assertEquals(b, ByteScaleUtils.mibToBytes(mib));
+    }
+
+    @Test
+    public void validateBytesToKib() {
+        long kib = 1024L * 3000L;
+        long b = 1024 * kib;
+        assertEquals(kib, ByteScaleUtils.bytesToKib(b));
+    }
+
+    @Test
+    public void validateMibToBytesIfIntTimesIntThenMustExtrapolateIntMaxValue() {
+        int mib = 3000;
+        long b = 1024L * 1024L * mib;
+        assertEquals(b, ByteScaleUtils.mibToBytes(mib));
+    }
+
+    @Test
+    public void validateBytesToKibIfIntByIntThenMustExtrapolateIntMaxValue(){
+        int b = Integer.MAX_VALUE;
+        assertEquals(b, ByteScaleUtils.bytesToKib(b * 1024L));
+    }
+}