|
45 | 45 | import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd; |
46 | 46 | import org.apache.cloudstack.api.command.user.volume.MigrateVolumeCmd; |
47 | 47 | import org.apache.cloudstack.context.CallContext; |
| 48 | +import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService; |
48 | 49 | import org.apache.cloudstack.engine.subsystem.api.storage.DataStore; |
49 | 50 | import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; |
50 | 51 | import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore; |
|
86 | 87 | import com.cloud.api.query.dao.ServiceOfferingJoinDao; |
87 | 88 | import com.cloud.configuration.Resource; |
88 | 89 | import com.cloud.configuration.Resource.ResourceType; |
| 90 | +import com.cloud.dc.ClusterVO; |
89 | 91 | import com.cloud.dc.DataCenterVO; |
| 92 | +import com.cloud.dc.HostPodVO; |
| 93 | +import com.cloud.dc.dao.ClusterDao; |
90 | 94 | import com.cloud.dc.dao.DataCenterDao; |
| 95 | +import com.cloud.dc.dao.HostPodDao; |
91 | 96 | import com.cloud.event.EventTypes; |
92 | 97 | import com.cloud.event.UsageEventUtils; |
93 | 98 | import com.cloud.exception.InvalidParameterValueException; |
|
122 | 127 | import com.cloud.utils.db.TransactionLegacy; |
123 | 128 | import com.cloud.utils.exception.CloudRuntimeException; |
124 | 129 | import com.cloud.utils.fsm.NoTransitionException; |
| 130 | +import com.cloud.vm.DiskProfile; |
125 | 131 | import com.cloud.vm.UserVmManager; |
126 | 132 | import com.cloud.vm.UserVmVO; |
127 | 133 | import com.cloud.vm.VirtualMachine; |
128 | 134 | import com.cloud.vm.VirtualMachine.State; |
| 135 | +import com.cloud.vm.VirtualMachineManager; |
129 | 136 | import com.cloud.vm.dao.UserVmDao; |
130 | 137 | import com.cloud.vm.dao.VMInstanceDao; |
131 | 138 | import com.cloud.vm.snapshot.VMSnapshotVO; |
@@ -199,6 +206,15 @@ public class VolumeApiServiceImplTest { |
199 | 206 | private DataStoreManager dataStoreMgr; |
200 | 207 | @Mock |
201 | 208 | private SnapshotHelper snapshotHelper; |
| 209 | + @Mock |
| 210 | + VirtualMachineManager virtualMachineManager; |
| 211 | + @Mock |
| 212 | + HostPodDao podDao; |
| 213 | + @Mock |
| 214 | + ClusterDao clusterDao; |
| 215 | + @Mock |
| 216 | + VolumeOrchestrationService volumeOrchestrationService; |
| 217 | + |
202 | 218 |
|
203 | 219 | private DetachVolumeCmd detachCmd = new DetachVolumeCmd(); |
204 | 220 | private Class<?> _detachCmdClass = detachCmd.getClass(); |
@@ -1820,4 +1836,237 @@ public void testValidationsForCheckVolumeAPIWithInvalidVolumeFormat() { |
1820 | 1836 |
|
1821 | 1837 | volumeApiServiceImpl.validationsForCheckVolumeOperation(volume); |
1822 | 1838 | } |
| 1839 | + |
| 1840 | + private UserVmVO getMockedVm() { |
| 1841 | + UserVmVO vm = Mockito.mock(UserVmVO.class); |
| 1842 | + Mockito.when(vm.getId()).thenReturn(1L); |
| 1843 | + Mockito.when(vm.getTemplateId()).thenReturn(10L); |
| 1844 | + Mockito.when(vm.getHostName()).thenReturn("test-vm"); |
| 1845 | + return vm; |
| 1846 | + } |
| 1847 | + |
| 1848 | + private VMTemplateVO getMockedTemplate() { |
| 1849 | + VMTemplateVO template = Mockito.mock(VMTemplateVO.class); |
| 1850 | + Mockito.when(template.isDeployAsIs()).thenReturn(false); |
| 1851 | + return template; |
| 1852 | + } |
| 1853 | + |
| 1854 | + @Test(expected = CloudRuntimeException.class) |
| 1855 | + public void testGetVmExistingVolumeForVolumeAttach_MultipleRootVolumes_ThrowsException() { |
| 1856 | + UserVmVO vm = getMockedVm(); |
| 1857 | + VMTemplateVO template = getMockedTemplate(); |
| 1858 | + when(templateDao.findById(10L)).thenReturn(template); |
| 1859 | + when(volumeDaoMock.findByInstanceAndType(1L, Volume.Type.ROOT)) |
| 1860 | + .thenReturn(Arrays.asList(Mockito.mock(VolumeVO.class), Mockito.mock(VolumeVO.class))); |
| 1861 | + volumeApiServiceImpl.getVmExistingVolumeForVolumeAttach(vm, Mockito.mock(VolumeInfo.class)); |
| 1862 | + } |
| 1863 | + |
| 1864 | + @Test |
| 1865 | + public void testGetVmExistingVolumeForVolumeAttach_SingleRootVolume() { |
| 1866 | + UserVmVO vm = getMockedVm(); |
| 1867 | + VMTemplateVO template = getMockedTemplate(); |
| 1868 | + VolumeVO rootVolume = Mockito.mock(VolumeVO.class); |
| 1869 | + Mockito.when(rootVolume.getId()).thenReturn(20L); |
| 1870 | + Mockito.when(templateDao.findById(10L)).thenReturn(template); |
| 1871 | + Mockito.when(volumeDaoMock.findByInstanceAndType(1L, Volume.Type.ROOT)) |
| 1872 | + .thenReturn(Collections.singletonList(rootVolume)); |
| 1873 | + VolumeVO result = volumeApiServiceImpl.getVmExistingVolumeForVolumeAttach(vm, Mockito.mock(VolumeInfo.class)); |
| 1874 | + Assert.assertNotNull(result); |
| 1875 | + Assert.assertEquals(20L, result.getId()); |
| 1876 | + } |
| 1877 | + |
| 1878 | + private VolumeVO getMockedDataVolume() { |
| 1879 | + VolumeVO volume = Mockito.mock(VolumeVO.class); |
| 1880 | + Mockito.when(volume.getId()).thenReturn(30L); |
| 1881 | + Mockito.when(volume.getState()).thenReturn(Volume.State.Ready); |
| 1882 | + return volume; |
| 1883 | + } |
| 1884 | + |
| 1885 | + @Test |
| 1886 | + public void testGetVmExistingVolumeForVolumeAttach_NoRootVolume_DataDiskAvailable() { |
| 1887 | + UserVmVO vm = getMockedVm(); |
| 1888 | + VMTemplateVO template = getMockedTemplate(); |
| 1889 | + VolumeVO dataDisk = getMockedDataVolume(); |
| 1890 | + List<VolumeVO> rootVolumes = Collections.emptyList(); |
| 1891 | + List<VolumeVO> dataVolumes = Collections.singletonList(dataDisk); |
| 1892 | + Mockito.when(templateDao.findById(10L)).thenReturn(template); |
| 1893 | + Mockito.when(volumeDaoMock.findByInstanceAndType(1L, Volume.Type.ROOT)).thenReturn(rootVolumes); |
| 1894 | + Mockito.when(volumeDaoMock.findByInstanceAndType(1L, Volume.Type.DATADISK)).thenReturn(dataVolumes); |
| 1895 | + VolumeVO result = volumeApiServiceImpl.getVmExistingVolumeForVolumeAttach(vm, Mockito.mock(VolumeInfo.class)); |
| 1896 | + Assert.assertNotNull(result); |
| 1897 | + Assert.assertEquals(30L, result.getId()); |
| 1898 | + } |
| 1899 | + |
| 1900 | + @Test |
| 1901 | + public void testGetVmExistingVolumeForVolumeAttach_NoVolumesAtAll() { |
| 1902 | + UserVmVO vm = getMockedVm(); |
| 1903 | + VMTemplateVO template = getMockedTemplate(); |
| 1904 | + Mockito.when(templateDao.findById(10L)).thenReturn(template); |
| 1905 | + Mockito.when(volumeDaoMock.findByInstanceAndType(1L, Volume.Type.ROOT)).thenReturn(Collections.emptyList()); |
| 1906 | + Mockito.when(volumeDaoMock.findByInstanceAndType(1L, Volume.Type.DATADISK)).thenReturn(Collections.emptyList()); |
| 1907 | + VolumeVO result = volumeApiServiceImpl.getVmExistingVolumeForVolumeAttach(vm, Mockito.mock(VolumeInfo.class)); |
| 1908 | + Assert.assertNull(result); |
| 1909 | + } |
| 1910 | + |
| 1911 | + private void mockDiskOffering() { |
| 1912 | + DiskOfferingVO offering = Mockito.mock(DiskOfferingVO.class); |
| 1913 | + Mockito.when(_diskOfferingDao.findById(1L)).thenReturn(offering); |
| 1914 | + Mockito.when(offering.isUseLocalStorage()).thenReturn(true); |
| 1915 | + Mockito.when(offering.isRecreatable()).thenReturn(false); |
| 1916 | + } |
| 1917 | + |
| 1918 | + private DataCenterVO mockZone() { |
| 1919 | + DataCenterVO zone = Mockito.mock(DataCenterVO.class); |
| 1920 | + Mockito.when(_dcDao.findById(1L)).thenReturn(zone); |
| 1921 | + return zone; |
| 1922 | + } |
| 1923 | + |
| 1924 | + @Test |
| 1925 | + public void testGetPoolForAllocatedOrUploadedVolumeForAttach_Success() { |
| 1926 | + VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class); |
| 1927 | + UserVmVO vm = Mockito.mock(UserVmVO.class); |
| 1928 | + ClusterVO cluster = Mockito.mock(ClusterVO.class); |
| 1929 | + HostPodVO pod = Mockito.mock(HostPodVO.class); |
| 1930 | + DataCenterVO zone = mockZone(); |
| 1931 | + mockDiskOffering(); |
| 1932 | + StoragePool pool = Mockito.mock(StoragePool.class); |
| 1933 | + when(vm.getDataCenterId()).thenReturn(1L); |
| 1934 | + when(virtualMachineManager.findClusterAndHostIdForVm(vm, false)).thenReturn(new Pair<>(1L, 2L)); |
| 1935 | + when(clusterDao.findById(1L)).thenReturn(cluster); |
| 1936 | + when(cluster.getPodId()).thenReturn(1L); |
| 1937 | + when(podDao.findById(1L)).thenReturn(pod); |
| 1938 | + when(volumeToAttach.getDiskOfferingId()).thenReturn(1L); |
| 1939 | + when(volumeOrchestrationService.findStoragePool(any(DiskProfile.class), eq(zone), eq(pod), eq(1L), eq(2L), eq(vm), eq(Collections.emptySet()))) |
| 1940 | + .thenReturn(pool); |
| 1941 | + StoragePool result = volumeApiServiceImpl.getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm); |
| 1942 | + Assert.assertNotNull(result); |
| 1943 | + Assert.assertEquals(pool, result); |
| 1944 | + } |
| 1945 | + |
| 1946 | + @Test(expected = CloudRuntimeException.class) |
| 1947 | + public void testGetPoolForAllocatedOrUploadedVolumeForAttach_NoPoolFound_ThrowsException() { |
| 1948 | + VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class); |
| 1949 | + UserVmVO vm = Mockito.mock(UserVmVO.class); |
| 1950 | + DataCenterVO zone = mockZone(); |
| 1951 | + Pair<Long, Long> clusterHostId = new Pair<>(1L, 2L); |
| 1952 | + ClusterVO cluster = Mockito.mock(ClusterVO.class); |
| 1953 | + HostPodVO pod = Mockito.mock(HostPodVO.class); |
| 1954 | + mockDiskOffering(); |
| 1955 | + when(vm.getDataCenterId()).thenReturn(1L); |
| 1956 | + when(clusterDao.findById(1L)).thenReturn(cluster); |
| 1957 | + when(virtualMachineManager.findClusterAndHostIdForVm(vm, false)).thenReturn(clusterHostId); |
| 1958 | + when(podDao.findById(anyLong())).thenReturn(pod); |
| 1959 | + when(volumeToAttach.getDiskOfferingId()).thenReturn(1L); |
| 1960 | + when(volumeOrchestrationService.findStoragePool(any(DiskProfile.class), eq(zone), eq(pod), eq(1L), eq(2L), eq(vm), eq(Collections.emptySet()))) |
| 1961 | + .thenReturn(null); |
| 1962 | + volumeApiServiceImpl.getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm); |
| 1963 | + } |
| 1964 | + |
| 1965 | + @Test |
| 1966 | + public void testGetPoolForAllocatedOrUploadedVolumeForAttach_NoCluster() { |
| 1967 | + VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class); |
| 1968 | + UserVmVO vm = Mockito.mock(UserVmVO.class); |
| 1969 | + DataCenterVO zone = mockZone(); |
| 1970 | + HostPodVO pod = Mockito.mock(HostPodVO.class); |
| 1971 | + mockDiskOffering(); |
| 1972 | + StoragePool pool = Mockito.mock(StoragePool.class); |
| 1973 | + when(vm.getDataCenterId()).thenReturn(1L); |
| 1974 | + when(vm.getPodIdToDeployIn()).thenReturn(2L); |
| 1975 | + when(virtualMachineManager.findClusterAndHostIdForVm(vm, false)).thenReturn(new Pair<>(null, 2L)); |
| 1976 | + when(podDao.findById(2L)).thenReturn(pod); |
| 1977 | + when(volumeToAttach.getDiskOfferingId()).thenReturn(1L); |
| 1978 | + when(volumeOrchestrationService.findStoragePool(any(DiskProfile.class), eq(zone), eq(pod), eq(null), eq(2L), eq(vm), eq(Collections.emptySet()))) |
| 1979 | + .thenReturn(pool); |
| 1980 | + StoragePool result = volumeApiServiceImpl.getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm); |
| 1981 | + Assert.assertNotNull(result); |
| 1982 | + Assert.assertEquals(pool, result); |
| 1983 | + } |
| 1984 | + |
| 1985 | + |
| 1986 | + @Test |
| 1987 | + public void testCreateVolumeOnSecondaryForAttachIfNeeded_VolumeNotAllocatedOrUploaded() { |
| 1988 | + VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class); |
| 1989 | + Mockito.when(volumeToAttach.getState()).thenReturn(Volume.State.Ready); |
| 1990 | + VolumeInfo result = volumeApiServiceImpl.createVolumeOnPrimaryForAttachIfNeeded( |
| 1991 | + volumeToAttach, Mockito.mock(UserVmVO.class), null); |
| 1992 | + Assert.assertSame(volumeToAttach, result); |
| 1993 | + Mockito.verifyNoInteractions(primaryDataStoreDaoMock, volumeOrchestrationService); |
| 1994 | + } |
| 1995 | + |
| 1996 | + @Test |
| 1997 | + public void testCreateVolumeOnSecondaryForAttachIfNeeded_ExistingVolumeDeterminesStoragePool() { |
| 1998 | + VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class); |
| 1999 | + Mockito.when(volumeToAttach.getState()).thenReturn(Volume.State.Uploaded); |
| 2000 | + UserVmVO vm = Mockito.mock(UserVmVO.class); |
| 2001 | + VolumeVO existingVolume = Mockito.mock(VolumeVO.class); |
| 2002 | + Mockito.when(existingVolume.getState()).thenReturn(Volume.State.Ready); |
| 2003 | + when(existingVolume.getPoolId()).thenReturn(1L); |
| 2004 | + StoragePoolVO destPrimaryStorage = Mockito.mock(StoragePoolVO.class); |
| 2005 | + Mockito.when(destPrimaryStorage.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem); |
| 2006 | + Mockito.when(primaryDataStoreDaoMock.findById(1L)).thenReturn(destPrimaryStorage); |
| 2007 | + VolumeInfo newVolumeOnPrimaryStorage = Mockito.mock(VolumeInfo.class); |
| 2008 | + try { |
| 2009 | + Mockito.when(volumeOrchestrationService.createVolumeOnPrimaryStorage(vm, volumeToAttach, vm.getHypervisorType(), destPrimaryStorage)) |
| 2010 | + .thenReturn(newVolumeOnPrimaryStorage); |
| 2011 | + } catch (NoTransitionException nte) { |
| 2012 | + Assert.fail(nte.getMessage()); |
| 2013 | + } |
| 2014 | + VolumeInfo result = volumeApiServiceImpl.createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, existingVolume); |
| 2015 | + Assert.assertSame(newVolumeOnPrimaryStorage, result); |
| 2016 | + Mockito.verify(primaryDataStoreDaoMock).findById(1L); |
| 2017 | + } |
| 2018 | + |
| 2019 | + @Test |
| 2020 | + public void testCreateVolumeOnPrimaryForAttachIfNeeded_UsesGetPoolForAttach() { |
| 2021 | + VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class); |
| 2022 | + Mockito.when(volumeToAttach.getState()).thenReturn(Volume.State.Allocated); |
| 2023 | + UserVmVO vm = Mockito.mock(UserVmVO.class); |
| 2024 | + StoragePool destPrimaryStorage = Mockito.mock(StoragePool.class); |
| 2025 | + Mockito.doReturn(destPrimaryStorage).when(volumeApiServiceImpl) |
| 2026 | + .getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm); |
| 2027 | + VolumeInfo newVolumeOnPrimaryStorage = Mockito.mock(VolumeInfo.class); |
| 2028 | + try { |
| 2029 | + Mockito.when(volumeOrchestrationService.createVolumeOnPrimaryStorage( |
| 2030 | + vm, volumeToAttach, vm.getHypervisorType(), destPrimaryStorage)) |
| 2031 | + .thenReturn(newVolumeOnPrimaryStorage); |
| 2032 | + } catch (NoTransitionException nte) { |
| 2033 | + Assert.fail(nte.getMessage()); |
| 2034 | + } |
| 2035 | + VolumeInfo result = volumeApiServiceImpl.createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, null); |
| 2036 | + Assert.assertSame(newVolumeOnPrimaryStorage, result); |
| 2037 | + verify(volumeApiServiceImpl).getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm); |
| 2038 | + } |
| 2039 | + |
| 2040 | + @Test(expected = InvalidParameterValueException.class) |
| 2041 | + public void testCreateVolumeOnPrimaryForAttachIfNeeded_UnsupportedPoolType_ThrowsException() { |
| 2042 | + VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class); |
| 2043 | + when(volumeToAttach.getState()).thenReturn(Volume.State.Uploaded); |
| 2044 | + UserVmVO vm = Mockito.mock(UserVmVO.class); |
| 2045 | + StoragePool destPrimaryStorage = Mockito.mock(StoragePool.class); |
| 2046 | + when(destPrimaryStorage.getPoolType()).thenReturn(Storage.StoragePoolType.PowerFlex); |
| 2047 | + Mockito.doReturn(destPrimaryStorage).when(volumeApiServiceImpl) |
| 2048 | + .getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm); |
| 2049 | + volumeApiServiceImpl.createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, null); |
| 2050 | + } |
| 2051 | + |
| 2052 | + @Test |
| 2053 | + public void testCreateVolumeOnSecondaryForAttachIfNeeded_CreateVolumeFails_ThrowsException() { |
| 2054 | + VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class); |
| 2055 | + Mockito.when(volumeToAttach.getState()).thenReturn(Volume.State.Uploaded); |
| 2056 | + UserVmVO vm = Mockito.mock(UserVmVO.class); |
| 2057 | + StoragePool destPrimaryStorage = Mockito.mock(StoragePool.class); |
| 2058 | + Mockito.when(destPrimaryStorage.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem); |
| 2059 | + Mockito.doReturn(destPrimaryStorage).when(volumeApiServiceImpl) |
| 2060 | + .getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm); |
| 2061 | + try { |
| 2062 | + Mockito.when(volumeOrchestrationService.createVolumeOnPrimaryStorage(vm, volumeToAttach, vm.getHypervisorType(), destPrimaryStorage)) |
| 2063 | + .thenThrow(new NoTransitionException("Mocked exception")); |
| 2064 | + } catch (NoTransitionException nte) { |
| 2065 | + Assert.fail(nte.getMessage()); |
| 2066 | + } |
| 2067 | + CloudRuntimeException exception = Assert.assertThrows(CloudRuntimeException.class, () -> |
| 2068 | + volumeApiServiceImpl.createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, null) |
| 2069 | + ); |
| 2070 | + Assert.assertTrue(exception.getMessage().contains("Failed to create volume on primary storage")); |
| 2071 | + } |
1823 | 2072 | } |
0 commit comments