Repository navigation
CSTACKEX-262: Support for Compute + Volume migration and Volume migration #112
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
25d31cb
1a1c61a
5db110f
c238cce
b5938ce
cf3b256
200aef3
be02af2
fe0a3c6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,7 @@ | |
|
|
||
| import static com.cloud.configuration.ConfigurationManagerImpl.EXPOSE_ERRORS_TO_USER; | ||
| import static com.cloud.configuration.ConfigurationManagerImpl.MIGRATE_VM_ACROSS_CLUSTERS; | ||
| import static org.apache.commons.lang3.StringUtils.isNotBlank; | ||
|
|
||
| import java.lang.reflect.Field; | ||
| import java.net.URI; | ||
|
|
@@ -319,6 +320,10 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac | |
| public static final String VM_WORK_JOB_HANDLER = VirtualMachineManagerImpl.class.getSimpleName(); | ||
|
|
||
| private static final String VM_SYNC_ALERT_SUBJECT = "VM state sync alert"; | ||
| private static final String ONTAP_SVM_NAME_DETAIL = "svmName"; | ||
| private static final String ONTAP_SVM_UUID_DETAIL = "svmUUID"; | ||
| private static final String ONTAP_STORAGE_IP_DETAIL = "storageIP"; | ||
| private static final String ONTAP_PROTOCOL_DETAIL = "protocol"; | ||
|
|
||
| @Inject | ||
| private UserVmManager _userVmMgr; | ||
|
|
@@ -3528,6 +3533,9 @@ protected void executeManagedStorageChecksWhenTargetStoragePoolProvided(StorageP | |
| if (currentPool.getId() == targetPool.getId()) { | ||
| return; | ||
| } | ||
| if (isSupportedOntapLiveStorageMigration(currentPool, targetPool)) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we do not need this change at all if we enable ALLOW_MIGRATE_OTHER_POOLS with ONTAP pools. That code is already there below. And for an existing pool, we can take care of those through upgrade.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have specific set of validations to allow migration, which isn't the case for other storage vendors. If we use ALLOW_MIGRATE_OTHER_POOLS and rely on the checks in OntapDataMotionStrategy, VM migration would've been started while each volume individually reaches the strategy class and if the checks fail only for one volume out of say 2 disks, migration for that volume would fail while the other volume gets migrated. Cloudstack doesn't support moving that volume back. Also, ALLOW_MIGRATE_OTHER_POOLS is currently used only by storage vendors whose 'managed' flag is set to 'false'. Once our own pool types are ready, we must be able to reduce the flags here. |
||
| return; | ||
| } | ||
|
|
||
| Map<String, String> details = _storagePoolDao.getDetails(currentPool.getId()); | ||
| if (details != null && Boolean.parseBoolean(details.get(Storage.Capability.ALLOW_MIGRATE_OTHER_POOLS.toString()))) { | ||
|
|
@@ -3537,6 +3545,38 @@ protected void executeManagedStorageChecksWhenTargetStoragePoolProvided(StorageP | |
| volume.getUuid(), currentPool.getUuid(), targetPool.getUuid())); | ||
| } | ||
|
|
||
| protected boolean isSupportedOntapLiveStorageMigration(StoragePoolVO currentPool, StoragePoolVO targetPool) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is about validating the inputs if it is landing on the ONTAP plugin for migration, can this validation go insode plugin code and get it checked once control is inside plugin block ?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As replied to the previous comment, we can definitely discuss on the list of checks that we want to do. But, until we get our own pool type for NFS, we would be needing some ontap specific checks here. |
||
| if (!DataStoreProvider.ONTAP_PLUGIN_NAME.equals(currentPool.getStorageProviderName()) | ||
| || !DataStoreProvider.ONTAP_PLUGIN_NAME.equals(targetPool.getStorageProviderName())) { | ||
| return false; | ||
| } | ||
|
|
||
| Map<String, String> currentDetails = _storagePoolDao.getDetails(currentPool.getId()); | ||
| Map<String, String> targetDetails = _storagePoolDao.getDetails(targetPool.getId()); | ||
| if (currentDetails == null || targetDetails == null) { | ||
| return false; | ||
| } | ||
|
|
||
| String currentSvmName = currentDetails.get(ONTAP_SVM_NAME_DETAIL); | ||
| String targetSvmName = targetDetails.get(ONTAP_SVM_NAME_DETAIL); | ||
| String currentSvmUuid = currentDetails.get(ONTAP_SVM_UUID_DETAIL); | ||
| String targetSvmUuid = targetDetails.get(ONTAP_SVM_UUID_DETAIL); | ||
| String currentStorageIp = currentDetails.get(ONTAP_STORAGE_IP_DETAIL); | ||
| String targetStorageIp = targetDetails.get(ONTAP_STORAGE_IP_DETAIL); | ||
| String currentProtocol = currentDetails.get(ONTAP_PROTOCOL_DETAIL); | ||
| String targetProtocol = targetDetails.get(ONTAP_PROTOCOL_DETAIL); | ||
| boolean isSameSvm = isNotBlank(currentSvmUuid) || isNotBlank(targetSvmUuid) | ||
| ? isNotBlank(currentSvmUuid) && currentSvmUuid.equals(targetSvmUuid) | ||
| : isNotBlank(currentSvmName) && currentSvmName.equals(targetSvmName); | ||
| return isNotBlank(currentStorageIp) | ||
| && currentStorageIp.equals(targetStorageIp) | ||
| && isSameSvm | ||
| && isNotBlank(currentProtocol) | ||
| && currentProtocol.equalsIgnoreCase(targetProtocol) | ||
| && currentPool.getPoolType() != null | ||
| && currentPool.getPoolType() == targetPool.getPoolType(); | ||
| } | ||
|
|
||
| /** | ||
| * For each one of the volumes we will map it to a storage pool that is available via the target host. | ||
| * An exception is thrown if we cannot find a storage pool that is accessible in the target host to migrate the volume to. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -48,6 +48,7 @@ | |
| import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreCapabilities; | ||
| import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreDriver; | ||
| import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager; | ||
| import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider; | ||
|
sandeeplocharla marked this conversation as resolved.
|
||
| import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint; | ||
| import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector; | ||
| import org.apache.cloudstack.engine.subsystem.api.storage.HostScope; | ||
|
|
@@ -233,13 +234,24 @@ public StrategyPriority canHandle(DataObject srcData, DataObject destData) { | |
|
|
||
| if (srcData instanceof VolumeInfo && destData instanceof VolumeInfo) { | ||
| VolumeInfo srcVolumeInfo = (VolumeInfo)srcData; | ||
| VolumeInfo destVolumeInfo = (VolumeInfo)destData; | ||
|
|
||
| // OntapDataMotionStrategy also returns HIGHEST for these and a tie keeps the first strategy. | ||
| if (srcVolumeInfo.getState() == Volume.State.Migrating | ||
| && srcVolumeInfo.getDataStore().getRole() == DataStoreRole.Primary | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is not this condition of matching primary redundant if outside if already compares source and destination both as VolumeInfo ? secondary storage to primary for volume to volume itself is not there.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah good catch! since we already have primary checks before this check can be removed. |
||
| && destVolumeInfo.getDataStore().getRole() == DataStoreRole.Primary) { | ||
| StoragePoolVO srcPool = _storagePoolDao.findById(srcVolumeInfo.getDataStore().getId()); | ||
| StoragePoolVO destPool = _storagePoolDao.findById(destVolumeInfo.getDataStore().getId()); | ||
| if ((srcPool != null && DataStoreProvider.ONTAP_PLUGIN_NAME.equals(srcPool.getStorageProviderName())) | ||
| || (destPool != null && DataStoreProvider.ONTAP_PLUGIN_NAME.equals(destPool.getStorageProviderName()))) { | ||
| return StrategyPriority.CANT_HANDLE; | ||
| } | ||
| } | ||
|
|
||
| if (isVolumeOnManagedStorage(srcVolumeInfo)) { | ||
| return StrategyPriority.HIGHEST; | ||
| } | ||
|
|
||
| VolumeInfo destVolumeInfo = (VolumeInfo)destData; | ||
|
|
||
| if (isVolumeOnManagedStorage(destVolumeInfo)) { | ||
| return StrategyPriority.HIGHEST; | ||
| } | ||
|
|
@@ -320,6 +332,20 @@ public final StrategyPriority canHandle(Map<VolumeInfo, DataStore> volumeMap, Ho | |
| * Handles migrating volumes on managed Storage. | ||
| */ | ||
| protected StrategyPriority internalCanHandle(Map<VolumeInfo, DataStore> volumeMap, Host srcHost, Host destHost) { | ||
| // OntapDataMotionStrategy also returns HIGHEST for these and a tie keeps the first strategy. | ||
| for (VolumeInfo volumeInfo : volumeMap.keySet()) { | ||
| StoragePoolVO storagePoolVO = _storagePoolDao.findById(volumeInfo.getPoolId()); | ||
| if (storagePoolVO != null && DataStoreProvider.ONTAP_PLUGIN_NAME.equals(storagePoolVO.getStorageProviderName())) { | ||
| return StrategyPriority.CANT_HANDLE; | ||
| } | ||
| } | ||
| for (DataStore dataStore : volumeMap.values()) { | ||
| StoragePoolVO storagePoolVO = _storagePoolDao.findById(dataStore.getId()); | ||
| if (storagePoolVO != null && DataStoreProvider.ONTAP_PLUGIN_NAME.equals(storagePoolVO.getStorageProviderName())) { | ||
| return StrategyPriority.CANT_HANDLE; | ||
| } | ||
| } | ||
|
|
||
| Set<VolumeInfo> volumeInfoSet = volumeMap.keySet(); | ||
|
|
||
| for (VolumeInfo volumeInfo : volumeInfoSet) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why are these required in this file ? These are ONTAP specific .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For migration of a volume with VM (Live/Stopped),
executeManagedStorageChecksWhenTargetStoragePoolProvidedis executed and it rejects all 'managed' storages, unless the pool is of type 'PowerFlex'. Now, as we only have our own pool type for iSCSI but not for NFS, we would be needing explicit ontap relevant checks here.But we can definitely discuss on the list of checks that we want to keep here. I've retained the same list as in the OntapDataMotionStrategy class with the intention to fail fast.