Skip to content

Commit bfc4f60

Browse files
authored
[VMware to KVM migration] Check source VM against the selected offering (apache#11908)
* [VMware to KVM migration] Check source VM against the selected offering * Fix build
1 parent c2c1e11 commit bfc4f60

File tree

4 files changed

+124
-3
lines changed

4 files changed

+124
-3
lines changed

plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
import org.apache.cloudstack.vm.UnmanagedInstanceTO;
6262
import org.apache.commons.collections.CollectionUtils;
6363
import org.apache.commons.lang.BooleanUtils;
64+
import org.apache.commons.lang3.ObjectUtils;
6465
import org.apache.commons.lang3.StringUtils;
6566

6667
import com.cloud.agent.api.BackupSnapshotCommand;
@@ -1393,6 +1394,9 @@ public Pair<UnmanagedInstanceTO, Boolean> getHypervisorVMOutOfBandAndCloneIfRequ
13931394
String datacenter = params.get(VmDetailConstants.VMWARE_DATACENTER_NAME);
13941395
String username = params.get(VmDetailConstants.VMWARE_VCENTER_USERNAME);
13951396
String password = params.get(VmDetailConstants.VMWARE_VCENTER_PASSWORD);
1397+
Integer requestedCpuNumber = params.containsKey(VmDetailConstants.CPU_NUMBER) ? Integer.parseInt(params.get(VmDetailConstants.CPU_NUMBER)) : null;
1398+
Integer requestedCpuSpeed = params.containsKey(VmDetailConstants.CPU_SPEED) ? Integer.parseInt(params.get(VmDetailConstants.CPU_SPEED)) : null;
1399+
Integer requestedMemory = params.containsKey(VmDetailConstants.MEMORY) ? Integer.parseInt(params.get(VmDetailConstants.MEMORY)) : null;
13961400

13971401
try {
13981402
VmwareContext context = connectToVcenter(vcenter, username, password);
@@ -1428,6 +1432,8 @@ public Pair<UnmanagedInstanceTO, Boolean> getHypervisorVMOutOfBandAndCloneIfRequ
14281432
}
14291433
}
14301434

1435+
checkSourceVmResourcesAgainstSelectedOfferingResources(vmMo, requestedCpuNumber, requestedCpuSpeed, requestedMemory);
1436+
14311437
logger.debug(String.format("Cloning VM %s at VMware host %s on vCenter %s", vmName, hostIp, vcenter));
14321438
VirtualMachineMO clonedVM = createCloneFromSourceVM(vmName, vmMo, dataCenterMO);
14331439
logger.debug(String.format("VM %s cloned successfully, to VM %s", vmName, clonedVM.getName()));
@@ -1444,6 +1450,29 @@ public Pair<UnmanagedInstanceTO, Boolean> getHypervisorVMOutOfBandAndCloneIfRequ
14441450
}
14451451
}
14461452

1453+
protected void checkSourceVmResourcesAgainstSelectedOfferingResources(VirtualMachineMO vmMo, Integer requestedCpuNumber, Integer requestedCpuSpeed, Integer requestedMemory) throws Exception {
1454+
if (ObjectUtils.allNull(requestedCpuNumber, requestedCpuSpeed, requestedMemory)) {
1455+
return;
1456+
}
1457+
VirtualMachineConfigSummary configSummary = vmMo.getConfigSummary();
1458+
if (configSummary != null) {
1459+
compareSourceVmResourceAgainstRequested(configSummary.getNumCpu(), requestedCpuNumber, "CPU number");
1460+
compareSourceVmResourceAgainstRequested(configSummary.getCpuReservation(), requestedCpuSpeed, "CPU speed");
1461+
compareSourceVmResourceAgainstRequested(configSummary.getMemorySizeMB(), requestedMemory, "Memory");
1462+
}
1463+
}
1464+
1465+
protected void compareSourceVmResourceAgainstRequested(Integer actualResource, Integer requestedResource, String resourceName) throws Exception {
1466+
if (ObjectUtils.anyNull(actualResource, requestedResource)) {
1467+
return;
1468+
}
1469+
if (requestedResource < actualResource) {
1470+
String err = String.format("The requested %s (%d) is less than the source VM %s (%d)", resourceName, requestedResource, resourceName, actualResource);
1471+
logger.error(err);
1472+
throw new CloudRuntimeException(err);
1473+
}
1474+
}
1475+
14471476
private boolean isWindowsVm(VirtualMachineMO vmMo) throws Exception {
14481477
UnmanagedInstanceTO sourceInstance = VmwareHelper.getUnmanagedInstance(vmMo.getRunningHost(), vmMo);
14491478
return sourceInstance.getOperatingSystem().toLowerCase().contains("windows");

plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/guru/VMwareGuruTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.util.List;
3434
import java.util.Map;
3535

36+
import com.vmware.vim25.VirtualMachineConfigSummary;
3637
import org.apache.cloudstack.storage.NfsMountManager;
3738
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
3839
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
@@ -626,4 +627,24 @@ public void testRemoveVMTemplateFileOutOfBand() throws Exception {
626627
boolean result = vMwareGuru.removeVMTemplateOutOfBand(dataStore, templateDir);
627628
assertTrue(result);
628629
}
630+
631+
@Test(expected = CloudRuntimeException.class)
632+
public void testCheckSourceVmResourcesAgainstSelectedOfferingResourcesInsufficientMemory() throws Exception {
633+
VirtualMachineMO virtualMachineMO = Mockito.mock(VirtualMachineMO.class);
634+
VirtualMachineConfigSummary configSummary = Mockito.mock(VirtualMachineConfigSummary.class);
635+
Mockito.when(virtualMachineMO.getConfigSummary()).thenReturn(configSummary);
636+
Mockito.when(configSummary.getNumCpu()).thenReturn(1);
637+
Mockito.when(configSummary.getMemorySizeMB()).thenReturn(2048);
638+
vMwareGuru.checkSourceVmResourcesAgainstSelectedOfferingResources(virtualMachineMO, 1, 500, 1024);
639+
}
640+
641+
@Test
642+
public void testCheckSourceVmResourcesAgainstSelectedOfferingResourcesGreaterOffering() throws Exception {
643+
VirtualMachineMO virtualMachineMO = Mockito.mock(VirtualMachineMO.class);
644+
VirtualMachineConfigSummary configSummary = Mockito.mock(VirtualMachineConfigSummary.class);
645+
Mockito.when(virtualMachineMO.getConfigSummary()).thenReturn(configSummary);
646+
Mockito.when(configSummary.getNumCpu()).thenReturn(1);
647+
Mockito.when(configSummary.getMemorySizeMB()).thenReturn(1024);
648+
vMwareGuru.checkSourceVmResourcesAgainstSelectedOfferingResources(virtualMachineMO, 2, 1500, 2048);
649+
}
629650
}

server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1644,16 +1644,45 @@ private UserVm importUnmanagedInstanceFromHypervisor(DataCenter zone, Cluster cl
16441644
}
16451645

16461646
private Pair<UnmanagedInstanceTO, Boolean> getSourceVmwareUnmanagedInstance(String vcenter, String datacenterName, String username,
1647-
String password, String clusterName, String sourceHostName,
1648-
String sourceVM) {
1647+
String password, String clusterName, String sourceHostName,
1648+
String sourceVM, ServiceOfferingVO serviceOffering) {
16491649
HypervisorGuru vmwareGuru = hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.VMware);
16501650

16511651
Map<String, String> params = createParamsForTemplateFromVmwareVmMigration(vcenter, datacenterName,
16521652
username, password, clusterName, sourceHostName, sourceVM);
1653+
addServiceOfferingDetailsToParams(params, serviceOffering);
16531654

16541655
return vmwareGuru.getHypervisorVMOutOfBandAndCloneIfRequired(sourceHostName, sourceVM, params);
16551656
}
16561657

1658+
/**
1659+
* Add the minimum resources to check on the hypervisor source VM before converting the instance against the selected offering resources
1660+
* @param params sets the minimum CPU number, CPU speed and memory to be checked against the source VM
1661+
* @param serviceOffering service offering for the converted VM
1662+
*/
1663+
protected void addServiceOfferingDetailsToParams(Map<String, String> params, ServiceOfferingVO serviceOffering) {
1664+
if (serviceOffering != null) {
1665+
serviceOfferingDao.loadDetails(serviceOffering);
1666+
Map<String, String> serviceOfferingDetails = serviceOffering.getDetails();
1667+
1668+
if (serviceOffering.getCpu() != null) {
1669+
params.put(VmDetailConstants.CPU_NUMBER, String.valueOf(serviceOffering.getCpu()));
1670+
} else if (MapUtils.isNotEmpty(serviceOfferingDetails) && serviceOfferingDetails.containsKey(ApiConstants.MIN_CPU_NUMBER)) {
1671+
params.put(VmDetailConstants.CPU_NUMBER, serviceOfferingDetails.get(ApiConstants.MIN_CPU_NUMBER));
1672+
}
1673+
1674+
if (serviceOffering.getSpeed() != null) {
1675+
params.put(VmDetailConstants.CPU_SPEED, String.valueOf(serviceOffering.getSpeed()));
1676+
}
1677+
1678+
if (serviceOffering.getRamSize() != null) {
1679+
params.put(VmDetailConstants.MEMORY, String.valueOf(serviceOffering.getRamSize()));
1680+
} else if (MapUtils.isNotEmpty(serviceOfferingDetails) && serviceOfferingDetails.containsKey(ApiConstants.MIN_MEMORY)) {
1681+
params.put(VmDetailConstants.MEMORY, serviceOfferingDetails.get(ApiConstants.MIN_MEMORY));
1682+
}
1683+
}
1684+
}
1685+
16571686
private String createOvfTemplateOfSourceVmwareUnmanagedInstance(String vcenter, String datacenterName, String username,
16581687
String password, String clusterName, String sourceHostName,
16591688
String sourceVMwareInstanceName, DataStoreTO convertLocation, int threadsCountToExportOvf) {
@@ -1734,7 +1763,7 @@ protected UserVm importUnmanagedInstanceFromVmwareToKvm(DataCenter zone, Cluster
17341763
// sourceVMwareInstance could be a cloned instance from sourceVMName, of the sourceVMName itself if its powered off.
17351764
// isClonedInstance indicates if the VM is a clone of sourceVMName
17361765

1737-
Pair<UnmanagedInstanceTO, Boolean> sourceInstanceDetails = getSourceVmwareUnmanagedInstance(vcenter, datacenterName, username, password, clusterName, sourceHostName, sourceVMName);
1766+
Pair<UnmanagedInstanceTO, Boolean> sourceInstanceDetails = getSourceVmwareUnmanagedInstance(vcenter, datacenterName, username, password, clusterName, sourceHostName, sourceVMName, serviceOffering);
17381767
sourceVMwareInstance = sourceInstanceDetails.first();
17391768
isClonedInstance = sourceInstanceDetails.second();
17401769

server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040

4141
import com.cloud.offering.DiskOffering;
4242
import com.cloud.vm.ImportVMTaskVO;
43+
import org.apache.cloudstack.api.ApiConstants;
4344
import org.apache.cloudstack.api.ResponseGenerator;
4445
import org.apache.cloudstack.api.ResponseObject;
4546
import org.apache.cloudstack.api.ServerApiException;
@@ -1309,4 +1310,45 @@ public void testCheckExtraParamsAllowedEnabledParamNotInTheAllowedList() {
13091310
Mockito.when(configKeyMockParamsAllowedList.value()).thenReturn("network,x");
13101311
unmanagedVMsManager.checkExtraParamsAllowed("--mac 00:0c:29:e6:3d:9d:ip:192.168.0.89,192.168.0.1,24,192.168.0.254 -x");
13111312
}
1313+
1314+
@Test
1315+
public void testAddServiceOfferingDetailsToParamsFixedOffering() {
1316+
Map<String, String> params = new HashMap<>();
1317+
ServiceOfferingVO serviceOfferingVO = mock(ServiceOfferingVO.class);
1318+
Mockito.when(serviceOfferingVO.getCpu()).thenReturn(2);
1319+
Mockito.when(serviceOfferingVO.getRamSize()).thenReturn(2048);
1320+
unmanagedVMsManager.addServiceOfferingDetailsToParams(params, serviceOfferingVO);
1321+
Assert.assertEquals("2", params.get(VmDetailConstants.CPU_NUMBER));
1322+
Assert.assertEquals("2048", params.get(VmDetailConstants.MEMORY));
1323+
}
1324+
1325+
@Test
1326+
public void testAddServiceOfferingDetailsToParamsCustomConstrainedOffering() {
1327+
Map<String, String> params = new HashMap<>();
1328+
ServiceOfferingVO serviceOfferingVO = mock(ServiceOfferingVO.class);
1329+
Map<String, String> details = new HashMap<>();
1330+
details.put(ApiConstants.MIN_CPU_NUMBER, "1");
1331+
details.put(ApiConstants.MIN_MEMORY, "1024");
1332+
Mockito.when(serviceOfferingVO.getDetails()).thenReturn(details);
1333+
Mockito.when(serviceOfferingVO.getCpu()).thenReturn(null);
1334+
Mockito.when(serviceOfferingVO.getSpeed()).thenReturn(1500);
1335+
Mockito.when(serviceOfferingVO.getRamSize()).thenReturn(null);
1336+
unmanagedVMsManager.addServiceOfferingDetailsToParams(params, serviceOfferingVO);
1337+
Assert.assertEquals("1", params.get(VmDetailConstants.CPU_NUMBER));
1338+
Assert.assertEquals("1500", params.get(VmDetailConstants.CPU_SPEED));
1339+
Assert.assertEquals("1024", params.get(VmDetailConstants.MEMORY));
1340+
}
1341+
1342+
@Test
1343+
public void testAddServiceOfferingDetailsToParamsCustomUnconstrainedOffering() {
1344+
Map<String, String> params = new HashMap<>();
1345+
ServiceOfferingVO serviceOfferingVO = mock(ServiceOfferingVO.class);
1346+
Mockito.when(serviceOfferingVO.getCpu()).thenReturn(null);
1347+
Mockito.when(serviceOfferingVO.getSpeed()).thenReturn(null);
1348+
Mockito.when(serviceOfferingVO.getRamSize()).thenReturn(null);
1349+
unmanagedVMsManager.addServiceOfferingDetailsToParams(params, serviceOfferingVO);
1350+
Assert.assertFalse(params.containsKey(VmDetailConstants.CPU_NUMBER));
1351+
Assert.assertFalse(params.containsKey(VmDetailConstants.CPU_SPEED));
1352+
Assert.assertFalse(params.containsKey(VmDetailConstants.MEMORY));
1353+
}
13121354
}

0 commit comments

Comments
 (0)