mirror of
https://github.com/apache/cloudstack.git
synced 2025-11-02 20:02:29 +01:00
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 <daniel@scclouds.com.br>
This commit is contained in:
parent
9c51009134
commit
8a16729fcf
@ -146,6 +146,10 @@ public class VirtualMachineTO {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(Type type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public BootloaderType getBootloader() {
|
||||
return bootloader;
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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";
|
||||
}
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
@ -2628,6 +2615,37 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
||||
vm.setHvsType(HypervisorType.LXC.toString().toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
public void setMemBacking(String memBacking) {
|
||||
_memBacking = memBacking;
|
||||
this.currentMemory = currMem;
|
||||
}
|
||||
|
||||
public void setVcpuNum(int vcpu) {
|
||||
_vcpu = vcpu;
|
||||
this.vcpu = vcpu;
|
||||
}
|
||||
|
||||
public void setMemBalloning(boolean turnon) {
|
||||
_memBalloning = turnon;
|
||||
public void setMaxVcpuNum(int maxVcpu) {
|
||||
this.maxVcpu = maxVcpu;
|
||||
}
|
||||
|
||||
public int getVcpu() {
|
||||
return vcpu;
|
||||
}
|
||||
|
||||
public int getMaxVcpu() {
|
||||
return maxVcpu;
|
||||
}
|
||||
|
||||
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");
|
||||
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));
|
||||
}
|
||||
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");
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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());
|
||||
|
||||
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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};
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 (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 (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 (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());
|
||||
}
|
||||
if (guestOsMapping == null || host == null) {
|
||||
to.setPlatformEmulator(guestOS.getDisplayName() == null ? "Other" : guestOS.getDisplayName());
|
||||
} else {
|
||||
to.setPlatformEmulator(guestOsMapping.getGuestOsName());
|
||||
return new Pair<>(host.getTotalMemory(), host.getCpus());
|
||||
}
|
||||
|
||||
return to;
|
||||
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 {
|
||||
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 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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user