4040import com .cloud .utils .Ternary ;
4141import com .cloud .utils .db .GlobalLock ;
4242import com .cloud .utils .exception .CloudRuntimeException ;
43+ import com .cloud .vm .UserVmDetailVO ;
4344import com .cloud .vm .VMInstanceVO ;
4445import com .cloud .vm .VirtualMachine ;
4546import com .cloud .vm .VmDetailConstants ;
47+ import com .cloud .vm .dao .UserVmDetailsDao ;
4648import com .cloud .vm .dao .VMInstanceDao ;
4749import org .apache .cloudstack .affinity .dao .AffinityGroupVMMapDao ;
4850import org .apache .cloudstack .api .command .admin .cluster .GenerateClusterDrsPlanCmd ;
@@ -121,6 +123,9 @@ public class ClusterDrsServiceImplTest {
121123 @ Mock
122124 private AffinityGroupVMMapDao affinityGroupVMMapDao ;
123125
126+ @ Mock
127+ private UserVmDetailsDao userVmDetailsDao ;
128+
124129 @ Spy
125130 @ InjectMocks
126131 private ClusterDrsServiceImpl clusterDrsService = new ClusterDrsServiceImpl ();
@@ -294,6 +299,8 @@ public void testGetDrsPlanWithSystemVMs() throws ConfigurationException {
294299
295300 List <Ternary <VirtualMachine , Host , Host >> result = clusterDrsService .getDrsPlan (cluster , 5 );
296301 assertEquals (0 , result .size ());
302+ Mockito .verify (managementServer , Mockito .never ()).listHostsForMigrationOfVM (
303+ Mockito .eq (systemVm ), Mockito .anyLong (), Mockito .anyLong (), Mockito .any (), Mockito .anyList ());
297304 }
298305
299306 @ Test
@@ -334,6 +341,8 @@ public void testGetDrsPlanWithNonRunningVMs() throws ConfigurationException {
334341
335342 List <Ternary <VirtualMachine , Host , Host >> result = clusterDrsService .getDrsPlan (cluster , 5 );
336343 assertEquals (0 , result .size ());
344+ Mockito .verify (managementServer , Mockito .never ()).listHostsForMigrationOfVM (
345+ Mockito .eq (stoppedVm ), Mockito .anyLong (), Mockito .anyLong (), Mockito .any (), Mockito .anyList ());
337346 }
338347
339348 @ Test
@@ -350,9 +359,6 @@ public void testGetDrsPlanWithSkipDrsFlag() throws ConfigurationException {
350359 Mockito .when (skippedVm .getHostId ()).thenReturn (1L );
351360 Mockito .when (skippedVm .getType ()).thenReturn (VirtualMachine .Type .User );
352361 Mockito .when (skippedVm .getState ()).thenReturn (VirtualMachine .State .Running );
353- Map <String , String > details = new HashMap <>();
354- details .put (VmDetailConstants .SKIP_DRS , "true" );
355- Mockito .when (skippedVm .getDetails ()).thenReturn (details );
356362
357363 List <HostVO > hostList = new ArrayList <>();
358364 hostList .add (host1 );
@@ -370,13 +376,21 @@ public void testGetDrsPlanWithSkipDrsFlag() throws ConfigurationException {
370376 Mockito .when (hostJoin1 .getMemReservedCapacity ()).thenReturn (0L );
371377 Mockito .when (hostJoin1 .getTotalMemory ()).thenReturn (8192L );
372378
379+ // Return the SKIP_DRS detail for skippedVm so the flag is actually honoured
380+ UserVmDetailVO skipDrsDetail = new UserVmDetailVO (1L , VmDetailConstants .SKIP_DRS , "true" , true );
381+ Mockito .when (userVmDetailsDao .listDetailsForResourceIdsAndKey (Mockito .anyList (),
382+ Mockito .eq (VmDetailConstants .SKIP_DRS ))).thenReturn (List .of (skipDrsDetail ));
383+
373384 Mockito .when (hostDao .findByClusterId (1L )).thenReturn (hostList );
374385 Mockito .when (vmInstanceDao .listByClusterId (1L )).thenReturn (vmList );
375386 Mockito .when (balancedAlgorithm .needsDrs (Mockito .any (), Mockito .anyList (), Mockito .anyList ())).thenReturn (true );
376387 Mockito .when (hostJoinDao .searchByIds (Mockito .any ())).thenReturn (List .of (hostJoin1 ));
377388
378389 List <Ternary <VirtualMachine , Host , Host >> result = clusterDrsService .getDrsPlan (cluster , 5 );
379390 assertEquals (0 , result .size ());
391+ // Verify the VM was skipped before any host-compatibility lookup was attempted
392+ Mockito .verify (managementServer , Mockito .never ()).listHostsForMigrationOfVM (
393+ Mockito .eq (skippedVm ), Mockito .anyLong (), Mockito .anyLong (), Mockito .any (), Mockito .anyList ());
380394 }
381395
382396 @ Test
@@ -393,7 +407,6 @@ public void testGetDrsPlanWithNoCompatibleHosts() throws ConfigurationException
393407 Mockito .when (vm1 .getHostId ()).thenReturn (1L );
394408 Mockito .when (vm1 .getType ()).thenReturn (VirtualMachine .Type .User );
395409 Mockito .when (vm1 .getState ()).thenReturn (VirtualMachine .State .Running );
396- Mockito .when (vm1 .getDetails ()).thenReturn (Collections .emptyMap ());
397410
398411 List <HostVO > hostList = new ArrayList <>();
399412 hostList .add (host1 );
@@ -418,6 +431,10 @@ public void testGetDrsPlanWithNoCompatibleHosts() throws ConfigurationException
418431 Mockito .when (balancedAlgorithm .needsDrs (Mockito .any (), Mockito .anyList (), Mockito .anyList ())).thenReturn (true );
419432 Mockito .when (serviceOfferingDao .findByIdIncludingRemoved (Mockito .anyLong (), Mockito .anyLong ())).thenReturn (serviceOffering );
420433 Mockito .when (hostJoinDao .searchByIds (Mockito .any ())).thenReturn (List .of (hostJoin1 ));
434+ // Return a Ternary with an empty suitable-hosts list to exercise the "no compatible hosts" path
435+ Mockito .when (managementServer .listHostsForMigrationOfVM (Mockito .eq (vm1 ), Mockito .anyLong (),
436+ Mockito .anyLong (), Mockito .any (), Mockito .anyList ()))
437+ .thenReturn (new Ternary <>(new Pair <>(Collections .emptyList (), 0 ), Collections .emptyList (), Collections .emptyMap ()));
421438
422439 List <Ternary <VirtualMachine , Host , Host >> result = clusterDrsService .getDrsPlan (cluster , 5 );
423440 assertEquals (0 , result .size ());
@@ -438,7 +455,6 @@ public void testGetDrsPlanWithExceptionInCompatibilityCheck() throws Configurati
438455 Mockito .when (vm1 .getHostId ()).thenReturn (1L );
439456 Mockito .when (vm1 .getType ()).thenReturn (VirtualMachine .Type .User );
440457 Mockito .when (vm1 .getState ()).thenReturn (VirtualMachine .State .Running );
441- Mockito .when (vm1 .getDetails ()).thenReturn (Collections .emptyMap ());
442458
443459 List <HostVO > hostList = new ArrayList <>();
444460 hostList .add (host1 );
@@ -463,6 +479,10 @@ public void testGetDrsPlanWithExceptionInCompatibilityCheck() throws Configurati
463479 Mockito .when (balancedAlgorithm .needsDrs (Mockito .any (), Mockito .anyList (), Mockito .anyList ())).thenReturn (true );
464480 Mockito .when (serviceOfferingDao .findByIdIncludingRemoved (Mockito .anyLong (), Mockito .anyLong ())).thenReturn (serviceOffering );
465481 Mockito .when (hostJoinDao .searchByIds (Mockito .any ())).thenReturn (List .of (hostJoin1 ));
482+ // Throw an explicit exception so the catch-and-log path is exercised intentionally
483+ Mockito .when (managementServer .listHostsForMigrationOfVM (Mockito .eq (vm1 ), Mockito .anyLong (),
484+ Mockito .anyLong (), Mockito .any (), Mockito .anyList ()))
485+ .thenThrow (new RuntimeException ("Simulated host compatibility check failure" ));
466486
467487 List <Ternary <VirtualMachine , Host , Host >> result = clusterDrsService .getDrsPlan (cluster , 5 );
468488 assertEquals (0 , result .size ());
@@ -484,7 +504,6 @@ public void testGetDrsPlanWithNoBestMigration() throws ConfigurationException {
484504 Mockito .when (vm1 .getHostId ()).thenReturn (1L );
485505 Mockito .when (vm1 .getType ()).thenReturn (VirtualMachine .Type .User );
486506 Mockito .when (vm1 .getState ()).thenReturn (VirtualMachine .State .Running );
487- Mockito .when (vm1 .getDetails ()).thenReturn (Collections .emptyMap ());
488507
489508 List <HostVO > hostList = new ArrayList <>();
490509 hostList .add (host1 );
@@ -539,14 +558,12 @@ public void testGetDrsPlanWithMultipleIterations() throws ConfigurationException
539558 Mockito .when (vm1 .getHostId ()).thenReturn (1L );
540559 Mockito .when (vm1 .getType ()).thenReturn (VirtualMachine .Type .User );
541560 Mockito .when (vm1 .getState ()).thenReturn (VirtualMachine .State .Running );
542- Mockito .when (vm1 .getDetails ()).thenReturn (Collections .emptyMap ());
543561
544562 VMInstanceVO vm2 = Mockito .mock (VMInstanceVO .class );
545563 Mockito .when (vm2 .getId ()).thenReturn (2L );
546564 Mockito .when (vm2 .getHostId ()).thenReturn (1L );
547565 Mockito .when (vm2 .getType ()).thenReturn (VirtualMachine .Type .User );
548566 Mockito .when (vm2 .getState ()).thenReturn (VirtualMachine .State .Running );
549- Mockito .when (vm2 .getDetails ()).thenReturn (Collections .emptyMap ());
550567
551568 List <HostVO > hostList = new ArrayList <>();
552569 hostList .add (host1 );
@@ -619,7 +636,6 @@ public void testGetDrsPlanWithMigrationToOriginalHost() throws ConfigurationExce
619636 Mockito .when (vm1 .getHostId ()).thenReturn (1L );
620637 Mockito .when (vm1 .getType ()).thenReturn (VirtualMachine .Type .User );
621638 Mockito .when (vm1 .getState ()).thenReturn (VirtualMachine .State .Running );
622- Mockito .when (vm1 .getDetails ()).thenReturn (Collections .emptyMap ());
623639
624640 List <HostVO > hostList = new ArrayList <>();
625641 hostList .add (host1 );
@@ -811,15 +827,9 @@ public void testGetBestMigration() throws ConfigurationException {
811827
812828 VMInstanceVO vm1 = Mockito .mock (VMInstanceVO .class );
813829 Mockito .when (vm1 .getId ()).thenReturn (1L );
814- Mockito .when (vm1 .getType ()).thenReturn (VirtualMachine .Type .User );
815- Mockito .when (vm1 .getState ()).thenReturn (VirtualMachine .State .Running );
816- Mockito .when (vm1 .getDetails ()).thenReturn (Collections .emptyMap ());
817830
818831 VMInstanceVO vm2 = Mockito .mock (VMInstanceVO .class );
819832 Mockito .when (vm2 .getId ()).thenReturn (2L );
820- Mockito .when (vm2 .getType ()).thenReturn (VirtualMachine .Type .User );
821- Mockito .when (vm2 .getState ()).thenReturn (VirtualMachine .State .Running );
822- Mockito .when (vm2 .getDetails ()).thenReturn (Collections .emptyMap ());
823833
824834 List <VirtualMachine > vmList = new ArrayList <>();
825835 vmList .add (vm1 );
@@ -890,15 +900,9 @@ public void testGetBestMigrationDifferentCluster() throws ConfigurationException
890900
891901 VMInstanceVO vm1 = Mockito .mock (VMInstanceVO .class );
892902 Mockito .when (vm1 .getId ()).thenReturn (1L );
893- Mockito .when (vm1 .getType ()).thenReturn (VirtualMachine .Type .User );
894- Mockito .when (vm1 .getState ()).thenReturn (VirtualMachine .State .Running );
895- Mockito .when (vm1 .getDetails ()).thenReturn (Collections .emptyMap ());
896903
897904 VMInstanceVO vm2 = Mockito .mock (VMInstanceVO .class );
898905 Mockito .when (vm2 .getId ()).thenReturn (2L );
899- Mockito .when (vm2 .getType ()).thenReturn (VirtualMachine .Type .User );
900- Mockito .when (vm2 .getState ()).thenReturn (VirtualMachine .State .Running );
901- Mockito .when (vm2 .getDetails ()).thenReturn (Collections .emptyMap ());
902906
903907 List <VirtualMachine > vmList = new ArrayList <>();
904908 vmList .add (vm1 );
0 commit comments