|
32 | 32 |
|
33 | 33 | import javax.inject.Inject; |
34 | 34 |
|
| 35 | +import com.cloud.storage.ClvmLockManager; |
| 36 | +import com.cloud.vm.VMInstanceVO; |
35 | 37 | import com.cloud.vm.dao.VMInstanceDao; |
36 | 38 | import org.apache.cloudstack.annotation.AnnotationService; |
37 | 39 | import org.apache.cloudstack.annotation.dao.AnnotationDao; |
@@ -221,6 +223,8 @@ public class VolumeServiceImpl implements VolumeService { |
221 | 223 | private PassphraseDao passphraseDao; |
222 | 224 | @Inject |
223 | 225 | protected DiskOfferingDao diskOfferingDao; |
| 226 | + @Inject |
| 227 | + ClvmLockManager clvmLockManager; |
224 | 228 |
|
225 | 229 | public VolumeServiceImpl() { |
226 | 230 | } |
@@ -2963,4 +2967,166 @@ public void moveVolumeOnSecondaryStorageToAnotherAccount(Volume volume, Account |
2963 | 2967 | protected String buildVolumePath(long accountId, long volumeId) { |
2964 | 2968 | return String.format("%s/%s/%s", TemplateConstants.DEFAULT_VOLUME_ROOT_DIR, accountId, volumeId); |
2965 | 2969 | } |
| 2970 | + |
| 2971 | + @Override |
| 2972 | + public boolean transferVolumeLock(VolumeInfo volume, Long sourceHostId, Long destHostId) { |
| 2973 | + StoragePoolVO pool = storagePoolDao.findById(volume.getPoolId()); |
| 2974 | + if (pool == null) { |
| 2975 | + logger.error("Cannot transfer volume lock for volume {}: storage pool not found", volume.getUuid()); |
| 2976 | + return false; |
| 2977 | + } |
| 2978 | + |
| 2979 | + logger.info("Transferring CLVM lock for volume {} (pool: {}) from host {} to host {}", |
| 2980 | + volume.getUuid(), pool.getName(), sourceHostId, destHostId); |
| 2981 | + |
| 2982 | + return clvmLockManager.transferClvmVolumeLock(volume.getUuid(), volume.getId(), volume.getPath(), |
| 2983 | + pool, sourceHostId, destHostId); |
| 2984 | + } |
| 2985 | + |
| 2986 | + @Override |
| 2987 | + public Long findVolumeLockHost(VolumeInfo volume) { |
| 2988 | + if (volume == null) { |
| 2989 | + logger.warn("Cannot find volume lock host: volume is null"); |
| 2990 | + return null; |
| 2991 | + } |
| 2992 | + |
| 2993 | + Long lockHostId = clvmLockManager.getClvmLockHostId(volume.getId(), volume.getUuid()); |
| 2994 | + if (lockHostId != null) { |
| 2995 | + logger.debug("Found explicit lock host {} for volume {}", lockHostId, volume.getUuid()); |
| 2996 | + return lockHostId; |
| 2997 | + } |
| 2998 | + |
| 2999 | + Long instanceId = volume.getInstanceId(); |
| 3000 | + if (instanceId != null) { |
| 3001 | + VMInstanceVO vmInstance = vmDao.findById(instanceId); |
| 3002 | + if (vmInstance != null && vmInstance.getHostId() != null) { |
| 3003 | + logger.debug("Volume {} is attached to VM {} on host {}", |
| 3004 | + volume.getUuid(), vmInstance.getUuid(), vmInstance.getHostId()); |
| 3005 | + return vmInstance.getHostId(); |
| 3006 | + } |
| 3007 | + } |
| 3008 | + |
| 3009 | + StoragePoolVO pool = storagePoolDao.findById(volume.getPoolId()); |
| 3010 | + if (pool != null && pool.getClusterId() != null) { |
| 3011 | + List<HostVO> hosts = _hostDao.findByClusterId(pool.getClusterId()); |
| 3012 | + if (hosts != null && !hosts.isEmpty()) { |
| 3013 | + for (HostVO host : hosts) { |
| 3014 | + if (host.getStatus() == com.cloud.host.Status.Up) { |
| 3015 | + logger.debug("Using fallback: first UP host {} in cluster {} for volume {}", |
| 3016 | + host.getId(), pool.getClusterId(), volume.getUuid()); |
| 3017 | + return host.getId(); |
| 3018 | + } |
| 3019 | + } |
| 3020 | + } |
| 3021 | + } |
| 3022 | + |
| 3023 | + logger.warn("Could not determine lock host for volume {}", volume.getUuid()); |
| 3024 | + return null; |
| 3025 | + } |
| 3026 | + |
| 3027 | + @Override |
| 3028 | + public VolumeInfo performLockMigration(VolumeInfo volume, Long destHostId) { |
| 3029 | + if (volume == null) { |
| 3030 | + throw new CloudRuntimeException("Cannot perform CLVM lock migration: volume is null"); |
| 3031 | + } |
| 3032 | + |
| 3033 | + String volumeUuid = volume.getUuid(); |
| 3034 | + logger.info("Starting CLVM lock migration for volume {} (id: {}) to host {}", |
| 3035 | + volumeUuid, volume.getUuid(), destHostId); |
| 3036 | + |
| 3037 | + Long sourceHostId = findVolumeLockHost(volume); |
| 3038 | + if (sourceHostId == null) { |
| 3039 | + logger.warn("Could not determine source host for CLVM volume {} lock, assuming volume is not exclusively locked", |
| 3040 | + volumeUuid); |
| 3041 | + sourceHostId = destHostId; |
| 3042 | + } |
| 3043 | + |
| 3044 | + if (sourceHostId.equals(destHostId)) { |
| 3045 | + logger.info("CLVM volume {} already has lock on destination host {}, no migration needed", |
| 3046 | + volumeUuid, destHostId); |
| 3047 | + return volume; |
| 3048 | + } |
| 3049 | + |
| 3050 | + logger.info("Migrating CLVM volume {} lock from host {} to host {}", |
| 3051 | + volumeUuid, sourceHostId, destHostId); |
| 3052 | + |
| 3053 | + boolean success = transferVolumeLock(volume, sourceHostId, destHostId); |
| 3054 | + if (!success) { |
| 3055 | + throw new CloudRuntimeException( |
| 3056 | + String.format("Failed to transfer CLVM lock for volume %s from host %s to host %s", |
| 3057 | + volumeUuid, sourceHostId, destHostId)); |
| 3058 | + } |
| 3059 | + |
| 3060 | + logger.info("Successfully migrated CLVM volume {} lock from host {} to host {}", |
| 3061 | + volumeUuid, sourceHostId, destHostId); |
| 3062 | + |
| 3063 | + return volFactory.getVolume(volume.getId()); |
| 3064 | + } |
| 3065 | + |
| 3066 | + @Override |
| 3067 | + public boolean areBothPoolsClvmType(StoragePoolType volumePoolType, StoragePoolType vmPoolType) { |
| 3068 | + if (volumePoolType == null || vmPoolType == null) { |
| 3069 | + logger.debug("Cannot check if both pools are CLVM type: one or both pool types are null"); |
| 3070 | + return false; |
| 3071 | + } |
| 3072 | + return ClvmLockManager.isClvmPoolType(volumePoolType) && |
| 3073 | + ClvmLockManager.isClvmPoolType(vmPoolType); |
| 3074 | + } |
| 3075 | + |
| 3076 | + @Override |
| 3077 | + public boolean isLockTransferRequired(VolumeInfo volumeToAttach, StoragePoolType volumePoolType, StoragePoolType vmPoolType, |
| 3078 | + Long volumePoolId, Long vmPoolId, Long vmHostId) { |
| 3079 | + if (volumePoolType != null && !ClvmLockManager.isClvmPoolType(volumePoolType)) { |
| 3080 | + return false; |
| 3081 | + } |
| 3082 | + |
| 3083 | + if (volumePoolId == null || !volumePoolId.equals(vmPoolId)) { |
| 3084 | + return false; |
| 3085 | + } |
| 3086 | + |
| 3087 | + Long volumeLockHostId = findVolumeLockHost(volumeToAttach); |
| 3088 | + |
| 3089 | + if (volumeLockHostId == null) { |
| 3090 | + VolumeVO volumeVO = _volumeDao.findById(volumeToAttach.getId()); |
| 3091 | + if (volumeVO != null && volumeVO.getState() == Volume.State.Ready && volumeVO.getInstanceId() == null) { |
| 3092 | + logger.debug("CLVM volume {} is detached on same pool, lock transfer may be needed", |
| 3093 | + volumeToAttach.getUuid()); |
| 3094 | + return true; |
| 3095 | + } |
| 3096 | + } |
| 3097 | + |
| 3098 | + if (volumeLockHostId != null && vmHostId != null && !volumeLockHostId.equals(vmHostId)) { |
| 3099 | + logger.info("CLVM lock transfer required: Volume {} lock is on host {} but VM is on host {}", |
| 3100 | + volumeToAttach.getUuid(), volumeLockHostId, vmHostId); |
| 3101 | + return true; |
| 3102 | + } |
| 3103 | + |
| 3104 | + return false; |
| 3105 | + } |
| 3106 | + |
| 3107 | + @Override |
| 3108 | + public boolean isLightweightMigrationNeeded(StoragePoolType volumePoolType, StoragePoolType vmPoolType, |
| 3109 | + String volumePoolPath, String vmPoolPath) { |
| 3110 | + if (!areBothPoolsClvmType(volumePoolType, vmPoolType)) { |
| 3111 | + return false; |
| 3112 | + } |
| 3113 | + |
| 3114 | + String volumeVgName = extractVgNameFromPath(volumePoolPath); |
| 3115 | + String vmVgName = extractVgNameFromPath(vmPoolPath); |
| 3116 | + |
| 3117 | + if (volumeVgName != null && volumeVgName.equals(vmVgName)) { |
| 3118 | + logger.info("CLVM lightweight migration detected: Volume is in same VG ({}), only lock transfer needed (no data copy)", volumeVgName); |
| 3119 | + return true; |
| 3120 | + } |
| 3121 | + |
| 3122 | + return false; |
| 3123 | + } |
| 3124 | + |
| 3125 | + private String extractVgNameFromPath(String poolPath) { |
| 3126 | + if (poolPath == null) { |
| 3127 | + return null; |
| 3128 | + } |
| 3129 | + return poolPath.startsWith("/") ? poolPath.substring(1) : poolPath; |
| 3130 | + } |
2966 | 3131 | } |
| 3132 | + |
0 commit comments