Skip to content

Commit 1f28df8

Browse files
committed
Refactor listHostsforMigrationofVM method
1 parent f4b0e1d commit 1f28df8

File tree

2 files changed

+110
-29
lines changed

2 files changed

+110
-29
lines changed

server/src/main/java/com/cloud/server/ManagementServerImpl.java

Lines changed: 107 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,17 +1454,27 @@ protected boolean zoneWideVolumeRequiresStorageMotion(PrimaryDataStore volumeDat
14541454
return false;
14551455
}
14561456

1457-
@Override
1458-
public Ternary<Pair<List<? extends Host>, Integer>, List<? extends Host>, Map<Host, Boolean>> listHostsForMigrationOfVM(final VirtualMachine vm, final Long startIndex, final Long pageSize,
1459-
final String keyword, List<VirtualMachine> vmList) {
1460-
1461-
validateVmForHostMigration(vm);
1457+
/**
1458+
* Get technically compatible hosts for VM migration (storage, hypervisor, UEFI filtering).
1459+
* This determines which hosts are technically capable of hosting the VM based on
1460+
* storage requirements, hypervisor capabilities, and UEFI requirements.
1461+
*
1462+
* @param vm The virtual machine to migrate
1463+
* @param startIndex Starting index for pagination
1464+
* @param pageSize Page size for pagination
1465+
* @param keyword Keyword filter for host search
1466+
* @return Ternary containing: (all hosts with count, filtered compatible hosts, storage motion requirements map)
1467+
*/
1468+
protected Ternary<Pair<List<HostVO>, Integer>, List<HostVO>, Map<Host, Boolean>> getTechnicallyCompatibleHosts(
1469+
final VirtualMachine vm,
1470+
final Long startIndex,
1471+
final Long pageSize,
1472+
final String keyword) {
14621473

1474+
// GPU check
14631475
if (_serviceOfferingDetailsDao.findDetail(vm.getServiceOfferingId(), GPU.Keys.pciDevice.toString()) != null) {
14641476
logger.info(" Live Migration of GPU enabled VM : " + vm.getInstanceName() + " is not supported");
1465-
// Return empty list.
1466-
return new Ternary<>(new Pair<>(new ArrayList<>(), 0),
1467-
new ArrayList<>(), new HashMap<>());
1477+
return new Ternary<>(new Pair<>(new ArrayList<>(), 0), new ArrayList<>(), new HashMap<>());
14681478
}
14691479

14701480
final long srcHostId = vm.getHostId();
@@ -1478,6 +1488,7 @@ public Ternary<Pair<List<? extends Host>, Integer>, List<? extends Host>, Map<Ho
14781488
ex.addProxyObject(vm.getUuid(), "vmId");
14791489
throw ex;
14801490
}
1491+
14811492
String srcHostVersion = srcHost.getHypervisorVersion();
14821493
if (HypervisorType.KVM.equals(srcHost.getHypervisorType()) && srcHostVersion == null) {
14831494
srcHostVersion = "";
@@ -1513,7 +1524,7 @@ public Ternary<Pair<List<? extends Host>, Integer>, List<? extends Host>, Map<Ho
15131524
List<HostVO> allHosts = null;
15141525
List<HostVO> filteredHosts = null;
15151526
final Map<Host, Boolean> requiresStorageMotion = new HashMap<>();
1516-
DataCenterDeployment plan = null;
1527+
15171528
if (canMigrateWithStorage) {
15181529
Long podId = !VirtualMachine.Type.User.equals(vm.getType()) ? srcHost.getPodId() : null;
15191530
allHostsPair = searchForServers(startIndex, pageSize, null, hostType, null, srcHost.getDataCenterId(), podId, null, null, keyword,
@@ -1562,7 +1573,6 @@ public Ternary<Pair<List<? extends Host>, Integer>, List<? extends Host>, Map<Ho
15621573
if (CollectionUtils.isEmpty(filteredHosts)) {
15631574
return new Ternary<>(new Pair<>(allHosts, allHostsPair.second()), new ArrayList<>(), new HashMap<>());
15641575
}
1565-
plan = new DataCenterDeployment(srcHost.getDataCenterId(), podId, null, null, null, null);
15661576
} else {
15671577
final Long cluster = srcHost.getClusterId();
15681578
if (logger.isDebugEnabled()) {
@@ -1571,19 +1581,37 @@ public Ternary<Pair<List<? extends Host>, Integer>, List<? extends Host>, Map<Ho
15711581
allHostsPair = searchForServers(startIndex, pageSize, null, hostType, null, null, null, cluster, null, keyword, null, null, null,
15721582
null, srcHost.getId());
15731583
allHosts = allHostsPair.first();
1574-
plan = new DataCenterDeployment(srcHost.getDataCenterId(), srcHost.getPodId(), srcHost.getClusterId(), null, null, null);
1584+
filteredHosts = allHosts;
15751585
}
15761586

1577-
final Pair<List<? extends Host>, Integer> otherHosts = new Pair<>(allHosts, allHostsPair.second());
1587+
final Pair<List<HostVO>, Integer> allHostsPairResult = new Pair<>(allHosts, allHostsPair.second());
15781588
Pair<Boolean, List<HostVO>> uefiFilteredResult = filterUefiHostsForMigration(allHosts, filteredHosts, vm);
15791589
if (!uefiFilteredResult.first()) {
1580-
return new Ternary<>(otherHosts, new ArrayList<>(), new HashMap<>());
1590+
return new Ternary<>(allHostsPairResult, new ArrayList<>(), new HashMap<>());
15811591
}
15821592
filteredHosts = uefiFilteredResult.second();
15831593

1584-
List<Host> suitableHosts = new ArrayList<>();
1594+
return new Ternary<>(allHostsPairResult, filteredHosts, requiresStorageMotion);
1595+
}
1596+
1597+
/**
1598+
* Apply affinity group constraints and other exclusion rules for VM migration.
1599+
* This builds an ExcludeList based on affinity groups, DPDK requirements, and dedicated resources.
1600+
*
1601+
* @param vm The virtual machine to migrate
1602+
* @param vmProfile The VM profile
1603+
* @param plan The deployment plan
1604+
* @param vmList List of VMs with current/simulated placements for affinity processing
1605+
* @return ExcludeList containing hosts to avoid
1606+
*/
1607+
protected ExcludeList applyAffinityConstraints(
1608+
final VirtualMachine vm,
1609+
final VirtualMachineProfile vmProfile,
1610+
final DataCenterDeployment plan,
1611+
final List<VirtualMachine> vmList) {
1612+
15851613
final ExcludeList excludes = new ExcludeList();
1586-
excludes.addHost(srcHostId);
1614+
excludes.addHost(vm.getHostId());
15871615

15881616
if (dpdkHelper.isVMDpdkEnabled(vm.getId())) {
15891617
excludeNonDPDKEnabledHosts(plan, excludes);
@@ -1599,13 +1627,37 @@ public Ternary<Pair<List<? extends Host>, Integer>, List<? extends Host>, Map<Ho
15991627
}
16001628

16011629
if (vm.getType() == VirtualMachine.Type.User || vm.getType() == VirtualMachine.Type.DomainRouter) {
1602-
final DataCenterVO dc = _dcDao.findById(srcHost.getDataCenterId());
1630+
final DataCenterVO dc = _dcDao.findById(plan.getDataCenterId());
16031631
_dpMgr.checkForNonDedicatedResources(vmProfile, dc, excludes);
16041632
}
16051633

1634+
return excludes;
1635+
}
1636+
1637+
/**
1638+
* Get hosts with available capacity using host allocators, and apply architecture filtering.
1639+
*
1640+
* @param vm The virtual machine (for architecture filtering)
1641+
* @param vmProfile The VM profile
1642+
* @param plan The deployment plan
1643+
* @param compatibleHosts List of technically compatible hosts
1644+
* @param excludes ExcludeList with hosts to avoid
1645+
* @param srcHost Source host (for architecture filtering)
1646+
* @return List of suitable hosts with capacity
1647+
*/
1648+
protected List<Host> getCapableSuitableHosts(
1649+
final VirtualMachine vm,
1650+
final VirtualMachineProfile vmProfile,
1651+
final DataCenterDeployment plan,
1652+
final List<HostVO> compatibleHosts,
1653+
final ExcludeList excludes,
1654+
final Host srcHost) {
1655+
1656+
List<Host> suitableHosts = new ArrayList<>();
1657+
16061658
for (final HostAllocator allocator : hostAllocators) {
1607-
if (CollectionUtils.isNotEmpty(filteredHosts)) {
1608-
suitableHosts = allocator.allocateTo(vmProfile, plan, Host.Type.Routing, excludes, filteredHosts, HostAllocator.RETURN_UPTO_ALL, false);
1659+
if (CollectionUtils.isNotEmpty(compatibleHosts)) {
1660+
suitableHosts = allocator.allocateTo(vmProfile, plan, Host.Type.Routing, excludes, compatibleHosts, HostAllocator.RETURN_UPTO_ALL, false);
16091661
} else {
16101662
suitableHosts = allocator.allocateTo(vmProfile, plan, Host.Type.Routing, excludes, HostAllocator.RETURN_UPTO_ALL, false);
16111663
}
@@ -1631,6 +1683,43 @@ public Ternary<Pair<List<? extends Host>, Integer>, List<? extends Host>, Map<Ho
16311683
}
16321684
}
16331685

1686+
return suitableHosts;
1687+
}
1688+
1689+
@Override
1690+
public Ternary<Pair<List<? extends Host>, Integer>, List<? extends Host>, Map<Host, Boolean>> listHostsForMigrationOfVM(final VirtualMachine vm, final Long startIndex, final Long pageSize,
1691+
final String keyword, List<VirtualMachine> vmList) {
1692+
1693+
validateVmForHostMigration(vm);
1694+
1695+
// Get technically compatible hosts (storage, hypervisor, UEFI)
1696+
Ternary<Pair<List<HostVO>, Integer>, List<HostVO>, Map<Host, Boolean>> compatibilityResult =
1697+
getTechnicallyCompatibleHosts(vm, startIndex, pageSize, keyword);
1698+
1699+
Pair<List<HostVO>, Integer> allHostsPair = compatibilityResult.first();
1700+
List<HostVO> filteredHosts = compatibilityResult.second();
1701+
Map<Host, Boolean> requiresStorageMotion = compatibilityResult.third();
1702+
1703+
// If no compatible hosts, return early
1704+
if (CollectionUtils.isEmpty(filteredHosts)) {
1705+
final Pair<List<? extends Host>, Integer> otherHosts = new Pair<>(allHostsPair.first(), allHostsPair.second());
1706+
return new Ternary<>(otherHosts, new ArrayList<>(), requiresStorageMotion);
1707+
}
1708+
1709+
// Create deployment plan and VM profile
1710+
final Host srcHost = _hostDao.findById(vm.getHostId());
1711+
final DataCenterDeployment plan = new DataCenterDeployment(
1712+
srcHost.getDataCenterId(), srcHost.getPodId(), srcHost.getClusterId(), null, null, null);
1713+
final VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(
1714+
vm, null, _offeringDao.findById(vm.getId(), vm.getServiceOfferingId()), null, null);
1715+
1716+
// Apply affinity constraints
1717+
final ExcludeList excludes = applyAffinityConstraints(vm, vmProfile, plan, vmList);
1718+
1719+
// Get hosts with capacity
1720+
List<Host> suitableHosts = getCapableSuitableHosts(vm, vmProfile, plan, filteredHosts, excludes, srcHost);
1721+
1722+
final Pair<List<? extends Host>, Integer> otherHosts = new Pair<>(allHostsPair.first(), allHostsPair.second());
16341723
return new Ternary<>(otherHosts, suitableHosts, requiresStorageMotion);
16351724
}
16361725

server/src/test/java/com/cloud/server/ManagementServerImplTest.java

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,10 +1134,6 @@ public void testListHostsForMigrationOfVMNonUefiVm() {
11341134
DiskOfferingVO diskOffering = mockSharedDiskOffering(1L);
11351135
Mockito.when(diskOfferingDao.findById(volume.getDiskOfferingId())).thenReturn(diskOffering);
11361136

1137-
// No UEFI detail - VM is NOT UEFI-enabled
1138-
Mockito.when(userVmDetailsDao.findDetail(vm.getId(), ApiConstants.BootType.UEFI.toString()))
1139-
.thenReturn(null);
1140-
11411137
// Mock searchForServers for cluster-scoped search
11421138
HostVO host1 = mockHost(101L, 1L, 1L, 1L, HypervisorType.KVM);
11431139
HostVO host2 = mockHost(102L, 1L, 1L, 1L, HypervisorType.KVM);
@@ -1206,7 +1202,7 @@ public void testListHostsForMigrationOfVMWithUefiVmClusterScope() {
12061202
List<HostVO> uefiCompatibleHosts = List.of(host1);
12071203
Pair<Boolean, List<HostVO>> uefiFilterResult = new Pair<>(true, uefiCompatibleHosts);
12081204
Mockito.doReturn(uefiFilterResult).when(spy).filterUefiHostsForMigration(
1209-
Mockito.anyList(), Mockito.isNull(), Mockito.any());
1205+
Mockito.anyList(), Mockito.anyList(), Mockito.any());
12101206

12111207
// Setup other mocks
12121208
Mockito.when(dpdkHelper.isVMDpdkEnabled(vm.getId())).thenReturn(false);
@@ -1351,7 +1347,7 @@ public void testListHostsForMigrationOfVMUefiFilteringReturnsEmpty() {
13511347
// This simulates the scenario where UEFI VM has no compatible hosts
13521348
Pair<Boolean, List<HostVO>> uefiFilterResult = new Pair<>(false, null);
13531349
Mockito.doReturn(uefiFilterResult).when(spy).filterUefiHostsForMigration(
1354-
Mockito.anyList(), Mockito.isNull(), Mockito.any());
1350+
Mockito.anyList(), Mockito.anyList(), Mockito.any());
13551351

13561352
// Note: No other mocks needed because when filterUefiHostsForMigration returns false,
13571353
// the method returns early and doesn't proceed to host allocation or other processing
@@ -1747,7 +1743,7 @@ public void testListHostsForMigrationOfVMWithNoVolumes() {
17471743
// Set up mocks without volume since there are no volumes
17481744
Pair<Boolean, List<HostVO>> uefiResult = new Pair<>(true, hosts);
17491745
Mockito.doReturn(uefiResult).when(spy).filterUefiHostsForMigration(
1750-
Mockito.anyList(), Mockito.isNull(), Mockito.any());
1746+
Mockito.anyList(), Mockito.anyList(), Mockito.any());
17511747
Mockito.when(dpdkHelper.isVMDpdkEnabled(vm.getId())).thenReturn(false);
17521748
Mockito.when(affinityGroupVMMapDao.countAffinityGroupsForVm(vm.getId())).thenReturn(0L);
17531749
DataCenterVO dc = Mockito.mock(DataCenterVO.class);
@@ -2175,10 +2171,6 @@ private void setupMigrationMocks(VMInstanceVO vm, HostVO srcHost,
21752171
Mockito.when(hostAllocator.allocateTo(Mockito.any(), Mockito.any(), Mockito.any(),
21762172
Mockito.any(), Mockito.anyList(), Mockito.anyInt(), Mockito.anyBoolean()))
21772173
.thenReturn(new ArrayList<>(targetHosts));
2178-
// 2. Version without filteredHosts list (used when canMigrateWithStorage = false)
2179-
Mockito.when(hostAllocator.allocateTo(Mockito.any(), Mockito.any(), Mockito.any(),
2180-
Mockito.any(), Mockito.anyInt(), Mockito.anyBoolean()))
2181-
.thenReturn(new ArrayList<>(targetHosts));
21822174
}
21832175

21842176
private VMInstanceVO mockRunningVM(Long id, HypervisorType hypervisorType) {

0 commit comments

Comments
 (0)