Skip to content

Commit 5eecd77

Browse files
KVM: assign device ID 0 to root volumes imported from shared/local storage
importVm with importsource=shared or importsource=local allocated the root volume with device ID 0, then overwrote it with an explicit ID 1 when finalizing the imported volume. Pass no device ID so the volume manager keeps the root default (0), as the other import paths do. Fixes #13771
1 parent 30ed411 commit 5eecd77

3 files changed

Lines changed: 44 additions & 6 deletions

File tree

‎engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestratorTest.java‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,10 @@
4242
import com.cloud.uservm.UserVm;
4343
import com.cloud.utils.db.EntityManager;
4444
import com.cloud.utils.exception.CloudRuntimeException;
45+
import com.cloud.vm.DiskProfile;
46+
import com.cloud.vm.UserVmVO;
4547
import com.cloud.vm.VirtualMachine;
48+
import com.cloud.vm.dao.UserVmDao;
4649
import com.cloud.utils.Pair;
4750
import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
4851
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
@@ -67,6 +70,7 @@
6770
import org.junit.Before;
6871
import org.junit.Test;
6972
import org.junit.runner.RunWith;
73+
import org.mockito.ArgumentCaptor;
7074
import org.mockito.InjectMocks;
7175
import org.mockito.Mock;
7276
import org.mockito.MockedConstruction;
@@ -108,6 +112,9 @@ public class VolumeOrchestratorTest {
108112
@Mock
109113
private ImageStoreDao imageStoreDaoMock;
110114

115+
@Mock
116+
private UserVmDao userVmDao;
117+
111118

112119
@Spy
113120
@InjectMocks
@@ -255,6 +262,37 @@ public void testImportVolume() {
255262
Mockito.verify(volume, Mockito.times(1)).setState(Volume.State.Ready);
256263
}
257264

265+
@Test
266+
public void testUpdateImportedVolumeGivesRootDeviceIdZeroWhenNoneIsGiven() {
267+
updateImportedVolumeAndAssertSavedDeviceId(Type.ROOT, null, 0L);
268+
}
269+
270+
@Test
271+
public void testUpdateImportedVolumeKeepsExplicitDeviceId() {
272+
updateImportedVolumeAndAssertSavedDeviceId(Type.DATADISK, 2L, 2L);
273+
}
274+
275+
private void updateImportedVolumeAndAssertSavedDeviceId(Type type, Long requestedDeviceId, long expectedDeviceId) {
276+
// behaves like a real VolumeVO, but is not created through its constructor, so that the VolumeVO
277+
// construction mock opened (and never closed) by testImportVolume cannot replace it
278+
VolumeVO volume = Mockito.mock(VolumeVO.class, Mockito.CALLS_REAL_METHODS);
279+
volume.setDeviceId(1L);
280+
DiskProfile diskProfile = Mockito.mock(DiskProfile.class);
281+
Mockito.when(diskProfile.getVolumeId()).thenReturn(10L);
282+
Mockito.when(volumeDao.findById(10L)).thenReturn(volume);
283+
VirtualMachine vm = Mockito.mock(VirtualMachine.class);
284+
Mockito.when(vm.getId()).thenReturn(MOCK_VM_ID);
285+
Mockito.when(vm.getType()).thenReturn(VirtualMachine.Type.User);
286+
Mockito.when(userVmDao.findById(MOCK_VM_ID)).thenReturn(Mockito.mock(UserVmVO.class));
287+
288+
volumeOrchestrator.updateImportedVolume(type, Mockito.mock(DiskOffering.class), vm, null, requestedDeviceId,
289+
MOCK_POOL_ID, Storage.StoragePoolType.NetworkFilesystem, "/mnt/pool/disk.qcow2", null, diskProfile, Storage.ImageFormat.QCOW2);
290+
291+
ArgumentCaptor<VolumeVO> savedVolume = ArgumentCaptor.forClass(VolumeVO.class);
292+
Mockito.verify(volumeDao).update(Mockito.anyLong(), savedVolume.capture());
293+
Assert.assertEquals(Long.valueOf(expectedDeviceId), savedVolume.getValue().getDeviceId());
294+
}
295+
258296
@Test
259297
public void testAllocateDuplicateVolumeVOBasic() {
260298
Volume oldVol = Mockito.mock(Volume.class);

‎server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3014,17 +3014,16 @@ private UserVm importKvmVirtualMachineFromDisk(final ImportSource importSource,
30143014

30153015
List<Pair<DiskProfile, StoragePool>> diskProfileStoragePoolList = new ArrayList<>();
30163016
try {
3017-
long deviceId = 1L;
30183017
Storage.ImageFormat diskFormat = getImageFormatFromVolumeDetails(volumeDetails);
30193018
if (storagePool.getPoolType() == Storage.StoragePoolType.StorPool) {
30203019
diskFormat = Storage.ImageFormat.QCOW2;
30213020
}
30223021
if(ImportSource.SHARED == importSource) {
30233022
diskProfileStoragePoolList.add(importKVMSharedDisk(userVm, diskOffering, Volume.Type.ROOT,
3024-
template, deviceId, poolId, diskPath, diskProfile, diskFormat));
3023+
template, null, poolId, diskPath, diskProfile, diskFormat));
30253024
} else if(ImportSource.LOCAL == importSource) {
30263025
diskProfileStoragePoolList.add(importKVMLocalDisk(userVm, diskOffering, Volume.Type.ROOT,
3027-
template, deviceId, hostId, diskPath, diskProfile, diskFormat));
3026+
template, null, hostId, diskPath, diskProfile, diskFormat));
30283027
}
30293028
} catch (Exception e) {
30303029
logger.error(String.format("Failed to import volumes while importing vm: %s", instanceName), e);

‎server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,7 +1018,7 @@ private void importFromDisk(String source, Storage.StoragePoolType poolType, Str
10181018
DiskProfile diskProfile = Mockito.mock(DiskProfile.class);
10191019
when(volumeManager.allocateRawVolume(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), anyBoolean()))
10201020
.thenReturn(diskProfile);
1021-
when(volumeManager.updateImportedVolume(any(), any(), any(), any(), anyLong(), anyLong(), any(), anyString(), any(), any(), any()))
1021+
when(volumeManager.updateImportedVolume(any(), any(), any(), any(), nullable(Long.class), anyLong(), any(), anyString(), any(), any(), any()))
10221022
.thenReturn(diskProfile);
10231023
Map<Volume, StoragePool> storage = new HashMap<>();
10241024
VolumeVO volume = Mockito.mock(VolumeVO.class);
@@ -1046,8 +1046,9 @@ private void importFromDisk(String source, Storage.StoragePoolType poolType, Str
10461046
unmanagedVMsManager.importVm(cmd);
10471047
}
10481048
// the imported volume must record the format the agent reported for the image on the pool,
1049-
// not the hypervisor default, so an RBD image is stored as RAW
1050-
verify(volumeManager).updateImportedVolume(any(), any(), any(), any(), anyLong(), anyLong(), Mockito.eq(poolType),
1049+
// not the hypervisor default, so an RBD image is stored as RAW; it is the ROOT volume and passes
1050+
// no device id, so the volume manager keeps the root default (0)
1051+
verify(volumeManager).updateImportedVolume(Mockito.eq(Volume.Type.ROOT), any(), any(), any(), Mockito.isNull(), anyLong(), Mockito.eq(poolType),
10511052
Mockito.eq(diskPath), Mockito.isNull(), any(), Mockito.eq(expectedFormat));
10521053

10531054
// the storage driver must be told the volume is now imported, so e.g. StorPool can tag it

0 commit comments

Comments
 (0)