/* * Copyright (c) 2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.scaleio; import static java.util.Arrays.asList; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.model.BlockConsistencyGroup; import com.emc.storageos.db.client.model.BlockObject; import com.emc.storageos.db.client.model.BlockSnapshot; import com.emc.storageos.db.client.model.ExportGroup; import com.emc.storageos.db.client.model.ExportMask; import com.emc.storageos.db.client.model.HostInterface; import com.emc.storageos.db.client.model.Initiator; import com.emc.storageos.db.client.model.StoragePool; import com.emc.storageos.db.client.model.StorageProvider; import com.emc.storageos.db.client.model.StorageSystem; import com.emc.storageos.db.client.model.StringSet; import com.emc.storageos.db.client.model.Volume; import com.emc.storageos.db.client.model.Volume.ReplicationState; import com.emc.storageos.db.client.util.CustomQueryUtility; import com.emc.storageos.db.client.util.NullColumnValueGetter; import com.emc.storageos.db.exceptions.DatabaseException; import com.emc.storageos.exceptions.DeviceControllerErrors; import com.emc.storageos.exceptions.DeviceControllerException; import com.emc.storageos.scaleio.ScaleIOException; import com.emc.storageos.scaleio.api.restapi.ScaleIORestClient; import com.emc.storageos.scaleio.api.restapi.response.ScaleIOVolume; import com.emc.storageos.svcs.errorhandling.model.ServiceCoded; import com.emc.storageos.svcs.errorhandling.model.ServiceError; import com.emc.storageos.volumecontroller.CloneOperations; import com.emc.storageos.volumecontroller.DefaultBlockStorageDevice; import com.emc.storageos.volumecontroller.SnapshotOperations; import com.emc.storageos.volumecontroller.TaskCompleter; import com.emc.storageos.volumecontroller.impl.ControllerUtils; import com.emc.storageos.volumecontroller.impl.block.taskcompleter.CleanupMetaVolumeMembersCompleter; import com.emc.storageos.volumecontroller.impl.block.taskcompleter.VolumeCreateCompleter; import com.emc.storageos.volumecontroller.impl.block.taskcompleter.VolumeExpandCompleter; import com.emc.storageos.volumecontroller.impl.smis.MetaVolumeRecommendation; import com.emc.storageos.volumecontroller.impl.smis.ReplicationUtils; import com.emc.storageos.volumecontroller.impl.utils.ExportMaskUtils; import com.emc.storageos.volumecontroller.impl.utils.VirtualPoolCapabilityValuesWrapper; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; public class ScaleIOStorageDevice extends DefaultBlockStorageDevice { private static final Logger log = LoggerFactory.getLogger(ScaleIOStorageDevice.class); private static final String VOLUME_NOT_MAPPED_TO_SDC = "Volume not mapped to SDC"; private static final String VOLUME_NOT_MAPPED_TO_SDC_V2 = "volume is not mapped to SDC"; private static final String VOLUME_NOT_MAPPED_TO_SCSI = "Volume not mapped to SCSI Initiator"; private static final String ALREADY_MAPPED_TO = "already mapped to"; private static volatile ScaleIOStorageDevice instance; private DbClient dbClient; private ScaleIOHandleFactory scaleIOHandleFactory; private SnapshotOperations snapshotOperations; private CloneOperations cloneOperations; public ScaleIOStorageDevice() { instance = this; } public static ScaleIOStorageDevice getInstance() { return instance; } public void setDbClient(DbClient dbClient) { this.dbClient = dbClient; } @SuppressWarnings("UnusedDeclaration") public void setScaleIOHandleFactory(ScaleIOHandleFactory scaleIOHandleFactory) { this.scaleIOHandleFactory = scaleIOHandleFactory; } @SuppressWarnings("UnusedDeclaration") public void setSnapshotOperations(SnapshotOperations snapshotOperations) { this.snapshotOperations = snapshotOperations; } @SuppressWarnings("UnusedDeclaration") public void setCloneOperations(CloneOperations cloneOperations) { this.cloneOperations = cloneOperations; } /** * Refresh connection status for all ScaleIO providers. * * @return A list of providers whose connections were successful. */ public List<URI> refreshConnectionStatusForAllSIOProviders() { log.info("Refreshing connection statuses for ScaleIO providers"); List<URI> activeProviders = Lists.newArrayList(); List<StorageProvider> providers = CustomQueryUtility.getActiveStorageProvidersByInterfaceType(dbClient, StorageProvider.InterfaceType.scaleioapi.name()); for (StorageProvider provider : providers) { try { // Flag for success/failure boolean success = false; // Prepare to try secondary IPs if necessary StringSet secondaryIps = provider.getSecondaryIps(); Iterator<String> iterator = secondaryIps.iterator(); // Cache the current IP address String currentIPAddress = provider.getIPAddress(); String nextIp = null; do { try { ScaleIORestClient handle = scaleIOHandleFactory.using(dbClient).getClientHandle(provider); handle.getSystem(); // Ignore the result on success, otherwise catch the exception log.info("Successfully connected to ScaleIO MDM {}: {}", provider.getIPAddress(), provider.getId()); success = true; break; } catch (Exception e) { log.error(String.format("Failed to connect to ScaleIO MDM %s: %s", provider.getIPAddress(), provider.getId()), e); if (iterator.hasNext()) { nextIp = iterator.next(); log.info("Attempting connection to potential new Primary MDM {}: {}", nextIp, provider.getId()); provider.setIPAddress(nextIp); } else { log.warn("Exhausted list of secondary IPs for ScaleIO provider: {}", provider.getId()); nextIp = null; } } } while (nextIp != null); // while we have more IPs to try if (success) { // Update secondary IP addresses if we switched over if (!provider.getIPAddress().equalsIgnoreCase(currentIPAddress)) { StringSet newSecondaryIps = new StringSet(); // Copy old secondary list newSecondaryIps.addAll(secondaryIps); // Remove the new primary IP newSecondaryIps.remove(provider.getIPAddress()); // Add the old primary IP newSecondaryIps.add(currentIPAddress); // TODO Improve how we update the StringSet based on infra team suggestions provider.setSecondaryIps(newSecondaryIps); } activeProviders.add(provider.getId()); provider.setConnectionStatus(StorageProvider.ConnectionStatus.CONNECTED.toString()); } else { provider.setIPAddress(currentIPAddress); provider.setConnectionStatus(StorageProvider.ConnectionStatus.NOTCONNECTED.toString()); } } finally { dbClient.persistObject(provider); } } return activeProviders; } @Override public void doCreateVolumes(StorageSystem storage, StoragePool storagePool, String opId, List<Volume> volumes, VirtualPoolCapabilityValuesWrapper capabilities, TaskCompleter taskCompleter) throws DeviceControllerException { int index = 1; try { ScaleIORestClient scaleIOHandle = scaleIOHandleFactory.using(dbClient).getClientHandle(storage); String protectionDomainName = storage.getSerialNumber(); Long volumeSize = capabilities.getSize() / ScaleIOHelper.BYTES_IN_GB; int count = volumes.size(); Set<URI> poolsToUpdate = new HashSet<>(); boolean thinlyProvisioned = capabilities.getThinProvisioning(); Set<URI> consistencyGroups = new HashSet<>(); Multimap<URI, String> poolToVolumesMap = ArrayListMultimap.create(); String systemId = scaleIOHandle.getSystemId(); for (; index <= count; index++) { Volume volume = volumes.get(index - 1); Long size = capabilities.getSize(); String poolId = storagePool.getNativeId(); ScaleIOVolume result = scaleIOHandle.addVolume(protectionDomainName, poolId, volume.getLabel(), size.toString(), thinlyProvisioned); ScaleIOHelper.updateVolumeWithAddVolumeInfo(dbClient, volume, systemId, volumeSize, result); poolsToUpdate.add(volume.getPool()); if (!NullColumnValueGetter.isNullURI(volume.getConsistencyGroup())) { consistencyGroups.add(volume.getConsistencyGroup()); } poolToVolumesMap.put(volume.getPool(), volume.getId().toString()); } updateConsistencyGroupsWithStorageSystem(consistencyGroups, storage); List<StoragePool> pools = dbClient.queryObject(StoragePool.class, Lists.newArrayList(poolsToUpdate)); for (StoragePool pool : pools) { pool.removeReservedCapacityForVolumes(poolToVolumesMap.get(pool.getId())); ScaleIOHelper.updateStoragePoolCapacity(dbClient, scaleIOHandle, pool, storage); } dbClient.persistObject(volumes); taskCompleter.ready(dbClient); } catch (Exception e) { log.error("Encountered an exception", e); for (int cleanup = index; cleanup <= volumes.size(); cleanup++) { volumes.get(cleanup - 1).setInactive(true); } dbClient.persistObject(volumes); ServiceCoded code = DeviceControllerErrors.scaleio.encounteredAnExceptionFromScaleIOOperation("addVolume", e.getMessage()); taskCompleter.error(dbClient, code); } } @Override public void doCreateMetaVolume(StorageSystem storage, StoragePool storagePool, Volume volume, VirtualPoolCapabilityValuesWrapper capabilities, MetaVolumeRecommendation recommendation, VolumeCreateCompleter completer) throws DeviceControllerException { completeTaskAsUnsupported(completer); } @Override public void doCreateMetaVolumes(StorageSystem storage, StoragePool storagePool, List<Volume> volumes, VirtualPoolCapabilityValuesWrapper capabilities, MetaVolumeRecommendation recommendation, TaskCompleter completer) throws DeviceControllerException { completeTaskAsUnsupported(completer); } @Override public void doExpandVolume(StorageSystem storage, StoragePool pool, Volume volume, Long size, TaskCompleter taskCompleter) throws DeviceControllerException { Long volumeSize = size / ScaleIOHelper.BYTES_IN_GB; Long expandSize = volumeSize; // ScaleIO volume size has to be granularity of 8 long remainder = volumeSize % 8; if (remainder != 0) { expandSize += (8 - remainder); log.info("The requested size is {} GB, increase it to {} GB, so that it is granularity of 8", volumeSize, expandSize); } try { ScaleIORestClient scaleIOHandle = scaleIOHandleFactory.using(dbClient).getClientHandle(storage); ScaleIOVolume result = scaleIOHandle.modifyVolumeCapacity(volume.getNativeId(), expandSize.toString()); long newSize = Long.parseLong(result.getSizeInKb()) * 1024L; volume.setProvisionedCapacity(newSize); volume.setAllocatedCapacity(newSize); volume.setCapacity(size); dbClient.persistObject(volume); ScaleIOHelper.updateStoragePoolCapacity(dbClient, scaleIOHandle, pool, storage); pool.removeReservedCapacityForVolumes(Arrays.asList(volume.getId().toString())); taskCompleter.ready(dbClient); } catch (Exception e) { log.error("Encountered an exception", e); ServiceCoded code = DeviceControllerErrors.scaleio.encounteredAnExceptionFromScaleIOOperation("expandVolume", e.getMessage()); taskCompleter.error(dbClient, code); } } @Override public void doExpandAsMetaVolume(StorageSystem storageSystem, StoragePool storagePool, Volume volume, long size, MetaVolumeRecommendation recommendation, VolumeExpandCompleter volumeCompleter) throws DeviceControllerException { completeTaskAsUnsupported(volumeCompleter); } @Override public void doDeleteVolumes(StorageSystem storageSystem, String opId, List<Volume> volumes, TaskCompleter completer) throws DeviceControllerException { try { ScaleIORestClient scaleIOHandle = scaleIOHandleFactory.using(dbClient).getClientHandle(storageSystem); Set<URI> poolsToUpdate = new HashSet<>(); for (Volume volume : volumes) { scaleIOHandle.removeVolume(volume.getNativeId()); volume.setInactive(true); poolsToUpdate.add(volume.getPool()); if (!NullColumnValueGetter.isNullURI(volume.getConsistencyGroup())) { volume.setConsistencyGroup(NullColumnValueGetter.getNullURI()); } } dbClient.persistObject(volumes); List<StoragePool> pools = dbClient.queryObject(StoragePool.class, Lists.newArrayList(poolsToUpdate)); for (StoragePool pool : pools) { ScaleIOHelper.updateStoragePoolCapacity(dbClient, scaleIOHandle, pool, storageSystem); } completer.ready(dbClient); } catch (Exception e) { log.error("Encountered an exception", e); ServiceCoded code = DeviceControllerErrors.scaleio.encounteredAnExceptionFromScaleIOOperation("deleteVolume", e.getMessage()); completer.error(dbClient, code); } } @Override public void doExportCreate(StorageSystem storage, ExportMask exportMask, Map<URI, Integer> volumeMap, List<Initiator> initiators, List<URI> targets, TaskCompleter taskCompleter) throws DeviceControllerException { filterInitiators(initiators); mapVolumes(storage, volumeMap, initiators, taskCompleter); } @Override public void doExportDelete(StorageSystem storage, ExportMask exportMask, List<URI> volumeURIs, List<URI> initiatorURIs, TaskCompleter taskCompleter) throws DeviceControllerException { List<URI> maskVolumeURIs = ExportMaskUtils.getVolumeURIs(exportMask); Set<Initiator> maskInitiators = ExportMaskUtils.getInitiatorsForExportMask(dbClient, exportMask, null); filterInitiators(maskInitiators); unmapVolumes(storage, maskVolumeURIs, maskInitiators, taskCompleter); } @Override public void doExportAddVolume(StorageSystem storage, ExportMask exportMask, URI volume, Integer lun, List<Initiator> initiators, TaskCompleter taskCompleter) throws DeviceControllerException { Map<URI, Integer> volumes = new HashMap<>(); volumes.put(volume, lun); Set<Initiator> maskInitiators = ExportMaskUtils.getInitiatorsForExportMask(dbClient, exportMask, null); filterInitiators(maskInitiators); mapVolumes(storage, volumes, maskInitiators, taskCompleter); } @Override public void doExportAddVolumes(StorageSystem storage, ExportMask exportMask, List<Initiator> initiators, Map<URI, Integer> volumes, TaskCompleter taskCompleter) throws DeviceControllerException { Set<Initiator> maskInitiators = ExportMaskUtils.getInitiatorsForExportMask(dbClient, exportMask, null); filterInitiators(maskInitiators); mapVolumes(storage, volumes, maskInitiators, taskCompleter); } @Override public void doExportRemoveVolume(StorageSystem storage, ExportMask exportMask, URI volume, List<Initiator> initiators, TaskCompleter taskCompleter) throws DeviceControllerException { Set<Initiator> maskInitiators = ExportMaskUtils.getInitiatorsForExportMask(dbClient, exportMask, null); filterInitiators(maskInitiators); unmapVolumes(storage, asList(volume), maskInitiators, taskCompleter); } @Override public void doExportRemoveVolumes(StorageSystem storage, ExportMask exportMask, List<URI> volumes, List<Initiator> initiators, TaskCompleter taskCompleter) throws DeviceControllerException { Set<Initiator> maskInitiators = ExportMaskUtils.getInitiatorsForExportMask(dbClient, exportMask, null); filterInitiators(maskInitiators); unmapVolumes(storage, volumes, maskInitiators, taskCompleter); } @Override public void doExportAddInitiator(StorageSystem storage, ExportMask exportMask, List<URI> volumeURIs, Initiator initiator, List<URI> targets, TaskCompleter taskCompleter) throws DeviceControllerException { Map<URI, Integer> volumes = createVolumeMapForExportMask(exportMask); mapVolumes(storage, volumes, asList(initiator), taskCompleter); } @Override public void doExportAddInitiators(StorageSystem storage, ExportMask exportMask, List<URI> volumeURIs, List<Initiator> initiators, List<URI> targets, TaskCompleter taskCompleter) throws DeviceControllerException { Map<URI, Integer> volumes = createVolumeMapForExportMask(exportMask); mapVolumes(storage, volumes, initiators, taskCompleter); } @Override public void doExportRemoveInitiator(StorageSystem storage, ExportMask exportMask, List<URI> volumes, Initiator initiator, List<URI> targets, TaskCompleter taskCompleter) throws DeviceControllerException { List<URI> volumeURIs = ExportMaskUtils.getVolumeURIs(exportMask); unmapVolumes(storage, volumeURIs, asList(initiator), taskCompleter); } @Override public void doExportRemoveInitiators(StorageSystem storage, ExportMask exportMask, List<URI> volumes, List<Initiator> initiators, List<URI> targets, TaskCompleter taskCompleter) throws DeviceControllerException { List<URI> volumeURIs = ExportMaskUtils.getVolumeURIs(exportMask); unmapVolumes(storage, volumeURIs, initiators, taskCompleter); } @Override public void doCreateSnapshot(StorageSystem storage, List<URI> snapshotList, Boolean createInactive, Boolean readOnly, TaskCompleter taskCompleter) throws DeviceControllerException { try { List<BlockSnapshot> snapshots = dbClient.queryObject(BlockSnapshot.class, snapshotList); if (ControllerUtils.checkSnapshotsInConsistencyGroup(snapshots, dbClient, taskCompleter)) { snapshotOperations.createGroupSnapshots(storage, snapshotList, createInactive, readOnly, taskCompleter); } else { URI snapshot = snapshots.get(0).getId(); snapshotOperations.createSingleVolumeSnapshot(storage, snapshot, createInactive, readOnly, taskCompleter); } } catch (DatabaseException e) { String message = String.format("IO exception when trying to create snapshot(s) on array %s", storage.getSerialNumber()); log.error(message, e); ServiceError error = DeviceControllerErrors.smis.methodFailed("doCreateSnapshot", e.getMessage()); taskCompleter.error(dbClient, error); } } @Override public void doActivateSnapshot(StorageSystem storage, List<URI> snapshotList, TaskCompleter taskCompleter) throws DeviceControllerException { completeTaskAsUnsupported(taskCompleter); } @Override public void doDeleteSnapshot(StorageSystem storage, URI snapshot, TaskCompleter taskCompleter) throws DeviceControllerException { try { BlockSnapshot blockSnapshot = dbClient.queryObject(BlockSnapshot.class, snapshot); List<BlockSnapshot> groupSnapshots = ControllerUtils.getSnapshotsPartOfReplicationGroup(blockSnapshot, dbClient); // We check the snapset size here because SIO consistency groups require more than 1 device if (ControllerUtils.checkSnapshotsInConsistencyGroup(Arrays.asList(blockSnapshot), dbClient, taskCompleter) && groupSnapshots.size() > 1) { snapshotOperations.deleteGroupSnapshots(storage, snapshot, taskCompleter); } else { snapshotOperations.deleteSingleVolumeSnapshot(storage, snapshot, taskCompleter); } } catch (DatabaseException e) { String message = String.format( "IO exception when trying to delete snapshot(s) on array %s", storage.getSerialNumber()); log.error(message, e); ServiceError error = DeviceControllerErrors.smis.methodFailed("doDeleteSnapshot", e.getMessage()); taskCompleter.error(dbClient, error); } catch (Exception e) { String message = String.format( "Exception when trying to delete snapshot(s) on array %s", storage.getSerialNumber()); log.error(message, e); ServiceError error = DeviceControllerErrors.smis.methodFailed("doDeleteSnapshot", e.getMessage()); taskCompleter.error(dbClient, error); } } @Override public void doRestoreFromSnapshot(StorageSystem storage, URI volume, URI snapshot, TaskCompleter taskCompleter) throws DeviceControllerException { completeTaskAsUnsupported(taskCompleter); } @Override public void doCreateMirror(StorageSystem storage, URI mirror, Boolean createInactive, TaskCompleter taskCompleter) throws DeviceControllerException { completeTaskAsUnsupported(taskCompleter); } @Override public void doFractureMirror(StorageSystem storage, URI mirror, Boolean sync, TaskCompleter taskCompleter) throws DeviceControllerException { completeTaskAsUnsupported(taskCompleter); } @Override public void doDetachMirror(StorageSystem storage, URI mirror, TaskCompleter taskCompleter) throws DeviceControllerException { completeTaskAsUnsupported(taskCompleter); } @Override public void doResumeNativeContinuousCopy(StorageSystem storage, URI mirror, TaskCompleter taskCompleter) throws DeviceControllerException { completeTaskAsUnsupported(taskCompleter); } @Override public void doDeleteMirror(StorageSystem storage, URI mirror, TaskCompleter taskCompleter) throws DeviceControllerException { completeTaskAsUnsupported(taskCompleter); } @Override public void doCreateClone(StorageSystem storageSystem, URI sourceVolume, URI cloneVolume, Boolean createInactive, TaskCompleter taskCompleter) { cloneOperations.createSingleClone(storageSystem, sourceVolume, cloneVolume, createInactive, taskCompleter); } @Override public void doDetachClone(StorageSystem storage, URI cloneVolume, TaskCompleter taskCompleter) { log.info("Nothing to do here. ScaleIO full copies do not require detaching."); // no operation, set to ready Volume clone = dbClient.queryObject(Volume.class, cloneVolume); ReplicationUtils.removeDetachedFullCopyFromSourceFullCopiesList(clone, dbClient); clone.setAssociatedSourceVolume(NullColumnValueGetter.getNullURI()); clone.setReplicaState(ReplicationState.DETACHED.name()); dbClient.persistObject(clone); taskCompleter.ready(dbClient); } @Override public void doCreateConsistencyGroup(StorageSystem storage, URI consistencyGroup, String replicationGroupName, TaskCompleter taskCompleter) throws DeviceControllerException { log.info("Nothing to do here. ScaleIO consistency groups are formed automatically on-demand."); taskCompleter.ready(dbClient); } @Override public void doDeleteConsistencyGroup(StorageSystem storage, URI consistencyGroup, String replicationGroupName, Boolean keepRGName, Boolean markInactive, TaskCompleter taskCompleter) throws DeviceControllerException { log.info("Going to delete BlockConsistency Group {}", consistencyGroup); if (markInactive && consistencyGroup != null) { BlockConsistencyGroup cg = dbClient.queryObject(BlockConsistencyGroup.class, consistencyGroup); if (cg != null) { dbClient.markForDeletion(cg); } } taskCompleter.ready(dbClient); } @Override public void doDeleteConsistencyGroup(StorageSystem storage, final URI consistencyGroupId, String replicationGroupName, Boolean keepRGName, Boolean markInactive, String sourceReplicationGroup, final TaskCompleter taskCompleter) throws DeviceControllerException { doDeleteConsistencyGroup(storage, consistencyGroupId, replicationGroupName, keepRGName, markInactive, taskCompleter); } @Override public void doConnect(StorageSystem storage) { } @Override public void doDisconnect(StorageSystem storage) { } @Override public String doAddStorageSystem(StorageSystem storage) throws DeviceControllerException { return null; } @Override public void doRemoveStorageSystem(StorageSystem storage) throws DeviceControllerException { } @Override public void doCopySnapshotsToTarget(StorageSystem storage, List<URI> snapshotList, TaskCompleter taskCompleter) throws DeviceControllerException { completeTaskAsUnsupported(taskCompleter); } @Override public Map<String, Set<URI>> findExportMasks(StorageSystem storage, List<String> initiatorNames, boolean mustHaveAllPorts) throws DeviceControllerException { return null; } @Override public ExportMask refreshExportMask(StorageSystem storage, ExportMask mask) throws DeviceControllerException { return null; } @Override public void doActivateFullCopy(StorageSystem storageSystem, URI fullCopy, TaskCompleter completer) { completeTaskAsUnsupported(completer); } @Override public void doCleanupMetaMembers(StorageSystem storageSystem, Volume volume, CleanupMetaVolumeMembersCompleter cleanupCompleter) throws DeviceControllerException { } @Override public Integer checkSyncProgress(URI storage, URI source, URI target) { return null; } @Override public void doWaitForSynchronized(Class<? extends BlockObject> clazz, StorageSystem storageObj, URI target, TaskCompleter completer) { log.info("Nothing to do here. ScaleIO does not require a wait for synchronization"); completer.ready(dbClient); } @Override public void doWaitForGroupSynchronized(StorageSystem storageObj, List<URI> target, TaskCompleter completer) { log.info("Nothing to do here. ScaleIO does not require a wait for synchronization"); completer.ready(dbClient); } @Override public void doAddToConsistencyGroup(StorageSystem storage, URI consistencyGroupId, String replicationGroupName, List<URI> blockObjects, TaskCompleter taskCompleter) throws DeviceControllerException { completeTaskAsUnsupported(taskCompleter); } @Override public void doRemoveFromConsistencyGroup(StorageSystem storage, URI consistencyGroupId, List<URI> blockObjects, TaskCompleter taskCompleter) throws DeviceControllerException { completeTaskAsUnsupported(taskCompleter); } @Override public boolean validateStorageProviderConnection(String ipAddress, Integer portNumber) { return false; } /** * Method calls the completer with error message indicating that the caller's method is unsupported * * @param completer * [in] - TaskCompleter */ private void completeTaskAsUnsupported(TaskCompleter completer) { StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); String methodName = stackTrace[2].getMethodName(); ServiceCoded code = DeviceControllerErrors.scaleio.operationIsUnsupported(methodName); completer.error(dbClient, code); } /** * Given a mapping of volumes and initiators, make the ScaleIO API calls to map the volume * to the specified ScaleIO initiators * * @param storage * [in] - StorageSystem object (ScaleIO array abstraction) * @param volumeMap * [in] - Volume URI to Integer LUN map * @param initiators * [in] - Collection of Initiator objects * @param completer * [in] - TaskCompleter */ private void mapVolumes(StorageSystem storage, Map<URI, Integer> volumeMap, Collection<Initiator> initiators, TaskCompleter completer) { try { ScaleIORestClient scaleIOHandle = scaleIOHandleFactory.using(dbClient).getClientHandle(storage); for (Map.Entry<URI, Integer> volMapEntry : volumeMap.entrySet()) { BlockObject blockObject = BlockObject.fetch(dbClient, volMapEntry.getKey()); String nativeId = blockObject.getNativeId(); for (Initiator initiator : initiators) { String port = initiator.getInitiatorPort(); boolean wasMapped = false; if (initiator.getProtocol().equals(HostInterface.Protocol.ScaleIO.name())) { wasMapped = mapToSDC(scaleIOHandle, nativeId, port, completer); } else if (initiator.getProtocol().equals(HostInterface.Protocol.iSCSI.name())) { wasMapped = mapToSCSI(scaleIOHandle, nativeId, port, initiator.getLabel(), completer); } else { ServiceCoded code = DeviceControllerErrors.scaleio.mapVolumeToClientFailed(nativeId, port, String.format("Unexpected initiator type %s", initiator.getProtocol())); completer.error(dbClient, code); } if (!wasMapped) { // Failed to map the volume return; } } } completer.ready(dbClient); } catch (Exception e) { log.error("Encountered an exception", e); ServiceCoded code = DeviceControllerErrors.scaleio.encounteredAnExceptionFromScaleIOOperation("mapVolume", e.getMessage()); completer.error(dbClient, code); } } private boolean mapToSDC(ScaleIORestClient scaleIOHandle, String volumeId, String sdcId, TaskCompleter completer) throws Exception { try { scaleIOHandle.mapVolumeToSDC(volumeId, sdcId); } catch (ScaleIOException e) { String error = e.getMessage(); log.info(error); if (!error.contains(ALREADY_MAPPED_TO)) { ServiceCoded code = DeviceControllerErrors.scaleio.mapVolumeToClientFailed(volumeId, sdcId, error); completer.error(dbClient, code); return false; } } return true; } private boolean mapToSCSI(ScaleIORestClient scaleIOHandle, String volumeId, String iqn, String scsiId, TaskCompleter completer) throws Exception { try { scaleIOHandle.mapVolumeToSCSIInitiator(volumeId, scsiId); } catch (Exception e) { String error = e.getMessage(); if (!error.contains(ALREADY_MAPPED_TO)) { ServiceCoded code = DeviceControllerErrors.scaleio.mapVolumeToClientFailed(volumeId, iqn, error); completer.error(dbClient, code); return false; } } return true; } /** * Given a mapping of volumes and initiators, make the ScaleIO API calls to un-map the volume * to the specified ScaleIO initiators * * @param storage * [in] - StorageSystem object (ScaleIO array abstraction) * @param volumeURIs * [in] - Collection of Volume URIs * @param initiators * [in] - Collection of Initiator objects * @param completer * [in] - TaskCompleter */ private void unmapVolumes(StorageSystem storage, Collection<URI> volumeURIs, Collection<Initiator> initiators, TaskCompleter completer) { try { ScaleIORestClient scaleIOHandle = scaleIOHandleFactory.using(dbClient).getClientHandle(storage); for (URI volumeURI : volumeURIs) { BlockObject blockObject = BlockObject.fetch(dbClient, volumeURI); if (blockObject == null || blockObject.getInactive()) { log.warn(String.format("Attempted to unmap BlockObject %s, which was either not found in the DB or was inactive", volumeURI.toString())); continue; } String nativeId = blockObject.getNativeId(); for (Initiator initiator : initiators) { String port = initiator.getInitiatorPort(); boolean wasUnMapped = false; if (initiator.getProtocol().equals(HostInterface.Protocol.ScaleIO.name())) { wasUnMapped = unmapFromSDC(scaleIOHandle, nativeId, port, completer); } else if (initiator.getProtocol().equals(HostInterface.Protocol.iSCSI.name())) { wasUnMapped = unmapFromSCSI(scaleIOHandle, nativeId, port, initiator.getLabel(), completer); } else { ServiceCoded code = DeviceControllerErrors.scaleio.unmapVolumeToClientFailed(nativeId, port, String.format("Unexpected initiator type %s", initiator.getProtocol())); completer.error(dbClient, code); } if (!wasUnMapped) { // Failed to map the volume return; } } } completer.ready(dbClient); } catch (Exception e) { log.error("Encountered an exception", e); ServiceCoded code = DeviceControllerErrors.scaleio.encounteredAnExceptionFromScaleIOOperation("unmapVolume", e.getMessage()); completer.error(dbClient, code); } } private boolean unmapFromSDC(ScaleIORestClient scaleIOHandle, String volumeId, String sdcId, TaskCompleter completer) { try { scaleIOHandle.unMapVolumeToSDC(volumeId, sdcId); } catch (Exception e) { String error = e.getMessage(); if (!error.toLowerCase().contains(VOLUME_NOT_MAPPED_TO_SDC.toLowerCase()) && !error.toLowerCase().contains(VOLUME_NOT_MAPPED_TO_SDC_V2.toLowerCase())) { ServiceCoded code = DeviceControllerErrors.scaleio.unmapVolumeToClientFailed(volumeId, sdcId, error); completer.error(dbClient, code); return false; } } return true; } private boolean unmapFromSCSI(ScaleIORestClient scaleIOHandle, String volumeId, String iqn, String scsiId, TaskCompleter completer) { try { scaleIOHandle.unMapVolumeFromSCSIInitiator(volumeId, scsiId); } catch (Exception e) { String error = e.getMessage(); if (!error.contains(VOLUME_NOT_MAPPED_TO_SCSI)) { ServiceCoded code = DeviceControllerErrors.scaleio.unmapVolumeToClientFailed(volumeId, iqn, error); completer.error(dbClient, code); return false; } } return true; } /** * Given a collection of Initiators, go through and filter out any initiators * that are not ScaleIO or IP types. The passed in Collection will be modified. * * @param initiators * [in/out] - Collection of Initiator objects */ private void filterInitiators(Collection<Initiator> initiators) { Iterator<Initiator> initiatorIterator = initiators.iterator(); while (initiatorIterator.hasNext()) { Initiator initiator = initiatorIterator.next(); if (!initiator.getProtocol().equals(Initiator.Protocol.ScaleIO.name()) && !initiator.getProtocol().equals(Initiator.Protocol.iSCSI.name())) { initiatorIterator.remove(); } } } /** * Using the ExportMask object, create a volume URI to HLU map. For ScaleIO, * there isn't any HLU required * * @param exportMask * [in] - ExportMask object * @return Volume URI to HLU integer value (allows ExportGroup.LUN_UNASSIGNED) */ private Map<URI, Integer> createVolumeMapForExportMask(ExportMask exportMask) { Map<URI, Integer> map = new HashMap<>(); for (URI uri : ExportMaskUtils.getVolumeURIs(exportMask)) { map.put(uri, ExportGroup.LUN_UNASSIGNED); } return map; } private void updateConsistencyGroupsWithStorageSystem(Set<URI> consistencyGroups, StorageSystem storageSystem) { List<BlockConsistencyGroup> updateCGs = new ArrayList<>(); Iterator<BlockConsistencyGroup> consistencyGroupIterator = dbClient.queryIterativeObjects(BlockConsistencyGroup.class, consistencyGroups, true); while (consistencyGroupIterator.hasNext()) { BlockConsistencyGroup consistencyGroup = consistencyGroupIterator.next(); consistencyGroup.setStorageController(storageSystem.getId()); consistencyGroup.addConsistencyGroupTypes(BlockConsistencyGroup.Types.LOCAL.name()); consistencyGroup.addSystemConsistencyGroup(storageSystem.getId().toString(), consistencyGroup.getLabel()); updateCGs.add(consistencyGroup); } dbClient.updateAndReindexObject(updateCGs); } @Override public void doFractureClone(StorageSystem storageDevice, URI source, URI clone, TaskCompleter completer) { completeTaskAsUnsupported(completer); } @Override public void doRestoreFromClone(StorageSystem storage, URI cloneVolume, TaskCompleter taskCompleter) { completeTaskAsUnsupported(taskCompleter); } @Override public void doResyncClone(StorageSystem storage, URI cloneVolume, TaskCompleter taskCompleter) { completeTaskAsUnsupported(taskCompleter); } @Override public void doCreateGroupClone(StorageSystem storageDevice, List<URI> clones, Boolean createInactive, TaskCompleter completer) { cloneOperations.createGroupClone(storageDevice, clones, createInactive, completer); } @Override public void doDetachGroupClone(StorageSystem storage, List<URI> cloneVolume, TaskCompleter taskCompleter) { cloneOperations.detachGroupClones(storage, cloneVolume, taskCompleter); } @Override public void doRestoreFromGroupClone(StorageSystem storageSystem, List<URI> cloneVolume, TaskCompleter taskCompleter) { completeTaskAsUnsupported(taskCompleter); } @Override public void doActivateGroupFullCopy(StorageSystem storageSystem, List<URI> fullCopy, TaskCompleter completer) { completeTaskAsUnsupported(completer); } @Override public void doResyncGroupClone(StorageSystem storageDevice, List<URI> clone, TaskCompleter completer) throws Exception { completeTaskAsUnsupported(completer); } }