diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmd.java index 731cb67aa839..c618d222810a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmd.java @@ -83,6 +83,12 @@ public class MigrateVirtualMachineWithVolumeCmd extends BaseAsyncCmd { "<1b331390-59f2-4796-9993-bf11c6e76225>&migrateto[2].pool=<41fdb564-9d3b-447d-88ed-7628f7640cbc>") private Map migrateVolumeTo; + @Parameter(name = ApiConstants.AUTO_SELECT, + since = "4.19.0", + type = CommandType.BOOLEAN, + description = "Automatically select a destination host for a running instance, if hostId is not specified. false by default") + private Boolean autoSelect; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -151,23 +157,26 @@ public void execute() { } VirtualMachine virtualMachine = _userVmService.getVm(getVirtualMachineId()); - if (!VirtualMachine.State.Running.equals(virtualMachine.getState()) && hostId != null) { + if (!VirtualMachine.State.Running.equals(virtualMachine.getState()) && (hostId != null || Boolean.TRUE.equals(autoSelect))) { throw new InvalidParameterValueException(String.format("%s is not in the Running state to migrate it to the new host.", virtualMachine)); } - if (!VirtualMachine.State.Stopped.equals(virtualMachine.getState()) && hostId == null) { - throw new InvalidParameterValueException(String.format("%s is not in the Stopped state to migrate, use the %s parameter to migrate it to a new host.", - virtualMachine, ApiConstants.HOST_ID)); + if (!VirtualMachine.State.Stopped.equals(virtualMachine.getState()) && hostId == null && Boolean.FALSE.equals(autoSelect)) { + throw new InvalidParameterValueException(String.format("%s is not in the Stopped state to migrate, use the %s or %s parameter to migrate it to a new host.", + virtualMachine, ApiConstants.HOST_ID,ApiConstants.AUTO_SELECT)); } try { VirtualMachine migratedVm = null; - if (hostId != null) { - Host destinationHost = _resourceService.getHost(getHostId()); - // OfflineVmwareMigration: destination host would have to not be a required parameter for stopped VMs - if (destinationHost == null) { - s_logger.error(String.format("Unable to find the host with ID [%s].", getHostId())); - throw new InvalidParameterValueException("Unable to find the specified host to migrate the VM."); + if (getHostId() != null || Boolean.TRUE.equals(autoSelect)) { + Host destinationHost = null; + if (!Boolean.TRUE.equals(autoSelect)) { + destinationHost = _resourceService.getHost(getHostId()); + // OfflineVmwareMigration: destination host would have to not be a required parameter for stopped VMs + if (destinationHost == null) { + s_logger.error(String.format("Unable to find the host with ID [%s].", getHostId())); + throw new InvalidParameterValueException("Unable to find the specified host to migrate the VM."); + } } migratedVm = _userVmService.migrateVirtualMachineWithVolume(getVirtualMachineId(), destinationHost, getVolumeToPool()); } else if (MapUtils.isNotEmpty(migrateVolumeTo)) { diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmdTest.java index 7a98626ca45c..a4556d4eb125 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/vm/MigrateVirtualMachineWithVolumeCmdTest.java @@ -16,16 +16,10 @@ // under the License. package org.apache.cloudstack.api.command.admin.vm; -import com.cloud.exception.InvalidParameterValueException; -import com.cloud.exception.ManagementServerException; -import com.cloud.exception.ResourceUnavailableException; -import com.cloud.exception.VirtualMachineMigrationException; -import com.cloud.host.Host; -import com.cloud.resource.ResourceService; -import com.cloud.uservm.UserVm; -import com.cloud.utils.db.UUIDManager; -import com.cloud.vm.UserVmService; -import com.cloud.vm.VirtualMachine; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ResponseGenerator; import org.apache.cloudstack.api.ResponseObject; @@ -40,13 +34,21 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.Spy; -import org.powermock.modules.junit4.PowerMockRunner; +import org.mockito.junit.MockitoJUnitRunner; import org.springframework.test.util.ReflectionTestUtils; -import java.util.List; -import java.util.Map; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ManagementServerException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.exception.VirtualMachineMigrationException; +import com.cloud.host.Host; +import com.cloud.resource.ResourceService; +import com.cloud.uservm.UserVm; +import com.cloud.utils.db.UUIDManager; +import com.cloud.vm.UserVmService; +import com.cloud.vm.VirtualMachine; -@RunWith(PowerMockRunner.class) +@RunWith(MockitoJUnitRunner.class) public class MigrateVirtualMachineWithVolumeCmdTest { @Mock UserVmService userVmServiceMock; @@ -68,24 +70,25 @@ public class MigrateVirtualMachineWithVolumeCmdTest { @Spy @InjectMocks - MigrateVirtualMachineWithVolumeCmd cmdSpy = new MigrateVirtualMachineWithVolumeCmd(); + MigrateVirtualMachineWithVolumeCmd cmdSpy; private Long hostId = 1L; - private Long virtualMachineUuid = 1L; + private Long virtualMachineId = 1L; private String virtualMachineName = "VM-name"; - private Map migrateVolumeTo = Map.of("key","value"); + private Map migrateVolumeTo = null; private SystemVmResponse systemVmResponse = new SystemVmResponse(); private UserVmResponse userVmResponse = new UserVmResponse(); @Before - public void setup() { - Mockito.when(cmdSpy.getVirtualMachineId()).thenReturn(virtualMachineUuid); - Mockito.when(cmdSpy.getHostId()).thenReturn(hostId); - Mockito.when(cmdSpy.getVolumeToPool()).thenReturn(migrateVolumeTo); + public void setUp() throws Exception { + ReflectionTestUtils.setField(cmdSpy, "virtualMachineId", virtualMachineId); + migrateVolumeTo = new HashMap<>(); + migrateVolumeTo.put("volume", "abc"); + migrateVolumeTo.put("pool", "xyz"); } @Test - public void executeTestHostIdIsNullAndMigrateVolumeToIsNullThrowsInvalidParameterValueException(){ + public void executeTestHostIdIsNullAndMigrateVolumeToIsNullThrowsInvalidParameterValueException() { ReflectionTestUtils.setField(cmdSpy, "hostId", null); ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", null); @@ -99,13 +102,13 @@ public void executeTestHostIdIsNullAndMigrateVolumeToIsNullThrowsInvalidParamete } @Test - public void executeTestVMIsStoppedAndHostIdIsNotNullThrowsInvalidParameterValueException(){ + public void executeTestVMIsStoppedAndHostIdIsNotNullThrowsInvalidParameterValueException() { ReflectionTestUtils.setField(cmdSpy, "hostId", hostId); ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", migrateVolumeTo); Mockito.when(userVmServiceMock.getVm(Mockito.anyLong())).thenReturn(virtualMachineMock); Mockito.when(virtualMachineMock.getState()).thenReturn(VirtualMachine.State.Stopped); - Mockito.when(virtualMachineMock.toString()).thenReturn(String.format("VM [uuid: %s, name: %s]", virtualMachineUuid, virtualMachineName)); + Mockito.when(virtualMachineMock.toString()).thenReturn(String.format("VM [uuid: %s, name: %s]", virtualMachineId, virtualMachineName)); try { cmdSpy.execute(); @@ -117,33 +120,35 @@ public void executeTestVMIsStoppedAndHostIdIsNotNullThrowsInvalidParameterValueE } @Test - public void executeTestVMIsRunningAndHostIdIsNullThrowsInvalidParameterValueException(){ + public void executeTestVMIsRunningAndHostIdIsNullThrowsInvalidParameterValueException() { ReflectionTestUtils.setField(cmdSpy, "hostId", null); + ReflectionTestUtils.setField(cmdSpy, "autoSelect", false); ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", migrateVolumeTo); Mockito.when(userVmServiceMock.getVm(Mockito.anyLong())).thenReturn(virtualMachineMock); Mockito.when(virtualMachineMock.getState()).thenReturn(VirtualMachine.State.Running); - Mockito.when(virtualMachineMock.toString()).thenReturn(String.format("VM [uuid: %s, name: %s]", virtualMachineUuid, virtualMachineName)); + Mockito.when(virtualMachineMock.toString()).thenReturn(String.format("VM [uuid: %s, name: %s]", virtualMachineId, virtualMachineName)); try { cmdSpy.execute(); } catch (Exception e) { Assert.assertEquals(InvalidParameterValueException.class, e.getClass()); - String expected = String.format("%s is not in the Stopped state to migrate, use the %s parameter to migrate it to a new host.", virtualMachineMock, - ApiConstants.HOST_ID); + String expected = String.format("%s is not in the Stopped state to migrate, use the %s or %s parameter to migrate it to a new host.", virtualMachineMock, + ApiConstants.HOST_ID, ApiConstants.AUTO_SELECT); Assert.assertEquals(expected , e.getMessage()); } } @Test - public void executeTestHostIdIsNullThrowsInvalidParameterValueException(){ + public void executeTestHostIdIsNullThrowsInvalidParameterValueException() { + ReflectionTestUtils.setField(cmdSpy, "virtualMachineId", virtualMachineId); ReflectionTestUtils.setField(cmdSpy, "hostId", hostId); ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", migrateVolumeTo); + ReflectionTestUtils.setField(cmdSpy, "autoSelect", false); Mockito.when(userVmServiceMock.getVm(Mockito.anyLong())).thenReturn(virtualMachineMock); Mockito.when(virtualMachineMock.getState()).thenReturn(VirtualMachine.State.Running); Mockito.when(resourceServiceMock.getHost(Mockito.anyLong())).thenReturn(null); - Mockito.when(uuidManagerMock.getUuid(Host.class, virtualMachineUuid)).thenReturn(virtualMachineUuid.toString()); try { cmdSpy.execute(); @@ -154,15 +159,22 @@ public void executeTestHostIdIsNullThrowsInvalidParameterValueException(){ } } + private Map getMockedMigrateVolumeToApiCmdParam() { + Map migrateVolumeTo = new HashMap<>(); + migrateVolumeTo.put("volume", "abc"); + migrateVolumeTo.put("pool", "xyz"); + return Map.of("", migrateVolumeTo); + } + @Test public void executeTestHostIsNotNullMigratedVMIsNullThrowsServerApiException() throws ManagementServerException, ResourceUnavailableException, VirtualMachineMigrationException { ReflectionTestUtils.setField(cmdSpy, "hostId", hostId); - ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", migrateVolumeTo); + ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", getMockedMigrateVolumeToApiCmdParam()); Mockito.when(userVmServiceMock.getVm(Mockito.anyLong())).thenReturn(virtualMachineMock); Mockito.when(virtualMachineMock.getState()).thenReturn(VirtualMachine.State.Running); - Mockito.when(resourceServiceMock.getHost(Mockito.anyLong())).thenReturn(hostMock); - Mockito.when(userVmServiceMock.migrateVirtualMachineWithVolume(virtualMachineUuid, hostMock, migrateVolumeTo)).thenReturn(null); + Mockito.when(resourceServiceMock.getHost(hostId)).thenReturn(hostMock); + Mockito.when(userVmServiceMock.migrateVirtualMachineWithVolume(Mockito.anyLong(), Mockito.any(), Mockito.anyMap())).thenReturn(null); try { cmdSpy.execute(); @@ -176,11 +188,11 @@ public void executeTestHostIsNotNullMigratedVMIsNullThrowsServerApiException() t @Test public void executeTestHostIsNullMigratedVMIsNullThrowsServerApiException() { ReflectionTestUtils.setField(cmdSpy, "hostId", null); - ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", migrateVolumeTo); + ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", getMockedMigrateVolumeToApiCmdParam()); Mockito.when(userVmServiceMock.getVm(Mockito.anyLong())).thenReturn(virtualMachineMock); Mockito.when(virtualMachineMock.getState()).thenReturn(VirtualMachine.State.Stopped); - Mockito.when(userVmServiceMock.vmStorageMigration(virtualMachineUuid, migrateVolumeTo)).thenReturn(null); + Mockito.when(userVmServiceMock.vmStorageMigration(Mockito.anyLong(), Mockito.anyMap())).thenReturn(null); try { cmdSpy.execute(); @@ -194,11 +206,11 @@ public void executeTestHostIsNullMigratedVMIsNullThrowsServerApiException() { @Test public void executeTestSystemVMMigratedWithSuccess() { ReflectionTestUtils.setField(cmdSpy, "hostId", null); - ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", migrateVolumeTo); + ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", getMockedMigrateVolumeToApiCmdParam()); Mockito.when(userVmServiceMock.getVm(Mockito.anyLong())).thenReturn(virtualMachineMock); Mockito.when(virtualMachineMock.getState()).thenReturn(VirtualMachine.State.Stopped); - Mockito.when(userVmServiceMock.vmStorageMigration(virtualMachineUuid, migrateVolumeTo)).thenReturn(virtualMachineMock); + Mockito.when(userVmServiceMock.vmStorageMigration(Mockito.anyLong(), Mockito.anyMap())).thenReturn(virtualMachineMock); Mockito.when(virtualMachineMock.getType()).thenReturn(VirtualMachine.Type.ConsoleProxy); Mockito.when(responseGeneratorMock.createSystemVmResponse(virtualMachineMock)).thenReturn(systemVmResponse); @@ -211,11 +223,11 @@ public void executeTestSystemVMMigratedWithSuccess() { public void executeTestUserVMMigratedWithSuccess() { UserVm userVmMock = Mockito.mock(UserVm.class); ReflectionTestUtils.setField(cmdSpy, "hostId", null); - ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", migrateVolumeTo); + ReflectionTestUtils.setField(cmdSpy, "migrateVolumeTo", getMockedMigrateVolumeToApiCmdParam()); Mockito.when(userVmServiceMock.getVm(Mockito.anyLong())).thenReturn(userVmMock); Mockito.when(userVmMock.getState()).thenReturn(VirtualMachine.State.Stopped); - Mockito.when(userVmServiceMock.vmStorageMigration(virtualMachineUuid, migrateVolumeTo)).thenReturn(userVmMock); + Mockito.when(userVmServiceMock.vmStorageMigration(Mockito.anyLong(), Mockito.anyMap())).thenReturn(userVmMock); Mockito.when(userVmMock.getType()).thenReturn(VirtualMachine.Type.User); Mockito.when(responseGeneratorMock.createUserVmResponse(ResponseObject.ResponseView.Full, "virtualmachine", userVmMock)).thenReturn(List.of(userVmResponse)); diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 3f73b620c389..fda3df42f0bb 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -129,6 +129,7 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.math.NumberUtils; +import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; @@ -6357,22 +6358,8 @@ private void checkDestinationHypervisorType(StoragePool destPool, VMInstanceVO v } public boolean isVMUsingLocalStorage(VMInstanceVO vm) { - boolean usesLocalStorage = false; - List volumes = _volsDao.findByInstance(vm.getId()); - for (VolumeVO vol : volumes) { - DiskOfferingVO diskOffering = _diskOfferingDao.findById(vol.getDiskOfferingId()); - if (diskOffering.isUseLocalStorage()) { - usesLocalStorage = true; - break; - } - StoragePoolVO storagePool = _storagePoolDao.findById(vol.getPoolId()); - if (storagePool.isLocal()) { - usesLocalStorage = true; - break; - } - } - return usesLocalStorage; + return isAnyVmVolumeUsingLocalStorage(volumes); } @Override @@ -6431,7 +6418,7 @@ public VirtualMachine migrateVirtualMachine(Long vmId, Host destinationHost) thr DeployDestination dest = null; if (destinationHost == null) { - dest = chooseVmMigrationDestination(vm, srcHost); + dest = chooseVmMigrationDestination(vm, srcHost, null); } else { dest = checkVmMigrationDestination(vm, srcHost, destinationHost); } @@ -6446,7 +6433,7 @@ public VirtualMachine migrateVirtualMachine(Long vmId, Host destinationHost) thr return findMigratedVm(vm.getId(), vm.getType()); } - private DeployDestination chooseVmMigrationDestination(VMInstanceVO vm, Host srcHost) { + private DeployDestination chooseVmMigrationDestination(VMInstanceVO vm, Host srcHost, Long poolId) { vm.setLastHostId(null); // Last host does not have higher priority in vm migration final ServiceOfferingVO offering = serviceOfferingDao.findById(vm.getId(), vm.getServiceOfferingId()); final VirtualMachineProfile profile = new VirtualMachineProfileImpl(vm, null, offering, null, null); @@ -6454,7 +6441,7 @@ private DeployDestination chooseVmMigrationDestination(VMInstanceVO vm, Host src final Host host = _hostDao.findById(srcHostId); ExcludeList excludes = new ExcludeList(); excludes.addHost(srcHostId); - final DataCenterDeployment plan = _itMgr.getMigrationDeployment(vm, host, null, excludes); + final DataCenterDeployment plan = _itMgr.getMigrationDeployment(vm, host, poolId, excludes); try { return _planningMgr.planDeployment(profile, plan, excludes, null); } catch (final AffinityConflictException e2) { @@ -6754,8 +6741,21 @@ private boolean isImplicitPlannerUsedByOffering(long offeringId) { return implicitPlannerUsed; } - private boolean isVmVolumesOnZoneWideStore(VMInstanceVO vm) { - final List volumes = _volsDao.findCreatedByInstance(vm.getId()); + private boolean isAnyVmVolumeUsingLocalStorage(final List volumes) { + for (VolumeVO vol : volumes) { + DiskOfferingVO diskOffering = _diskOfferingDao.findById(vol.getDiskOfferingId()); + if (diskOffering.isUseLocalStorage()) { + return true; + } + StoragePoolVO storagePool = _storagePoolDao.findById(vol.getPoolId()); + if (storagePool.isLocal()) { + return true; + } + } + return false; + } + + private boolean isAllVmVolumesOnZoneWideStore(final List volumes) { if (CollectionUtils.isEmpty(volumes)) { return false; } @@ -6779,6 +6779,10 @@ private Pair getHostsForMigrateVmWithStorage(VMInstanceVO vm, Host d throw new InvalidParameterValueException("Cannot migrate VM, host with ID: " + srcHostId + " for VM not found"); } + if (destinationHost == null) { + return new Pair<>(srcHost, null); + } + // Check if source and destination hosts are valid and migrating to same host if (destinationHost.getId() == srcHostId) { throw new InvalidParameterValueException(String.format("Cannot migrate VM as it is already present on host %s (ID: %s), please specify valid destination host to migrate the VM", @@ -6898,6 +6902,18 @@ private Map getVolumePoolMappingForMigrateVmWithStorage(VMInstanceVO return volToPoolObjectMap; } + protected Host chooseVmMigrationDestinationUsingVolumePoolMap(VMInstanceVO vm, Host srcHost, Map volToPoolObjectMap) { + Long poolId = null; + if (MapUtils.isNotEmpty(volToPoolObjectMap)) { + poolId = new ArrayList<>(volToPoolObjectMap.values()).get(0); + } + DeployDestination deployDestination = chooseVmMigrationDestination(vm, srcHost, poolId); + if (ObjectUtils.anyNull(deployDestination, deployDestination.getHost())) { + throw new CloudRuntimeException("Unable to find suitable destination to migrate VM " + vm.getInstanceName()); + } + return deployDestination.getHost(); + } + @Override @ActionEvent(eventType = EventTypes.EVENT_VM_MIGRATE, eventDescription = "migrating VM", async = true) public VirtualMachine migrateVirtualMachineWithVolume(Long vmId, Host destinationHost, Map volumeToPool) throws ResourceUnavailableException, @@ -6942,8 +6958,9 @@ public VirtualMachine migrateVirtualMachineWithVolume(Long vmId, Host destinatio Pair sourceDestinationHosts = getHostsForMigrateVmWithStorage(vm, destinationHost); Host srcHost = sourceDestinationHosts.first(); - if (!isVMUsingLocalStorage(vm) && MapUtils.isEmpty(volumeToPool) - && (destinationHost.getClusterId().equals(srcHost.getClusterId()) || isVmVolumesOnZoneWideStore(vm))){ + final List volumes = _volsDao.findCreatedByInstance(vm.getId()); + if (!isAnyVmVolumeUsingLocalStorage(volumes) && MapUtils.isEmpty(volumeToPool) && destinationHost != null + && (destinationHost.getClusterId().equals(srcHost.getClusterId()) || isAllVmVolumesOnZoneWideStore(volumes))) { // If volumes do not have to be migrated // call migrateVirtualMachine for non-user VMs else throw exception if (!VirtualMachine.Type.User.equals(vm.getType())) { @@ -6955,6 +6972,10 @@ public VirtualMachine migrateVirtualMachineWithVolume(Long vmId, Host destinatio Map volToPoolObjectMap = getVolumePoolMappingForMigrateVmWithStorage(vm, volumeToPool); + if (destinationHost == null) { + destinationHost = chooseVmMigrationDestinationUsingVolumePoolMap(vm, srcHost, volToPoolObjectMap); + } + checkHostsDedication(vm, srcHost.getId(), destinationHost.getId()); _itMgr.migrateWithStorage(vm.getUuid(), srcHost.getId(), destinationHost.getId(), volToPoolObjectMap); diff --git a/ui/src/views/compute/MigrateWizard.vue b/ui/src/views/compute/MigrateWizard.vue index 0819a5a33510..8aad50d4fc04 100644 --- a/ui/src/views/compute/MigrateWizard.vue +++ b/ui/src/views/compute/MigrateWizard.vue @@ -188,7 +188,8 @@ export default { } ], migrateWithStorage: false, - volumeToPoolSelection: [] + volumeToPoolSelection: [], + volumes: [] } }, created () { @@ -248,6 +249,7 @@ export default { handleSelectedHostChange (host) { if (host.id === -1) { this.migrateWithStorage = false + this.fetchVolumes() } this.selectedHost = host this.selectedVolumeForStoragePoolSelection = {} @@ -259,6 +261,31 @@ export default { handleVolumeToPoolChange (volumeToPool) { this.volumeToPoolSelection = volumeToPool }, + fetchVolumes () { + this.loading = true + this.volumes = [] + api('listVolumes', { + listAll: true, + virtualmachineid: this.resource.id + }).then(response => { + this.volumes = response.listvolumesresponse.volume + }).finally(() => { + this.loading = false + }) + }, + requiresStorageMigration () { + if (this.selectedHost.requiresStorageMotion || this.volumeToPoolSelection.length > 0) { + return true + } + if (this.selectedHost.id === -1 && this.volumes && this.volumes.length > 0) { + for (var volume of this.volumes) { + if (volume.storagetype === 'local') { + return true + } + } + } + return false + }, handleKeyboardSubmit () { if (this.selectedHost.id) { this.submitForm() @@ -271,7 +298,7 @@ export default { if (this.loading) return this.loading = true const migrateApi = this.isUserVm - ? (this.selectedHost.requiresStorageMotion || this.volumeToPoolSelection.length > 0) + ? this.requiresStorageMigration() ? 'migrateVirtualMachineWithVolume' : 'migrateVirtualMachine' : 'migrateSystemVm'