|
38 | 38 | import javax.inject.Inject; |
39 | 39 | import javax.naming.ConfigurationException; |
40 | 40 |
|
| 41 | +import com.cloud.agent.AgentManager; |
41 | 42 | import com.cloud.deploy.DeploymentClusterPlanner; |
42 | 43 | import com.cloud.exception.ResourceAllocationException; |
| 44 | +import com.cloud.storage.ClvmLockManager; |
43 | 45 | import com.cloud.storage.DiskOfferingVO; |
44 | 46 | import com.cloud.storage.VMTemplateVO; |
45 | 47 | import com.cloud.storage.dao.VMTemplateDao; |
@@ -273,6 +275,10 @@ public enum UserVmCloneType { |
273 | 275 | ConfigurationDao configurationDao; |
274 | 276 | @Inject |
275 | 277 | VMInstanceDao vmInstanceDao; |
| 278 | + @Inject |
| 279 | + ClvmLockManager clvmLockManager; |
| 280 | + @Inject |
| 281 | + AgentManager _agentMgr; |
276 | 282 |
|
277 | 283 | @Inject |
278 | 284 | protected SnapshotHelper snapshotHelper; |
@@ -753,9 +759,7 @@ public VolumeInfo createVolume(VolumeInfo volumeInfo, VirtualMachine vm, Virtual |
753 | 759 | hostId, volumeInfo.getUuid()); |
754 | 760 | volumeInfo.setDestinationHostId(hostId); |
755 | 761 |
|
756 | | - // Persist CLVM lock host to database immediately so it survives across VolumeInfo object recreations |
757 | | - // and serves as both the creation routing hint and the lock host tracker |
758 | | - setClvmLockHostId(volumeInfo.getId(), hostId); |
| 762 | + clvmLockManager.setClvmLockHostId(volumeInfo.getId(), hostId); |
759 | 763 | } |
760 | 764 |
|
761 | 765 | for (int i = 0; i < 2; i++) { |
@@ -799,26 +803,6 @@ private String getVolumeIdentificationInfos(Volume volume) { |
799 | 803 | return String.format("uuid: %s, name: %s", volume.getUuid(), volume.getName()); |
800 | 804 | } |
801 | 805 |
|
802 | | - /** |
803 | | - * Sets or updates the CLVM_LOCK_HOST_ID detail for a volume. |
804 | | - * If the detail already exists, it will be updated. Otherwise, it will be created. |
805 | | - * |
806 | | - * @param volumeId The ID of the volume |
807 | | - * @param hostId The host ID that holds/should hold the CLVM exclusive lock |
808 | | - */ |
809 | | - private void setClvmLockHostId(long volumeId, long hostId) { |
810 | | - VolumeDetailVO existingDetail = _volDetailDao.findDetail(volumeId, VolumeInfo.CLVM_LOCK_HOST_ID); |
811 | | - |
812 | | - if (existingDetail != null) { |
813 | | - existingDetail.setValue(String.valueOf(hostId)); |
814 | | - _volDetailDao.update(existingDetail.getId(), existingDetail); |
815 | | - logger.debug("Updated CLVM_LOCK_HOST_ID for volume {} to host {}", volumeId, hostId); |
816 | | - } else { |
817 | | - _volDetailDao.addDetail(volumeId, VolumeInfo.CLVM_LOCK_HOST_ID, String.valueOf(hostId), false); |
818 | | - logger.debug("Created CLVM_LOCK_HOST_ID for volume {} with host {}", volumeId, hostId); |
819 | | - } |
820 | | - } |
821 | | - |
822 | 806 | /** |
823 | 807 | * Updates the CLVM_LOCK_HOST_ID for a migrated volume if applicable. |
824 | 808 | * For CLVM volumes that are attached to a VM, this updates the lock host tracking |
@@ -847,11 +831,81 @@ private void updateClvmLockHostAfterMigration(Volume migratedVolume, StoragePool |
847 | 831 | return; |
848 | 832 | } |
849 | 833 |
|
850 | | - setClvmLockHostId(migratedVolume.getId(), vm.getHostId()); |
| 834 | + clvmLockManager.setClvmLockHostId(migratedVolume.getId(), vm.getHostId()); |
851 | 835 | logger.debug("Updated CLVM_LOCK_HOST_ID for {} volume {} to host {} where VM {} is running", |
852 | 836 | operationType, migratedVolume.getUuid(), vm.getHostId(), vm.getInstanceName()); |
853 | 837 | } |
854 | 838 |
|
| 839 | + /** |
| 840 | + * Retrieves the CLVM lock host ID from any existing volume of the specified VM. |
| 841 | + * This is useful when attaching a new volume to a stopped VM - we want to maintain |
| 842 | + * consistency by using the same host that manages the VM's other CLVM volumes. |
| 843 | + * |
| 844 | + * @param vmId The ID of the VM |
| 845 | + * @return The host ID if found, null otherwise |
| 846 | + */ |
| 847 | + private Long getClvmLockHostFromVmVolumes(Long vmId) { |
| 848 | + if (vmId == null) { |
| 849 | + return null; |
| 850 | + } |
| 851 | + |
| 852 | + List<VolumeVO> vmVolumes = _volsDao.findByInstance(vmId); |
| 853 | + if (vmVolumes == null || vmVolumes.isEmpty()) { |
| 854 | + return null; |
| 855 | + } |
| 856 | + |
| 857 | + for (VolumeVO volume : vmVolumes) { |
| 858 | + if (volume.getPoolId() == null) { |
| 859 | + continue; |
| 860 | + } |
| 861 | + |
| 862 | + StoragePoolVO pool = _storagePoolDao.findById(volume.getPoolId()); |
| 863 | + if (pool != null && pool.getPoolType() == Storage.StoragePoolType.CLVM) { |
| 864 | + Long lockHostId = clvmLockManager.getClvmLockHostId(volume.getId(), volume.getUuid()); |
| 865 | + if (lockHostId != null) { |
| 866 | + logger.debug("Found CLVM lock host {} from existing volume {} of VM {}", |
| 867 | + lockHostId, volume.getUuid(), vmId); |
| 868 | + return lockHostId; |
| 869 | + } |
| 870 | + } |
| 871 | + } |
| 872 | + |
| 873 | + return null; |
| 874 | + } |
| 875 | + |
| 876 | + private void transferClvmLocksForVmStart(List<VolumeVO> volumes, Long destHostId, VMInstanceVO vm) { |
| 877 | + if (volumes == null || volumes.isEmpty() || destHostId == null) { |
| 878 | + return; |
| 879 | + } |
| 880 | + |
| 881 | + for (VolumeVO volume : volumes) { |
| 882 | + if (volume.getPoolId() == null) { |
| 883 | + continue; |
| 884 | + } |
| 885 | + |
| 886 | + StoragePoolVO pool = _storagePoolDao.findById(volume.getPoolId()); |
| 887 | + if (pool == null || pool.getPoolType() != Storage.StoragePoolType.CLVM) { |
| 888 | + continue; |
| 889 | + } |
| 890 | + |
| 891 | + Long currentLockHost = clvmLockManager.getClvmLockHostId(volume.getId(), volume.getUuid()); |
| 892 | + |
| 893 | + if (currentLockHost == null) { |
| 894 | + clvmLockManager.setClvmLockHostId(volume.getId(), destHostId); |
| 895 | + } else if (!currentLockHost.equals(destHostId)) { |
| 896 | + logger.info("CLVM volume {} is locked on host {} but VM {} starting on host {}. Transferring lock.", |
| 897 | + volume.getUuid(), currentLockHost, vm.getInstanceName(), destHostId); |
| 898 | + |
| 899 | + if (!clvmLockManager.transferClvmVolumeLock(volume.getUuid(), volume.getId(), |
| 900 | + volume.getPath(), pool, currentLockHost, destHostId)) { |
| 901 | + throw new CloudRuntimeException( |
| 902 | + String.format("Failed to transfer CLVM lock for volume %s from host %s to host %s", |
| 903 | + volume.getUuid(), currentLockHost, destHostId)); |
| 904 | + } |
| 905 | + } |
| 906 | + } |
| 907 | + } |
| 908 | + |
855 | 909 | public String getRandomVolumeName() { |
856 | 910 | return UUID.randomUUID().toString(); |
857 | 911 | } |
@@ -1270,10 +1324,22 @@ public VolumeInfo createVolumeOnPrimaryStorage(VirtualMachine vm, VolumeInfo vol |
1270 | 1324 | Long clusterId = storagePool.getClusterId(); |
1271 | 1325 | logger.trace("storage-pool {}/{} is associated with cluster {}",storagePool.getName(), storagePool.getUuid(), clusterId); |
1272 | 1326 | Long hostId = vm.getHostId(); |
1273 | | - if (hostId == null && storagePool.isLocal()) { |
1274 | | - List<StoragePoolHostVO> poolHosts = storagePoolHostDao.listByPoolId(storagePool.getId()); |
1275 | | - if (poolHosts.size() > 0) { |
1276 | | - hostId = poolHosts.get(0).getHostId(); |
| 1327 | + if (hostId == null && (storagePool.isLocal() || storagePool.getPoolType() == Storage.StoragePoolType.CLVM)) { |
| 1328 | + if (storagePool.getPoolType() == Storage.StoragePoolType.CLVM) { |
| 1329 | + hostId = getClvmLockHostFromVmVolumes(vm.getId()); |
| 1330 | + if (hostId != null) { |
| 1331 | + logger.debug("Using CLVM lock host {} from VM {}'s existing volumes for new volume creation", |
| 1332 | + hostId, vm.getUuid()); |
| 1333 | + } |
| 1334 | + } |
| 1335 | + |
| 1336 | + if (hostId == null) { |
| 1337 | + List<StoragePoolHostVO> poolHosts = storagePoolHostDao.listByPoolId(storagePool.getId()); |
| 1338 | + if (!poolHosts.isEmpty()) { |
| 1339 | + hostId = poolHosts.get(0).getHostId(); |
| 1340 | + logger.debug("Selected host {} from storage pool {} for stopped VM {} volume creation", |
| 1341 | + hostId, storagePool.getUuid(), vm.getUuid()); |
| 1342 | + } |
1277 | 1343 | } |
1278 | 1344 | } |
1279 | 1345 |
|
@@ -1935,9 +2001,8 @@ private Pair<VolumeVO, DataStore> recreateVolume(VolumeVO vol, VirtualMachinePro |
1935 | 2001 | if (poolVO != null && poolVO.getPoolType() == Storage.StoragePoolType.CLVM) { |
1936 | 2002 | Long hostId = vm.getVirtualMachine().getHostId(); |
1937 | 2003 | if (hostId != null) { |
1938 | | - // Using CLVM_LOCK_HOST_ID which serves dual purpose: creation routing and lock tracking |
1939 | 2004 | volume.setDestinationHostId(hostId); |
1940 | | - setClvmLockHostId(volume.getId(), hostId); |
| 2005 | + clvmLockManager.setClvmLockHostId(volume.getId(), hostId); |
1941 | 2006 | logger.info("CLVM pool detected during volume creation from template. Setting lock host {} for volume {} (persisted to DB) to route creation to correct host", |
1942 | 2007 | hostId, volume.getUuid()); |
1943 | 2008 | } |
@@ -2058,13 +2123,18 @@ public void prepare(VirtualMachineProfile vm, DeployDestination dest) throws Sto |
2058 | 2123 | throw new CloudRuntimeException(msg); |
2059 | 2124 | } |
2060 | 2125 |
|
2061 | | - // don't allow to start vm that doesn't have a root volume |
2062 | 2126 | if (_volsDao.findByInstanceAndType(vm.getId(), Volume.Type.ROOT).isEmpty()) { |
2063 | 2127 | throw new CloudRuntimeException(String.format("ROOT volume is missing, unable to prepare volumes for the VM [%s].", vm.getVirtualMachine())); |
2064 | 2128 | } |
2065 | 2129 |
|
2066 | 2130 | List<VolumeVO> vols = _volsDao.findUsableVolumesForInstance(vm.getId()); |
2067 | 2131 |
|
| 2132 | + VirtualMachine vmInstance = vm.getVirtualMachine(); |
| 2133 | + VMInstanceVO vmInstanceVO = vmInstanceDao.findById(vmInstance.getId()); |
| 2134 | + if (vmInstance.getState() == State.Starting && dest.getHost() != null) { |
| 2135 | + transferClvmLocksForVmStart(vols, dest.getHost().getId(), vmInstanceVO); |
| 2136 | + } |
| 2137 | + |
2068 | 2138 | List<VolumeTask> tasks = getTasks(vols, dest.getStorageForDisks(), vm); |
2069 | 2139 | Volume vol = null; |
2070 | 2140 | PrimaryDataStore store; |
|
0 commit comments