From e65c9ffe70059b51a327a4d05f47a73a4ea411fd Mon Sep 17 00:00:00 2001 From: Vishesh Date: Tue, 7 Nov 2023 19:41:00 +0530 Subject: [PATCH 1/3] Fix: Select another pod if all hosts in the pod becomes unavailable (#8085) --- .../cloud/vm/VirtualMachineManagerImpl.java | 21 +- .../vm/VirtualMachineManagerImplTest.java | 300 +++++++++++++++--- 2 files changed, 282 insertions(+), 39 deletions(-) diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java index ca453ed0de1..7792afd2c63 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -569,7 +569,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac allocate(vmInstanceName, template, serviceOffering, new DiskOfferingInfo(diskOffering), new ArrayList<>(), networks, plan, hyperType, null, null); } - private VirtualMachineGuru getVmGuru(final VirtualMachine vm) { + VirtualMachineGuru getVmGuru(final VirtualMachine vm) { if(vm != null) { return _vmGurus.get(vm.getType()); } @@ -1443,6 +1443,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } if (canRetry) { try { + conditionallySetPodToDeployIn(vm); changeState(vm, Event.OperationFailed, null, work, Step.Done); } catch (final NoTransitionException e) { throw new ConcurrentOperationException(e.getMessage()); @@ -1460,6 +1461,24 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } } + /** + * Setting pod id to null can result in migration of Volumes across pods. This is not desirable for VMs which + * have a volume in Ready state (happens when a VM is shutdown and started again). + * So, we set it to null only when + * migration of VM across cluster is enabled + * Or, volumes are still in allocated state for that VM (happens when VM is Starting/deployed for the first time) + */ + private void conditionallySetPodToDeployIn(VMInstanceVO vm) { + if (MIGRATE_VM_ACROSS_CLUSTERS.valueIn(vm.getDataCenterId()) || areAllVolumesAllocated(vm.getId())) { + vm.setPodIdToDeployIn(null); + } + } + + boolean areAllVolumesAllocated(long vmId) { + final List vols = _volsDao.findByInstance(vmId); + return CollectionUtils.isEmpty(vols) || vols.stream().allMatch(v -> Volume.State.Allocated.equals(v.getState())); + } + private void logBootModeParameters(Map params) { if (params == null) { return; diff --git a/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java b/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java index 15a2f2c0ac1..20af10ed338 100644 --- a/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java +++ b/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java @@ -19,9 +19,12 @@ package com.cloud.vm; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyLong; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; @@ -35,6 +38,23 @@ import java.util.Map; import java.util.Random; import java.util.stream.Collectors; +import com.cloud.dc.ClusterDetailsDao; +import com.cloud.dc.ClusterDetailsVO; +import com.cloud.dc.Pod; +import com.cloud.deploy.DeployDestination; +import com.cloud.deploy.DeploymentPlanningManager; +import com.cloud.hypervisor.HypervisorGuruManager; +import com.cloud.org.Cluster; +import com.cloud.template.VirtualMachineTemplate; +import com.cloud.user.Account; +import com.cloud.user.User; +import com.cloud.utils.Journal; +import com.cloud.utils.Pair; +import com.cloud.utils.Ternary; +import com.cloud.utils.db.EntityManager; +import com.cloud.utils.fsm.StateMachine2; +import com.cloud.vm.dao.UserVmDetailsDao; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator; import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; @@ -49,7 +69,6 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.Spy; import org.mockito.stubbing.Answer; -import org.mockito.runners.MockitoJUnitRunner; import com.cloud.agent.AgentManager; import com.cloud.agent.api.Command; @@ -88,13 +107,20 @@ import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.springframework.test.util.ReflectionTestUtils; -@RunWith(MockitoJUnitRunner.class) +@RunWith(PowerMockRunner.class) +@PrepareForTest(CallContext.class) +@PowerMockIgnore({"javax.xml.*", "org.w3c.dom.*", "org.apache.xerces.*", "org.xml.*"}) public class VirtualMachineManagerImplTest { @Spy @InjectMocks - private VirtualMachineManagerImpl virtualMachineManagerImpl; + private VirtualMachineManagerImpl virtualMachineManagerImpl = new VirtualMachineManagerImpl(); @Mock private AgentManager agentManagerMock; @Mock @@ -155,6 +181,20 @@ public class VirtualMachineManagerImplTest { private UserVmDao userVmDaoMock; @Mock private UserVmVO userVmMock; + @Mock + private EntityManager _entityMgr; + @Mock + private DeploymentPlanningManager _dpMgr; + @Mock + private HypervisorGuruManager _hvGuruMgr; + @Mock + private ClusterDetailsDao _clusterDetailsDao; + @Mock + private UserVmDetailsDao userVmDetailsDao; + @Mock + private ItWorkDao _workDao; + @Mock + protected StateMachine2 _stateMachine; @Before public void setup() { @@ -433,7 +473,7 @@ public class VirtualMachineManagerImplTest { HashMap userDefinedVolumeToStoragePoolMap = new HashMap<>(); userDefinedVolumeToStoragePoolMap.put(volumeMockId, storagePoolVoMockId); - Mockito.doNothing().when(virtualMachineManagerImpl).executeManagedStorageChecksWhenTargetStoragePoolProvided(Mockito.any(StoragePoolVO.class), Mockito.any(VolumeVO.class), Mockito.any(StoragePoolVO.class)); + Mockito.doNothing().when(virtualMachineManagerImpl).executeManagedStorageChecksWhenTargetStoragePoolProvided(any(StoragePoolVO.class), any(VolumeVO.class), any(StoragePoolVO.class)); Mockito.doReturn(null).when(storagePoolHostDaoMock).findByPoolHost(storagePoolVoMockId, hostMockId); virtualMachineManagerImpl.buildMapUsingUserInformation(virtualMachineProfileMock, hostMock, userDefinedVolumeToStoragePoolMap); @@ -445,8 +485,8 @@ public class VirtualMachineManagerImplTest { HashMap userDefinedVolumeToStoragePoolMap = Mockito.spy(new HashMap<>()); userDefinedVolumeToStoragePoolMap.put(volumeMockId, storagePoolVoMockId); - Mockito.doNothing().when(virtualMachineManagerImpl).executeManagedStorageChecksWhenTargetStoragePoolProvided(Mockito.any(StoragePoolVO.class), Mockito.any(VolumeVO.class), - Mockito.any(StoragePoolVO.class)); + Mockito.doNothing().when(virtualMachineManagerImpl).executeManagedStorageChecksWhenTargetStoragePoolProvided(any(StoragePoolVO.class), any(VolumeVO.class), + any(StoragePoolVO.class)); Mockito.doReturn(Mockito.mock(StoragePoolHostVO.class)).when(storagePoolHostDaoMock).findByPoolHost(storagePoolVoMockId, hostMockId); Map volumeToPoolObjectMap = virtualMachineManagerImpl.buildMapUsingUserInformation(virtualMachineProfileMock, hostMock, userDefinedVolumeToStoragePoolMap); @@ -482,7 +522,7 @@ public class VirtualMachineManagerImplTest { virtualMachineManagerImpl.executeManagedStorageChecksWhenTargetStoragePoolNotProvided(hostMock, storagePoolVoMock, volumeVoMock); Mockito.verify(storagePoolVoMock).isManaged(); - Mockito.verify(storagePoolHostDaoMock, Mockito.times(0)).findByPoolHost(Mockito.anyLong(), Mockito.anyLong()); + Mockito.verify(storagePoolHostDaoMock, Mockito.times(0)).findByPoolHost(anyLong(), anyLong()); } @Test @@ -506,15 +546,15 @@ public class VirtualMachineManagerImplTest { @Test public void getCandidateStoragePoolsToMigrateLocalVolumeTestLocalVolume() { - Mockito.doReturn(Mockito.mock(DiskOfferingVO.class)).when(diskOfferingDaoMock).findById(Mockito.anyLong()); + Mockito.doReturn(Mockito.mock(DiskOfferingVO.class)).when(diskOfferingDaoMock).findById(anyLong()); Mockito.doReturn(true).when(storagePoolVoMock).isLocal(); List poolListMock = new ArrayList<>(); poolListMock.add(storagePoolVoMock); - Mockito.doReturn(poolListMock).when(storagePoolAllocatorMock).allocateToPool(Mockito.any(DiskProfile.class), Mockito.any(VirtualMachineProfile.class), Mockito.any(DeploymentPlan.class), - Mockito.any(ExcludeList.class), Mockito.eq(StoragePoolAllocator.RETURN_UPTO_ALL)); + Mockito.doReturn(poolListMock).when(storagePoolAllocatorMock).allocateToPool(any(DiskProfile.class), any(VirtualMachineProfile.class), any(DeploymentPlan.class), + any(ExcludeList.class), Mockito.eq(StoragePoolAllocator.RETURN_UPTO_ALL)); List poolList = virtualMachineManagerImpl.getCandidateStoragePoolsToMigrateLocalVolume(virtualMachineProfileMock, dataCenterDeploymentMock, volumeVoMock); @@ -524,15 +564,15 @@ public class VirtualMachineManagerImplTest { @Test public void getCandidateStoragePoolsToMigrateLocalVolumeTestCrossClusterMigration() { - Mockito.doReturn(Mockito.mock(DiskOfferingVO.class)).when(diskOfferingDaoMock).findById(Mockito.anyLong()); + Mockito.doReturn(Mockito.mock(DiskOfferingVO.class)).when(diskOfferingDaoMock).findById(anyLong()); Mockito.doReturn(false).when(storagePoolVoMock).isLocal(); List poolListMock = new ArrayList<>(); poolListMock.add(storagePoolVoMock); - Mockito.doReturn(poolListMock).when(storagePoolAllocatorMock).allocateToPool(Mockito.any(DiskProfile.class), Mockito.any(VirtualMachineProfile.class), Mockito.any(DeploymentPlan.class), - Mockito.any(ExcludeList.class), Mockito.eq(StoragePoolAllocator.RETURN_UPTO_ALL)); + Mockito.doReturn(poolListMock).when(storagePoolAllocatorMock).allocateToPool(any(DiskProfile.class), any(VirtualMachineProfile.class), any(DeploymentPlan.class), + any(ExcludeList.class), Mockito.eq(StoragePoolAllocator.RETURN_UPTO_ALL)); Mockito.doReturn(true).when(virtualMachineManagerImpl).isStorageCrossClusterMigration(clusterMockId, storagePoolVoMock); List poolList = virtualMachineManagerImpl.getCandidateStoragePoolsToMigrateLocalVolume(virtualMachineProfileMock, dataCenterDeploymentMock, volumeVoMock); @@ -543,15 +583,15 @@ public class VirtualMachineManagerImplTest { @Test public void getCandidateStoragePoolsToMigrateLocalVolumeTestWithinClusterMigration() { - Mockito.doReturn(Mockito.mock(DiskOfferingVO.class)).when(diskOfferingDaoMock).findById(Mockito.anyLong()); + Mockito.doReturn(Mockito.mock(DiskOfferingVO.class)).when(diskOfferingDaoMock).findById(anyLong()); Mockito.doReturn(false).when(storagePoolVoMock).isLocal(); List poolListMock = new ArrayList<>(); poolListMock.add(storagePoolVoMock); - Mockito.doReturn(poolListMock).when(storagePoolAllocatorMock).allocateToPool(Mockito.any(DiskProfile.class), Mockito.any(VirtualMachineProfile.class), Mockito.any(DeploymentPlan.class), - Mockito.any(ExcludeList.class), Mockito.eq(StoragePoolAllocator.RETURN_UPTO_ALL)); + Mockito.doReturn(poolListMock).when(storagePoolAllocatorMock).allocateToPool(any(DiskProfile.class), any(VirtualMachineProfile.class), any(DeploymentPlan.class), + any(ExcludeList.class), Mockito.eq(StoragePoolAllocator.RETURN_UPTO_ALL)); Mockito.doReturn(false).when(virtualMachineManagerImpl).isStorageCrossClusterMigration(clusterMockId, storagePoolVoMock); List poolList = virtualMachineManagerImpl.getCandidateStoragePoolsToMigrateLocalVolume(virtualMachineProfileMock, dataCenterDeploymentMock, volumeVoMock); @@ -571,33 +611,33 @@ public class VirtualMachineManagerImplTest { virtualMachineManagerImpl.setStoragePoolAllocators(storagePoolAllocatorsMock); - Mockito.doReturn(Mockito.mock(DiskOfferingVO.class)).when(diskOfferingDaoMock).findById(Mockito.anyLong()); + Mockito.doReturn(Mockito.mock(DiskOfferingVO.class)).when(diskOfferingDaoMock).findById(anyLong()); Mockito.doReturn(false).when(storagePoolVoMock).isLocal(); List poolListMock = new ArrayList<>(); poolListMock.add(storagePoolVoMock); - Mockito.doReturn(poolListMock).when(storagePoolAllocatorMock).allocateToPool(Mockito.any(DiskProfile.class), Mockito.any(VirtualMachineProfile.class), Mockito.any(DeploymentPlan.class), - Mockito.any(ExcludeList.class), Mockito.eq(StoragePoolAllocator.RETURN_UPTO_ALL)); + Mockito.doReturn(poolListMock).when(storagePoolAllocatorMock).allocateToPool(any(DiskProfile.class), any(VirtualMachineProfile.class), any(DeploymentPlan.class), + any(ExcludeList.class), Mockito.eq(StoragePoolAllocator.RETURN_UPTO_ALL)); - Mockito.doReturn(null).when(storagePoolAllocatorMock2).allocateToPool(Mockito.any(DiskProfile.class), Mockito.any(VirtualMachineProfile.class), Mockito.any(DeploymentPlan.class), - Mockito.any(ExcludeList.class), Mockito.eq(StoragePoolAllocator.RETURN_UPTO_ALL)); + Mockito.doReturn(null).when(storagePoolAllocatorMock2).allocateToPool(any(DiskProfile.class), any(VirtualMachineProfile.class), any(DeploymentPlan.class), + any(ExcludeList.class), Mockito.eq(StoragePoolAllocator.RETURN_UPTO_ALL)); - Mockito.doReturn(new ArrayList<>()).when(storagePoolAllocatorMock3).allocateToPool(Mockito.any(DiskProfile.class), Mockito.any(VirtualMachineProfile.class), Mockito.any(DeploymentPlan.class), - Mockito.any(ExcludeList.class), Mockito.eq(StoragePoolAllocator.RETURN_UPTO_ALL)); + Mockito.doReturn(new ArrayList<>()).when(storagePoolAllocatorMock3).allocateToPool(any(DiskProfile.class), any(VirtualMachineProfile.class), any(DeploymentPlan.class), + any(ExcludeList.class), Mockito.eq(StoragePoolAllocator.RETURN_UPTO_ALL)); Mockito.doReturn(false).when(virtualMachineManagerImpl).isStorageCrossClusterMigration(clusterMockId, storagePoolVoMock); List poolList = virtualMachineManagerImpl.getCandidateStoragePoolsToMigrateLocalVolume(virtualMachineProfileMock, dataCenterDeploymentMock, volumeVoMock); Assert.assertTrue(poolList.isEmpty()); - Mockito.verify(storagePoolAllocatorMock).allocateToPool(Mockito.any(DiskProfile.class), Mockito.any(VirtualMachineProfile.class), Mockito.any(DeploymentPlan.class), - Mockito.any(ExcludeList.class), Mockito.eq(StoragePoolAllocator.RETURN_UPTO_ALL)); - Mockito.verify(storagePoolAllocatorMock2).allocateToPool(Mockito.any(DiskProfile.class), Mockito.any(VirtualMachineProfile.class), Mockito.any(DeploymentPlan.class), - Mockito.any(ExcludeList.class), Mockito.eq(StoragePoolAllocator.RETURN_UPTO_ALL)); - Mockito.verify(storagePoolAllocatorMock3).allocateToPool(Mockito.any(DiskProfile.class), Mockito.any(VirtualMachineProfile.class), Mockito.any(DeploymentPlan.class), - Mockito.any(ExcludeList.class), Mockito.eq(StoragePoolAllocator.RETURN_UPTO_ALL)); + Mockito.verify(storagePoolAllocatorMock).allocateToPool(any(DiskProfile.class), any(VirtualMachineProfile.class), any(DeploymentPlan.class), + any(ExcludeList.class), Mockito.eq(StoragePoolAllocator.RETURN_UPTO_ALL)); + Mockito.verify(storagePoolAllocatorMock2).allocateToPool(any(DiskProfile.class), any(VirtualMachineProfile.class), any(DeploymentPlan.class), + any(ExcludeList.class), Mockito.eq(StoragePoolAllocator.RETURN_UPTO_ALL)); + Mockito.verify(storagePoolAllocatorMock3).allocateToPool(any(DiskProfile.class), any(VirtualMachineProfile.class), any(DeploymentPlan.class), + any(ExcludeList.class), Mockito.eq(StoragePoolAllocator.RETURN_UPTO_ALL)); } @Test(expected = CloudRuntimeException.class) @@ -686,8 +726,8 @@ public class VirtualMachineManagerImplTest { HashMap volumeToPoolObjectMap = new HashMap<>(); Mockito.doReturn(ScopeType.CLUSTER).when(storagePoolVoMock).getScope(); - Mockito.doNothing().when(virtualMachineManagerImpl).executeManagedStorageChecksWhenTargetStoragePoolNotProvided(Mockito.any(), Mockito.any(), Mockito.any()); - Mockito.doReturn(false).when(virtualMachineManagerImpl).isStorageCrossClusterMigration(Mockito.anyLong(), Mockito.any()); + Mockito.doNothing().when(virtualMachineManagerImpl).executeManagedStorageChecksWhenTargetStoragePoolNotProvided(any(), any(), any()); + Mockito.doReturn(false).when(virtualMachineManagerImpl).isStorageCrossClusterMigration(anyLong(), any()); virtualMachineManagerImpl.createStoragePoolMappingsForVolumes(virtualMachineProfileMock, dataCenterDeploymentMock, volumeToPoolObjectMap, allVolumes); @@ -710,7 +750,7 @@ public class VirtualMachineManagerImplTest { Mockito.doReturn(volumesNotMapped).when(virtualMachineManagerImpl).findVolumesThatWereNotMappedByTheUser(virtualMachineProfileMock, volumeToPoolObjectMap); Mockito.doNothing().when(virtualMachineManagerImpl).createStoragePoolMappingsForVolumes(Mockito.eq(virtualMachineProfileMock), - Mockito.any(DataCenterDeployment.class), Mockito.eq(volumeToPoolObjectMap), Mockito.eq(volumesNotMapped)); + any(DataCenterDeployment.class), Mockito.eq(volumeToPoolObjectMap), Mockito.eq(volumesNotMapped)); Map mappingVolumeAndStoragePool = virtualMachineManagerImpl.createMappingVolumeAndStoragePool(virtualMachineProfileMock, hostMock, new HashMap<>()); @@ -720,7 +760,7 @@ public class VirtualMachineManagerImplTest { inOrder.verify(virtualMachineManagerImpl).buildMapUsingUserInformation(Mockito.eq(virtualMachineProfileMock), Mockito.eq(hostMock), Mockito.anyMapOf(Long.class, Long.class)); inOrder.verify(virtualMachineManagerImpl).findVolumesThatWereNotMappedByTheUser(virtualMachineProfileMock, volumeToPoolObjectMap); inOrder.verify(virtualMachineManagerImpl).createStoragePoolMappingsForVolumes(Mockito.eq(virtualMachineProfileMock), - Mockito.any(DataCenterDeployment.class), Mockito.eq(volumeToPoolObjectMap), Mockito.eq(volumesNotMapped)); + any(DataCenterDeployment.class), Mockito.eq(volumeToPoolObjectMap), Mockito.eq(volumesNotMapped)); } @Test @@ -774,11 +814,11 @@ public class VirtualMachineManagerImplTest { private void prepareAndTestIsRootVolumeOnLocalStorage(ScopeType scope, boolean expected) { StoragePoolVO storagePoolVoMock = Mockito.mock(StoragePoolVO.class); - Mockito.doReturn(storagePoolVoMock).when(storagePoolDaoMock).findById(Mockito.anyLong()); + Mockito.doReturn(storagePoolVoMock).when(storagePoolDaoMock).findById(anyLong()); Mockito.doReturn(scope).when(storagePoolVoMock).getScope(); List mockedVolumes = new ArrayList<>(); mockedVolumes.add(volumeVoMock); - Mockito.doReturn(mockedVolumes).when(volumeDaoMock).findByInstanceAndType(Mockito.anyLong(), Mockito.any()); + Mockito.doReturn(mockedVolumes).when(volumeDaoMock).findByInstanceAndType(anyLong(), any()); boolean result = virtualMachineManagerImpl.isRootVolumeOnLocalStorage(0l); @@ -806,7 +846,7 @@ public class VirtualMachineManagerImplTest { } private void prepareAndRunCheckIfNewOfferingStorageScopeMatchesStoragePool(boolean isRootOnLocal, boolean isOfferingUsingLocal) { - Mockito.doReturn(isRootOnLocal).when(virtualMachineManagerImpl).isRootVolumeOnLocalStorage(Mockito.anyLong()); + Mockito.doReturn(isRootOnLocal).when(virtualMachineManagerImpl).isRootVolumeOnLocalStorage(anyLong()); Mockito.doReturn("vmInstanceMockedToString").when(vmInstanceMock).toString(); Mockito.doReturn(isOfferingUsingLocal).when(diskOfferingMock).isUseLocalStorage(); virtualMachineManagerImpl.checkIfNewOfferingStorageScopeMatchesStoragePool(vmInstanceMock, diskOfferingMock); @@ -895,4 +935,188 @@ public class VirtualMachineManagerImplTest { map.put(Mockito.mock(Volume.class), pool2); virtualMachineManagerImpl.checkAndAttemptMigrateVmAcrossCluster(vm, destinationClusterId, map); } + + @Test + public void testOrchestrateStartNonNullPodId() throws Exception { + VMInstanceVO vmInstance = new VMInstanceVO(); + ReflectionTestUtils.setField(vmInstance, "id", 1L); + ReflectionTestUtils.setField(vmInstance, "uuid", "vm-uuid"); + ReflectionTestUtils.setField(vmInstance, "serviceOfferingId", 2L); + ReflectionTestUtils.setField(vmInstance, "instanceName", "myVm"); + ReflectionTestUtils.setField(vmInstance, "hostId", 2L); + ReflectionTestUtils.setField(vmInstance, "type", VirtualMachine.Type.User); + ReflectionTestUtils.setField(vmInstance, "dataCenterId", 1L); + ReflectionTestUtils.setField(vmInstance, "hypervisorType", HypervisorType.KVM); + + VirtualMachineGuru vmGuru = mock(VirtualMachineGuru.class); + + User user = mock(User.class); + + Account account = mock(Account.class); + + ReservationContext ctx = mock(ReservationContext.class); + + ItWorkVO work = mock(ItWorkVO.class); + + ServiceOfferingVO serviceOffering = mock(ServiceOfferingVO.class); + + VirtualMachineTemplate template = mock(VirtualMachineTemplate.class); + when(template.isDeployAsIs()).thenReturn(false); + + DataCenterDeployment plan = mock(DataCenterDeployment.class); + when(plan.getDataCenterId()).thenReturn(1L); + when(plan.getPodId()).thenReturn(1L); + + Map params = new HashMap<>(); + + DeploymentPlanner planner = mock(DeploymentPlanner.class); + + when(vmInstanceDaoMock.findByUuid("vm-uuid")).thenReturn(vmInstance); + + doReturn(vmGuru).when(virtualMachineManagerImpl).getVmGuru(vmInstance); + + Ternary start = new Ternary<>(vmInstance, ctx, work); + Mockito.doReturn(start).when(virtualMachineManagerImpl).changeToStartState(vmGuru, vmInstance, user, account); + + when(ctx.getJournal()).thenReturn(Mockito.mock(Journal.class)); + + when(serviceOfferingDaoMock.findById(vmInstance.getId(), vmInstance.getServiceOfferingId())).thenReturn(serviceOffering); + + when(_entityMgr.findByIdIncludingRemoved(VirtualMachineTemplate.class, vmInstance.getTemplateId())).thenReturn(template); + + Host destHost = mock(Host.class); + Pod destPod = mock(Pod.class); + DeployDestination dest = mock(DeployDestination.class); + when(dest.getHost()).thenReturn(destHost); + when(dest.getPod()).thenReturn(destPod); + when(dest.getCluster()).thenReturn(mock(Cluster.class)); + when(destHost.getId()).thenReturn(1L); + when(destPod.getId()).thenReturn(2L); + when(_dpMgr.planDeployment(any(VirtualMachineProfileImpl.class), any(DataCenterDeployment.class), any(ExcludeList.class), any(DeploymentPlanner.class))).thenReturn(dest); + + doNothing().when(virtualMachineManagerImpl).checkIfTemplateNeededForCreatingVmVolumes(vmInstance); + + when(_workDao.updateStep(any(), any())).thenReturn(true); + when(_stateMachine.transitTo(vmInstance, VirtualMachine.Event.OperationRetry, new Pair(vmInstance.getHostId(), 1L), vmInstanceDaoMock)).thenThrow(new CloudRuntimeException("Error while transitioning")); + when(_stateMachine.transitTo(vmInstance, VirtualMachine.Event.OperationFailed, new Pair(vmInstance.getHostId(), null), vmInstanceDaoMock)).thenReturn(true); + + + Cluster cluster = mock(Cluster.class); + when(dest.getCluster()).thenReturn(cluster); + ClusterDetailsVO cluster_detail_cpu = mock(ClusterDetailsVO.class); + ClusterDetailsVO cluster_detail_ram = mock(ClusterDetailsVO.class); + when(cluster.getId()).thenReturn(1L); + when(_clusterDetailsDao.findDetail(1L, VmDetailConstants.CPU_OVER_COMMIT_RATIO)).thenReturn(cluster_detail_cpu); + when(_clusterDetailsDao.findDetail(1L, VmDetailConstants.MEMORY_OVER_COMMIT_RATIO)).thenReturn(cluster_detail_ram); + when(userVmDetailsDao.findDetail(anyLong(), Mockito.anyString())).thenReturn(null); + when(cluster_detail_cpu.getValue()).thenReturn("1.0"); + when(cluster_detail_ram.getValue()).thenReturn("1.0"); + doReturn(false).when(virtualMachineManagerImpl).areAllVolumesAllocated(Mockito.anyLong()); + + CallContext callContext = mock(CallContext.class); + Mockito.when(callContext.getCallingAccount()).thenReturn(account); + Mockito.when(callContext.getCallingUser()).thenReturn(user); + PowerMockito.mockStatic(CallContext.class); + PowerMockito.when(CallContext.current()).thenReturn(callContext); + + try { + virtualMachineManagerImpl.orchestrateStart("vm-uuid", params, plan, planner); + } catch (CloudRuntimeException e) { + assertEquals(e.getMessage(), "Error while transitioning"); + } + + assertEquals(vmInstance.getPodIdToDeployIn(), (Long) destPod.getId()); + } + + @Test + public void testOrchestrateStartNullPodId() throws Exception { + VMInstanceVO vmInstance = new VMInstanceVO(); + ReflectionTestUtils.setField(vmInstance, "id", 1L); + ReflectionTestUtils.setField(vmInstance, "uuid", "vm-uuid"); + ReflectionTestUtils.setField(vmInstance, "serviceOfferingId", 2L); + ReflectionTestUtils.setField(vmInstance, "instanceName", "myVm"); + ReflectionTestUtils.setField(vmInstance, "hostId", 2L); + ReflectionTestUtils.setField(vmInstance, "type", VirtualMachine.Type.User); + ReflectionTestUtils.setField(vmInstance, "dataCenterId", 1L); + ReflectionTestUtils.setField(vmInstance, "hypervisorType", HypervisorType.KVM); + + VirtualMachineGuru vmGuru = mock(VirtualMachineGuru.class); + + User user = mock(User.class); + + Account account = mock(Account.class); + + ReservationContext ctx = mock(ReservationContext.class); + + ItWorkVO work = mock(ItWorkVO.class); + + ServiceOfferingVO serviceOffering = mock(ServiceOfferingVO.class); + + VirtualMachineTemplate template = mock(VirtualMachineTemplate.class); + when(template.isDeployAsIs()).thenReturn(false); + + DataCenterDeployment plan = mock(DataCenterDeployment.class); + when(plan.getDataCenterId()).thenReturn(1L); + when(plan.getPodId()).thenReturn(1L); + + Map params = new HashMap<>(); + + DeploymentPlanner planner = mock(DeploymentPlanner.class); + + when(vmInstanceDaoMock.findByUuid("vm-uuid")).thenReturn(vmInstance); + + doReturn(vmGuru).when(virtualMachineManagerImpl).getVmGuru(vmInstance); + + Ternary start = new Ternary<>(vmInstance, ctx, work); + Mockito.doReturn(start).when(virtualMachineManagerImpl).changeToStartState(vmGuru, vmInstance, user, account); + + when(ctx.getJournal()).thenReturn(Mockito.mock(Journal.class)); + + when(serviceOfferingDaoMock.findById(vmInstance.getId(), vmInstance.getServiceOfferingId())).thenReturn(serviceOffering); + + when(_entityMgr.findByIdIncludingRemoved(VirtualMachineTemplate.class, vmInstance.getTemplateId())).thenReturn(template); + + Host destHost = mock(Host.class); + Pod destPod = mock(Pod.class); + DeployDestination dest = mock(DeployDestination.class); + when(dest.getHost()).thenReturn(destHost); + when(dest.getPod()).thenReturn(destPod); + when(dest.getCluster()).thenReturn(mock(Cluster.class)); + when(destHost.getId()).thenReturn(1L); + when(destPod.getId()).thenReturn(2L); + when(_dpMgr.planDeployment(any(VirtualMachineProfileImpl.class), any(DataCenterDeployment.class), any(ExcludeList.class), any(DeploymentPlanner.class))).thenReturn(dest); + + doNothing().when(virtualMachineManagerImpl).checkIfTemplateNeededForCreatingVmVolumes(vmInstance); + + when(_workDao.updateStep(any(), any())).thenReturn(true); + when(_stateMachine.transitTo(vmInstance, VirtualMachine.Event.OperationRetry, new Pair(vmInstance.getHostId(), 1L), vmInstanceDaoMock)).thenThrow(new CloudRuntimeException("Error while transitioning")); + when(_stateMachine.transitTo(vmInstance, VirtualMachine.Event.OperationFailed, new Pair(vmInstance.getHostId(), null), vmInstanceDaoMock)).thenReturn(true); + + + Cluster cluster = mock(Cluster.class); + when(dest.getCluster()).thenReturn(cluster); + ClusterDetailsVO cluster_detail_cpu = mock(ClusterDetailsVO.class); + ClusterDetailsVO cluster_detail_ram = mock(ClusterDetailsVO.class); + when(cluster.getId()).thenReturn(1L); + when(_clusterDetailsDao.findDetail(1L, VmDetailConstants.CPU_OVER_COMMIT_RATIO)).thenReturn(cluster_detail_cpu); + when(_clusterDetailsDao.findDetail(1L, VmDetailConstants.MEMORY_OVER_COMMIT_RATIO)).thenReturn(cluster_detail_ram); + when(userVmDetailsDao.findDetail(anyLong(), Mockito.anyString())).thenReturn(null); + when(cluster_detail_cpu.getValue()).thenReturn("1.0"); + when(cluster_detail_ram.getValue()).thenReturn("1.0"); + doReturn(true).when(virtualMachineManagerImpl).areAllVolumesAllocated(Mockito.anyLong()); + + CallContext callContext = mock(CallContext.class); + Mockito.when(callContext.getCallingAccount()).thenReturn(account); + Mockito.when(callContext.getCallingUser()).thenReturn(user); + PowerMockito.mockStatic(CallContext.class); + PowerMockito.when(CallContext.current()).thenReturn(callContext); + + try { + virtualMachineManagerImpl.orchestrateStart("vm-uuid", params, plan, planner); + } catch (CloudRuntimeException e) { + assertEquals(e.getMessage(), "Error while transitioning"); + } + + assertNull(vmInstance.getPodIdToDeployIn()); + } } From b2e83271f8b71a25890043179511115df8402c8d Mon Sep 17 00:00:00 2001 From: Rohit Yadav Date: Wed, 8 Nov 2023 14:38:05 +0530 Subject: [PATCH 2/3] ui: Admin, account and project dashboard improvements (#8193) Signed-off-by: Rohit Yadav --- ui/public/locales/en.json | 1 + ui/src/components/widgets/ChartCard.vue | 1 - ui/src/components/widgets/Drawer.vue | 2 +- ui/src/config/router.js | 23 +- ui/src/core/lazy_lib/components_use.js | 4 +- ui/src/style/dark-mode.less | 4 +- ui/src/utils/device.js | 6 +- ui/src/views/dashboard/CapacityDashboard.vue | 538 +++++++++++++---- ui/src/views/dashboard/UsageDashboard.vue | 581 ++++++++++++++----- 9 files changed, 891 insertions(+), 269 deletions(-) diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 55f07357de9..d1f0d84e755 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -2148,6 +2148,7 @@ "label.volumetotal": "Volume", "label.volumetype": "Volume Type", "label.vpc": "VPC", +"label.vpcs": "VPCs", "label.vpc.id": "VPC ID", "label.vpc.offerings": "VPC offerings", "label.vpc.virtual.router": "VPC virtual router", diff --git a/ui/src/components/widgets/ChartCard.vue b/ui/src/components/widgets/ChartCard.vue index 8d5e413c9db..a41cd69a3f1 100644 --- a/ui/src/components/widgets/ChartCard.vue +++ b/ui/src/components/widgets/ChartCard.vue @@ -93,7 +93,6 @@ export default { } &-footer { - border-top: 1px solid #e8e8e8; padding-top: 9px; margin-top: 8px; } diff --git a/ui/src/components/widgets/Drawer.vue b/ui/src/components/widgets/Drawer.vue index 38636522dbd..0a1c535e5f8 100644 --- a/ui/src/components/widgets/Drawer.vue +++ b/ui/src/components/widgets/Drawer.vue @@ -134,7 +134,7 @@ export default { text-align: center; transition: all 0.5s; cursor: pointer; - top: calc(50% - 45px); + top: calc(100% - 45px); z-index: 100; &.left{ diff --git a/ui/src/config/router.js b/ui/src/config/router.js index e7e8e642877..502a0246edf 100644 --- a/ui/src/config/router.js +++ b/ui/src/config/router.js @@ -20,7 +20,7 @@ import { UserLayout, BasicLayout, RouteView } from '@/layouts' import AutogenView from '@/views/AutogenView.vue' import IFramePlugin from '@/views/plugins/IFramePlugin.vue' -import { shallowRef, defineAsyncComponent } from 'vue' +import { shallowRef } from 'vue' import { vueProps } from '@/vue-app' import compute from '@/config/section/compute' @@ -201,26 +201,7 @@ export function asyncRouterMap () { name: 'dashboard', meta: { title: 'label.dashboard', - icon: 'DashboardOutlined', - tabs: [ - { - name: 'dashboard', - component: shallowRef(defineAsyncComponent(() => import('@/views/dashboard/UsageDashboardChart'))) - }, - { - name: 'accounts', - show: (record, route, user) => { return record.account === user.account || ['Admin', 'DomainAdmin'].includes(user.roletype) }, - component: shallowRef(defineAsyncComponent(() => import('@/views/project/AccountsTab'))) - }, - { - name: 'limits', - params: { - projectid: 'id' - }, - show: (record, route, user) => { return ['Admin'].includes(user.roletype) }, - component: shallowRef(defineAsyncComponent(() => import('@/components/view/ResourceLimitTab.vue'))) - } - ] + icon: 'DashboardOutlined' }, component: () => import('@/views/dashboard/Dashboard') }, diff --git a/ui/src/core/lazy_lib/components_use.js b/ui/src/core/lazy_lib/components_use.js index 10790d61bc0..3c61c02c08f 100644 --- a/ui/src/core/lazy_lib/components_use.js +++ b/ui/src/core/lazy_lib/components_use.js @@ -63,7 +63,8 @@ import { Slider, AutoComplete, Collapse, - Space + Space, + Statistic } from 'ant-design-vue' import VueClipboard from 'vue3-clipboard' import VueCropper from 'vue-cropper' @@ -127,5 +128,6 @@ export default { app.use(Collapse) app.use(Descriptions) app.use(Space) + app.use(Statistic) } } diff --git a/ui/src/style/dark-mode.less b/ui/src/style/dark-mode.less index 1b338ea9ae6..4d8ae25104f 100644 --- a/ui/src/style/dark-mode.less +++ b/ui/src/style/dark-mode.less @@ -36,7 +36,7 @@ .dark-mode { background: @dark-bgColor; - h1, h2, h3, h4, h5, h6 { + h1, h2, h3, h4, h5, h6, .ant-statistic-title, .ant-statistic-content { color: @dark-text-color-3; } @@ -959,4 +959,4 @@ .button-clear-notification { background-color: @dark-secondary-bgColor; } -} \ No newline at end of file +} diff --git a/ui/src/utils/device.js b/ui/src/utils/device.js index ce6deab7b39..86c7b1e7d84 100644 --- a/ui/src/utils/device.js +++ b/ui/src/utils/device.js @@ -43,7 +43,7 @@ export const deviceEnquire = function (callback) { } enquireJs - .register('screen and (max-width: 800px)', matchMobile) - .register('screen and (min-width: 800px) and (max-width: 1366px)', matchTablet) - .register('screen and (min-width: 1367px)', matchDesktop) + .register('screen and (max-width: 765px)', matchMobile) + .register('screen and (min-width: 766px) and (max-width: 1279px)', matchTablet) + .register('screen and (min-width: 1280px)', matchDesktop) } diff --git a/ui/src/views/dashboard/CapacityDashboard.vue b/ui/src/views/dashboard/CapacityDashboard.vue index 2d7b31ed467..a174e60b709 100644 --- a/ui/src/views/dashboard/CapacityDashboard.vue +++ b/ui/src/views/dashboard/CapacityDashboard.vue @@ -16,8 +16,8 @@ // under the License.