From 6c810aae4d2aa9b7d10aee48ebab6a27068f0acb Mon Sep 17 00:00:00 2001 From: vishesh92 Date: Mon, 2 Feb 2026 15:38:04 +0530 Subject: [PATCH] Fix live migrate with local storage --- .../com/cloud/storage/StorageManagerImpl.java | 2 +- .../java/com/cloud/vm/UserVmManagerImpl.java | 26 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java index d1dca0fa901b..38bfc0d6d418 100644 --- a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java @@ -510,7 +510,7 @@ public boolean isLocalStorageActiveOnHost(Long hostId) { List storagePoolHostRefs = _storagePoolHostDao.listByHostId(hostId); for (StoragePoolHostVO storagePoolHostRef : storagePoolHostRefs) { StoragePoolVO primaryDataStoreVO = _storagePoolDao.findById(storagePoolHostRef.getPoolId()); - if (primaryDataStoreVO != null && (primaryDataStoreVO.getPoolType() == StoragePoolType.LVM || primaryDataStoreVO.getPoolType() == StoragePoolType.EXT)) { + if (primaryDataStoreVO != null && (primaryDataStoreVO.getPoolType() == StoragePoolType.LVM || primaryDataStoreVO.getPoolType() == StoragePoolType.EXT || primaryDataStoreVO.getPoolType() == StoragePoolType.Filesystem)) { SearchBuilder volumeSB = volumeDao.createSearchBuilder(); volumeSB.and("poolId", volumeSB.entity().getPoolId(), SearchCriteria.Op.EQ); volumeSB.and("removed", volumeSB.entity().getRemoved(), SearchCriteria.Op.NULL); diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 815ac4f70fe8..e05ae5497d23 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -6794,6 +6794,14 @@ private DeployDestination chooseVmMigrationDestination(VMInstanceVO vm, Host src final Host host = _hostDao.findById(srcHostId); ExcludeList excludes = new ExcludeList(); excludes.addHost(srcHostId); + + List volumes = _volsDao.findByInstance(vm.getId()); + for (VolumeVO volume : volumes) { + DiskOfferingVO diskOffering = _diskOfferingDao.findByIdIncludingRemoved(volume.getDiskOfferingId()); + if (diskOffering != null && diskOffering.isUseLocalStorage()) { + excludes.addPool(volume.getPoolId()); + } + } final DataCenterDeployment plan = _itMgr.getMigrationDeployment(vm, host, poolId, excludes); try { return _planningMgr.planDeployment(profile, plan, excludes, null); @@ -7362,6 +7370,24 @@ public VirtualMachine migrateVirtualMachineWithVolume(Long vmId, Host destinatio Map volToPoolObjectMap = getVolumePoolMappingForMigrateVmWithStorage(vm, volumeToPool); + // If volumeToPool is empty and there are local storage volumes, auto-populate the mapping + if (MapUtils.isEmpty(volToPoolObjectMap) && isAnyVmVolumeUsingLocalStorage(volumes)) { + // First, find a destination host if not provided + if (destinationHost == null) { + DeployDestination deployDestination = chooseVmMigrationDestination(vm, srcHost, null); + if (deployDestination == null || deployDestination.getHost() == null) { + throw new CloudRuntimeException("Unable to find suitable destination host to migrate VM " + vm.getInstanceName()); + } + destinationHost = deployDestination.getHost(); + } + + // Verify the destination host has local storage + if (!storageManager.isLocalStorageActiveOnHost(destinationHost.getId())) { + throw new CloudRuntimeException(String.format("Destination host %s (ID: %s) does not have local storage, but VM %s (ID: %s) has volumes using local storage", + destinationHost.getName(), destinationHost.getUuid(), vm.getInstanceName(), vm.getUuid())); + } + } + if (destinationHost == null) { destinationHost = chooseVmMigrationDestinationUsingVolumePoolMap(vm, srcHost, volToPoolObjectMap); }