server: fix pod retrieval during volume attach (#10324)

Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
This commit is contained in:
Abhishek Kumar 2025-02-07 17:29:23 +05:30 committed by GitHub
parent 802bf5fce7
commit a627ab67c2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 361 additions and 40 deletions

View File

@ -133,7 +133,9 @@ import com.cloud.dc.ClusterDetailsDao;
import com.cloud.dc.DataCenter;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.Pod;
import com.cloud.dc.dao.ClusterDao;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.dc.dao.HostPodDao;
import com.cloud.domain.Domain;
import com.cloud.domain.dao.DomainDao;
import com.cloud.event.ActionEvent;
@ -153,6 +155,7 @@ import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.hypervisor.HypervisorCapabilitiesVO;
import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao;
import com.cloud.offering.DiskOffering;
import com.cloud.org.Cluster;
import com.cloud.org.Grouping;
import com.cloud.projects.Project;
import com.cloud.projects.ProjectManager;
@ -323,6 +326,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
@Inject
private VmWorkJobDao _workJobDao;
@Inject
ClusterDao clusterDao;
@Inject
private ClusterDetailsDao _clusterDetailsDao;
@Inject
private StorageManager storageMgr;
@ -346,6 +351,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
protected ProjectManager projectManager;
@Inject
protected StoragePoolDetailsDao storagePoolDetailsDao;
@Inject
HostPodDao podDao;
protected Gson _gson;
@ -2380,17 +2387,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
return attachVolumeToVM(command.getVirtualMachineId(), command.getId(), command.getDeviceId());
}
private Volume orchestrateAttachVolumeToVM(Long vmId, Long volumeId, Long deviceId) {
VolumeInfo volumeToAttach = volFactory.getVolume(volumeId);
if (volumeToAttach.isAttachedVM()) {
throw new CloudRuntimeException("This volume is already attached to a VM.");
}
UserVmVO vm = _userVmDao.findById(vmId);
protected VolumeVO getVmExistingVolumeForVolumeAttach(UserVmVO vm, VolumeInfo volumeToAttach) {
VolumeVO existingVolumeOfVm = null;
VMTemplateVO template = _templateDao.findById(vm.getTemplateId());
List<VolumeVO> rootVolumesOfVm = _volsDao.findByInstanceAndType(vmId, Volume.Type.ROOT);
List<VolumeVO> rootVolumesOfVm = _volsDao.findByInstanceAndType(vm.getId(), Volume.Type.ROOT);
if (rootVolumesOfVm.size() > 1 && template != null && !template.isDeployAsIs()) {
throw new CloudRuntimeException("The VM " + vm.getHostName() + " has more than one ROOT volume and is in an invalid state.");
} else {
@ -2398,7 +2398,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
existingVolumeOfVm = rootVolumesOfVm.get(0);
} else {
// locate data volume of the vm
List<VolumeVO> diskVolumesOfVm = _volsDao.findByInstanceAndType(vmId, Volume.Type.DATADISK);
List<VolumeVO> diskVolumesOfVm = _volsDao.findByInstanceAndType(vm.getId(), Volume.Type.DATADISK);
for (VolumeVO diskVolume : diskVolumesOfVm) {
if (diskVolume.getState() != Volume.State.Allocated) {
existingVolumeOfVm = diskVolume;
@ -2407,44 +2407,90 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
}
}
}
if (existingVolumeOfVm == null) {
if (s_logger.isTraceEnabled()) {
s_logger.trace(String.format("No existing volume found for VM (%s/%s) to attach volume %s/%s",
vm.getName(), vm.getUuid(),
volumeToAttach.getName(), volumeToAttach.getUuid()));
}
return null;
}
if (s_logger.isTraceEnabled()) {
String msg = "attaching volume %s/%s to a VM (%s/%s) with an existing volume %s/%s on primary storage %s";
if (existingVolumeOfVm != null) {
s_logger.trace(String.format(msg,
volumeToAttach.getName(), volumeToAttach.getUuid(),
vm.getName(), vm.getUuid(),
existingVolumeOfVm.getName(), existingVolumeOfVm.getUuid(),
existingVolumeOfVm.getPoolId()));
}
s_logger.trace(String.format(msg,
volumeToAttach.getName(), volumeToAttach.getUuid(),
vm.getName(), vm.getUuid(),
existingVolumeOfVm.getName(), existingVolumeOfVm.getUuid(),
existingVolumeOfVm.getPoolId()));
}
return existingVolumeOfVm;
}
HypervisorType rootDiskHyperType = vm.getHypervisorType();
HypervisorType volumeToAttachHyperType = _volsDao.getHypervisorType(volumeToAttach.getId());
protected StoragePool getSuitablePoolForAllocatedOrUploadedVolumeForAttach(final VolumeInfo volumeToAttach, final UserVmVO vm) {
DataCenter zone = _dcDao.findById(vm.getDataCenterId());
Pair<Long, Long> clusterHostId = virtualMachineManager.findClusterAndHostIdForVm(vm, false);
Long podId = vm.getPodIdToDeployIn();
if (clusterHostId.first() != null) {
Cluster cluster = clusterDao.findById(clusterHostId.first());
podId = cluster.getPodId();
}
Pod pod = podDao.findById(podId);
DiskOfferingVO offering = _diskOfferingDao.findById(volumeToAttach.getDiskOfferingId());
DiskProfile diskProfile = new DiskProfile(volumeToAttach.getId(), volumeToAttach.getVolumeType(),
volumeToAttach.getName(), volumeToAttach.getId(), volumeToAttach.getSize(), offering.getTagsArray(),
offering.isUseLocalStorage(), offering.isRecreatable(),
volumeToAttach.getTemplateId());
diskProfile.setHyperType(vm.getHypervisorType());
return _volumeMgr.findStoragePool(diskProfile, zone, pod, clusterHostId.first(),
clusterHostId.second(), vm, Collections.emptySet());
}
protected VolumeInfo createVolumeOnPrimaryForAttachIfNeeded(final VolumeInfo volumeToAttach, final UserVmVO vm, VolumeVO existingVolumeOfVm) {
VolumeInfo newVolumeOnPrimaryStorage = volumeToAttach;
boolean volumeOnSecondary = volumeToAttach.getState() == Volume.State.Uploaded;
if (!Arrays.asList(Volume.State.Allocated, Volume.State.Uploaded).contains(volumeToAttach.getState())) {
return newVolumeOnPrimaryStorage;
}
//don't create volume on primary storage if its being attached to the vm which Root's volume hasn't been created yet
StoragePoolVO destPrimaryStorage = null;
StoragePool destPrimaryStorage = null;
if (existingVolumeOfVm != null && !existingVolumeOfVm.getState().equals(Volume.State.Allocated)) {
destPrimaryStorage = _storagePoolDao.findById(existingVolumeOfVm.getPoolId());
if (s_logger.isTraceEnabled() && destPrimaryStorage != null) {
s_logger.trace(String.format("decided on target storage: %s/%s", destPrimaryStorage.getName(), destPrimaryStorage.getUuid()));
}
}
boolean volumeOnSecondary = volumeToAttach.getState() == Volume.State.Uploaded;
if (destPrimaryStorage != null && (volumeToAttach.getState() == Volume.State.Allocated || volumeOnSecondary)) {
try {
if (volumeOnSecondary && destPrimaryStorage.getPoolType() == Storage.StoragePoolType.PowerFlex) {
throw new InvalidParameterValueException("Cannot attach uploaded volume, this operation is unsupported on storage pool type " + destPrimaryStorage.getPoolType());
if (destPrimaryStorage == null) {
destPrimaryStorage = getSuitablePoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm);
if (destPrimaryStorage == null) {
if (Volume.State.Allocated.equals(volumeToAttach.getState()) && State.Stopped.equals(vm.getState())) {
return newVolumeOnPrimaryStorage;
}
newVolumeOnPrimaryStorage = _volumeMgr.createVolumeOnPrimaryStorage(vm, volumeToAttach, rootDiskHyperType, destPrimaryStorage);
} catch (NoTransitionException e) {
s_logger.debug("Failed to create volume on primary storage", e);
throw new CloudRuntimeException("Failed to create volume on primary storage", e);
throw new CloudRuntimeException(String.format("Failed to find a primary storage for volume in state: %s", volumeToAttach.getState()));
}
}
try {
if (volumeOnSecondary && Storage.StoragePoolType.PowerFlex.equals(destPrimaryStorage.getPoolType())) {
throw new InvalidParameterValueException("Cannot attach uploaded volume, this operation is unsupported on storage pool type " + destPrimaryStorage.getPoolType());
}
newVolumeOnPrimaryStorage = _volumeMgr.createVolumeOnPrimaryStorage(vm, volumeToAttach,
vm.getHypervisorType(), destPrimaryStorage);
} catch (NoTransitionException e) {
s_logger.debug("Failed to create volume on primary storage", e);
throw new CloudRuntimeException("Failed to create volume on primary storage", e);
}
return newVolumeOnPrimaryStorage;
}
private Volume orchestrateAttachVolumeToVM(Long vmId, Long volumeId, Long deviceId) {
VolumeInfo volumeToAttach = volFactory.getVolume(volumeId);
if (volumeToAttach.isAttachedVM()) {
throw new CloudRuntimeException("This volume is already attached to a VM.");
}
UserVmVO vm = _userVmDao.findById(vmId);
VolumeVO existingVolumeOfVm = getVmExistingVolumeForVolumeAttach(vm, volumeToAttach);
VolumeInfo newVolumeOnPrimaryStorage = createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, existingVolumeOfVm);
// reload the volume from db
newVolumeOnPrimaryStorage = volFactory.getVolume(newVolumeOnPrimaryStorage.getId());
@ -2463,19 +2509,17 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
StoragePoolVO vmRootVolumePool = _storagePoolDao.findById(existingVolumeOfVm.getPoolId());
try {
HypervisorType volumeToAttachHyperType = _volsDao.getHypervisorType(volumeToAttach.getId());
newVolumeOnPrimaryStorage = _volumeMgr.moveVolume(newVolumeOnPrimaryStorage, vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(),
volumeToAttachHyperType);
} catch (ConcurrentOperationException e) {
s_logger.debug("move volume failed", e);
throw new CloudRuntimeException("move volume failed", e);
} catch (StorageUnavailableException e) {
} catch (ConcurrentOperationException | StorageUnavailableException e) {
s_logger.debug("move volume failed", e);
throw new CloudRuntimeException("move volume failed", e);
}
}
VolumeVO newVol = _volsDao.findById(newVolumeOnPrimaryStorage.getId());
// Getting the fresh vm object in case of volume migration to check the current state of VM
if (moveVolumeNeeded || volumeOnSecondary) {
if (moveVolumeNeeded) {
vm = _userVmDao.findById(vmId);
if (vm == null) {
throw new InvalidParameterValueException("VM not found.");
@ -2659,9 +2703,6 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
if (!_volsDao.findByInstanceAndDeviceId(vm.getId(), 0).isEmpty()) {
throw new InvalidParameterValueException("Vm already has root volume attached to it");
}
if (volumeToAttach.getState() == Volume.State.Uploaded) {
throw new InvalidParameterValueException("No support for Root volume attach in state " + Volume.State.Uploaded);
}
}
}

View File

@ -45,6 +45,7 @@ import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.MigrateVolumeCmd;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
@ -86,8 +87,12 @@ import org.springframework.test.util.ReflectionTestUtils;
import com.cloud.api.query.dao.ServiceOfferingJoinDao;
import com.cloud.configuration.Resource;
import com.cloud.configuration.Resource.ResourceType;
import com.cloud.dc.ClusterVO;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.HostPodVO;
import com.cloud.dc.dao.ClusterDao;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.dc.dao.HostPodDao;
import com.cloud.event.EventTypes;
import com.cloud.event.UsageEventUtils;
import com.cloud.exception.InvalidParameterValueException;
@ -122,10 +127,12 @@ import com.cloud.utils.Pair;
import com.cloud.utils.db.TransactionLegacy;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.fsm.NoTransitionException;
import com.cloud.vm.DiskProfile;
import com.cloud.vm.UserVmManager;
import com.cloud.vm.UserVmVO;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachine.State;
import com.cloud.vm.VirtualMachineManager;
import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.dao.VMInstanceDao;
import com.cloud.vm.snapshot.VMSnapshotVO;
@ -199,6 +206,15 @@ public class VolumeApiServiceImplTest {
private DataStoreManager dataStoreMgr;
@Mock
private SnapshotHelper snapshotHelper;
@Mock
VirtualMachineManager virtualMachineManager;
@Mock
HostPodDao podDao;
@Mock
ClusterDao clusterDao;
@Mock
VolumeOrchestrationService volumeOrchestrationService;
private DetachVolumeCmd detachCmd = new DetachVolumeCmd();
private Class<?> _detachCmdClass = detachCmd.getClass();
@ -1820,4 +1836,268 @@ public class VolumeApiServiceImplTest {
volumeApiServiceImpl.validationsForCheckVolumeOperation(volume);
}
private UserVmVO getMockedVm() {
UserVmVO vm = Mockito.mock(UserVmVO.class);
Mockito.when(vm.getId()).thenReturn(1L);
Mockito.when(vm.getTemplateId()).thenReturn(10L);
Mockito.when(vm.getHostName()).thenReturn("test-vm");
return vm;
}
private VMTemplateVO getMockedTemplate() {
VMTemplateVO template = Mockito.mock(VMTemplateVO.class);
Mockito.when(template.isDeployAsIs()).thenReturn(false);
return template;
}
@Test(expected = CloudRuntimeException.class)
public void testGetVmExistingVolumeForVolumeAttach_MultipleRootVolumes_ThrowsException() {
UserVmVO vm = getMockedVm();
VMTemplateVO template = getMockedTemplate();
when(templateDao.findById(10L)).thenReturn(template);
when(volumeDaoMock.findByInstanceAndType(1L, Volume.Type.ROOT))
.thenReturn(Arrays.asList(Mockito.mock(VolumeVO.class), Mockito.mock(VolumeVO.class)));
volumeApiServiceImpl.getVmExistingVolumeForVolumeAttach(vm, Mockito.mock(VolumeInfo.class));
}
@Test
public void testGetVmExistingVolumeForVolumeAttach_SingleRootVolume() {
UserVmVO vm = getMockedVm();
VMTemplateVO template = getMockedTemplate();
VolumeVO rootVolume = Mockito.mock(VolumeVO.class);
Mockito.when(rootVolume.getId()).thenReturn(20L);
Mockito.when(templateDao.findById(10L)).thenReturn(template);
Mockito.when(volumeDaoMock.findByInstanceAndType(1L, Volume.Type.ROOT))
.thenReturn(Collections.singletonList(rootVolume));
VolumeVO result = volumeApiServiceImpl.getVmExistingVolumeForVolumeAttach(vm, Mockito.mock(VolumeInfo.class));
Assert.assertNotNull(result);
Assert.assertEquals(20L, result.getId());
}
private VolumeVO getMockedDataVolume() {
VolumeVO volume = Mockito.mock(VolumeVO.class);
Mockito.when(volume.getId()).thenReturn(30L);
Mockito.when(volume.getState()).thenReturn(Volume.State.Ready);
return volume;
}
@Test
public void testGetVmExistingVolumeForVolumeAttach_NoRootVolume_DataDiskAvailable() {
UserVmVO vm = getMockedVm();
VMTemplateVO template = getMockedTemplate();
VolumeVO dataDisk = getMockedDataVolume();
List<VolumeVO> rootVolumes = Collections.emptyList();
List<VolumeVO> dataVolumes = Collections.singletonList(dataDisk);
Mockito.when(templateDao.findById(10L)).thenReturn(template);
Mockito.when(volumeDaoMock.findByInstanceAndType(1L, Volume.Type.ROOT)).thenReturn(rootVolumes);
Mockito.when(volumeDaoMock.findByInstanceAndType(1L, Volume.Type.DATADISK)).thenReturn(dataVolumes);
VolumeVO result = volumeApiServiceImpl.getVmExistingVolumeForVolumeAttach(vm, Mockito.mock(VolumeInfo.class));
Assert.assertNotNull(result);
Assert.assertEquals(30L, result.getId());
}
@Test
public void testGetVmExistingVolumeForVolumeAttach_NoVolumesAtAll() {
UserVmVO vm = getMockedVm();
VMTemplateVO template = getMockedTemplate();
Mockito.when(templateDao.findById(10L)).thenReturn(template);
Mockito.when(volumeDaoMock.findByInstanceAndType(1L, Volume.Type.ROOT)).thenReturn(Collections.emptyList());
Mockito.when(volumeDaoMock.findByInstanceAndType(1L, Volume.Type.DATADISK)).thenReturn(Collections.emptyList());
VolumeVO result = volumeApiServiceImpl.getVmExistingVolumeForVolumeAttach(vm, Mockito.mock(VolumeInfo.class));
Assert.assertNull(result);
}
private void mockDiskOffering() {
DiskOfferingVO offering = Mockito.mock(DiskOfferingVO.class);
Mockito.when(_diskOfferingDao.findById(1L)).thenReturn(offering);
Mockito.when(offering.isUseLocalStorage()).thenReturn(true);
Mockito.when(offering.isRecreatable()).thenReturn(false);
}
private DataCenterVO mockZone() {
DataCenterVO zone = Mockito.mock(DataCenterVO.class);
Mockito.when(_dcDao.findById(1L)).thenReturn(zone);
return zone;
}
@Test
public void testGetPoolForAllocatedOrUploadedVolumeForAttach_Success() {
VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class);
UserVmVO vm = Mockito.mock(UserVmVO.class);
ClusterVO cluster = Mockito.mock(ClusterVO.class);
HostPodVO pod = Mockito.mock(HostPodVO.class);
DataCenterVO zone = mockZone();
mockDiskOffering();
StoragePool pool = Mockito.mock(StoragePool.class);
when(vm.getDataCenterId()).thenReturn(1L);
when(virtualMachineManager.findClusterAndHostIdForVm(vm, false)).thenReturn(new Pair<>(1L, 2L));
when(clusterDao.findById(1L)).thenReturn(cluster);
when(cluster.getPodId()).thenReturn(1L);
when(podDao.findById(1L)).thenReturn(pod);
when(volumeToAttach.getDiskOfferingId()).thenReturn(1L);
when(volumeOrchestrationService.findStoragePool(any(DiskProfile.class), eq(zone), eq(pod), eq(1L), eq(2L), eq(vm), eq(Collections.emptySet())))
.thenReturn(pool);
StoragePool result = volumeApiServiceImpl.getSuitablePoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm);
Assert.assertNotNull(result);
Assert.assertEquals(pool, result);
}
@Test
public void testGetPoolForAllocatedOrUploadedVolumeForAttach_NoSuitablePoolFound_ReturnsNull() {
VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class);
UserVmVO vm = Mockito.mock(UserVmVO.class);
DataCenterVO zone = mockZone();
Pair<Long, Long> clusterHostId = new Pair<>(1L, 2L);
ClusterVO cluster = Mockito.mock(ClusterVO.class);
HostPodVO pod = Mockito.mock(HostPodVO.class);
mockDiskOffering();
when(vm.getDataCenterId()).thenReturn(1L);
when(clusterDao.findById(1L)).thenReturn(cluster);
when(virtualMachineManager.findClusterAndHostIdForVm(vm, false)).thenReturn(clusterHostId);
when(podDao.findById(anyLong())).thenReturn(pod);
when(volumeToAttach.getDiskOfferingId()).thenReturn(1L);
when(volumeOrchestrationService.findStoragePool(any(DiskProfile.class), eq(zone), eq(pod), eq(1L), eq(2L), eq(vm), eq(Collections.emptySet())))
.thenReturn(null);
Assert.assertNull(volumeApiServiceImpl.getSuitablePoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm));
}
@Test
public void testGetSuitablePoolForAllocatedOrUploadedVolumeForAttach_NoCluster() {
VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class);
UserVmVO vm = Mockito.mock(UserVmVO.class);
DataCenterVO zone = mockZone();
HostPodVO pod = Mockito.mock(HostPodVO.class);
mockDiskOffering();
StoragePool pool = Mockito.mock(StoragePool.class);
when(vm.getDataCenterId()).thenReturn(1L);
when(vm.getPodIdToDeployIn()).thenReturn(2L);
when(virtualMachineManager.findClusterAndHostIdForVm(vm, false)).thenReturn(new Pair<>(null, 2L));
when(podDao.findById(2L)).thenReturn(pod);
when(volumeToAttach.getDiskOfferingId()).thenReturn(1L);
when(volumeOrchestrationService.findStoragePool(any(DiskProfile.class), eq(zone), eq(pod), eq(null), eq(2L), eq(vm), eq(Collections.emptySet())))
.thenReturn(pool);
StoragePool result = volumeApiServiceImpl.getSuitablePoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm);
Assert.assertNotNull(result);
Assert.assertEquals(pool, result);
}
@Test
public void testCreateVolumeOnSecondaryForAttachIfNeeded_VolumeNotAllocatedOrUploaded() {
VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class);
Mockito.when(volumeToAttach.getState()).thenReturn(Volume.State.Ready);
VolumeInfo result = volumeApiServiceImpl.createVolumeOnPrimaryForAttachIfNeeded(
volumeToAttach, Mockito.mock(UserVmVO.class), null);
Assert.assertSame(volumeToAttach, result);
Mockito.verifyNoInteractions(primaryDataStoreDaoMock, volumeOrchestrationService);
}
@Test
public void testCreateVolumeOnSecondaryForAttachIfNeeded_ExistingVolumeDeterminesStoragePool() {
VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class);
Mockito.when(volumeToAttach.getState()).thenReturn(Volume.State.Uploaded);
UserVmVO vm = Mockito.mock(UserVmVO.class);
VolumeVO existingVolume = Mockito.mock(VolumeVO.class);
Mockito.when(existingVolume.getState()).thenReturn(Volume.State.Ready);
when(existingVolume.getPoolId()).thenReturn(1L);
StoragePoolVO destPrimaryStorage = Mockito.mock(StoragePoolVO.class);
Mockito.when(destPrimaryStorage.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
Mockito.when(primaryDataStoreDaoMock.findById(1L)).thenReturn(destPrimaryStorage);
VolumeInfo newVolumeOnPrimaryStorage = Mockito.mock(VolumeInfo.class);
try {
Mockito.when(volumeOrchestrationService.createVolumeOnPrimaryStorage(vm, volumeToAttach, vm.getHypervisorType(), destPrimaryStorage))
.thenReturn(newVolumeOnPrimaryStorage);
} catch (NoTransitionException nte) {
Assert.fail(nte.getMessage());
}
VolumeInfo result = volumeApiServiceImpl.createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, existingVolume);
Assert.assertSame(newVolumeOnPrimaryStorage, result);
Mockito.verify(primaryDataStoreDaoMock).findById(1L);
}
@Test
public void testCreateVolumeOnPrimaryForAttachIfNeeded_UsesGetPoolForAttach() {
VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class);
Mockito.when(volumeToAttach.getState()).thenReturn(Volume.State.Allocated);
UserVmVO vm = Mockito.mock(UserVmVO.class);
StoragePool destPrimaryStorage = Mockito.mock(StoragePool.class);
Mockito.doReturn(destPrimaryStorage).when(volumeApiServiceImpl)
.getSuitablePoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm);
VolumeInfo newVolumeOnPrimaryStorage = Mockito.mock(VolumeInfo.class);
try {
Mockito.when(volumeOrchestrationService.createVolumeOnPrimaryStorage(
vm, volumeToAttach, vm.getHypervisorType(), destPrimaryStorage))
.thenReturn(newVolumeOnPrimaryStorage);
} catch (NoTransitionException nte) {
Assert.fail(nte.getMessage());
}
VolumeInfo result = volumeApiServiceImpl.createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, null);
Assert.assertSame(newVolumeOnPrimaryStorage, result);
verify(volumeApiServiceImpl).getSuitablePoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm);
}
@Test(expected = InvalidParameterValueException.class)
public void testCreateVolumeOnPrimaryForAttachIfNeeded_UnsupportedPoolType_ThrowsException() {
VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class);
when(volumeToAttach.getState()).thenReturn(Volume.State.Uploaded);
UserVmVO vm = Mockito.mock(UserVmVO.class);
StoragePool destPrimaryStorage = Mockito.mock(StoragePool.class);
when(destPrimaryStorage.getPoolType()).thenReturn(Storage.StoragePoolType.PowerFlex);
Mockito.doReturn(destPrimaryStorage).when(volumeApiServiceImpl)
.getSuitablePoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm);
volumeApiServiceImpl.createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, null);
}
@Test
public void testCreateVolumeOnSecondaryForAttachIfNeeded_CreateVolumeFails_ThrowsException() {
VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class);
Mockito.when(volumeToAttach.getState()).thenReturn(Volume.State.Uploaded);
UserVmVO vm = Mockito.mock(UserVmVO.class);
StoragePool destPrimaryStorage = Mockito.mock(StoragePool.class);
Mockito.when(destPrimaryStorage.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
Mockito.doReturn(destPrimaryStorage).when(volumeApiServiceImpl)
.getSuitablePoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm);
try {
Mockito.when(volumeOrchestrationService.createVolumeOnPrimaryStorage(vm, volumeToAttach, vm.getHypervisorType(), destPrimaryStorage))
.thenThrow(new NoTransitionException("Mocked exception"));
} catch (NoTransitionException nte) {
Assert.fail(nte.getMessage());
}
CloudRuntimeException exception = Assert.assertThrows(CloudRuntimeException.class, () ->
volumeApiServiceImpl.createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, null)
);
Assert.assertTrue(exception.getMessage().contains("Failed to create volume on primary storage"));
}
@Test
public void testCreateVolumeOnSecondaryForAttachIfNeeded_NoSuitablePool_ThrowsException() {
VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class);
Mockito.when(volumeToAttach.getState()).thenReturn(Volume.State.Uploaded);
UserVmVO vm = Mockito.mock(UserVmVO.class);
Mockito.doReturn(null).when(volumeApiServiceImpl)
.getSuitablePoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm);
CloudRuntimeException exception = Assert.assertThrows(CloudRuntimeException.class, () ->
volumeApiServiceImpl.createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, null)
);
Assert.assertTrue(exception.getMessage().startsWith("Failed to find a primary storage for volume"));
}
@Test
public void testCreateVolumeOnSecondaryForAttachIfNeeded_NoSuitablePool_ReturnSameVolumeInfo() {
VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class);
Mockito.when(volumeToAttach.getState()).thenReturn(Volume.State.Allocated);
UserVmVO vm = Mockito.mock(UserVmVO.class);
Mockito.when(vm.getState()).thenReturn(State.Stopped);
Mockito.doReturn(null).when(volumeApiServiceImpl)
.getSuitablePoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm);
VolumeInfo result = volumeApiServiceImpl.createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, null);
Assert.assertSame(volumeToAttach, result);
try {
Mockito.verify(volumeOrchestrationService, Mockito.never()).createVolumeOnPrimaryStorage(Mockito.any(),
Mockito.any(), Mockito.any(), Mockito.any());
} catch (NoTransitionException e) {
Assert.fail();
}
}
}