From eb5c8a5a8c9ec6dc79fe5033f98b15b94d2a33d1 Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Mon, 9 Jan 2023 15:33:10 +0530 Subject: [PATCH] server: correctly list suitable hosts for migration with uefi capability (#7024) Signed-off-by: Abhishek Kumar --- .../com/cloud/host/dao/HostDetailsDao.java | 3 + .../cloud/host/dao/HostDetailsDaoImpl.java | 13 +- .../cloud/server/ManagementServerImpl.java | 106 +++++++++------- .../server/ManagementServerImplTest.java | 120 +++++++++++++++++- 4 files changed, 197 insertions(+), 45 deletions(-) diff --git a/engine/schema/src/main/java/com/cloud/host/dao/HostDetailsDao.java b/engine/schema/src/main/java/com/cloud/host/dao/HostDetailsDao.java index 7f1a6185e62..8dc4efa91f3 100644 --- a/engine/schema/src/main/java/com/cloud/host/dao/HostDetailsDao.java +++ b/engine/schema/src/main/java/com/cloud/host/dao/HostDetailsDao.java @@ -16,6 +16,7 @@ // under the License. package com.cloud.host.dao; +import java.util.List; import java.util.Map; import com.cloud.host.DetailVO; @@ -29,4 +30,6 @@ public interface HostDetailsDao extends GenericDao { DetailVO findDetail(long hostId, String name); void deleteDetails(long hostId); + + List findByName(String name); } diff --git a/engine/schema/src/main/java/com/cloud/host/dao/HostDetailsDaoImpl.java b/engine/schema/src/main/java/com/cloud/host/dao/HostDetailsDaoImpl.java index b864a592105..9c1340592f9 100644 --- a/engine/schema/src/main/java/com/cloud/host/dao/HostDetailsDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/host/dao/HostDetailsDaoImpl.java @@ -22,7 +22,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; - import org.springframework.stereotype.Component; import com.cloud.host.DetailVO; @@ -37,6 +36,7 @@ import com.cloud.utils.exception.CloudRuntimeException; public class HostDetailsDaoImpl extends GenericDaoBase implements HostDetailsDao { protected final SearchBuilder HostSearch; protected final SearchBuilder DetailSearch; + protected final SearchBuilder DetailNameSearch; public HostDetailsDaoImpl() { HostSearch = createSearchBuilder(); @@ -47,6 +47,10 @@ public class HostDetailsDaoImpl extends GenericDaoBase implement DetailSearch.and("hostId", DetailSearch.entity().getHostId(), SearchCriteria.Op.EQ); DetailSearch.and("name", DetailSearch.entity().getName(), SearchCriteria.Op.EQ); DetailSearch.done(); + + DetailNameSearch = createSearchBuilder(); + DetailNameSearch.and("name", DetailNameSearch.entity().getName(), SearchCriteria.Op.EQ); + DetailNameSearch.done(); } @Override @@ -119,4 +123,11 @@ public class HostDetailsDaoImpl extends GenericDaoBase implement } txn.commit(); } + + @Override + public List findByName(String name) { + SearchCriteria sc = DetailNameSearch.create(); + sc.setParameters("name", name); + return listBy(sc); + } } diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index 540b2e39159..c1a3e9905c1 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -43,22 +43,6 @@ import javax.crypto.spec.SecretKeySpec; import javax.inject.Inject; import javax.naming.ConfigurationException; -import com.cloud.agent.api.Answer; -import com.cloud.agent.api.Command; -import com.cloud.agent.api.PatchSystemVmAnswer; -import com.cloud.agent.api.PatchSystemVmCommand; -import com.cloud.agent.api.routing.NetworkElementCommand; -import com.cloud.agent.manager.Commands; -import com.cloud.dc.DomainVlanMapVO; -import com.cloud.dc.dao.DomainVlanMapDao; -import com.cloud.exception.AgentUnavailableException; -import com.cloud.network.Networks; -import com.cloud.utils.db.UUIDManager; -import com.cloud.utils.fsm.StateMachine2; -import com.cloud.vm.DomainRouterVO; -import com.cloud.vm.NicVO; -import com.cloud.vm.dao.DomainRouterDao; -import com.cloud.vm.dao.NicDao; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.affinity.AffinityGroupProcessor; @@ -611,8 +595,14 @@ import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; import com.cloud.agent.api.GetVncPortAnswer; import com.cloud.agent.api.GetVncPortCommand; +import com.cloud.agent.api.PatchSystemVmAnswer; +import com.cloud.agent.api.PatchSystemVmCommand; +import com.cloud.agent.api.routing.NetworkElementCommand; +import com.cloud.agent.manager.Commands; import com.cloud.agent.manager.allocator.HostAllocator; import com.cloud.alert.Alert; import com.cloud.alert.AlertManager; @@ -633,6 +623,7 @@ import com.cloud.consoleproxy.ConsoleProxyManager; import com.cloud.dc.AccountVlanMapVO; import com.cloud.dc.ClusterVO; import com.cloud.dc.DataCenterVO; +import com.cloud.dc.DomainVlanMapVO; import com.cloud.dc.HostPodVO; import com.cloud.dc.Pod; import com.cloud.dc.PodVlanMapVO; @@ -642,6 +633,7 @@ import com.cloud.dc.VlanVO; import com.cloud.dc.dao.AccountVlanMapDao; import com.cloud.dc.dao.ClusterDao; import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.DomainVlanMapDao; import com.cloud.dc.dao.HostPodDao; import com.cloud.dc.dao.PodVlanMapDao; import com.cloud.dc.dao.VlanDao; @@ -656,6 +648,7 @@ import com.cloud.event.ActionEventUtils; import com.cloud.event.EventTypes; import com.cloud.event.EventVO; import com.cloud.event.dao.EventDao; +import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientAddressCapacityException; import com.cloud.exception.InvalidParameterValueException; @@ -686,6 +679,7 @@ import com.cloud.network.IpAddressManager; import com.cloud.network.IpAddressManagerImpl; import com.cloud.network.Network; import com.cloud.network.NetworkModel; +import com.cloud.network.Networks; import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.IPAddressVO; import com.cloud.network.dao.LoadBalancerDao; @@ -760,13 +754,17 @@ import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.Transaction; import com.cloud.utils.db.TransactionCallbackNoReturn; import com.cloud.utils.db.TransactionStatus; +import com.cloud.utils.db.UUIDManager; import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.fsm.StateMachine2; import com.cloud.utils.net.MacAddress; import com.cloud.utils.net.NetUtils; import com.cloud.utils.ssh.SSHKeysHelper; import com.cloud.vm.ConsoleProxyVO; import com.cloud.vm.DiskProfile; +import com.cloud.vm.DomainRouterVO; import com.cloud.vm.InstanceGroupVO; +import com.cloud.vm.NicVO; import com.cloud.vm.SecondaryStorageVmVO; import com.cloud.vm.UserVmDetailVO; import com.cloud.vm.UserVmManager; @@ -778,7 +776,9 @@ import com.cloud.vm.VirtualMachineManager; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.VirtualMachineProfileImpl; import com.cloud.vm.dao.ConsoleProxyDao; +import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.InstanceGroupDao; +import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.SecondaryStorageVmDao; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.UserVmDetailsDao; @@ -794,6 +794,9 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe public static final ConfigKey customCsIdentifier = new ConfigKey("Advanced", String.class, "custom.cs.identifier", UUID.randomUUID().toString().split("-")[0].substring(4), "Custom identifier for the cloudstack installation", true, ConfigKey.Scope.Global); private static final VirtualMachine.Type []systemVmTypes = { VirtualMachine.Type.SecondaryStorageVm, VirtualMachine.Type.ConsoleProxy}; + private static final List LIVE_MIGRATION_SUPPORTING_HYPERVISORS = List.of(HypervisorType.Hyperv, HypervisorType.KVM, + HypervisorType.LXC, HypervisorType.Ovm, HypervisorType.Ovm3, HypervisorType.Simulator, HypervisorType.VMware, HypervisorType.XenServer); + @Inject public AccountManager _accountMgr; @Inject @@ -807,7 +810,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe @Inject private ClusterDao _clusterDao; @Inject - private UserVmDetailsDao _UserVmDetailsDao; + protected UserVmDetailsDao _UserVmDetailsDao; @Inject private SecondaryStorageVmDao _secStorageVmDao; @Inject @@ -823,7 +826,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe @Inject private HostDao _hostDao; @Inject - private HostDetailsDao _detailsDao; + protected HostDetailsDao _detailsDao; @Inject private UserDao _userDao; @Inject @@ -1277,6 +1280,26 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe return new Pair, Integer>(result.first(), result.second()); } + protected Pair> filterUefiHostsForMigration(List allHosts, List filteredHosts, VMInstanceVO vm) { + UserVmDetailVO userVmDetailVO = _UserVmDetailsDao.findDetail(vm.getId(), ApiConstants.BootType.UEFI.toString()); + if (userVmDetailVO != null && + (ApiConstants.BootMode.LEGACY.toString().equalsIgnoreCase(userVmDetailVO.getValue()) || + ApiConstants.BootMode.SECURE.toString().equalsIgnoreCase(userVmDetailVO.getValue()))) { + s_logger.info(" Live Migration of UEFI enabled VM : " + vm.getInstanceName() + " is not supported"); + if (CollectionUtils.isEmpty(filteredHosts)) { + filteredHosts = new ArrayList<>(allHosts); + } + List details = _detailsDao.findByName(Host.HOST_UEFI_ENABLE); + List uefiEnabledHosts = details.stream().filter(x -> Boolean.TRUE.toString().equalsIgnoreCase(x.getValue())).map(DetailVO::getHostId).collect(Collectors.toList()); + if (CollectionUtils.isEmpty(uefiEnabledHosts)) { + return new Pair<>(false, null); + } + filteredHosts.removeIf(host -> !uefiEnabledHosts.contains(host.getId())); + return new Pair<>(!filteredHosts.isEmpty(), filteredHosts); + } + return new Pair<>(true, filteredHosts); + } + @Override public Ternary, Integer>, List, Map> listHostsForMigrationOfVM(final Long vmId, final Long startIndex, final Long pageSize, final String keyword) { @@ -1303,33 +1326,21 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe throw ex; } - UserVmDetailVO userVmDetailVO = _UserVmDetailsDao.findDetail(vm.getId(), ApiConstants.BootType.UEFI.toString()); - if (userVmDetailVO != null) { - s_logger.info(" Live Migration of UEFI enabled VM : " + vm.getInstanceName() + " is not supported"); - if ("legacy".equalsIgnoreCase(userVmDetailVO.getValue()) || "secure".equalsIgnoreCase(userVmDetailVO.getValue())) { - // Return empty list. - return new Ternary, Integer>, List, Map>(new Pair, - Integer>(new ArrayList(), new Integer(0)), new ArrayList(), new HashMap()); - } - } - if (_serviceOfferingDetailsDao.findDetail(vm.getServiceOfferingId(), GPU.Keys.pciDevice.toString()) != null) { s_logger.info(" Live Migration of GPU enabled VM : " + vm.getInstanceName() + " is not supported"); // Return empty list. - return new Ternary, Integer>, List, Map>(new Pair, Integer>(new ArrayList(), new Integer(0)), - new ArrayList(), new HashMap()); + return new Ternary<>(new Pair<>(new ArrayList(), new Integer(0)), + new ArrayList<>(), new HashMap<>()); } - if (!vm.getHypervisorType().equals(HypervisorType.XenServer) && !vm.getHypervisorType().equals(HypervisorType.VMware) && !vm.getHypervisorType().equals(HypervisorType.KVM) - && !vm.getHypervisorType().equals(HypervisorType.Ovm) && !vm.getHypervisorType().equals(HypervisorType.Hyperv) && !vm.getHypervisorType().equals(HypervisorType.LXC) - && !vm.getHypervisorType().equals(HypervisorType.Simulator) && !vm.getHypervisorType().equals(HypervisorType.Ovm3)) { + if (!LIVE_MIGRATION_SUPPORTING_HYPERVISORS.contains(vm.getHypervisorType())) { if (s_logger.isDebugEnabled()) { s_logger.debug(vm + " is not XenServer/VMware/KVM/Ovm/Hyperv/Ovm3, cannot migrate this VM."); } throw new InvalidParameterValueException("Unsupported Hypervisor Type for VM migration, we support " + "XenServer/VMware/KVM/Ovm/Hyperv/Ovm3 only"); } - if (vm.getType().equals(VirtualMachine.Type.User) && vm.getHypervisorType().equals(HypervisorType.LXC)) { + if (VirtualMachine.Type.User.equals(vm.getType()) && HypervisorType.LXC.equals(vm.getHypervisorType())) { throw new InvalidParameterValueException("Unsupported Hypervisor Type for User VM migration, we support XenServer/VMware/KVM/Ovm/Hyperv/Ovm3 only"); } @@ -1376,21 +1387,21 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe final Type hostType = srcHost.getType(); Pair, Integer> allHostsPair = null; List allHosts = null; - List hostsForMigrationWithStorage = null; - final Map requiresStorageMotion = new HashMap(); + List filteredHosts = null; + final Map requiresStorageMotion = new HashMap<>(); DataCenterDeployment plan = null; if (canMigrateWithStorage) { Long podId = !VirtualMachine.Type.User.equals(vm.getType()) ? srcHost.getPodId() : null; allHostsPair = searchForServers(startIndex, pageSize, null, hostType, null, srcHost.getDataCenterId(), podId, null, null, keyword, null, null, srcHost.getHypervisorType(), null, srcHost.getId()); allHosts = allHostsPair.first(); - hostsForMigrationWithStorage = new ArrayList<>(allHosts); + filteredHosts = new ArrayList<>(allHosts); for (final VolumeVO volume : volumes) { StoragePool storagePool = _poolDao.findById(volume.getPoolId()); Long volClusterId = storagePool.getClusterId(); - for (Iterator iterator = hostsForMigrationWithStorage.iterator(); iterator.hasNext();) { + for (Iterator iterator = filteredHosts.iterator(); iterator.hasNext();) { final Host host = iterator.next(); String hostVersion = host.getHypervisorVersion(); if (HypervisorType.KVM.equals(host.getHypervisorType()) && hostVersion == null) { @@ -1434,6 +1445,9 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } } + if (CollectionUtils.isEmpty(filteredHosts)) { + return new Ternary<>(new Pair<>(allHosts, allHostsPair.second()), new ArrayList<>(), new HashMap<>()); + } plan = new DataCenterDeployment(srcHost.getDataCenterId(), podId, null, null, null, null); } else { final Long cluster = srcHost.getClusterId(); @@ -1446,8 +1460,14 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe plan = new DataCenterDeployment(srcHost.getDataCenterId(), srcHost.getPodId(), srcHost.getClusterId(), null, null, null); } - final Pair, Integer> otherHosts = new Pair, Integer>(allHosts, allHostsPair.second()); - List suitableHosts = new ArrayList(); + final Pair, Integer> otherHosts = new Pair<>(allHosts, allHostsPair.second()); + Pair> uefiFilteredResult = filterUefiHostsForMigration(allHosts, filteredHosts, vm); + if (!uefiFilteredResult.first()) { + return new Ternary<>(otherHosts, new ArrayList<>(), new HashMap<>()); + } + filteredHosts = uefiFilteredResult.second(); + + List suitableHosts = new ArrayList<>(); final ExcludeList excludes = new ExcludeList(); excludes.addHost(srcHostId); @@ -1470,8 +1490,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } for (final HostAllocator allocator : hostAllocators) { - if (canMigrateWithStorage) { - suitableHosts = allocator.allocateTo(vmProfile, plan, Host.Type.Routing, excludes, hostsForMigrationWithStorage, HostAllocator.RETURN_UPTO_ALL, false); + if (CollectionUtils.isNotEmpty(filteredHosts)) { + suitableHosts = allocator.allocateTo(vmProfile, plan, Host.Type.Routing, excludes, filteredHosts, HostAllocator.RETURN_UPTO_ALL, false); } else { suitableHosts = allocator.allocateTo(vmProfile, plan, Host.Type.Routing, excludes, HostAllocator.RETURN_UPTO_ALL, false); } @@ -1489,7 +1509,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } } - return new Ternary, Integer>, List, Map>(otherHosts, suitableHosts, requiresStorageMotion); + return new Ternary<>(otherHosts, suitableHosts, requiresStorageMotion); } /** diff --git a/server/src/test/java/com/cloud/server/ManagementServerImplTest.java b/server/src/test/java/com/cloud/server/ManagementServerImplTest.java index 2d6969fe966..d7e221702d7 100644 --- a/server/src/test/java/com/cloud/server/ManagementServerImplTest.java +++ b/server/src/test/java/com/cloud/server/ManagementServerImplTest.java @@ -21,9 +21,14 @@ import static org.mockito.Mockito.any; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.when; +import java.util.ArrayList; +import java.util.List; + +import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.command.user.address.ListPublicIpAddressesCmd; import org.apache.cloudstack.api.command.user.ssh.RegisterSSHKeyPairCmd; import org.apache.cloudstack.framework.config.ConfigKey; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -32,9 +37,14 @@ import org.mockito.Mockito; import org.mockito.Spy; import org.mockito.runners.MockitoJUnitRunner; import org.powermock.reflect.Whitebox; +import org.springframework.test.util.ReflectionTestUtils; import com.cloud.dc.Vlan.VlanType; import com.cloud.exception.InvalidParameterValueException; +import com.cloud.host.DetailVO; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.dao.HostDetailsDao; import com.cloud.network.IpAddress; import com.cloud.network.IpAddressManagerImpl; import com.cloud.network.dao.IPAddressVO; @@ -42,7 +52,11 @@ import com.cloud.user.Account; import com.cloud.user.SSHKeyPair; import com.cloud.user.SSHKeyPairVO; import com.cloud.user.dao.SSHKeyPairDao; +import com.cloud.utils.Pair; import com.cloud.utils.db.SearchCriteria; +import com.cloud.vm.UserVmDetailVO; +import com.cloud.vm.UserVmVO; +import com.cloud.vm.dao.UserVmDetailsDao; @RunWith(MockitoJUnitRunner.class) public class ManagementServerImplTest { @@ -61,7 +75,6 @@ public class ManagementServerImplTest { @Mock SSHKeyPairDao sshKeyPairDao; - ManagementServerImpl ms = new ManagementServerImpl(); @Mock SSHKeyPair sshKeyPair; @@ -74,10 +87,18 @@ public class ManagementServerImplTest { ConfigKey mockConfig; + @Mock + UserVmDetailsDao userVmDetailsDao; + + @Mock + HostDetailsDao hostDetailsDao; + @Before public void setup() { mockConfig = Mockito.mock(ConfigKey.class); Whitebox.setInternalState(ipAddressManagerImpl.getClass(), "SystemVmPublicIpReservationModeStrictness", mockConfig); + spy._UserVmDetailsDao = userVmDetailsDao; + spy._detailsDao = hostDetailsDao; } @Test(expected = InvalidParameterValueException.class) @@ -221,4 +242,101 @@ public class ManagementServerImplTest { Mockito.verify(sc, Mockito.times(1)).setParameters("sourceNetworkId", 10L); Mockito.verify(sc, Mockito.never()).setParameters("forsystemvms", false); } + + private UserVmVO mockFilterUefiHostsTestVm(String uefiValue) { + UserVmVO vm = Mockito.mock(UserVmVO.class); + Mockito.when(vm.getId()).thenReturn(1L); + if (uefiValue == null) { + Mockito.when(userVmDetailsDao.findDetail(vm.getId(), ApiConstants.BootType.UEFI.toString())).thenReturn(null); + } else { + UserVmDetailVO detail = new UserVmDetailVO(vm.getId(), ApiConstants.BootType.UEFI.toString(), uefiValue, true); + Mockito.when(userVmDetailsDao.findDetail(vm.getId(), ApiConstants.BootType.UEFI.toString())).thenReturn(detail); + Mockito.when(hostDetailsDao.findByName(Host.HOST_UEFI_ENABLE)).thenReturn(new ArrayList<>(List.of(new DetailVO(1l, Host.HOST_UEFI_ENABLE, "true"), new DetailVO(2l, Host.HOST_UEFI_ENABLE, "false")))); + } + return vm; + } + + private List mockHostsList(Long filtered) { + List hosts = new ArrayList<>(); + HostVO h1 = new HostVO("guid-1"); + ReflectionTestUtils.setField(h1, "id", 1L); + hosts.add(h1); + HostVO h2 = new HostVO("guid-2"); + ReflectionTestUtils.setField(h2, "id", 2L); + hosts.add(h2); + HostVO h3 = new HostVO("guid-3"); + ReflectionTestUtils.setField(h3, "id", 3L); + hosts.add(h3); + if (filtered != null) { + hosts.removeIf(h -> h.getId() == filtered); + } + return hosts; + } + + @Test + public void testFilterUefiHostsForMigrationBiosVm() { + UserVmVO vm = mockFilterUefiHostsTestVm(null); + Pair> result = spy.filterUefiHostsForMigration(new ArrayList<>(), new ArrayList<>(), vm); + Assert.assertTrue(result.first()); + } + + @Test + public void testFilterUefiHostsForMigrationBiosVmFiltered() { + List filteredHosts = mockHostsList(2L); + UserVmVO vm = mockFilterUefiHostsTestVm(null); + Pair> result = spy.filterUefiHostsForMigration(new ArrayList<>(), filteredHosts, vm); + Assert.assertTrue(result.first()); + Assert.assertNotNull(result.second()); + Assert.assertEquals(2, result.second().size()); + } + + @Test + public void testFilterUefiHostsForMigrationUefiInvalidValueVm() { + UserVmVO vm = mockFilterUefiHostsTestVm(""); + Pair> result = spy.filterUefiHostsForMigration(new ArrayList<>(), new ArrayList<>(), vm); + Assert.assertTrue(result.first()); + } + + @Test + public void testFilterUefiHostsForMigrationUefiVMHostsUnavailable() { + UserVmVO vm = mockFilterUefiHostsTestVm(ApiConstants.BootMode.SECURE.toString()); + List filteredHosts = new ArrayList<>(); + Mockito.when(hostDetailsDao.findByName(Host.HOST_UEFI_ENABLE)).thenReturn(new ArrayList<>()); + Pair> result = spy.filterUefiHostsForMigration(mockHostsList(null), filteredHosts, vm); + Assert.assertFalse(result.first()); + } + + @Test + public void testFilterUefiHostsForMigrationUefiVMHostsAvailable() { + UserVmVO vm = mockFilterUefiHostsTestVm(ApiConstants.BootMode.LEGACY.toString()); + Pair> result = spy.filterUefiHostsForMigration(mockHostsList(null), null, vm); + Assert.assertTrue(result.first()); + Assert.assertNotNull(result.second()); + Assert.assertEquals(1, result.second().size()); + } + + @Test + public void testFilterUefiHostsForMigrationNoHostsAvailable() { + UserVmVO vm = mockFilterUefiHostsTestVm(ApiConstants.BootMode.SECURE.toString()); + Pair> result = spy.filterUefiHostsForMigration(new ArrayList<>(), null, vm); + Assert.assertFalse(result.first()); + } + + @Test + public void testFilterUefiHostsForMigrationUefiVMHostsAvailableFiltered() { + UserVmVO vm = mockFilterUefiHostsTestVm(ApiConstants.BootMode.LEGACY.toString()); + Pair> result = spy.filterUefiHostsForMigration(mockHostsList(null), mockHostsList(3L), vm); + Assert.assertTrue(result.first()); + Assert.assertNotNull(result.second()); + Assert.assertEquals(1, result.second().size()); + } + + @Test + public void testFilterUefiHostsForMigrationUefiVMNoHostsAvailableFiltered() { + UserVmVO vm = mockFilterUefiHostsTestVm(ApiConstants.BootMode.SECURE.toString()); + Pair> result = spy.filterUefiHostsForMigration(mockHostsList(null), mockHostsList(1L), vm); + Assert.assertFalse(result.first()); + Assert.assertNotNull(result.second()); + Assert.assertEquals(0, result.second().size()); + } }