Skip to content

Commit 8ed2a3a

Browse files
committed
refactor
1 parent 22c4934 commit 8ed2a3a

File tree

3 files changed

+256
-159
lines changed

3 files changed

+256
-159
lines changed

engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/VolumeService.java

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import com.cloud.host.Host;
3131
import com.cloud.hypervisor.Hypervisor.HypervisorType;
3232
import com.cloud.offering.DiskOffering;
33+
import com.cloud.storage.Storage.StoragePoolType;
3334
import com.cloud.storage.Volume;
3435
import com.cloud.user.Account;
3536
import com.cloud.utils.Pair;
@@ -123,4 +124,71 @@ boolean copyPoliciesBetweenVolumesAndDestroySourceVolumeAfterMigration(ObjectInD
123124
void checkAndRepairVolumeBasedOnConfig(DataObject dataObject, Host host);
124125

125126
void validateChangeDiskOfferingEncryptionType(long existingDiskOfferingId, long newDiskOfferingId);
127+
128+
/**
129+
* Transfers exclusive lock for a volume on cluster-based storage (e.g., CLVM/CLVM_NG) from one host to another.
130+
* This is used for storage that requires host-level lock management for volumes on shared storage pools.
131+
* For non-CLVM pool types, this method returns false without taking action.
132+
*
133+
* @param volume The volume to transfer lock for
134+
* @param sourceHostId Host currently holding the exclusive lock
135+
* @param destHostId Host to receive the exclusive lock
136+
* @return true if lock transfer succeeded or was not needed, false if it failed
137+
*/
138+
boolean transferVolumeLock(VolumeInfo volume, Long sourceHostId, Long destHostId);
139+
140+
/**
141+
* Finds which host currently has the exclusive lock on a CLVM volume.
142+
* Checks in order: explicit lock tracking, attached VM's host, or first available cluster host.
143+
*
144+
* @param volume The CLVM volume
145+
* @return Host ID that has the exclusive lock, or null if cannot be determined
146+
*/
147+
Long findVolumeLockHost(VolumeInfo volume);
148+
149+
/**
150+
* Performs lightweight CLVM lock migration for a volume to a target host.
151+
* This transfers the LVM exclusive lock without copying data (CLVM volumes are on shared cluster storage).
152+
* If the volume already has the lock on the destination host, no action is taken.
153+
*
154+
* @param volume The volume to migrate lock for
155+
* @param destHostId Destination host ID
156+
* @return Updated VolumeInfo after lock migration
157+
*/
158+
VolumeInfo performLockMigration(VolumeInfo volume, Long destHostId);
159+
160+
/**
161+
* Checks if both storage pools are CLVM type (CLVM or CLVM_NG).
162+
*
163+
* @param volumePoolType Storage pool type for the volume
164+
* @param vmPoolType Storage pool type for the VM
165+
* @return true if both pools are CLVM type (CLVM or CLVM_NG)
166+
*/
167+
boolean areBothPoolsClvmType(StoragePoolType volumePoolType, StoragePoolType vmPoolType);
168+
169+
/**
170+
* Determines if CLVM lock transfer is required when a volume is already on the correct storage pool.
171+
*
172+
* @param volumeToAttach The volume being attached
173+
* @param volumePoolType Storage pool type for the volume
174+
* @param vmPoolType Storage pool type for the VM's existing volume
175+
* @param volumePoolId Storage pool ID for the volume
176+
* @param vmPoolId Storage pool ID for the VM's existing volume
177+
* @param vmHostId VM's current host ID (or last host ID if stopped)
178+
* @return true if CLVM lock transfer is needed
179+
*/
180+
boolean isLockTransferRequired(VolumeInfo volumeToAttach, StoragePoolType volumePoolType, StoragePoolType vmPoolType,
181+
Long volumePoolId, Long vmPoolId, Long vmHostId);
182+
183+
/**
184+
* Determines if lightweight CLVM migration is needed instead of full data copy.
185+
*
186+
* @param volumePoolType Storage pool type for the volume
187+
* @param vmPoolType Storage pool type for the VM
188+
* @param volumePoolPath Storage pool path for the volume
189+
* @param vmPoolPath Storage pool path for the VM
190+
* @return true if lightweight migration should be used
191+
*/
192+
boolean isLightweightMigrationNeeded(StoragePoolType volumePoolType, StoragePoolType vmPoolType,
193+
String volumePoolPath, String vmPoolPath);
126194
}

engine/storage/volume/src/main/java/org/apache/cloudstack/storage/volume/VolumeServiceImpl.java

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232

3333
import javax.inject.Inject;
3434

35+
import com.cloud.storage.ClvmLockManager;
36+
import com.cloud.vm.VMInstanceVO;
3537
import com.cloud.vm.dao.VMInstanceDao;
3638
import org.apache.cloudstack.annotation.AnnotationService;
3739
import org.apache.cloudstack.annotation.dao.AnnotationDao;
@@ -221,6 +223,8 @@ public class VolumeServiceImpl implements VolumeService {
221223
private PassphraseDao passphraseDao;
222224
@Inject
223225
protected DiskOfferingDao diskOfferingDao;
226+
@Inject
227+
ClvmLockManager clvmLockManager;
224228

225229
public VolumeServiceImpl() {
226230
}
@@ -2963,4 +2967,166 @@ public void moveVolumeOnSecondaryStorageToAnotherAccount(Volume volume, Account
29632967
protected String buildVolumePath(long accountId, long volumeId) {
29642968
return String.format("%s/%s/%s", TemplateConstants.DEFAULT_VOLUME_ROOT_DIR, accountId, volumeId);
29652969
}
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+
}
29663131
}
3132+

0 commit comments

Comments
 (0)