Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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";
Comment thread
sandeeplocharla marked this conversation as resolved.
Resolved
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;
Expand Down Expand Up @@ -3528,15 +3533,63 @@ protected void executeManagedStorageChecksWhenTargetStoragePoolProvided(StorageP
if (currentPool.getId() == targetPool.getId()) {
return;
}
if (isSupportedOntapLiveStorageMigration(currentPool, targetPool)) {
Comment thread
sandeeplocharla marked this conversation as resolved.
Resolved
return;
}

Map<String, String> details = _storagePoolDao.getDetails(currentPool.getId());
if (details != null && Boolean.parseBoolean(details.get(Storage.Capability.ALLOW_MIGRATE_OTHER_POOLS.toString()))) {
return;
}
if (DataStoreProvider.ONTAP_PLUGIN_NAME.equals(currentPool.getStorageProviderName())
&& DataStoreProvider.ONTAP_PLUGIN_NAME.equals(targetPool.getStorageProviderName())) {
Map<String, String> targetDetails = _storagePoolDao.getDetails(targetPool.getId());
throw new CloudRuntimeException(String.format("ONTAP migration requires matching storageIP, SVM, protocol, and pool type "
+ "[source: storageIP=%s, svmUUID=%s, svmName=%s, protocol=%s, type=%s; destination: storageIP=%s, svmUUID=%s, svmName=%s, protocol=%s, type=%s].",
details != null ? details.get(ONTAP_STORAGE_IP_DETAIL) : null,
details != null ? details.get(ONTAP_SVM_UUID_DETAIL) : null,
details != null ? details.get(ONTAP_SVM_NAME_DETAIL) : null, details != null ? details.get(ONTAP_PROTOCOL_DETAIL) : null,
currentPool.getPoolType(), targetDetails != null ? targetDetails.get(ONTAP_STORAGE_IP_DETAIL) : null,
targetDetails != null ? targetDetails.get(ONTAP_SVM_UUID_DETAIL) : null,
targetDetails != null ? targetDetails.get(ONTAP_SVM_NAME_DETAIL) : null,
targetDetails != null ? targetDetails.get(ONTAP_PROTOCOL_DETAIL) : null, targetPool.getPoolType()));
}
throw new CloudRuntimeException(String.format("Currently, a volume on managed storage can only be 'migrated' to itself " + "[volumeId=%s, currentStoragePoolId=%s, targetStoragePoolId=%s].",
volume.getUuid(), currentPool.getUuid(), targetPool.getUuid()));
}

protected boolean isSupportedOntapLiveStorageMigration(StoragePoolVO currentPool, StoragePoolVO targetPool) {
Comment thread
sandeeplocharla marked this conversation as resolved.
Resolved
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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,62 @@ public void allowVolumeMigrationsForPowerFlexStorage() {
verify(storagePoolVoMock, Mockito.times(0)).getId();
}

@Test
public void allowVolumeMigrationBetweenOntapPoolsOnSameSvmAndProtocol() {
StoragePoolVO targetPool = Mockito.mock(StoragePoolVO.class);
Mockito.doReturn(true).when(storagePoolVoMock).isManaged();
Mockito.doReturn(Storage.StoragePoolType.OntapiSCSI).when(storagePoolVoMock).getPoolType();
Mockito.doReturn(org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider.ONTAP_PLUGIN_NAME)
.when(storagePoolVoMock).getStorageProviderName();
Mockito.doReturn(12L).when(targetPool).getId();
Mockito.doReturn(Storage.StoragePoolType.OntapiSCSI).when(targetPool).getPoolType();
Mockito.doReturn(org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider.ONTAP_PLUGIN_NAME)
.when(targetPool).getStorageProviderName();
Mockito.doReturn(Map.of("storageIP", "10.0.0.1", "svmName", "svm1", "protocol", "ISCSI"))
.when(storagePoolDaoMock).getDetails(storagePoolVoMockId);
Mockito.doReturn(Map.of("storageIP", "10.0.0.1", "svmName", "svm1", "protocol", "ISCSI"))
.when(storagePoolDaoMock).getDetails(12L);

virtualMachineManagerImpl.executeManagedStorageChecksWhenTargetStoragePoolProvided(storagePoolVoMock, volumeVoMock, targetPool);
}

@Test
public void rejectVolumeMigrationBetweenOntapPoolsUsingDifferentProtocols() {
StoragePoolVO targetPool = Mockito.mock(StoragePoolVO.class);
Mockito.doReturn(true).when(storagePoolVoMock).isManaged();
Mockito.doReturn(Storage.StoragePoolType.OntapiSCSI).when(storagePoolVoMock).getPoolType();
Mockito.doReturn(org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider.ONTAP_PLUGIN_NAME)
.when(storagePoolVoMock).getStorageProviderName();
Mockito.doReturn(12L).when(targetPool).getId();
Mockito.doReturn(org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider.ONTAP_PLUGIN_NAME)
.when(targetPool).getStorageProviderName();
Mockito.doReturn(Map.of("storageIP", "10.0.0.1", "svmName", "svm1", "protocol", "ISCSI"))
.when(storagePoolDaoMock).getDetails(storagePoolVoMockId);
Mockito.doReturn(Map.of("storageIP", "10.0.0.1", "svmName", "svm1", "protocol", "NFS3"))
.when(storagePoolDaoMock).getDetails(12L);

CloudRuntimeException exception = assertThrows(CloudRuntimeException.class,
() -> virtualMachineManagerImpl.executeManagedStorageChecksWhenTargetStoragePoolProvided(storagePoolVoMock, volumeVoMock, targetPool));
assertTrue(exception.getMessage().contains("protocol=ISCSI"));
assertTrue(exception.getMessage().contains("protocol=NFS3"));
}

@Test
public void rejectOntapPoolsWithSameSvmNameOnDifferentStorageSystems() {
StoragePoolVO targetPool = Mockito.mock(StoragePoolVO.class);
Mockito.doReturn(org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider.ONTAP_PLUGIN_NAME)
.when(storagePoolVoMock).getStorageProviderName();
Mockito.doReturn(12L).when(targetPool).getId();
Mockito.doReturn(org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider.ONTAP_PLUGIN_NAME)
.when(targetPool).getStorageProviderName();
Mockito.doReturn(Map.of("storageIP", "10.0.0.1", "svmName", "svm1", "protocol", "NFS3"))
.when(storagePoolDaoMock).getDetails(storagePoolVoMockId);
Mockito.doReturn(Map.of("storageIP", "10.0.0.2", "svmName", "svm1", "protocol", "NFS3"))
.when(storagePoolDaoMock).getDetails(12L);

assertFalse(virtualMachineManagerImpl.isSupportedOntapLiveStorageMigration(storagePoolVoMock, targetPool));
}

@Test
public void executeManagedStorageChecksWhenTargetStoragePoolProvidedTestCurrentStoragePoolEqualsTargetPool() {
Mockito.doReturn(true).when(storagePoolVoMock).isManaged();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Comment thread
sandeeplocharla marked this conversation as resolved.
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;
Expand Down Expand Up @@ -233,13 +234,22 @@ 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) {
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;
}
Expand Down Expand Up @@ -320,6 +330,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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

import org.apache.cloudstack.engine.subsystem.api.storage.DataObject;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProvider;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.StrategyPriority;
import org.apache.cloudstack.engine.subsystem.api.storage.VolumeInfo;
Expand Down Expand Up @@ -133,6 +134,46 @@ public void internalCanHandleTestNoStoragePoolsIsManaged() {
configureAndTestInternalCanHandle(false, false, StrategyPriority.CANT_HANDLE);
}

@Test
public void canHandleDeclinesMigratingVolumeOnOntapPrimary() {
PrimaryDataStore srcStore = mock(PrimaryDataStoreImpl.class);
PrimaryDataStore destStore = mock(PrimaryDataStoreImpl.class);
VolumeObject srcVolume = mock(VolumeObject.class);
VolumeObject destVolume = mock(VolumeObject.class);
doReturn(Volume.State.Migrating).when(srcVolume).getState();
doReturn(srcStore).when(srcVolume).getDataStore();
doReturn(destStore).when(destVolume).getDataStore();
doReturn(1L).when(srcStore).getId();
doReturn(2L).when(destStore).getId();
StoragePoolVO srcPool = Mockito.spy(new StoragePoolVO());
StoragePoolVO destPool = Mockito.spy(new StoragePoolVO());
lenient().doReturn(true).when(srcPool).isManaged();
doReturn(DataStoreProvider.ONTAP_PLUGIN_NAME).when(destPool).getStorageProviderName();
doReturn(srcPool).when(primaryDataStoreDao).findById(1L);
doReturn(destPool).when(primaryDataStoreDao).findById(2L);

Assert.assertEquals(StrategyPriority.CANT_HANDLE, strategy.canHandle(srcVolume, destVolume));
}

@Test
public void internalCanHandleDeclinesMapWithOntapPool() {
VolumeObject volumeInfo = Mockito.spy(new VolumeObject());
Mockito.doReturn(0L).when(volumeInfo).getPoolId();
DataStore ds = Mockito.spy(new PrimaryDataStoreImpl());
Mockito.doReturn(1L).when(ds).getId();
Map<VolumeInfo, DataStore> volumeMap = new HashMap<>();
volumeMap.put(volumeInfo, ds);
StoragePoolVO srcPool = Mockito.spy(new StoragePoolVO());
lenient().doReturn(true).when(srcPool).isManaged();
StoragePoolVO destPool = Mockito.spy(new StoragePoolVO());
Mockito.doReturn(DataStoreProvider.ONTAP_PLUGIN_NAME).when(destPool).getStorageProviderName();
Mockito.doReturn(srcPool).when(primaryDataStoreDao).findById(0L);
Mockito.doReturn(destPool).when(primaryDataStoreDao).findById(1L);

Assert.assertEquals(StrategyPriority.CANT_HANDLE,
strategy.internalCanHandle(volumeMap, new HostVO("srcHostUuid"), new HostVO("destHostUuid")));
}

private void configureAndTestInternalCanHandle(boolean sPool0IsManaged, boolean sPool1IsManaged, StrategyPriority expectedStrategyPriority) {
VolumeObject volumeInfo = Mockito.spy(new VolumeObject());
Mockito.doReturn(0l).when(volumeInfo).getPoolId();
Expand Down Expand Up @@ -555,6 +596,7 @@ public void testInternalCanHandle_ClvmSourceManagedDestination() {
Mockito.doReturn(0L).when(volumeInfo).getPoolId();

DataStore ds = Mockito.spy(new PrimaryDataStoreImpl());
Mockito.lenient().doReturn(1L).when(ds).getId();

Map<VolumeInfo, DataStore> volumeMap = new HashMap<>();
volumeMap.put(volumeInfo, ds);
Expand All @@ -580,6 +622,7 @@ public void testInternalCanHandle_ClvmNgSourceManagedDestination() {
Mockito.doReturn(0L).when(volumeInfo).getPoolId();

DataStore ds = Mockito.spy(new PrimaryDataStoreImpl());
Mockito.lenient().doReturn(1L).when(ds).getId();

Map<VolumeInfo, DataStore> volumeMap = new HashMap<>();
volumeMap.put(volumeInfo, ds);
Expand All @@ -605,6 +648,7 @@ public void testInternalCanHandle_ClvmToClvmNg() {
Mockito.doReturn(0L).when(volumeInfo).getPoolId();

DataStore ds = Mockito.spy(new PrimaryDataStoreImpl());
Mockito.lenient().doReturn(1L).when(ds).getId();

Map<VolumeInfo, DataStore> volumeMap = new HashMap<>();
volumeMap.put(volumeInfo, ds);
Expand Down Expand Up @@ -633,6 +677,7 @@ public void testInternalCanHandle_ClvmNgToClvm() {
Mockito.doReturn(0L).when(volumeInfo).getPoolId();

DataStore ds = Mockito.spy(new PrimaryDataStoreImpl());
Mockito.lenient().doReturn(1L).when(ds).getId();

Map<VolumeInfo, DataStore> volumeMap = new HashMap<>();
volumeMap.put(volumeInfo, ds);
Expand Down
Loading
Loading