/* * Copyright (c) 2008-2011 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.smis; import static com.emc.storageos.volumecontroller.impl.ControllerUtils.checkSnapshotSessionConsistencyGroup; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.CP_INSTANCE_ID; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.CP_REPLICATION_GROUP; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.SYMM_SYNCHRONIZATION_ASPECT_FOR_SOURCE_GROUP; import static java.text.MessageFormat.format; 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 javax.cim.CIMArgument; import javax.cim.CIMInstance; import javax.cim.CIMObjectPath; import javax.cim.UnsignedInteger16; import javax.wbem.CloseableIterator; import javax.wbem.WBEMException; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.URIUtil; import com.emc.storageos.db.client.constraint.AlternateIdConstraint; import com.emc.storageos.db.client.constraint.ContainmentConstraint; import com.emc.storageos.db.client.constraint.PrefixConstraint; import com.emc.storageos.db.client.constraint.URIQueryResultList; import com.emc.storageos.db.client.model.BlockConsistencyGroup; import com.emc.storageos.db.client.model.BlockConsistencyGroup.Types; import com.emc.storageos.db.client.model.BlockMirror; import com.emc.storageos.db.client.model.BlockObject; import com.emc.storageos.db.client.model.BlockSnapshot; import com.emc.storageos.db.client.model.BlockSnapshotSession; import com.emc.storageos.db.client.model.DiscoveredDataObject; import com.emc.storageos.db.client.model.DiscoveredDataObject.Type; import com.emc.storageos.db.client.model.ExportMask; import com.emc.storageos.db.client.model.Initiator; import com.emc.storageos.db.client.model.NamedURI; import com.emc.storageos.db.client.model.Operation; import com.emc.storageos.db.client.model.RemoteDirectorGroup; 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.TenantOrg; import com.emc.storageos.db.client.model.VirtualPool; import com.emc.storageos.db.client.model.Volume; import com.emc.storageos.db.client.model.Volume.PersonalityTypes; import com.emc.storageos.db.client.model.Volume.ReplicationState; import com.emc.storageos.db.client.model.util.BlockConsistencyGroupUtils; import com.emc.storageos.db.client.util.CustomQueryUtility; import com.emc.storageos.db.client.util.NameGenerator; 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.plugins.common.Constants; import com.emc.storageos.protectioncontroller.impl.recoverpoint.RPHelper; import com.emc.storageos.svcs.errorhandling.model.ServiceError; import com.emc.storageos.svcs.errorhandling.resources.InternalException; import com.emc.storageos.util.ExportUtils; import com.emc.storageos.volumecontroller.CloneOperations; import com.emc.storageos.volumecontroller.DefaultBlockStorageDevice; import com.emc.storageos.volumecontroller.Job; import com.emc.storageos.volumecontroller.MetaVolumeOperations; import com.emc.storageos.volumecontroller.ReplicaOperations; import com.emc.storageos.volumecontroller.SnapshotOperations; import com.emc.storageos.volumecontroller.TaskCompleter; import com.emc.storageos.volumecontroller.impl.BiosCommandResult; import com.emc.storageos.volumecontroller.impl.ControllerServiceImpl; import com.emc.storageos.volumecontroller.impl.ControllerUtils; import com.emc.storageos.volumecontroller.impl.VolumeURIHLU; import com.emc.storageos.volumecontroller.impl.block.ExportMaskPolicy; import com.emc.storageos.volumecontroller.impl.block.taskcompleter.CleanupMetaVolumeMembersCompleter; import com.emc.storageos.volumecontroller.impl.block.taskcompleter.MetaVolumeTaskCompleter; import com.emc.storageos.volumecontroller.impl.block.taskcompleter.MultiVolumeTaskCompleter; import com.emc.storageos.volumecontroller.impl.block.taskcompleter.SRDFMirrorCreateCompleter; import com.emc.storageos.volumecontroller.impl.block.taskcompleter.VolumeCreateCompleter; import com.emc.storageos.volumecontroller.impl.block.taskcompleter.VolumeExpandCompleter; import com.emc.storageos.volumecontroller.impl.block.taskcompleter.VolumeTaskCompleter; import com.emc.storageos.volumecontroller.impl.job.QueueJob; import com.emc.storageos.volumecontroller.impl.providerfinders.FindProviderFactory; import com.emc.storageos.volumecontroller.impl.smis.job.SmisCleanupMetaVolumeMembersJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisCreateMultiVolumeJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisCreateVolumeJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisDeleteVolumeJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisVolumeExpandJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisWaitForGroupSynchronizedJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisWaitForSynchronizedJob; import com.emc.storageos.volumecontroller.impl.utils.ConsistencyGroupUtils; import com.emc.storageos.volumecontroller.impl.utils.VirtualPoolCapabilityValuesWrapper; import com.google.common.base.Joiner; import com.google.common.collect.Lists; /** * SMI-S specific block controller implementation. */ public class SmisStorageDevice extends DefaultBlockStorageDevice { private static final Logger _log = LoggerFactory.getLogger(SmisStorageDevice.class); private static final BiosCommandResult _ok = new BiosCommandResult(true, Operation.Status.ready.name(), ""); private static final BiosCommandResult _err = new BiosCommandResult(false, Operation.Status.error.name(), ""); private DbClient _dbClient; protected SmisCommandHelper _helper; private ExportMaskOperations _exportMaskOperationsHelper; private CIMObjectPathFactory _cimPath; private SnapshotOperations _snapshotOperations; private MirrorOperations _mirrorOperations; private CloneOperations _cloneOperations; private ReplicaOperations _replicaOperations; private NameGenerator _nameGenerator; private MetaVolumeOperations _metaVolumeOperations; private SRDFOperations _srdfOperations; private SmisStorageDevicePreProcessor _smisStorageDevicePreProcessor; private FindProviderFactory findProviderFactory; public void setCimObjectPathFactory(final CIMObjectPathFactory cimObjectPathFactory) { _cimPath = cimObjectPathFactory; } public void setDbClient(final DbClient dbClient) { _dbClient = dbClient; } public void setSmisCommandHelper(final SmisCommandHelper smisCommandHelper) { _helper = smisCommandHelper; } public void setExportMaskOperationsHelper(final ExportMaskOperations exportMaskOperationsHelper) { _exportMaskOperationsHelper = exportMaskOperationsHelper; } public void setSnapshotOperations(final SnapshotOperations snapshotOperations) { _snapshotOperations = snapshotOperations; } public void setMirrorOperations(final MirrorOperations mirrorOperations) { _mirrorOperations = mirrorOperations; } public void setCloneOperations(final CloneOperations cloneOperations) { _cloneOperations = cloneOperations; } public void setReplicaOperations(final ReplicaOperations replicaOperations) { _replicaOperations = replicaOperations; } public void setNameGenerator(final NameGenerator nameGenerator) { _nameGenerator = nameGenerator; } public void setMetaVolumeOperations(final MetaVolumeOperations metaVolumeOperations) { _metaVolumeOperations = metaVolumeOperations; } public void setSrdfOperations(final SRDFOperations srdfOperations) { _srdfOperations = srdfOperations; } public void setSmisStorageDevicePreProcessor( final SmisStorageDevicePreProcessor smisStorageDevicePreProcessor) { _smisStorageDevicePreProcessor = smisStorageDevicePreProcessor; } public void setFindProviderFactory(final FindProviderFactory findProviderFactory) { this.findProviderFactory = findProviderFactory; } @Override public void doCreateVolumes(final StorageSystem storageSystem, final StoragePool storagePool, final String opId, final List<Volume> volumes, final VirtualPoolCapabilityValuesWrapper capabilities, final TaskCompleter taskCompleter) throws DeviceControllerException { String label = null; Long capacity = null; Long thinVolumePreAllocationSize = null; CIMInstance poolSetting = null; boolean opCreationFailed = false; StringBuilder logMsgBuilder = new StringBuilder(String.format( "Create Volume Start - Array:%s, Pool:%s", storageSystem.getSerialNumber(), storagePool.getNativeGuid())); StorageSystem forProvider = _helper.getStorageSystemForProvider(storageSystem, volumes.get(0)); // volumeGroupObjectPath is required for VMAX3 CIMObjectPath volumeGroupObjectPath = _helper.getVolumeGroupPath(forProvider, storageSystem, volumes.get(0), storagePool); List<String> volumeLabels = new ArrayList<>(); for (Volume volume : volumes) { logMsgBuilder.append(String.format("%nVolume:%s , IsThinlyProvisioned: %s", volume.getLabel(), volume.getThinlyProvisioned())); String tenantName = ""; try { TenantOrg tenant = _dbClient.queryObject(TenantOrg.class, volume.getTenant() .getURI()); tenantName = tenant.getLabel(); } catch (DatabaseException e) { _log.error("Error lookup TenantOrb object", e); } label = _nameGenerator.generate(tenantName, volume.getLabel(), volume.getId() .toString(), '-', SmisConstants.MAX_VOLUME_NAME_LENGTH); volumeLabels.add(label); if (capacity == null) { capacity = volume.getCapacity(); } if (thinVolumePreAllocationSize == null && volume.getThinVolumePreAllocationSize() > 0) { thinVolumePreAllocationSize = volume.getThinVolumePreAllocationSize(); } } _log.info(logMsgBuilder.toString()); boolean isThinlyProvisioned = volumes.get(0).getThinlyProvisioned(); try { CIMObjectPath configSvcPath = _cimPath.getConfigSvcPath(storageSystem); CIMArgument[] inArgs = null; // only for vnxBlock, we need to associate StoragePool Setting as Goal // I didn't find any ways to add this branching logic based on device Types. if (DiscoveredDataObject.Type.vnxblock.toString().equalsIgnoreCase( storageSystem.getSystemType())) { String autoTierPolicyName = ControllerUtils.getAutoTieringPolicyName(volumes.get(0) .getId(), _dbClient); if (autoTierPolicyName.equals(Constants.NONE)) { autoTierPolicyName = null; } inArgs = _helper.getCreateVolumesInputArgumentsOnFastEnabledPool(storageSystem, storagePool, volumeLabels, capacity, volumes.size(), isThinlyProvisioned, autoTierPolicyName); } else { if (!storageSystem.checkIfVmax3() && isThinlyProvisioned && null != thinVolumePreAllocationSize) { poolSetting = _smisStorageDevicePreProcessor.createStoragePoolSetting( storageSystem, storagePool, thinVolumePreAllocationSize); } if (storageSystem.checkIfVmax3() && volumeGroupObjectPath != null) { inArgs = _helper.getCreateVolumesInputArguments(storageSystem, storagePool, volumeLabels, capacity, volumes.size(), isThinlyProvisioned, true, volumeGroupObjectPath, (null != thinVolumePreAllocationSize)); } else { inArgs = _helper.getCreateVolumesInputArguments(storageSystem, storagePool, volumeLabels, capacity, volumes.size(), isThinlyProvisioned, poolSetting, true); } } CIMArgument[] outArgs = new CIMArgument[5]; _helper.invokeMethod(forProvider, configSvcPath, _helper.createVolumesMethodName(forProvider), inArgs, outArgs); CIMObjectPath job = _cimPath.getCimObjectPathFromOutputArgs(outArgs, SmisConstants.JOB); if (job != null) { SmisJob createSmisJob = volumes.size() > 1 ? new SmisCreateMultiVolumeJob(job, forProvider.getId(), storagePool.getId(), volumes.size(), taskCompleter) : new SmisCreateVolumeJob(job, forProvider.getId(), storagePool.getId(), taskCompleter); ControllerServiceImpl.enqueueJob(new QueueJob(createSmisJob)); } } catch (final InternalException e) { _log.error("Problem in doCreateVolumes: ", e); opCreationFailed = true; taskCompleter.error(_dbClient, e); } catch (WBEMException e) { _log.error("Problem making SMI-S call: ", e); opCreationFailed = true; ServiceError serviceError = DeviceControllerErrors.smis.unableToCallStorageProvider(e .getMessage()); taskCompleter.error(_dbClient, serviceError); } catch (Exception e) { _log.error("Problem in doCreateVolumes: ", e); opCreationFailed = true; ServiceError serviceError = DeviceControllerErrors.smis.methodFailed("doCreateVolumes", e.getMessage()); taskCompleter.error(_dbClient, serviceError); } if (opCreationFailed) { for (Volume vol : volumes) { vol.setInactive(true); _dbClient.persistObject(vol); } } logMsgBuilder = new StringBuilder(String.format("Create Volumes End - Array:%s, Pool:%s", storageSystem.getSerialNumber(), storagePool.getNativeGuid())); for (Volume volume : volumes) { logMsgBuilder.append(String.format("%nVolume:%s", volume.getLabel())); } _log.info(logMsgBuilder.toString()); } @Override public void doCreateMetaVolumes(final StorageSystem storageSystem, final StoragePool storagePool, List<Volume> volumes, final VirtualPoolCapabilityValuesWrapper capabilities, final MetaVolumeRecommendation recommendation, final TaskCompleter taskCompleter) throws DeviceControllerException { StringBuilder logMsgBuilder = new StringBuilder(String.format( "Create Meta Volumes Start - Array:%s, Pool:%s %n", storageSystem.getSerialNumber(), storagePool.getNativeId())); StringBuilder volumesMsg = new StringBuilder(); for (Volume volume : volumes) { volumesMsg.append(String.format(" Volume: %s, id: %s %n", volume.getLabel(), volume.getId())); } _log.info(logMsgBuilder.toString() + volumesMsg.toString()); try { // Create meta volumes _metaVolumeOperations.createMetaVolumes(storageSystem, storagePool, volumes, capabilities, taskCompleter); } catch (Exception e) { _log.error( "Problem in doCreateMetaVolumes: failed to create meta volumes: " + volumesMsg.toString(), e); } // Get updated volumes ( we need to know their nativeId) volumesMsg = new StringBuilder(); for (Volume volume : volumes) { volume = _dbClient.queryObject(Volume.class, volume.getId()); volumesMsg.append(String.format(" Volume: %s, id: %s, nativeID: %s", volume.getLabel(), volume.getNativeId(), volume.getNativeId())); } logMsgBuilder = new StringBuilder(String.format( "Create Meta Volume End - Array:%s, Pool:%s%n", storageSystem.getSerialNumber(), storagePool.getNativeId())); _log.info(logMsgBuilder.toString() + volumesMsg.toString()); } @Override public void doCreateMetaVolume(final StorageSystem storageSystem, final StoragePool storagePool, Volume volume, final VirtualPoolCapabilityValuesWrapper capabilities, final MetaVolumeRecommendation recommendation, final VolumeCreateCompleter taskCompleter) throws DeviceControllerException { StringBuilder logMsgBuilder = new StringBuilder(String.format( "Create Meta Volume Start - Array:%s, Pool:%s %n Volume: %s, id: %s", storageSystem.getSerialNumber(), storagePool.getNativeId(), volume.getLabel(), volume.getId())); _log.info(logMsgBuilder.toString()); boolean isThinlyProvisioned = volume.getThinlyProvisioned(); String metaVolumeType = volume.getCompositionType(); long metaMemberCapacity = volume.getMetaMemberSize(); int metaMemberCount = volume.getMetaMemberCount(); MetaVolumeTaskCompleter metaVolumeTaskCompleter = new MetaVolumeTaskCompleter(taskCompleter); _log.info(String.format("Start of steps to create meta volume: %s, \n volume ID: %s" + "\n type: %s, member count: %s, member size: %s. isThinlyProvisioned: %s", volume.getLabel(), volume.getId(), metaVolumeType, metaMemberCount, metaMemberCapacity, isThinlyProvisioned)); try { // Step 1: Create meta volume head // Create meta volume head as bound to pool _metaVolumeOperations.createMetaVolumeHead(storageSystem, storagePool, volume, metaMemberCapacity, capabilities, metaVolumeTaskCompleter); // Step 2: Create meta members // Create members as unbound to pool (SMI-S requirement) List<String> metaMembers = null; if (metaVolumeTaskCompleter.getLastStepStatus() == Job.JobStatus.SUCCESS) { metaMembers = _metaVolumeOperations.createMetaVolumeMembers(storageSystem, storagePool, volume, metaMemberCount - 1, metaMemberCapacity, metaVolumeTaskCompleter); } // Step 3: Create meta volume from the head and meta members if (metaVolumeTaskCompleter.getLastStepStatus() == Job.JobStatus.SUCCESS) { // Get updated volume ( we need to know its nativeId) which was set in Step 1. Volume metaHead = _dbClient.queryObject(Volume.class, volume.getId()); _metaVolumeOperations.createMetaVolume(storageSystem, storagePool, metaHead, metaMembers, metaVolumeType, capabilities, metaVolumeTaskCompleter); } } catch (Exception e) { _log.error( "Problem in doCreateMetaVolume: failed to create meta volume " + volume.getLabel() + " .", e); } finally { _log.info(String.format("End of steps to create meta volume: %s, \n volume ID: %s" + "\n type: %s, member count: %s, member size: %s. isThinlyProvisioned: %s", volume.getLabel(), volume.getId(), metaVolumeType, metaMemberCount, metaMemberCapacity, isThinlyProvisioned)); } // Get updated volume ( we need to know its nativeId) which was set in Step 1. volume = _dbClient.queryObject(Volume.class, volume.getId()); logMsgBuilder = new StringBuilder(String.format( "Create Meta Volume End - Array:%s, Pool:%s%n Volume: %s, id: %s, nativeID: %s", storageSystem.getSerialNumber(), storagePool.getNativeId(), volume.getLabel(), volume.getId(), volume.getNativeId())); _log.info(logMsgBuilder.toString()); } @Override public void doExpandAsMetaVolume(final StorageSystem storageSystem, final StoragePool storagePool, final Volume volume, final long size, final MetaVolumeRecommendation recommendation, VolumeExpandCompleter volumeCompleter) { // To expand a volume as meta volume we need to execute sequence of two SMI-S requests. // First, we need to create required number of meta members to supply capacity. // Second step depends if input volume is a meta volume or a regular volume. // If input volume is a regular volume, we need to create a new meta volume with input // volume as its meta head. // If input volume is already a meta volume, we need to add new meta members to this volume. StringBuilder logMsgBuilder = new StringBuilder(String.format( "Expand Meta Volume Start - Array:%s, Pool:%s %n Volume: %s, id: %s", storageSystem.getSerialNumber(), storagePool.getNativeId(), volume.getLabel(), volume.getId())); _log.info(logMsgBuilder.toString()); String recommendedMetaVolumeType = recommendation.getMetaVolumeType().toString(); String expansionType = null; long metaMemberCapacity = recommendation.getMetaMemberSize(); int metaMemberCount = (int) recommendation.getMetaMemberCount(); MetaVolumeTaskCompleter metaVolumeTaskCompleter = new MetaVolumeTaskCompleter( volumeCompleter); try { boolean tagSet = _helper.doApplyRecoverPointTag(storageSystem, volume, false); if (!tagSet) { TaskCompleter taskCompleter = metaVolumeTaskCompleter.getVolumeTaskCompleter(); ServiceError error = DeviceControllerErrors.smis.errorSettingRecoverPointTag("disable"); taskCompleter.error(_dbClient, error); return; } // First of all check if we need to do cleanup of dangling meta volumes left from previous failed // expand attempt (may happen when rollback of expand failed due to smis connection issues -- typically // cleanup // is done by expand rollback) boolean cleanupSuccess = cleanupDanglingMetaMembers(storageSystem, volume); if (!cleanupSuccess) { // Failed to cleanup dangling meta members: probably still smis issues. Do not expand at this time. String errorMessage = String.format("Failed to delete meta volume: %s , nativeId: %s . \n" + " Could not cleanup dangling meta members.", volume.getId(), volume.getNativeId()); ServiceError error = DeviceControllerErrors.smis.methodFailed("doExpandAsMetaVolume", errorMessage); TaskCompleter taskCompleter = metaVolumeTaskCompleter.getVolumeTaskCompleter(); taskCompleter.error(_dbClient, error); _log.error(String.format(errorMessage)); return; } // Check if this is zero-capacity extension to cleanup dangling meta members. if (size == volume.getCapacity()) { // This is zero-capacity expansion executed as recovery to cleanup dangling meta members from previous // expand failure _log.info(String.format( "Zero-capacity expansion completed. Array: %s Pool:%s Volume:%s, Capacity: %s ", storageSystem.getId(), storagePool.getId(), volume.getId(), volume.getCapacity())); TaskCompleter taskCompleter = metaVolumeTaskCompleter.getVolumeTaskCompleter(); taskCompleter.ready(_dbClient); return; } // Check if this is expansion within current total capacity of meta members if (recommendation.getMetaMemberCount() == 0) { volume.setCapacity(size); _dbClient.persistObject(volume); _log.info(String.format( "Expanded volume within its total meta volume capacity (simple case) - Array: %s Pool:%s, \n" + " Volume: %s, IsMetaVolume: %s, Total meta volume capacity: %s, NewSize: %s", storageSystem.getId(), storagePool.getId(), volume.getId(), volume.getIsComposite(), volume.getTotalMetaMemberCapacity(), volume.getCapacity())); TaskCompleter taskCompleter = metaVolumeTaskCompleter.getVolumeTaskCompleter(); taskCompleter.ready(_dbClient); return; } // Check if we can expand volume using recommended meta volume type: // On VMAX striped meta can be formed only when meta head is in unbound from pool. // This is our assumption for now --- some ucode versions support case when meta head is bound to pool when // striped meta volume // is formed. expansionType = _metaVolumeOperations.defineExpansionType(storageSystem, volume, recommendedMetaVolumeType, metaVolumeTaskCompleter); _log.info(String .format("Meta volume type used for expansion: %s, recommended meta volume type: %s", expansionType, recommendedMetaVolumeType)); // update expansion type in completer volumeCompleter.setMetaVolumeType(expansionType); _log.info(String .format("Start of steps to expand volume as meta volume: %s, \n volume ID: %s" + "\n expansion type: %s, new member count: %s, member size: %s, is already meta volume: %s .", volume.getLabel(), volume.getId(), expansionType, metaMemberCount, metaMemberCapacity, volume.getIsComposite())); // Step 1: Create new meta members // Create members as unbound to pool (SMI-S requirement) List<String> metaMembers = null; metaMembers = _metaVolumeOperations.createMetaVolumeMembers(storageSystem, storagePool, volume, metaMemberCount, metaMemberCapacity, metaVolumeTaskCompleter); if (metaVolumeTaskCompleter.getLastStepStatus() == Job.JobStatus.SUCCESS) { if (volume.getIsComposite()) { // Step 2: Expand meta volume with meta members used for expansion _metaVolumeOperations.expandMetaVolume(storageSystem, storagePool, volume, metaMembers, metaVolumeTaskCompleter); // Step 3: Delete BCV helper volume from array. Required only for vmax. if (expansionType.equals(Volume.CompositionType.STRIPED.toString()) && metaVolumeTaskCompleter.getLastStepStatus() == Job.JobStatus.SUCCESS && storageSystem.getSystemType().equalsIgnoreCase(DiscoveredDataObject.Type.vmax.toString())) { _metaVolumeOperations.deleteBCVHelperVolume(storageSystem, volume); } } else { // Step 2: Create meta volume from the original volume (head) and meta // members used for expansion _metaVolumeOperations.expandVolumeAsMetaVolume(storageSystem, storagePool, volume, metaMembers, expansionType, metaVolumeTaskCompleter); } } } catch (Exception e) { _log.error( "Problem in doExpandMetaVolumes: failed to expand meta volume " + volume.getLabel() + " .", e); ServiceError error = DeviceControllerErrors.smis.methodFailed("doExpandAsMetaVolume", e.getMessage()); volumeCompleter.error(_dbClient, error); } finally { _log.info(String.format( "End of steps to expand volume as meta volume: %s, \n volume ID: %s" + "\n type: %s, new member count: %s, member size: %s.", volume.getLabel(), volume.getId(), expansionType, metaMemberCount, metaMemberCapacity)); } logMsgBuilder = new StringBuilder(String.format( "Expand Volume End - Array:%s, Pool:%s%n Volume: %s, id: %s", storageSystem.getSerialNumber(), storagePool.getNativeId(), volume.getLabel(), volume.getId())); _log.info(logMsgBuilder.toString()); } @Override public void doExpandVolume(final StorageSystem storageSystem, final StoragePool pool, final Volume volume, final Long size, final TaskCompleter taskCompleter) throws DeviceControllerException { _log.info(String.format( "Expand Volume Start - Array: %s, Pool: %s, Volume: %s, New size: %d", storageSystem.getSerialNumber(), pool.getNativeGuid(), volume.getLabel(), size)); MetaVolumeTaskCompleter metaVolumeTaskCompleter = new MetaVolumeTaskCompleter( taskCompleter); try { if (!doesStorageSystemSupportVolumeExpand(storageSystem)) { ServiceError error = DeviceControllerErrors.smis.volumeExpandIsNotSupported(storageSystem.getNativeGuid()); taskCompleter.error(_dbClient, error); return; } boolean tagSet = _helper.doApplyRecoverPointTag(storageSystem, volume, false); if (!tagSet) { ServiceError error = DeviceControllerErrors.smis.errorSettingRecoverPointTag("disable"); taskCompleter.error(_dbClient, error); return; } CIMObjectPath configSvcPath = _cimPath.getConfigSvcPath(storageSystem); CIMArgument[] inArgs = _helper.getExpandVolumeInputArguments(storageSystem, pool, volume, size); CIMArgument[] outArgs = new CIMArgument[5]; _helper.invokeMethod(storageSystem, configSvcPath, SmisConstants.CREATE_OR_MODIFY_ELEMENT_FROM_STORAGE_POOL, inArgs, outArgs); CIMObjectPath job = _cimPath.getCimObjectPathFromOutputArgs(outArgs, SmisConstants.JOB); if (job != null) { ControllerServiceImpl.enqueueJob(new QueueJob(new SmisVolumeExpandJob(job, storageSystem .getId(), pool.getId(), metaVolumeTaskCompleter, "ExpandVolume"))); } } catch (WBEMException e) { _log.error("Problem making SMI-S call: ", e); ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e .getMessage()); taskCompleter.error(_dbClient, error); } catch (Exception e) { _log.error("Problem in doExpandVolume: ", e); ServiceError error = DeviceControllerErrors.smis.methodFailed("doExpandVolume", e.getMessage()); taskCompleter.error(_dbClient, error); } _log.info(String.format("Expand Volume End - Array: %s, Pool: %s, Volume: %s", storageSystem.getSerialNumber(), pool.getNativeGuid(), volume.getLabel())); } @Override public void doDeleteVolumes(final StorageSystem storageSystem, final String opId, final List<Volume> volumes, final TaskCompleter taskCompleter) throws DeviceControllerException { try { List<String> volumeNativeIds = new ArrayList<String>(); StringBuilder logMsgBuilder = new StringBuilder(String.format( "Delete Volume Start - Array:%s", storageSystem.getSerialNumber())); MultiVolumeTaskCompleter multiVolumeTaskCompleter = (MultiVolumeTaskCompleter) taskCompleter; Set<CIMInstance> parkingSLOStorageGroups = new HashSet<>(); Set<Volume> cloneVolumes = new HashSet<Volume>(); _helper.callRefreshSystem(storageSystem, null, false); for (Volume volume : volumes) { logMsgBuilder.append(String.format("%nVolume:%s", volume.getLabel())); if (storageSystem.checkIfVmax3()) { // Flag to indicate whether or not we need to use the EMCForce flag on this operation. // We currently use this flag when dealing with RP Volumes as they are tagged for RP and the // operation on these volumes would fail otherwise. boolean forceFlag = ExportUtils.useEMCForceFlag(_dbClient, volume.getId()); CIMInstance sloStorageGroup = _helper.removeVolumeFromParkingSLOStorageGroup(storageSystem, volume.getNativeId(), forceFlag); if (sloStorageGroup != null) { parkingSLOStorageGroups.add(sloStorageGroup); } _log.info("Done invoking remove volume from storage group"); } if (volume.getConsistencyGroup() != null || NullColumnValueGetter.isNotNullValue(volume.getReplicationGroupInstance())) { _log.info(String.format("Volume [%s](%s) is a part of CG (%s), extra cleanup operations may be needed.", volume.getLabel(), volume.getId(), volume.getConsistencyGroup())); // Clean up any group backup snapshots (VNX only), if there are none this step will be skipped. if (storageSystem.deviceIsType(Type.vnxblock)) { cleanupAnyGroupBackupSnapshots(storageSystem, volume); } // Remove the volume from the backend CG, if it's not actually in a backend CG this step will be skipped. removeVolumeFromConsistencyGroup(storageSystem, volume); // Clean up any volume backup snapshots, if there are none this step will be skipped. cleanupAnyBackupSnapshots(storageSystem, volume); } else { // for VMAX3, clean up unlinked snapshot session, which is possible for ingested volume if (storageSystem.deviceIsType(Type.vnxblock) || storageSystem.checkIfVmax3()) { cleanupAnyBackupSnapshots(storageSystem, volume); } } if (storageSystem.deviceIsType(Type.vmax)) { // VMAX2 & VMAX3 - remove volume from Storage Groups if volume is not in any MaskingView // COP-16705, COP-21770 - Ingested non-exported Volume may be associated with SG outside of ViPR. _helper.removeVolumeFromStorageGroupsIfVolumeIsNotInAnyMV(storageSystem, volume); } CIMInstance volumeInstance = _helper.checkExists(storageSystem, _cimPath.getBlockObjectPath(storageSystem, volume), false, false); _helper.doApplyRecoverPointTag(storageSystem, volume, false); if (volumeInstance == null) { // related volume state (if any) has been deleted. skip processing, if already // deleted from array. _log.info(String.format("Volume %s already deleted: ", volume.getNativeId())); volume.setInactive(true); _dbClient.updateObject(volume); VolumeTaskCompleter deleteTaskCompleter = multiVolumeTaskCompleter .skipTaskCompleter(volume.getId()); deleteTaskCompleter.ready(_dbClient); continue; } // Compare the volume labels of the to-be-deleted and existing volumes /** * This will fail in the case when the user just changes the label of the * volume...until we subscribe to indications from the provider, we will live with * that. */ String volToDeleteLabel = volume.getDeviceLabel(); String volInstanceLabel = CIMPropertyFactory.getPropertyValue(volumeInstance, SmisConstants.CP_ELEMENT_NAME); if (volToDeleteLabel != null && volInstanceLabel != null && !volToDeleteLabel.equals(volInstanceLabel)) { // related volume state (if any) has been deleted. skip processing, if already // deleted from array. _log.info("VolToDeleteLabel {} : volInstancelabel {}", volToDeleteLabel, volInstanceLabel); _log.info(String.format("Volume %s already deleted: ", volume.getNativeId())); volume.setInactive(true); // clear the associated consistency group from the volume volume.setConsistencyGroup(NullColumnValueGetter.getNullURI()); _dbClient.updateObject(volume); VolumeTaskCompleter deleteTaskCompleter = multiVolumeTaskCompleter .skipTaskCompleter(volume.getId()); deleteTaskCompleter.ready(_dbClient); continue; } // Check if this volume has any dangling meta members on array. May not necessary be meta volume. // Regular volume can have // dangling meta members as a result of expansion failure (and rollback failure). if (!storageSystem.checkIfVmax3()) { boolean cleanupSuccess = cleanupDanglingMetaMembers(storageSystem, volume); if (!cleanupSuccess) { // cannot delete volume String errorMessage = String.format("Failed to delete meta volume: %s , nativeId: %s .\n" + "Could not cleanup dangling meta members.", volume.getId(), volume.getNativeId()); ServiceError error = DeviceControllerErrors.smis.methodFailed("doDeleteVolume", errorMessage); VolumeTaskCompleter deleteTaskCompleter = multiVolumeTaskCompleter.skipTaskCompleter(volume.getId()); deleteTaskCompleter.error(_dbClient, error); _log.error(String.format(errorMessage)); continue; } } if (!NullColumnValueGetter.isNullURI(volume.getAssociatedSourceVolume())) { cloneVolumes.add(volume); } volumeNativeIds.add(volume.getNativeId()); } _log.info(logMsgBuilder.toString()); // VMAX3 has parking SLO storage groups that the volumes will be removed from // prior to the deletion. We need to check any of these SLOs StorageGroups to // see if they are empty. If so, we will delete them as part of the volume // delete operation. if (!parkingSLOStorageGroups.isEmpty()) { _helper.deleteParkingSLOStorageGroupsIfEmpty(storageSystem, parkingSLOStorageGroups); } // execute SMI-S Call , only if any Volumes left for deletion. if (!multiVolumeTaskCompleter.isVolumeTaskCompletersEmpty()) { if (!cloneVolumes.isEmpty()) { processClonesBeforeDeletion(storageSystem, cloneVolumes); } CIMObjectPath configSvcPath = _cimPath.getConfigSvcPath(storageSystem); CIMArgument[] inArgs = _helper.getDeleteVolumesInputArguments(storageSystem, volumeNativeIds.toArray(new String[0])); CIMArgument[] outArgs = new CIMArgument[5]; String returnElementsMethod; if (storageSystem.getUsingSmis80()) { returnElementsMethod = SmisConstants.RETURN_ELEMENTS_TO_STORAGE_POOL; } else { returnElementsMethod = SmisConstants.EMC_RETURN_TO_STORAGE_POOL; } _helper.invokeMethod(storageSystem, configSvcPath, returnElementsMethod, inArgs, outArgs); CIMObjectPath job = _cimPath.getCimObjectPathFromOutputArgs(outArgs, SmisConstants.JOB); if (job != null) { ControllerServiceImpl.enqueueJob(new QueueJob(new SmisDeleteVolumeJob(job, storageSystem.getId(), taskCompleter))); } } else { // If we are here, there are no volumes to delete, we have // invoked ready() for the VolumeDeleteCompleter, and told // the multiVolumeTaskCompleter to skip these completers. // In this case, the multiVolumeTaskCompleter complete() // method will not be invoked and the result is that the // workflow that initiated this delete request will never // be updated. So, here we just call complete() on the // multiVolumeTaskCompleter to ensure the workflow status is // updated. multiVolumeTaskCompleter.ready(_dbClient); } } catch (WBEMException e) { _log.error("Problem making SMI-S call: ", e); ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e .getMessage()); taskCompleter.error(_dbClient, error); } catch (Exception e) { _log.error("Problem in doDeleteVolume: ", e); // Check to see if an Asynchronous job will now handle the task status. if (!taskCompleter.isAsynchronous()) { ServiceError error = DeviceControllerErrors.smis.methodFailed("doDeleteVolume", e.getMessage()); taskCompleter.error(_dbClient, error); } } StringBuilder logMsgBuilder = new StringBuilder(String.format( "Delete Volume End - Array: %s", storageSystem.getSerialNumber())); for (Volume volume : volumes) { logMsgBuilder.append(String.format("%nVolume:%s", volume.getLabel())); } _log.info(logMsgBuilder.toString()); } @Override public void doExportCreate(final StorageSystem storage, final ExportMask exportMask, final Map<URI, Integer> volumeMap, final List<Initiator> initiators, final List<URI> targets, final TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("{} doExportCreate START ...", storage.getSerialNumber()); VolumeURIHLU[] volumeLunArray = ControllerUtils.getVolumeURIHLUArray( storage.getSystemType(), volumeMap, _dbClient); _exportMaskOperationsHelper.createExportMask(storage, exportMask.getId(), volumeLunArray, targets, initiators, taskCompleter); _log.info("{} doExportCreate END ...", storage.getSerialNumber()); } @Override public void doExportDelete(final StorageSystem storage, final ExportMask exportMask, List<URI> volumeURIs, List<URI> initiatorURIs, final TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("{} doExportDelete START ...", storage.getSerialNumber()); List<Initiator> initiators = Lists.newArrayList(); if (initiatorURIs != null) { initiators.addAll(_dbClient.queryObject(Initiator.class, initiatorURIs)); } List<URI> volURIs = Lists.newArrayList(); if (volumeURIs != null) { volURIs.addAll(volumeURIs); } _exportMaskOperationsHelper.deleteExportMask(storage, exportMask.getId(), volURIs, new ArrayList<URI>(), initiators, taskCompleter); _log.info("{} doExportDelete END ...", storage.getSerialNumber()); } @Override public void doExportAddVolume(final StorageSystem storage, final ExportMask exportMask, final URI volume, final Integer lun, List<Initiator> initiators, final TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("{} doExportAddVolume START ...", storage.getSerialNumber()); Map<URI, Integer> map = new HashMap<URI, Integer>(); map.put(volume, lun); VolumeURIHLU[] volumeLunArray = ControllerUtils.getVolumeURIHLUArray( storage.getSystemType(), map, _dbClient); _exportMaskOperationsHelper.addVolumes(storage, exportMask.getId(), volumeLunArray, initiators, taskCompleter); _log.info("{} doExportAddVolume END ...", storage.getSerialNumber()); } @Override public void doExportAddVolumes(final StorageSystem storage, final ExportMask exportMask, List<Initiator> initiators, final Map<URI, Integer> volumes, final TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("{} doExportAddVolumes START ...", storage.getSerialNumber()); VolumeURIHLU[] volumeLunArray = ControllerUtils.getVolumeURIHLUArray( storage.getSystemType(), volumes, _dbClient); _exportMaskOperationsHelper.addVolumes(storage, exportMask.getId(), volumeLunArray, initiators, taskCompleter); _log.info("{} doExportAddVolumes END ...", storage.getSerialNumber()); } @Override public void doExportRemoveVolume(final StorageSystem storage, final ExportMask exportMask, final URI volume, List<Initiator> initiators, final TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("{} doExportRemoveVolume START ...", storage.getSerialNumber()); _exportMaskOperationsHelper.removeVolumes(storage, exportMask.getId(), Arrays.asList(volume), initiators, taskCompleter); _log.info("{} doExportRemoveVolume END ...", storage.getSerialNumber()); } @Override public void doExportRemoveVolumes(final StorageSystem storage, final ExportMask exportMask, final List<URI> volumes, List<Initiator> initiators, final TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("{} doExportRemoveVolume START ...", storage.getSerialNumber()); _exportMaskOperationsHelper.removeVolumes(storage, exportMask.getId(), volumes, initiators, taskCompleter); _log.info("{} doExportRemoveVolume END ...", storage.getSerialNumber()); } @Override public void doExportAddInitiator(final StorageSystem storage, final ExportMask exportMask, List<URI> volumeURIs, final Initiator initiator, final List<URI> targets, final TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("{} doExportAddInitiator START ...", storage.getSerialNumber()); _exportMaskOperationsHelper.addInitiators(storage, exportMask.getId(), volumeURIs, Arrays.asList(initiator), targets, taskCompleter); _log.info("{} doExportAddInitiator END ...", storage.getSerialNumber()); } @Override public void doExportAddInitiators(final StorageSystem storage, final ExportMask exportMask, List<URI> volumeURIs, final List<Initiator> initiators, final List<URI> targets, final TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("{} doExportAddInitiator START ...", storage.getSerialNumber()); _exportMaskOperationsHelper.addInitiators(storage, exportMask.getId(), volumeURIs, initiators, targets, taskCompleter); _log.info("{} doExportAddInitiator END ...", storage.getSerialNumber()); } @Override public void doExportRemoveInitiator(final StorageSystem storage, final ExportMask exportMask, List<URI> volumes, final Initiator initiator, final List<URI> targets, final TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("{} doExportRemoveInitiator START ...", storage.getSerialNumber()); _exportMaskOperationsHelper.removeInitiators(storage, exportMask.getId(), volumes, Arrays.asList(initiator), targets, taskCompleter); _log.info("{} doExportRemoveInitiator END ...", storage.getSerialNumber()); } @Override public void doExportRemoveInitiators(final StorageSystem storage, final ExportMask exportMask, List<URI> volumes, final List<Initiator> initiators, final List<URI> targets, final TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("{} doExportRemoveInitiators START ...", storage.getSerialNumber()); _exportMaskOperationsHelper.removeInitiators(storage, exportMask.getId(), volumes, initiators, targets, taskCompleter); _log.info("{} doExportRemoveInitiators END ...", storage.getSerialNumber()); } @Override public void doConnect(final StorageSystem storage) { try { _helper.getConnection(storage); } catch (Exception e) { throw new IllegalStateException("No cim connection for " + storage.getIpAddress(), e); } } @Override public void doDisconnect(final StorageSystem storage) { } @Override public void doCreateSingleSnapshot(final StorageSystem storage, final List<URI> snapshotList, final Boolean createInactive, final Boolean readOnly, final TaskCompleter taskCompleter) throws DeviceControllerException { try { List<BlockSnapshot> snapshots = _dbClient.queryObject(BlockSnapshot.class, snapshotList); 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("doCreateSingleSnapshot", e.getMessage()); taskCompleter.error(_dbClient, error); } } @Override public void doCreateSnapshot(final StorageSystem storage, final List<URI> snapshotList, final Boolean createInactive, final Boolean readOnly, final 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(final StorageSystem storage, final List<URI> snapshotList, final TaskCompleter taskCompleter) throws DeviceControllerException { try { List<BlockSnapshot> snapshots = _dbClient .queryObject(BlockSnapshot.class, snapshotList); URI snapshot = snapshots.get(0).getId(); if (ControllerUtils.checkSnapshotsInConsistencyGroup(snapshots, _dbClient, taskCompleter)) { _snapshotOperations.activateGroupSnapshots(storage, snapshot, taskCompleter); } else { _snapshotOperations.activateSingleVolumeSnapshot(storage, snapshot, 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("doActivateSnapshot", e.getMessage()); taskCompleter.error(_dbClient, error); } } @Override public void doDeleteSnapshot(final StorageSystem storage, final URI snapshot, final TaskCompleter taskCompleter) throws DeviceControllerException { try { List<BlockSnapshot> snapshots = _dbClient.queryObject(BlockSnapshot.class, Arrays.asList(snapshot)); if (ControllerUtils.checkSnapshotsInConsistencyGroup(snapshots, _dbClient, taskCompleter)) { _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); } } @Override public void doDeleteSelectedSnapshot(final StorageSystem storage, final URI snapshot, final TaskCompleter taskCompleter) throws DeviceControllerException { try { _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); } } @Override public void doRestoreFromSnapshot(final StorageSystem storage, final URI volume, final URI snapshot, final TaskCompleter taskCompleter) throws DeviceControllerException { try { List<BlockSnapshot> snapshots = _dbClient.queryObject(BlockSnapshot.class, Arrays.asList(snapshot)); if (ControllerUtils.checkSnapshotsInConsistencyGroup(snapshots, _dbClient, taskCompleter)) { _snapshotOperations.restoreGroupSnapshots(storage, volume, snapshot, taskCompleter); } else { _snapshotOperations.restoreSingleVolumeSnapshot(storage, volume, snapshot, taskCompleter); } } catch (DatabaseException e) { String message = String.format( "IO exception when trying to restore snapshot(s) on array %s", storage.getSerialNumber()); _log.error(message, e); ServiceError error = DeviceControllerErrors.smis.methodFailed("doRestoreFromSnapshot", e.getMessage()); taskCompleter.error(_dbClient, error); } catch (Exception e) { _log.error("Problem in doRestoreFromSnapshot: ", e); ServiceError error = DeviceControllerErrors.smis.methodFailed("doRestoreFromSnapshot", e.getMessage()); taskCompleter.error(_dbClient, error); } } @Override public void doResyncSnapshot(final StorageSystem storage, final URI volume, final URI snapshot, final TaskCompleter taskCompleter) throws DeviceControllerException { try { List<BlockSnapshot> snapshots = _dbClient.queryObject(BlockSnapshot.class, Arrays.asList(snapshot)); if (ControllerUtils.checkSnapshotsInConsistencyGroup(snapshots, _dbClient, taskCompleter)) { _snapshotOperations.resyncGroupSnapshots(storage, volume, snapshot, taskCompleter); } else { _snapshotOperations.resyncSingleVolumeSnapshot(storage, volume, snapshot, taskCompleter); } } catch (DatabaseException e) { String message = String.format( "IO exception when trying to resync snapshot(s) on array %s", storage.getSerialNumber()); _log.error(message, e); ServiceError error = DeviceControllerErrors.smis.methodFailed("doResyncSnapshot", e.getMessage()); taskCompleter.error(_dbClient, error); } } /** * This interface will return a mapping of the port name to the URI of the ExportMask in which * it is contained. * * @param storage * [in] - StorageSystem object representing the array * @param initiatorNames * [in] - Port identifiers (WWPN or iSCSI name) * @param mustHaveAllPorts * [in] Indicates if true, *all* the passed in initiators have to be in the existing * matching mask. If false, a mask with *any* of the specified initiators will be * considered a hit. * @return Map of port name to Set of ExportMask URIs */ @Override public Map<String, Set<URI>> findExportMasks(final StorageSystem storage, final List<String> initiatorNames, final boolean mustHaveAllPorts) throws DeviceControllerException { return _exportMaskOperationsHelper.findExportMasks(storage, initiatorNames, mustHaveAllPorts); } @Override public Set<Integer> findHLUsForInitiators(StorageSystem storage, List<String> initiatorNames, boolean mustHaveAllPorts) { return _exportMaskOperationsHelper.findHLUsForInitiators(storage, initiatorNames, mustHaveAllPorts); } @Override public ExportMask refreshExportMask(final StorageSystem storage, final ExportMask mask) throws DeviceControllerException { return _exportMaskOperationsHelper.refreshExportMask(storage, mask); } @Override public void doActivateFullCopy(final StorageSystem storageSystem, final URI fullCopy, final TaskCompleter completer) { _cloneOperations.activateSingleClone(storageSystem, fullCopy, completer); } @Override public Integer checkSyncProgress(final URI storage, final URI source, final URI target) throws DeviceControllerException { _log.info("START checkSyncProgress for source: {} target: {}", source, target); try { StorageSystem storageSystem = _dbClient.queryObject(StorageSystem.class, storage); BlockObject sourceObj = BlockObject.fetch(_dbClient, source); Volume targetObject = _dbClient.queryObject(Volume.class, target); String percentSyncValue = null; if (ReplicationState.getEnumValue(targetObject.getReplicaState()) == ReplicationState.DETACHED) { return -1; } CIMObjectPath syncObject = null; if (storageSystem.deviceIsType(Type.vmax) && ConsistencyGroupUtils.isCloneInConsistencyGroup(targetObject.getId(), _dbClient)) { String consistencyGroupName = ConsistencyGroupUtils.getSourceConsistencyGroupName(sourceObj, _dbClient); String replicationGroupName = targetObject.getReplicationGroupInstance(); syncObject = _cimPath.getGroupSynchronizedPath(storageSystem, consistencyGroupName, replicationGroupName); } else { syncObject = _cimPath.getStorageSynchronized(storageSystem, sourceObj, storageSystem, targetObject); } CIMInstance syncInstance = _helper.getInstance(storageSystem, syncObject, false, false, null); percentSyncValue = CIMPropertyFactory.getPropertyValue(syncInstance, SmisConstants.CP_PERCENT_SYNCED); String copyState = CIMPropertyFactory.getPropertyValue(syncInstance, SmisConstants.CP_COPY_STATE); if (copyState.equals(Integer.toString(SmisConstants.FRACTURED)) || copyState.equals(Integer.toString(SmisConstants.SPLIT))) { // when fractured or split, the synchronization should have been done. percentSyncValue = "100"; } _log.info("DBG Got progress {}", percentSyncValue); return Integer.parseInt(percentSyncValue); } catch (Exception e) { String msg = String.format("Failed to check synchronization progress for %s", target); _log.error(msg, e); } return null; } @Override public void doCreateMirror(final StorageSystem storage, final URI mirror, final Boolean createInactive, final TaskCompleter taskCompleter) throws DeviceControllerException { _mirrorOperations.createSingleVolumeMirror(storage, mirror, createInactive, taskCompleter); } @Override public void doCreateGroupMirrors(final StorageSystem storage, final List<URI> mirrorList, final Boolean createInactive, final TaskCompleter taskCompleter) throws DeviceControllerException { _mirrorOperations.createGroupMirrors(storage, mirrorList, createInactive, taskCompleter); } @Override public void doFractureMirror(final StorageSystem storage, final URI mirror, final Boolean sync, final TaskCompleter taskCompleter) throws DeviceControllerException { _mirrorOperations.fractureSingleVolumeMirror(storage, mirror, sync, taskCompleter); } @Override public void doFractureGroupMirrors(final StorageSystem storage, final List<URI> mirrorList, final Boolean sync, final TaskCompleter taskCompleter) throws DeviceControllerException { _mirrorOperations.fractureGroupMirrors(storage, mirrorList, sync, taskCompleter); } @Override public void doDetachMirror(final StorageSystem storage, final URI mirror, final TaskCompleter taskCompleter) throws DeviceControllerException { _mirrorOperations.detachSingleVolumeMirror(storage, mirror, taskCompleter); } @Override public void doDetachGroupMirrors(final StorageSystem storage, final List<URI> mirrorList, final Boolean deleteGroup, final TaskCompleter taskCompleter) throws DeviceControllerException { _mirrorOperations.detachGroupMirrors(storage, mirrorList, deleteGroup, taskCompleter); } @Override public void doResumeNativeContinuousCopy(final StorageSystem storage, final URI mirror, final TaskCompleter taskCompleter) throws DeviceControllerException { _mirrorOperations.resumeSingleVolumeMirror(storage, mirror, taskCompleter); } @Override public void doResumeGroupNativeContinuousCopies(final StorageSystem storage, final List<URI> mirrorList, final TaskCompleter taskCompleter) throws DeviceControllerException { _mirrorOperations.resumeGroupMirrors(storage, mirrorList, taskCompleter); } @Override public void doDeleteMirror(final StorageSystem storage, final URI mirror, final TaskCompleter taskCompleter) throws DeviceControllerException { _mirrorOperations.deleteSingleVolumeMirror(storage, mirror, taskCompleter); } @Override public void doDeleteGroupMirrors(final StorageSystem storage, final List<URI> mirrorList, final TaskCompleter taskCompleter) throws DeviceControllerException { _mirrorOperations.deleteGroupMirrors(storage, mirrorList, taskCompleter); } @Override public void doCreateClone(final StorageSystem storage, final URI sourceVolume, final URI cloneVolume, final Boolean createInactive, final TaskCompleter taskCompleter) { _cloneOperations.createSingleClone(storage, sourceVolume, cloneVolume, createInactive, taskCompleter); } @Override public void doDetachClone(final StorageSystem storage, final URI cloneVolume, final TaskCompleter taskCompleter) { Volume clone = _dbClient.queryObject(Volume.class, cloneVolume); if (clone != null && clone.getReplicaState().equals(ReplicationState.DETACHED.name())) { taskCompleter.ready(_dbClient); return; } _cloneOperations.detachSingleClone(storage, cloneVolume, taskCompleter); } @Override public void doRestoreFromClone(final StorageSystem storage, final URI cloneVolume, final TaskCompleter taskCompleter) { _cloneOperations.restoreFromSingleClone(storage, cloneVolume, taskCompleter); } @Override public void doResyncClone(final StorageSystem storage, final URI cloneVolume, final TaskCompleter taskCompleter) { _cloneOperations.resyncSingleClone(storage, cloneVolume, taskCompleter); } @Override public void doFractureClone(StorageSystem storageDevice, URI source, URI clone, TaskCompleter completer) { _cloneOperations.fractureSingleClone(storageDevice, source, clone, completer); } private boolean isSRDFProtected(final Volume volume) { return volume.getSrdfParent() != null || volume.getSrdfTargets() != null; } /** * This method is for adding volumes to a consistency group. Be aware that this method is going * to be invoked by the SmisCreateVolumeJob, after there is a successful completion of the * volume create. * * @param storage * @param consistencyGroup * @param volumes * @param taskCompleter * @throws DeviceControllerException */ public void addVolumesToConsistencyGroup(StorageSystem storage, final BlockConsistencyGroup consistencyGroup, final List<Volume> volumes, final String replicationGroupName, final TaskCompleter taskCompleter) throws DeviceControllerException { if (isSRDFProtected(volumes.get(0))) { return; } if (consistencyGroup == null || !consistencyGroup.created(storage.getId())) { final String errMsg = "Unable to add volumes to consistency group: no consistency group provided or it has not been created in the array"; _log.error(errMsg); ServiceError error = DeviceControllerErrors.smis.noConsistencyGroupProvided(); taskCompleter.error(_dbClient, error); return; } if (consistencyGroup.getTypes().contains(BlockConsistencyGroup.Types.SRDF.name())) { taskCompleter.ready(_dbClient); return; } if (volumes == null || volumes.isEmpty()) { final String errMsg = format( "Unable to add volumes to consistency group {0}: no volumes provided", consistencyGroup.getId()); _log.error(errMsg); ServiceError error = DeviceControllerErrors.smis.noConsistencyGroupProvided(); taskCompleter.error(_dbClient, error); return; } if (storage == null) { final String errMsg = format( "Unable to add volumes to consistency group {0}: no storage system provided", consistencyGroup.getId()); _log.error(errMsg); ServiceError error = DeviceControllerErrors.smis.noStorageSystemProvided(); taskCompleter.error(_dbClient, error); return; } _log.info("Adding Volumes to Consistency Group: {}", consistencyGroup.getId()); try { // Check if the consistency group exists String groupName = ControllerUtils.generateReplicationGroupName(storage, consistencyGroup, replicationGroupName, _dbClient); storage = findProviderFactory.withGroup(storage, groupName).find(); if (storage == null) { ServiceError error = DeviceControllerErrors.smis.noConsistencyGroupWithGivenName(); taskCompleter.error(_dbClient, error); return; } final CIMObjectPath replicationSvc = _cimPath.getControllerReplicationSvcPath(storage); // Build list of native ids final Map<String, Volume> nativeIdToVolumeMap = new HashMap<String, Volume>(); for (final Volume volume : volumes) { // Do not add RP+VPlex journal or target backing volumes to consistency groups. // This causes issues with local array snapshots of RP+VPlex volumes. if (!RPHelper.isAssociatedToRpVplexType(volume, _dbClient, PersonalityTypes.METADATA, PersonalityTypes.TARGET)) { nativeIdToVolumeMap.put(volume.getNativeId(), volume); } else { _log.info("Volume {} will not be added to consistency group because it is a backing volume for " + "an RP+VPlex virtual journal volume.", volume.getId().toString()); } } Set<String> nativeIds = nativeIdToVolumeMap.keySet(); _log.info("List of native ids to be added: {}", nativeIds); if (!nativeIds.isEmpty()) { // At this point the 'nativeIds' list would have a list of members that would need to be // added to the CG final CIMArgument[] outArgs = new CIMArgument[5]; final String[] memberNames = nativeIds.toArray(new String[nativeIds.size()]); final CIMObjectPath[] volumePaths = _cimPath.getVolumePaths(storage, memberNames); boolean cgHasGroupRelationship = ControllerUtils.checkCGHasGroupRelationship(storage, consistencyGroup.getId(), _dbClient); if (storage.deviceIsType(Type.vnxblock) || !cgHasGroupRelationship) { if (storage.deviceIsType(Type.vnxblock) && !consistencyGroup.getArrayConsistency()) { // nothing need to be done on array side _log.info("No array operation needed for VNX replication group {}", groupName); } else { final CIMObjectPath cgPath = _cimPath.getReplicationGroupPath(storage, groupName); final CIMArgument[] inArgs = _helper.getAddMembersInputArguments(cgPath, volumePaths); _helper.invokeMethod(storage, replicationSvc, SmisConstants.ADD_MEMBERS, inArgs, outArgs); } } else { final CIMObjectPath maskingGroupPath = _cimPath.getMaskingGroupPath(storage, groupName, SmisConstants.MASKING_GROUP_TYPE.SE_DeviceMaskingGroup); _log.info("Adding volumes {} to device masking group {}", StringUtils.join(memberNames, ", "), maskingGroupPath.toString()); final CIMArgument[] inArgs = _helper.getAddOrRemoveMaskingGroupMembersInputArguments(maskingGroupPath, volumePaths, true); _helper.invokeMethodSynchronously(storage, _cimPath.getControllerConfigSvcPath(storage), SmisConstants.ADD_MEMBERS, inArgs, outArgs, null); } Collection<Volume> volumesToUpdate = nativeIdToVolumeMap.values(); for (Volume volume : volumesToUpdate) { volume.setReplicationGroupInstance(groupName); } _dbClient.updateObject(volumesToUpdate); _log.info("Volumes sucessfully added to the Consistency Group: {}" + consistencyGroup.getId()); } } catch (Exception e) { _log.error("Problem making SMI-S call: ", e); ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e .getMessage()); taskCompleter.error(_dbClient, error); } } /** * Method will remove the volume from the consistency group to which it currently belongs. * * @param storage * [required] - StorageSystem object * @param volume * [required] - could be a clone, or a volume */ private void removeVolumeFromConsistencyGroup(StorageSystem storage, final Volume volume) throws Exception { _log.info(String.format("removeVolumeFromConsistencyGroup for volume [%s](%s)...", volume.getLabel(), volume.getId())); CloseableIterator<CIMObjectPath> assocVolNamesIter = null; try { // Check if the consistency group exists String groupName = null; // In case of clone, 'replicationgroupinstance' property contains the Replication Group name. if (NullColumnValueGetter.isNotNullValue(volume.getReplicationGroupInstance())) { groupName = volume.getReplicationGroupInstance(); } else { groupName = ConsistencyGroupUtils.getSourceConsistencyGroupName(volume, _dbClient); } if (groupName != null) { storage = findProviderFactory.withGroup(storage, groupName).find(); if (storage == null) { _log.info("Replication Group {} not found. Skipping Remove Volume from CG step.", groupName); return; } } else { _log.info(String.format("No Replication Group found for volume [%s](%s). Skipping remove volume from CG step.", volume.getLabel(), volume.getId())); return; } CIMObjectPath cgPath = _cimPath.getReplicationGroupPath(storage, groupName); CIMObjectPath replicationSvc = _cimPath.getControllerReplicationSvcPath(storage); CIMArgument[] inArgs; CIMArgument[] outArgs = new CIMArgument[5]; CIMInstance cgPathInstance = _helper.checkExists(storage, cgPath, false, false); if (cgPathInstance != null) { CIMObjectPath[] volumePaths = _cimPath.getVolumePaths(storage, new String[] { volume.getNativeId() }); boolean volumeIsInGroup = false; assocVolNamesIter = _helper.getAssociatorNames(storage, cgPath, null, SmisConstants.CIM_STORAGE_VOLUME, null, null); while (assocVolNamesIter.hasNext()) { CIMObjectPath assocVolPath = assocVolNamesIter.next(); String deviceId = assocVolPath.getKey(SmisConstants.CP_DEVICE_ID).getValue() .toString(); if (deviceId.equalsIgnoreCase(volume.getNativeId())) { volumeIsInGroup = true; break; } } if (volumeIsInGroup) { boolean cgHasGroupRelationship = false; if (volume.isInCG()) { cgHasGroupRelationship = ControllerUtils.checkCGHasGroupRelationship(storage, volume.getConsistencyGroup(), _dbClient); } if (cgHasGroupRelationship) { // remove from DeviceMaskingGroup CIMObjectPath maskingGroupPath = _cimPath.getMaskingGroupPath(storage, groupName, SmisConstants.MASKING_GROUP_TYPE.SE_DeviceMaskingGroup); _log.info("Removing volume {} from device masking group {}", volume.getNativeId(), maskingGroupPath.toString()); inArgs = _helper.getRemoveAndUnmapMaskingGroupMembersInputArguments(maskingGroupPath, volumePaths, storage, true); _helper.invokeMethodSynchronously(storage, _cimPath.getControllerConfigSvcPath(storage), SmisConstants.REMOVE_MEMBERS, inArgs, outArgs, null); } else { inArgs = _helper.getRemoveMembersInputArguments(cgPath, volumePaths); _helper.invokeMethod(storage, replicationSvc, SmisConstants.REMOVE_MEMBERS, inArgs, outArgs); } } else { _log.info("Volume {} is no longer in the replication group {}", volume.getNativeId(), cgPath.toString()); } } else { _log.info("The replication group {} does not exist on the array.", cgPath); } } catch (Exception e) { _log.error("Problem making SMI-S call: ", e); throw e; } finally { if (assocVolNamesIter != null) { assocVolNamesIter.close(); } } } /** * Method will make synchronous SMI-S calls to clean up any backup snapshots that may exist for * the volume. Typically, on VNX arrays, if there's a restore operation against an 'advanced' * snap, there will be backup snapshot created. There isn't any easy way to get to this backup * using the SMI-S API, so we'll have to clean them all up when we go to delete the volume. * * @param storage * [required] - StorageSystem object * @param volume * [required] - Volume object * @throws Exception */ private void cleanupAnyBackupSnapshots(final StorageSystem storage, final Volume volume) throws Exception { _log.info(String.format("cleanupAnyBackupSnapshots for volume [%s](%s)...", volume.getLabel(), volume.getId())); CIMObjectPath volumePath = _cimPath.getBlockObjectPath(storage, volume); if (_helper.checkExists(storage, volumePath, false, false) == null) { _log.info(String .format("cleanupAnyBackupSnapshots(%s, %s) -- volumePath does not exist, perhaps it has already been deleted?", storage.getSerialNumber(), volume.getLabel())); return; } CloseableIterator<CIMObjectPath> settingsIterator = null; try { settingsIterator = _helper.getReference(storage, volumePath, SmisConstants.CIM_SETTINGS_DEFINE_STATE, null); if (settingsIterator != null) { while (settingsIterator.hasNext()) { CIMObjectPath settingsPath = settingsIterator.next(); CIMArgument[] outArgs = new CIMArgument[5]; _helper.callModifySettingsDefineState(storage, _helper.getDeleteSettingsForSnapshotInputArguments(settingsPath, true), outArgs); } } } finally { if (settingsIterator != null) { settingsIterator.close(); } } } /** * Method will look up backup snapshots that were created when a snapshot restore operation was * performed, then clean them up. This would be required in order to do the volume delete. * * @param storage * [required] - StorageSystem object representing the array * @param volume * [required] - Volume object representing the volume that has a snapshot created for * it */ private void cleanupAnyGroupBackupSnapshots(final StorageSystem storage, final Volume volume) { CloseableIterator<CIMObjectPath> settingsIterator = null; _log.info(String.format("cleanupAnyGroupBackupSnapshots for volume [%s](%s)...", volume.getLabel(), volume.getId())); try { String groupName = ConsistencyGroupUtils.getSourceConsistencyGroupName(volume, _dbClient); CIMObjectPath cgPath = null; if (groupName != null) { cgPath = _cimPath.getReplicationGroupPath(storage, groupName); if (cgPath == null) { _log.info("Replication Group {} not found. Skipping cleanup of group backup snapsots step.", groupName); return; } } else { _log.info(String.format("No Replication Group found for volume [%s](%s). Skipping cleanup of group backup snapsots step.", volume.getLabel(), volume.getId())); return; } CIMArgument[] outArgs = new CIMArgument[5]; CIMInstance cgPathInstance = _helper.checkExists(storage, cgPath, false, false); if (cgPathInstance != null) { settingsIterator = _helper.getAssociatorNames(storage, cgPath, null, SmisConstants.CLAR_SYNCHRONIZATION_ASPECT_FOR_SOURCE_GROUP, null, null); while (settingsIterator.hasNext()) { CIMObjectPath aspectPath = settingsIterator.next(); CIMObjectPath settingsPath = _cimPath.getGroupSynchronizedSettingsPath(storage, groupName, (String) aspectPath.getKey(SmisConstants.CP_INSTANCE_ID) .getValue()); CIMArgument[] deleteSettingsInput = _helper .getDeleteSettingsForSnapshotInputArguments(settingsPath, true); _helper.callModifySettingsDefineState(storage, deleteSettingsInput, outArgs); } } } catch (Exception e) { _log.info("Problem making SMI-S call: ", e); } finally { if (settingsIterator != null) { settingsIterator.close(); } } } /** * Method will look up backup snapshots that were created when a snapshot restore operation was * performed, then clean them up. This would be required in order to delete the ReplicationGroup. * * @param storage * [required] - StorageSystem object representing the array * @param replicationGroupPath * [required] - CIMObjectPath object representing the ReplicationGroup */ private void cleanupAnyGroupBackupSnapshots(StorageSystem storage, CIMObjectPath replicationGroupPath) { _log.info("Cleaning up backup snapshots for: {}", replicationGroupPath); CloseableIterator<CIMObjectPath> settings = null; try { settings = _helper.getReference(storage, replicationGroupPath, SmisConstants.CLAR_SETTINGS_DEFINE_STATE_RG_SAFS, null); while (settings.hasNext()) { CIMObjectPath path = settings.next(); CIMArgument[] inArgs = _helper.getDeleteSettingsForSnapshotInputArguments(path, true); CIMArgument[] outArgs = new CIMArgument[5]; _helper.callModifySettingsDefineState(storage, inArgs, outArgs); } } catch (Exception e) { _log.warn("Problem making SMI-S call: ", e); } finally { if (settings != null) { settings.close(); } } } @Override public String doAddStorageSystem(final StorageSystem storage) throws DeviceControllerException { try { String system = ""; CIMObjectPath seSystemRegistrationSvc = _helper.getRegistrationService(storage); CIMArgument[] inArgs = _helper.getAddStorageCIMArguments(storage); CIMArgument[] outArgs = new CIMArgument[5]; Object result = _helper.invokeMethod(storage, seSystemRegistrationSvc, SmisConstants.EMC_ADD_SYSTEM, inArgs, outArgs); javax.cim.UnsignedInteger32 status = (javax.cim.UnsignedInteger32) result; if (status.intValue() == 0 && outArgs[0] != null) { outArgs[0].getName(); CIMObjectPath objPath = (CIMObjectPath) outArgs[0].getValue(); system = objPath.getKey(Constants._Name).getValue().toString(); } return system; } catch (WBEMException ex) { _log.debug("Failed to add storage system to SMI-S Provider : " + ex.getMessage()); throw new DeviceControllerException(ex); } } @Override public void doRemoveStorageSystem(final StorageSystem storage) throws DeviceControllerException { try { CIMObjectPath seSystemRegistrationSvc = _helper.getRegistrationService(storage); CIMArgument[] inArgs = _helper.getRemStorageCIMArguments(storage); CIMArgument[] outArgs = new CIMArgument[5]; _helper.invokeMethod(storage, seSystemRegistrationSvc, SmisConstants.EMC_REMOVE_SYSTEM, inArgs, outArgs); } catch (WBEMException ex) { _log.debug("Failed to remove storage system from SMI-S Provider : " + ex.getMessage()); throw new DeviceControllerException(ex); } } @Override public void doCopySnapshotsToTarget(final StorageSystem storage, final List<URI> snapshotList, final TaskCompleter taskCompleter) throws DeviceControllerException { try { List<BlockSnapshot> snapshots = _dbClient .queryObject(BlockSnapshot.class, snapshotList); if (ControllerUtils.checkSnapshotsInConsistencyGroup(snapshots, _dbClient, taskCompleter)) { _snapshotOperations .copyGroupSnapshotsToTarget(storage, snapshotList, taskCompleter); } else { for (URI snapshot : snapshotList) { _snapshotOperations.copySnapshotToTarget(storage, snapshot, taskCompleter); } } } catch (DatabaseException e) { taskCompleter.error(_dbClient, DatabaseException.fatals.queryFailed(e)); } } @Override public void doCreateConsistencyGroup(final StorageSystem storage, final URI consistencyGroupId, String groupName, final TaskCompleter taskCompleter) throws DeviceControllerException { BlockConsistencyGroup consistencyGroup = _dbClient.queryObject(BlockConsistencyGroup.class, consistencyGroupId); try { CIMArgument[] inArgs; CIMArgument[] outArgs = new CIMArgument[5]; boolean srdfCG = false; String deviceName = groupName; // create target CG on source provider StorageSystem forProvider = storage; if (consistencyGroup.getRequestedTypes().contains(Types.SRDF.name())) { srdfCG = true; if (NullColumnValueGetter.isNotNullValue(consistencyGroup.getAlternateLabel())) { forProvider = getSRDFSourceProvider(consistencyGroup); _log.debug("Creating target Consistency Group on source provider"); } } if (forProvider.deviceIsType(Type.vnxblock) && !consistencyGroup.getArrayConsistency()) { // nothing need to be done on array side _log.info("No array operation needed for VNX replication group {}", groupName); } else { inArgs = _helper.getCreateReplicationGroupInputArguments(groupName); CIMObjectPath replicationSvc = _cimPath.getControllerReplicationSvcPath(storage); _helper.invokeMethod(forProvider, replicationSvc, SmisConstants.CREATE_GROUP, inArgs, outArgs); // Grab the generated name from the instance ID and store it in the db final String instanceID = (String) _cimPath .getCimObjectPathFromOutputArgs(outArgs, CP_REPLICATION_GROUP) .getKey(CP_INSTANCE_ID).getValue(); // VMAX instanceID, e.g., 000196700567+EMC_SMI_RG1414546375042 (8.0.2 provider) deviceName = instanceID.split(Constants.PATH_DELIMITER_REGEX)[storage.getUsingSmis80() ? 1 : 0]; } consistencyGroup.addSystemConsistencyGroup(storage.getId().toString(), deviceName); if (srdfCG) { consistencyGroup.addConsistencyGroupTypes(Types.SRDF.name()); } else { consistencyGroup.addConsistencyGroupTypes(Types.LOCAL.name()); } if (!consistencyGroup.isProtectedCG() && NullColumnValueGetter.isNullURI(consistencyGroup.getStorageController())) { consistencyGroup.setStorageController(storage.getId()); } _dbClient.updateObject(consistencyGroup); // This function could be called from doAddToConsistencyGroup() with a null taskCompleter. if (taskCompleter != null) { // Set task to ready. taskCompleter.ready(_dbClient); } } catch (Exception e) { _log.error("Failed to create consistency group: ", e); ServiceError error = DeviceControllerErrors.smis.methodFailed( "doCreateConsistencyGroup", e.getMessage()); // Set task to error if (taskCompleter != null) { taskCompleter.error(_dbClient, error); } } } /** * Gets the SRDF source provider, given target CG. */ private StorageSystem getSRDFSourceProvider( BlockConsistencyGroup consistencyGroup) { StorageSystem sourceProvider = null; List<BlockConsistencyGroup> groups = CustomQueryUtility .queryActiveResourcesByConstraint(_dbClient, BlockConsistencyGroup.class, PrefixConstraint.Factory .getFullMatchConstraint( BlockConsistencyGroup.class, "label", consistencyGroup.getAlternateLabel())); BlockConsistencyGroup sourceCG = groups.iterator().next(); URI sourceSystemURI = sourceCG.getStorageController(); if (!NullColumnValueGetter.isNullURI(sourceSystemURI)) { sourceProvider = _dbClient.queryObject(StorageSystem.class, sourceSystemURI); } return sourceProvider; } /** * Attempts to delete a system consistency group via an SMI-S provider, if it exists. * Since a {@link BlockConsistencyGroup} may reference multiple system consistency groups, attempt to remove its * reference, in addition to updating the types field. * * This method may be called as part of a workflow rollback. * * @param storage * StorageSystem * @param consistencyGroupId * BlockConsistencyGroup URI * @param replicationGroupName * name of the replication group to be deleted * @param keepRGName * Boolean if true, ViPR will keep group name for CG * @param markInactive * True, if the user initiated removal of the BlockConsistencyGroup * @param taskCompleter * TaskCompleter * * @throws DeviceControllerException */ @Override public void doDeleteConsistencyGroup(StorageSystem storage, final URI consistencyGroupId, String replicationGroupName, Boolean keepRGName, Boolean markInactive, final TaskCompleter taskCompleter) throws DeviceControllerException { doDeleteConsistencyGroup(storage, consistencyGroupId, replicationGroupName, keepRGName, markInactive, null, taskCompleter); } /** * Attempts to delete a system consistency group via an SMI-S provider, if it exists. * Since a {@link BlockConsistencyGroup} may reference multiple system consistency groups, attempt to remove its * reference, in addition to updating the types field. * * This method may be called as part of a workflow rollback. * * @param storage * StorageSystem * @param consistencyGroupId * BlockConsistencyGroup URI * @param replicationGroupName * name of the replication group to be deleted * @param keepRGName * Boolean if true, ViPR will keep group name for CG * @param markInactive * True, if the user initiated removal of the BlockConsistencyGroup * @param sourceReplicatoinGroup * source replication group name * @param taskCompleter * TaskCompleter * * @throws DeviceControllerException */ // @Override public void doDeleteConsistencyGroup(StorageSystem storage, final URI consistencyGroupId, String replicationGroupName, Boolean keepRGName, Boolean markInactive, String sourceReplicationGroup, final TaskCompleter taskCompleter) throws DeviceControllerException { ServiceError serviceError = null; URI systemURI = storage.getId(); BlockConsistencyGroup consistencyGroup = null; try { if (consistencyGroupId != null) { // cg id will be null when deleting replication groups created for CG full copy volumes consistencyGroup = _dbClient.queryObject(BlockConsistencyGroup.class, consistencyGroupId); } if (replicationGroupName == null || (consistencyGroup == null || consistencyGroup.getInactive())) { _log.info(String.format("%s is inactive or deleted", consistencyGroupId)); return; } String groupName = _helper.getConsistencyGroupName(consistencyGroup, storage); // This will be null, if consistencyGroup references no system CG's for storage. if (groupName == null) { _log.info(String.format("%s contains no system CG for %s. Assuming it has already been deleted.", consistencyGroupId, systemURI)); // Clean up the system consistency group references BlockConsistencyGroupUtils.cleanUpCGAndUpdate(consistencyGroup, storage.getId(), groupName, markInactive, _dbClient); return; } // Find a provider with reference to the CG storage = findProviderFactory.withGroup(storage, groupName).find(); if (storage != null) { // Check if the CG exists CIMObjectPath cgPath = _cimPath.getReplicationGroupPath(storage, groupName); CIMObjectPath replicationSvc = _cimPath.getControllerReplicationSvcPath(storage); CIMInstance cgPathInstance = _helper.checkExists(storage, cgPath, false, false); if (cgPathInstance != null) { if (storage.deviceIsType(Type.vnxblock)) { cleanupAnyGroupBackupSnapshots(storage, cgPath); } if (storage.deviceIsType(Type.vmax) && storage.checkIfVmax3()) { // if deleting snap session replication group, we need to remove the EMCSFSEntries first _helper.removeSFSEntryForReplicaReplicationGroup(storage, replicationSvc, replicationGroupName); markSnapSessionsInactiveForReplicationGroup(systemURI, consistencyGroupId, replicationGroupName); } // Invoke the deletion of the consistency group CIMArgument[] inArgs; CIMArgument[] outArgs = new CIMArgument[5]; inArgs = _helper.getDeleteReplicationGroupInputArguments(storage, groupName); _helper.invokeMethod(storage, replicationSvc, SmisConstants.DELETE_GROUP, inArgs, outArgs); } } else { _log.info("No storage provider available with group {}. Assume it has already been deleted."); } if (keepRGName || consistencyGroup == null) { return; } // Clean up the system consistency group references BlockConsistencyGroupUtils.cleanUpCGAndUpdate(consistencyGroup, storage.getId(), groupName, markInactive, _dbClient); } catch (Exception e) { _log.error("Failed to delete consistency group: ", e); serviceError = DeviceControllerErrors.smis.methodFailed("doDeleteConsistencyGroup", e.getMessage()); } finally { if (serviceError != null) { taskCompleter.error(_dbClient, serviceError); } else { taskCompleter.ready(_dbClient); } } } /** * After clearing the snap session SFS entries in the SMI-S Provider for the replication group, * mark the associated snap sessions inactive in database. * * @param storage * the storage * @param cg * the consistency group * @param rgName * the replication group name */ private void markSnapSessionsInactiveForReplicationGroup(URI storage, URI cg, String rgName) { List<BlockSnapshotSession> sessionsList = CustomQueryUtility.queryActiveResourcesByConstraint(_dbClient, BlockSnapshotSession.class, AlternateIdConstraint.Factory.getSnapshotSessionReplicationGroupInstanceConstraint(rgName)); for (BlockSnapshotSession session : sessionsList) { if (storage.toString().equals(session.getStorageController().toString()) && (cg != null && cg.toString().equals(session.getConsistencyGroup().toString()))) { _log.info("Marking snap session in-active: {}", session.getLabel()); session.setInactive(true); _dbClient.updateObject(session); } } } private boolean cleanupDanglingMetaMembers(StorageSystem storageSystem, Volume volume) { // Check if this volume has associated meta members which have not be added to the volume // (create of meta volume failed before it was formed and rollback failed to clean meta members). // Delete these members. boolean isSuccess = false; try { StringSet metaMembers = volume.getMetaVolumeMembers(); if (metaMembers == null || metaMembers.isEmpty()) { // No members to clean up isSuccess = true; } else { // Delete meta member volumes from array URI volumeURI = volume.getId(); boolean isWFStep = false; CleanupMetaVolumeMembersCompleter cleanupCompleter = new CleanupMetaVolumeMembersCompleter(volumeURI, isWFStep, null, null); doCleanupMetaMembers(storageSystem, volume, cleanupCompleter); isSuccess = cleanupCompleter.isSuccess(); } } catch (DeviceControllerException e) { _log.error("Problem in cleanupDanglingMetaMembers: ", e); } return isSuccess; } @Override public void doCleanupMetaMembers(final StorageSystem storageSystem, final Volume volume, CleanupMetaVolumeMembersCompleter cleanupCompleter) throws DeviceControllerException { // Remove meta member volumes from storage device try { _log.info(String.format("doCleanupMetaMembers Start - Array: %s, Volume: %s", storageSystem.getSerialNumber(), volume.getLabel())); // Get meta volume members from the volume data StringSet metaMembers = volume.getMetaVolumeMembers(); if (metaMembers != null && !metaMembers.isEmpty()) { _log.info(String.format( "doCleanupMetaMembers: Members stored for meta volume: %n %s", metaMembers)); // Check if volumes still exist in array and if it is not composite member (already // added to the meta volume) Set<String> volumeIds = new HashSet<String>(); for (String nativeId : metaMembers) { CIMInstance volumeInstance = _helper.checkExists(storageSystem, _cimPath.getVolumePath(storageSystem, nativeId), false, false); if (volumeInstance != null) { // Check that volume is not "Composite Volume Member", "Usage" property // equals 15 String usage = CIMPropertyFactory.getPropertyValue(volumeInstance, SmisConstants.CP_VOLUME_USAGE); int usageInt = Integer.valueOf(usage); _log.debug("doCleanupMetaMembers: Volume: " + nativeId + ", Usage of volume: " + usageInt); if (usageInt != SmisConstants.COMPOSITE_ELEMENT_MEMBER) { volumeIds.add(nativeId); } } } if (volumeIds.isEmpty()) { _log.info("doCleanupMetaMembers: No meta members to cleanup in array."); cleanupCompleter.ready(_dbClient); } else { _log.info(String .format("doCleanupMetaMembers: Members to cleanup in array: %n %s", volumeIds)); String[] nativeIds = volumeIds.toArray(new String[0]); // Prepare parameters and call method to delete meta members from array CIMObjectPath configSvcPath = _cimPath.getConfigSvcPath(storageSystem); CIMArgument[] inArgs = _helper.getDeleteVolumesInputArguments(storageSystem, nativeIds); CIMArgument[] outArgs = new CIMArgument[5]; SmisCleanupMetaVolumeMembersJob smisJobCompleter = null; String returnElementsMethod; if (storageSystem.getUsingSmis80()) { returnElementsMethod = SmisConstants.RETURN_ELEMENTS_TO_STORAGE_POOL; } else { returnElementsMethod = SmisConstants.EMC_RETURN_TO_STORAGE_POOL; } // When "cleanup" is separate workflow step, call async (for example rollback // step in volume expand) // Otherwise, call synchronously (for example when cleanup is part of meta // volume create rollback) if (cleanupCompleter.isWFStep()) { // invoke async _helper.invokeMethod(storageSystem, configSvcPath, returnElementsMethod, inArgs, outArgs); CIMObjectPath job = _cimPath.getCimObjectPathFromOutputArgs(outArgs, SmisConstants.JOB); if (job != null) { ControllerServiceImpl.enqueueJob(new QueueJob( new SmisCleanupMetaVolumeMembersJob(job, storageSystem.getId(), volume.getId(), cleanupCompleter))); } } else { // invoke synchronously smisJobCompleter = new SmisCleanupMetaVolumeMembersJob(null, storageSystem.getId(), volume.getId(), cleanupCompleter); _helper.invokeMethodSynchronously(storageSystem, configSvcPath, returnElementsMethod, inArgs, outArgs, smisJobCompleter); } } } else { _log.info("doCleanupMetaMembers: No meta members stored for meta volume. Nothing to cleanup in array."); cleanupCompleter.ready(_dbClient); } } catch (WBEMException e) { _log.error("Problem making SMI-S call: ", e); ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e .getMessage()); cleanupCompleter.error(_dbClient, error); } catch (Exception e) { _log.error("Problem in doCleanupMetaMembers: ", e); ServiceError error = DeviceControllerErrors.smis.methodFailed("doCleanupMetaMembers", e.getMessage()); cleanupCompleter.error(_dbClient, error); } _log.info(String.format("doCleanupMetaMembers End - Array: %s, Volume: %s", storageSystem.getSerialNumber(), volume.getLabel())); } @Override public void doWaitForSynchronized(final Class<? extends BlockObject> clazz, final StorageSystem storageObj, final URI target, final TaskCompleter completer) { _log.info("START waitForSynchronized for {}", target); try { BlockObject targetObj = _dbClient.queryObject(clazz, target); CIMObjectPath path = _cimPath.getBlockObjectPath(storageObj, targetObj); ControllerServiceImpl.enqueueJob(new QueueJob(new SmisWaitForSynchronizedJob(clazz, path, storageObj.getId(), completer))); } catch (Exception e) { _log.error("Problem making SMI-S call: ", e); ServiceError serviceError = DeviceControllerException.errors.jobFailed(e); completer.error(_dbClient, serviceError); } } @Override public void doWaitForGroupSynchronized(StorageSystem storageObj, List<URI> targets, TaskCompleter completer) { _log.info("START waitForSynchronized for {}", targets.get(0)); try { if (storageObj.deviceIsType(Type.vmax)) { Volume clone = _dbClient.queryObject(Volume.class, targets.get(0)); Volume sourceVol = _dbClient.queryObject(Volume.class, clone.getAssociatedSourceVolume()); String consistencyGroupName = ConsistencyGroupUtils.getSourceConsistencyGroupName(sourceVol, _dbClient); String replicationGroupName = clone.getReplicationGroupInstance(); CIMObjectPath groupSynchronized = _cimPath.getGroupSynchronizedPath(storageObj, consistencyGroupName, replicationGroupName); ControllerServiceImpl.enqueueJob(new QueueJob(new SmisWaitForGroupSynchronizedJob(groupSynchronized, storageObj.getId(), completer))); } else { // for VNX throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } } catch (Exception e) { _log.info("Problem making SMI-S call: " + e); ServiceError serviceError = DeviceControllerException.errors.jobFailed(e); completer.error(_dbClient, serviceError); } } @Override public void doAddToConsistencyGroup(StorageSystem storage, final URI consistencyGroupId, String replicationGroupName, final List<URI> blockObjectURIs, final TaskCompleter taskCompleter) throws DeviceControllerException { BlockConsistencyGroup consistencyGroup = _dbClient.queryObject(BlockConsistencyGroup.class, consistencyGroupId); Map<URI, BlockObject> uriToBlockObjectMap = new HashMap<URI, BlockObject>(); List<URI> replicas = new ArrayList<URI>(); List<URI> volumes = new ArrayList<URI>(); try { List<BlockObject> blockObjects = new ArrayList<BlockObject>(); for (URI blockObjectURI : blockObjectURIs) { // FIXME Performance improvement here BlockObject blockObject = BlockObject.fetch(_dbClient, blockObjectURI); if (blockObject != null) { blockObjects.add(blockObject); uriToBlockObjectMap.put(blockObjectURI, blockObject); } } /** * Request: Volume CG with volume objects OR * Volume CG with replica objects(snap/clone/mirror) * * make sure that the blockObjects are of same type (Volume/Snap/Clone/Mirror) * If Volume: * add them to group * If Replica (supported only for 8.0): * If existing replicas do not have replicationGroupInstance set: * create new RG on array with random name, * add replicas to that RG, * set RG name in replicationGroupInstance field for new replicas. * Else: * Get RG name from existing replica, * Add new replicas to DMG with same name as RG name, * set RG name in replicationGroupInstance field for new replicas. * * For all objects except Clone, set CG URI. */ for (BlockObject blockObject : blockObjects) { boolean isFullCopy = false; if (blockObject instanceof Volume) { isFullCopy = ControllerUtils.isVolumeFullCopy((Volume) blockObject, _dbClient); } if (blockObject instanceof BlockSnapshot || isFullCopy || blockObject instanceof BlockMirror) { replicas.add(blockObject.getId()); } else { volumes.add(blockObject.getId()); } } // adding replicas to ReplicationGroup is supported only for 8.0 if (!storage.getUsingSmis80() && !replicas.isEmpty()) { String errMsg = "Adding replicas to Consistency Group is not supported on 4.6.x Provider"; _log.warn(errMsg); taskCompleter.error(_dbClient, DeviceControllerException.exceptions .failedToAddMembersToConsistencyGroup(consistencyGroup.getLabel(), consistencyGroup.fetchArrayCgName(storage.getId()), errMsg)); return; } if (!volumes.isEmpty() && !replicas.isEmpty()) { String errMsg = "Mix of Volumes and Replica types is not supported"; _log.warn(errMsg); taskCompleter.error(_dbClient, DeviceControllerException.exceptions .failedToAddMembersToConsistencyGroup(consistencyGroup.getLabel(), consistencyGroup.getCgNameOnStorageSystem(storage.getId()), errMsg)); return; } if (!replicas.isEmpty()) { addReplicasToConsistencyGroup(storage, consistencyGroup, replicas, uriToBlockObjectMap); } else if (!volumes.isEmpty()) { // get source provider for SRDF target volumes // target CG is created using source system provider StorageSystem forProvider = storage; boolean isSrdfTarget = false; Volume vol = (Volume) uriToBlockObjectMap.get(volumes.iterator().next()); if (vol.checkForSRDF() && !NullColumnValueGetter.isNullNamedURI(vol.getSrdfParent())) { Volume srcVolume = _dbClient.queryObject(Volume.class, vol.getSrdfParent().getURI()); forProvider = _dbClient.queryObject(StorageSystem.class, srcVolume.getStorageController()); isSrdfTarget = true; } // Check if the consistency group exists boolean createCG = false; CIMObjectPath cgPath = null; CIMInstance cgPathInstance = null; boolean isVPlexOrRP = consistencyGroup.checkForType(Types.VPLEX) || consistencyGroup.checkForType(Types.RP); String groupName = ControllerUtils.generateReplicationGroupName(storage, consistencyGroup, replicationGroupName, _dbClient); // If this is for VPlex or RP, we would create backend consistency group if it does not exist yet. if (!consistencyGroup.created(storage.getId(), groupName)) { if (isVPlexOrRP) { createCG = true; _log.info(String.format("No consistency group exists for the storage: %s", storage.getId())); } else { ServiceError error = DeviceControllerErrors.smis.noConsistencyGroupWithGivenName(); taskCompleter.error(_dbClient, error); return; } } else { if (!isSrdfTarget) { StorageSystem storageSystem = findProviderFactory.withGroup(storage, groupName).find(); if (storageSystem == null) { if (isVPlexOrRP) { _log.info(String.format("Could not find consistency group with the name: %s", groupName)); createCG = true; } else { ServiceError error = DeviceControllerErrors.smis.noConsistencyGroupWithGivenName(); taskCompleter.error(_dbClient, error); return; } } else { forProvider = storageSystem; } } if (!createCG && !(storage.deviceIsType(Type.vnxblock) && !consistencyGroup.getArrayConsistency())) { cgPath = _cimPath.getReplicationGroupPath(forProvider, storage.getSerialNumber(), groupName); cgPathInstance = _helper.checkExists(forProvider, cgPath, false, false); // If there is no consistency group with the given name, set the // operation to error if (cgPathInstance == null) { taskCompleter.error(_dbClient, DeviceControllerException.exceptions .consistencyGroupNotFound(consistencyGroup.getLabel(), consistencyGroup.getCgNameOnStorageSystem(storage.getId()))); return; } } } if (createCG) { doCreateConsistencyGroup(storage, consistencyGroupId, groupName, null); consistencyGroup = _dbClient.queryObject(BlockConsistencyGroup.class, consistencyGroupId); cgPath = _cimPath.getReplicationGroupPath(storage, groupName); } if (storage.deviceIsType(Type.vnxblock) && !consistencyGroup.getArrayConsistency()) { // nothing need to be done on array side _log.info("No array operation needed for VNX replication group {}", groupName); } else { CIMObjectPath replicationSvc = _cimPath.getControllerReplicationSvcPath(storage); String[] blockObjectNames = _helper.getBlockObjectAlternateNames(volumes); // Smis call to add volumes that are already available in Group, will result in error. // Refresh first for confidence and avoid false positives. ReplicationUtils.callEMCRefresh(_helper, storage, true); Set<String> blockObjectsToAdd = _helper.filterVolumesAlreadyPartOfReplicationGroup( storage, cgPath, blockObjectNames); if (!blockObjectsToAdd.isEmpty()) { CIMArgument[] output = new CIMArgument[5]; CIMObjectPath[] members = _cimPath.getVolumePaths(storage, blockObjectsToAdd.toArray(new String[blockObjectsToAdd.size()])); boolean cgHasGroupRelationship = ControllerUtils.checkCGHasGroupRelationship(storage, consistencyGroup.getId(), _dbClient); if (!cgHasGroupRelationship) { CIMArgument[] addMembersInput = _helper.getAddMembersInputArguments(cgPath, members); _helper.invokeMethod(storage, replicationSvc, SmisConstants.ADD_MEMBERS, addMembersInput, output); } else { final CIMObjectPath maskingGroupPath = _cimPath.getMaskingGroupPath(storage, groupName, SmisConstants.MASKING_GROUP_TYPE.SE_DeviceMaskingGroup); _log.info("Adding volumes {} to device masking group {}", StringUtils.join(blockObjectsToAdd, ", "), maskingGroupPath.toString()); final CIMArgument[] inArgs = _helper.getAddOrRemoveMaskingGroupMembersInputArguments(maskingGroupPath, members, true); _helper.invokeMethodSynchronously(storage, _cimPath.getControllerConfigSvcPath(storage), SmisConstants.ADD_MEMBERS, inArgs, output, null); } } else { _log.info("Requested volumes {} are already part of the Replication Group {}, hence skipping AddMembers call..", Joiner.on(", ").join(blockObjectNames), groupName); } } // refresh target provider to update its view on target CG if (isSrdfTarget) { refreshStorageSystem(storage.getId(), null); } } taskCompleter.ready(_dbClient); } catch (Exception e) { _log.error("Problem in adding volumes to Consistency Group {}", consistencyGroupId, e); // Remove any references to the consistency group for (URI volume : volumes) { BlockObject volumeObject = uriToBlockObjectMap.get(volume); volumeObject.setConsistencyGroup(NullColumnValueGetter.getNullURI()); volumeObject.setReplicationGroupInstance(NullColumnValueGetter.getNullStr()); _dbClient.updateObject(volumeObject); } // Remove replication group instance for (URI replica : replicas) { BlockObject replicaObject = uriToBlockObjectMap.get(replica); replicaObject.setReplicationGroupInstance(NullColumnValueGetter.getNullStr()); if (!(replicaObject instanceof Volume && ControllerUtils.isVolumeFullCopy((Volume) replicaObject, _dbClient))) { replicaObject.setConsistencyGroup(NullColumnValueGetter.getNullURI()); replicaObject.setReplicationGroupInstance(NullColumnValueGetter.getNullStr()); } _dbClient.updateObject(replicaObject); } taskCompleter.error(_dbClient, DeviceControllerException.exceptions .failedToAddMembersToConsistencyGroup(consistencyGroup.getLabel(), consistencyGroup.getCgNameOnStorageSystem(storage.getId()), e.getMessage())); } } private void fabricateSourceGroupAspects(StorageSystem storage, BlockConsistencyGroup cg, Map<String, List<BlockSnapshotSession>> sessionLabelsMap) throws WBEMException { /* * Each key in the map represents the snapshot-session name, where a value represents a list * of BlockSnapshotSession instances with this same name. */ for (Map.Entry<String, List<BlockSnapshotSession>> entry : sessionLabelsMap.entrySet()) { String sessionLabel = entry.getKey(); List<BlockSnapshotSession> oldSessions = entry.getValue(); BlockSnapshotSession templateSession = oldSessions.get(0); // get RG name from source (parent) volume String groupName = null; if (!NullColumnValueGetter.isNullNamedURI(templateSession.getParent())) { BlockObject source = BlockObject.fetch(_dbClient, templateSession.getParent().getURI()); groupName = (source != null) ? source.getReplicationGroupInstance() : null; } // 1) Run Harsha's method to fab SourceGroup aspect _log.info("Fabricating synchronization aspect for SourceGroup {}", cg.getLabel()); CIMObjectPath replicationSvc = _cimPath.getControllerReplicationSvcPath(storage); CIMArgument[] iArgs = _helper.fabricateSourceGroupSynchronizationAspectInputArguments(storage, groupName, sessionLabel); CIMArgument[] oArgs = new CIMArgument[5]; _helper.invokeMethod(storage, replicationSvc, "EMCRemoveSFSEntries", iArgs, oArgs); // 2) Prepare to remove non-CG BlockSnapshotSession instances StringSet consolidatedLinkedTargets = new StringSet(); for (BlockSnapshotSession oldSession : oldSessions) { oldSession.setInactive(true); StringSet linkedTargets = oldSession.getLinkedTargets(); if (linkedTargets != null && !linkedTargets.isEmpty()) { consolidatedLinkedTargets.addAll(linkedTargets); } } _dbClient.updateObject(oldSessions); // 3) Create new BlockSnapshotSession instance, pointing to the new Source CG BlockSnapshotSession newSession = new BlockSnapshotSession(); newSession.setId(URIUtil.createId(BlockSnapshotSession.class)); newSession.setConsistencyGroup(cg.getId()); newSession.setProject(new NamedURI(templateSession.getProject().getURI(), templateSession.getProject().getName())); newSession.setStorageController(storage.getId()); newSession.setLabel(templateSession.getSessionLabel()); newSession.setSessionLabel(templateSession.getSessionLabel()); newSession.setReplicationGroupInstance(groupName); newSession.setSessionSetName(groupName); newSession.setLinkedTargets(consolidatedLinkedTargets); _dbClient.createObject(newSession); // Determine the session instance and update the BlockSnapshotSession CIMObjectPath cgPath = _cimPath.getReplicationGroupPath(storage, groupName); CloseableIterator<CIMObjectPath> associatorNames = null; try { _log.info("Finding associated source group aspects..."); associatorNames = _helper.getAssociatorNames(storage, cgPath, null, SYMM_SYNCHRONIZATION_ASPECT_FOR_SOURCE_GROUP, null, null); while (associatorNames.hasNext()) { CIMObjectPath aspectPath = associatorNames.next(); _log.info("Found {}", aspectPath); String instanceId = aspectPath.getKeyValue(CP_INSTANCE_ID).toString(); if (instanceId.contains(newSession.getSessionLabel())) { newSession.setSessionInstance(instanceId); _dbClient.updateObject(newSession); break; } } } finally { if (associatorNames != null) { associatorNames.close(); } } } } /** * Adds the replicas to consistency group. */ private void addReplicasToConsistencyGroup(StorageSystem storage, BlockConsistencyGroup consistencyGroup, List<URI> replicas, Map<URI, BlockObject> uriToBlockObjectMap) throws Exception { String replicationGroupName = ControllerUtils.getGroupNameFromReplicas( replicas, consistencyGroup, _dbClient); if (replicationGroupName == null) { // create Replication Group with random name _log.info("Creating Replication Group for replicas"); CIMObjectPath replicationSvc = _cimPath.getControllerReplicationSvcPath(storage); CIMArgument[] inArgs = _helper.getCreateReplicationGroupInputArguments(null); CIMArgument[] outArgs = new CIMArgument[5]; _helper.invokeMethod(storage, replicationSvc, SmisConstants.CREATE_GROUP, inArgs, outArgs); // Grab the generated name from the instance. final String instanceID = (String) _cimPath .getCimObjectPathFromOutputArgs(outArgs, CP_REPLICATION_GROUP) .getKey(CP_INSTANCE_ID).getValue(); // VMAX instanceID, e.g., 000196700567+EMC_SMI_RG1414546375042 (8.0.2 provider) final String groupName = instanceID.split(Constants.PATH_DELIMITER_REGEX)[storage.getUsingSmis80() ? 1 : 0]; replicationGroupName = groupName; _log.info("Group name generated: {}", groupName); _log.info("Adding replicas to Replication Group {}", groupName); CIMObjectPath cgPath = _cimPath.getReplicationGroupPath(storage, groupName); String[] blockObjectNames = _helper.getBlockObjectAlternateNames(replicas); CIMObjectPath[] members = _cimPath.getVolumePaths(storage, blockObjectNames); CIMArgument[] addMembersInput = _helper.getAddMembersInputArguments(cgPath, members); CIMArgument[] output = new CIMArgument[5]; _helper.invokeMethod(storage, replicationSvc, SmisConstants.ADD_MEMBERS, addMembersInput, output); } else { // 8.0.3 will support adding replicas to consistency groups but the replicas should be added to the // device masking group corresponding to the consistency group _log.info("Adding replicas to Device Masking Group equivalent to its ReplicationGroup {}", replicationGroupName); List<URI> replicasToAdd = _helper.filterReplicasAlreadyPartOfReplicationGroup( storage, replicationGroupName, replicas); if (!replicasToAdd.isEmpty()) { CIMArgument[] inArgsAdd = _helper.getAddVolumesToMaskingGroupInputArguments(storage, replicationGroupName, replicasToAdd, null, true); CIMArgument[] outArgsAdd = new CIMArgument[5]; _helper.invokeMethodSynchronously(storage, _cimPath.getControllerConfigSvcPath(storage), SmisConstants.ADD_MEMBERS, inArgsAdd, outArgsAdd, null); } else { _log.info("Requested replicas {} are already part of the Replication Group {}, hence skipping AddMembers call..", Joiner.on(", ").join(replicas), replicationGroupName); } } // persist group name in Replica objects for (URI replica : replicas) { BlockObject replicaObject = uriToBlockObjectMap.get(replica); replicaObject.setReplicationGroupInstance(replicationGroupName); // don't set CG on Clones if (!(replicaObject instanceof Volume && ControllerUtils.isVolumeFullCopy((Volume) replicaObject, _dbClient))) { replicaObject.setConsistencyGroup(consistencyGroup.getId()); } else if (replicaObject instanceof BlockSnapshot) { String snapSetLabel = ControllerUtils.getSnapSetLabelFromExistingSnaps(replicationGroupName, replicaObject.getStorageController(), _dbClient); // set the snapsetLabel for the snapshots to add if (null != snapSetLabel) { ((BlockSnapshot) replicaObject).setSnapsetLabel(snapSetLabel); } else { ((BlockSnapshot) replicaObject).setSnapsetLabel(replicationGroupName); } } _dbClient.updateAndReindexObject(replicaObject); } } @Override public void doRemoveFromConsistencyGroup(StorageSystem storage, final URI consistencyGroupId, final List<URI> blockObjects, final TaskCompleter taskCompleter) throws DeviceControllerException { Set<String> groupNames = new HashSet<String>(); String grpName = null; try { // get the group name from one of the block objects; we expect all of them to be the same group Iterator<URI> itr = blockObjects.iterator(); while (itr.hasNext()) { BlockObject blockObject = BlockObject.fetch(_dbClient, itr.next()); if (blockObject != null && !blockObject.getInactive() && !NullColumnValueGetter.isNullValue(blockObject.getReplicationGroupInstance())) { groupNames.add(blockObject.getReplicationGroupInstance()); } } // Check if the replication group exists for (String groupName : groupNames) { grpName = groupName; storage = findProviderFactory.withGroup(storage, groupName).find(); if (storage == null) { ServiceError error = DeviceControllerErrors.smis.noConsistencyGroupWithGivenName(); taskCompleter.error(_dbClient, error); return; } String[] blockObjectNames = _helper.getBlockObjectAlternateNames(blockObjects); CIMObjectPath[] members = _cimPath.getVolumePaths(storage, blockObjectNames); CIMArgument[] output = new CIMArgument[5]; BlockConsistencyGroup consistencyGroup = _dbClient.queryObject(BlockConsistencyGroup.class, consistencyGroupId); if (!storage.deviceIsType(Type.vnxblock) && ControllerUtils.checkCGHasGroupRelationship(storage, consistencyGroupId, _dbClient)) { // remove from DeviceMaskingGroup CIMObjectPath maskingGroupPath = _cimPath.getMaskingGroupPath(storage, groupName, SmisConstants.MASKING_GROUP_TYPE.SE_DeviceMaskingGroup); _log.info("Removing volumes {} from device masking group {}", blockObjectNames, maskingGroupPath.toString()); CIMArgument[] inArgs = _helper.getRemoveAndUnmapMaskingGroupMembersInputArguments(maskingGroupPath, members, storage, true); _helper.invokeMethodSynchronously(storage, _cimPath.getControllerConfigSvcPath(storage), SmisConstants.REMOVE_MEMBERS, inArgs, output, null); } else { CIMObjectPath cgPath = _cimPath.getReplicationGroupPath(storage, groupName); CIMInstance cgPathInstance = _helper.checkExists(storage, cgPath, false, false); // If there is no replication group with the given name, return success if (cgPathInstance == null) { _log.info(String.format("no replication group with name %s exists on storage system %s", groupName, storage.getLabel())); } else { CIMObjectPath replicationSvc = _cimPath.getControllerReplicationSvcPath(storage); CIMArgument[] removeMembersInput = _helper.getRemoveMembersInputArguments(cgPath, members); _helper.invokeMethod(storage, replicationSvc, SmisConstants.REMOVE_MEMBERS, removeMembersInput, output); } } } taskCompleter.ready(_dbClient); } catch (Exception e) { BlockConsistencyGroup consistencyGroup = _dbClient.queryObject(BlockConsistencyGroup.class, consistencyGroupId); _log.error("Problem while removing volume from CG :{}", consistencyGroupId, e); taskCompleter.error(_dbClient, DeviceControllerException.exceptions .failedToRemoveMembersToConsistencyGroup((consistencyGroup == null ? "unknown cg" : consistencyGroup.getLabel()), (grpName == null ? "unknown replication group" : grpName), e.getMessage())); } } @Override public void doAddToReplicationGroup(final StorageSystem storage, final URI consistencyGroupId, final String replicationGroupName, final List<URI> replicas, final TaskCompleter taskCompleter) throws DeviceControllerException { // 8.0.3 will support adding replicas to replication group but the replicas should be added to the // device masking group corresponding to the consistency group _log.info("Adding replicas to Device Masking Group equivalent to its ReplicationGroup {}", replicationGroupName); List<URI> replicasToAdd; try { if (!storage.deviceIsType(Type.vnxblock)) { replicasToAdd = _helper.filterReplicasAlreadyPartOfReplicationGroup( storage, replicationGroupName, replicas); if (!replicasToAdd.isEmpty()) { CIMArgument[] inArgsAdd; inArgsAdd = _helper.getAddVolumesToMaskingGroupInputArguments(storage, replicationGroupName, replicasToAdd, null, true); CIMArgument[] outArgsAdd = new CIMArgument[5]; _helper.invokeMethodSynchronously(storage, _cimPath.getControllerConfigSvcPath(storage), SmisConstants.ADD_MEMBERS, inArgsAdd, outArgsAdd, null); } else { _log.info("Requested replicas {} are already part of the Replication Group {}, hence skipping AddMembers call..", Joiner.on(", ").join(replicas), replicationGroupName); } } else { _log.info("There is no corresponding replication group for VNX mirrors and clones"); } // persist group name/settings instance (for VMAX3) in replica objects List<BlockObject> replicaList = new ArrayList<BlockObject>(); String settingsInst = null; if (storage.checkIfVmax3() && URIUtil.isType(replicas.get(0), BlockSnapshot.class)) { List<BlockSnapshot> blockSnapshots = ControllerUtils.getSnapshotsPartOfReplicationGroup(replicationGroupName, storage.getId(), _dbClient); if (blockSnapshots != null && !blockSnapshots.isEmpty()) { settingsInst = blockSnapshots.get(0).getSettingsInstance(); } } for (URI replica : replicas) { BlockObject replicaObj = BlockObject.fetch(_dbClient, replica); replicaObj.setReplicationGroupInstance(replicationGroupName); // don't set CG on Clones if (replicaObj instanceof BlockMirror || replicaObj instanceof BlockSnapshot) { replicaObj.setConsistencyGroup(consistencyGroupId); if (replicaObj instanceof BlockMirror) { String groupName = ConsistencyGroupUtils.getSourceConsistencyGroupName(replicaObj, _dbClient); CIMObjectPath syncPath = _cimPath.getGroupSynchronizedPath(storage, groupName, replicationGroupName); ((BlockMirror) replicaObj).setSynchronizedInstance(syncPath.toString()); } else if (replicaObj instanceof BlockSnapshot) { if (settingsInst != null) { ((BlockSnapshot) replicaObj).setSettingsInstance(settingsInst); } } } replicaList.add(replicaObj); } _dbClient.updateAndReindexObject(replicaList); taskCompleter.ready(_dbClient); } catch (Exception e) { _log.error("Problem while adding replica to device masking group :{}", consistencyGroupId, e); if (null != replicas && !replicas.isEmpty()) { for (URI replicaURI : replicas) { BlockObject blockObj = _dbClient.queryObject(BlockObject.class, replicaURI); blockObj.setReplicationGroupInstance(NullColumnValueGetter.getNullStr()); _dbClient.updateObject(blockObj); } } taskCompleter.error(_dbClient, DeviceControllerException.exceptions .failedToAddMembersToReplicationGroup(replicationGroupName, storage.getLabel(), e.getMessage())); return; } } @Override public void doRemoveFromReplicationGroup(StorageSystem storage, URI consistencyGroupId, String replicationGroupName, List<URI> blockObjects, TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("Removing replicas from Device Masking Group equivalent to its ReplicationGroup {}", replicationGroupName); try { if (!storage.deviceIsType(Type.vnxblock)) { BlockObject replica = BlockObject.fetch(_dbClient, blockObjects.get(0)); CIMObjectPath maskingGroupPath = _cimPath.getMaskingGroupPath(storage, ControllerUtils.extractGroupName(replica.getReplicationGroupInstance()), SmisConstants.MASKING_GROUP_TYPE.SE_DeviceMaskingGroup); List<URI> replicasPartOfGroup = _helper.findVolumesInReplicationGroup(storage, maskingGroupPath, blockObjects); if (replicasPartOfGroup.isEmpty()) { _log.info("Replicas {} have already been removed from Device Masking Group {}", Joiner.on(", ").join(blockObjects), maskingGroupPath); } else { String[] members = _helper.getBlockObjectAlternateNames(replicasPartOfGroup); CIMObjectPath[] memberPaths = _cimPath.getVolumePaths(storage, members); CIMArgument[] inArgs = _helper.getRemoveAndUnmapMaskingGroupMembersInputArguments( maskingGroupPath, memberPaths, storage, true); CIMArgument[] outArgs = new CIMArgument[5]; _log.info("Invoking remove replicas {} from Device Masking Group equivalent to its Replication Group {}", Joiner.on(", ").join(members), replica.getReplicationGroupInstance()); _helper.invokeMethodSynchronously(storage, _cimPath.getControllerConfigSvcPath(storage), SmisConstants.REMOVE_MEMBERS, inArgs, outArgs, null); } } else { // nothing need to be done on VNX array side _log.info("No array operation needed for VNX replication group {}", replicationGroupName); } // Remove any references to the consistency group List<BlockObject> objectsToUpdate = new ArrayList<BlockObject>(blockObjects.size()); for (URI blockObjectURI : blockObjects) { BlockObject blockObject = BlockObject.fetch(_dbClient, blockObjectURI); if (blockObject != null) { blockObject.setConsistencyGroup(NullColumnValueGetter.getNullURI()); blockObject.setReplicationGroupInstance(NullColumnValueGetter.getNullStr()); // unset the Set name on clones if (blockObject instanceof Volume && NullColumnValueGetter.isNotNullValue(((Volume) blockObject).getFullCopySetName())) { ((Volume) blockObject).setFullCopySetName(NullColumnValueGetter.getNullStr()); } objectsToUpdate.add(blockObject); } } if (!objectsToUpdate.isEmpty()) { _dbClient.updateObject(objectsToUpdate); } taskCompleter.ready(_dbClient); } catch (Exception e) { _log.info("Failed to remove from replication group", e); taskCompleter.error(_dbClient, DeviceControllerException.exceptions .failedToRemoveMembersFromReplicationGroup(replicationGroupName, storage.getLabel(), e.getMessage())); } } @Override public void doAddVolumePairsToCg(StorageSystem system, List<URI> sourceURIs, URI remoteDirectorGroupURI, TaskCompleter completer) { _srdfOperations.addVolumePairsToCg(system, sourceURIs, remoteDirectorGroupURI, completer); } @Override public void doCreateLink(final StorageSystem system, final URI sourceURI, final URI targetURI, final TaskCompleter completer) { _srdfOperations.createSRDFVolumePair(system, sourceURI, targetURI, completer); } @Override public void doCreateListReplicas(StorageSystem system, List<URI> sources, List<URI> targets, boolean addWaitForCopyState, TaskCompleter completer) { _srdfOperations.createListReplicas(system, sources, targets, addWaitForCopyState, completer); } @Override public void doDetachLink(final StorageSystem system, final URI sourceURI, final URI targetURI, final boolean onGroup, final TaskCompleter completer) { Volume target = _dbClient.queryObject(Volume.class, targetURI); _srdfOperations.performDetach(system, target, onGroup, completer); } @Override public void doRemoveDeviceGroups(final StorageSystem system, final URI sourceURI, final URI targetURI, final TaskCompleter completer) { _srdfOperations.removeDeviceGroups(system, sourceURI, targetURI, completer); } @Override public void doRollbackLinks(final StorageSystem system, final List<URI> sourceURIs, final List<URI> targetURIs, final boolean isGroupRollback, final TaskCompleter completer) { _srdfOperations.rollbackSRDFMirrors(system, sourceURIs, targetURIs, isGroupRollback, completer); } @Override public void doSplitLink(final StorageSystem system, final Volume targetVolume, boolean rollback, final TaskCompleter completer) { _srdfOperations.performSplit(system, targetVolume, completer); } @Override public void doSuspendLink(StorageSystem system, Volume targetVolume, boolean consExempt, boolean refreshVolumeProperties, TaskCompleter completer) { _srdfOperations.performSuspend(system, targetVolume, consExempt, refreshVolumeProperties, completer); } @Override public void doResumeLink(final StorageSystem system, final Volume targetVolume, boolean refreshVolumeProperties, final TaskCompleter completer) { _srdfOperations.performEstablish(system, targetVolume, refreshVolumeProperties, completer); } @Override public void doFailoverLink(final StorageSystem system, final Volume targetVolume, final TaskCompleter completer) { _srdfOperations.performFailover(system, targetVolume, completer); } @Override public void doFailoverCancelLink(final StorageSystem system, final Volume targetVolume, final TaskCompleter completer) { _srdfOperations.failoverCancelSyncPair(system, targetVolume, completer); } @Override public void doResyncLink(final StorageSystem system, final URI sourceURI, final URI targetURI, final TaskCompleter completer) { _srdfOperations.reSyncSRDFSyncVolumePair(system, sourceURI, targetURI, completer); } @Override public void doRemoveVolumePair(final StorageSystem system, final URI sourceURI, final URI targetURI, final boolean rollback, final TaskCompleter completer) { _srdfOperations.removeSRDFSyncPair(system, sourceURI, targetURI, rollback, completer); } @Override public void doRemoveMirrorFromDeviceMaskingGroup( final StorageSystem system, final List<URI> mirrors, final TaskCompleter completer) { _mirrorOperations.removeMirrorFromDeviceMaskingGroup(system, mirrors, completer); } @Override public void doStartLink(final StorageSystem system, final Volume targetVolume, final TaskCompleter completer) { _srdfOperations.startSRDFLink(system, targetVolume, completer); } @Override public void doStopLink(final StorageSystem system, final Volume targetVolume, final TaskCompleter completer) { _srdfOperations.performStop(system, targetVolume, completer); } @Override public void doCreateCgPairs(StorageSystem system, List<URI> sourceURIs, List<URI> targetURIs, SRDFMirrorCreateCompleter completer) { _srdfOperations.createSRDFCgPairs(system, sourceURIs, targetURIs, completer); } @Override public Set<String> findVolumesPartOfRemoteGroup(StorageSystem system, RemoteDirectorGroup rdfGroup) { return _srdfOperations.findVolumesPartOfRDFGroups(system, rdfGroup); } @Override public boolean validateStorageProviderConnection(String ipAddress, Integer portNumber) { return _helper.validateStorageProviderConnection(ipAddress, portNumber); } @Override public void doSwapVolumePair(StorageSystem system, Volume targetVolume, TaskCompleter completer) { _srdfOperations.performSwap(system, targetVolume, completer); } @Override public void updatePolicyAndLimits(StorageSystem storage, ExportMask exportMask, List<URI> volumeURIs, VirtualPool newVpool, boolean rollback, TaskCompleter taskCompleter) throws Exception { _exportMaskOperationsHelper.updateStorageGroupPolicyAndLimits( storage, exportMask, volumeURIs, newVpool, rollback, taskCompleter); } @Override public void doTerminateAnyRestoreSessions(StorageSystem storageDevice, URI source, BlockObject snapshot, TaskCompleter completer) throws Exception { _snapshotOperations.terminateAnyRestoreSessions(storageDevice, snapshot, source, completer); } @Override public ExportMaskPolicy getExportMaskPolicy(StorageSystem storage, ExportMask mask) { return _helper.getExportMaskPolicy(storage, mask); } @Override public void doEstablishVolumeNativeContinuousCopyGroupRelation(final StorageSystem storage, final URI sourceVolume, final URI mirror, final TaskCompleter taskCompleter) throws DeviceControllerException { _mirrorOperations.establishVolumeNativeContinuousCopyGroupRelation(storage, sourceVolume, mirror, taskCompleter); } @Override public void doEstablishVolumeSnapshotGroupRelation(final StorageSystem storage, final URI sourceVolume, final URI snapshot, final TaskCompleter taskCompleter) throws DeviceControllerException { _snapshotOperations.establishVolumeSnapshotGroupRelation(storage, sourceVolume, snapshot, taskCompleter); } @Override public void doSyncLink(StorageSystem targetSystem, Volume targetVolume, TaskCompleter completer) throws Exception { _srdfOperations.performRestore(targetSystem, targetVolume, completer); } @Override public void doUpdateSourceAndTargetPairings(List<URI> sourceURIs, List<URI> targetURIs) { _srdfOperations.updateSourceAndTargetPairings(sourceURIs, targetURIs); } @Override public void doCreateGroupClone(final StorageSystem storage, final List<URI> cloneVolumes, final Boolean createInactive, final TaskCompleter taskCompleter) { _cloneOperations.createGroupClone(storage, cloneVolumes, createInactive, taskCompleter); } @Override public void doDetachGroupClone(StorageSystem storage, List<URI> cloneVolumes, TaskCompleter taskCompleter) { Volume clone = _dbClient.queryObject(Volume.class, cloneVolumes.get(0)); if (clone != null && clone.getReplicaState().equals(ReplicationState.DETACHED.name())) { taskCompleter.ready(_dbClient); return; } _cloneOperations.detachGroupClones(storage, cloneVolumes, taskCompleter); } @Override public void doEstablishVolumeFullCopyGroupRelation(final StorageSystem storage, final URI sourceVolume, final URI fullCopy, final TaskCompleter taskCompleter) throws DeviceControllerException { _cloneOperations.establishVolumeCloneGroupRelation(storage, sourceVolume, fullCopy, taskCompleter); } @Override public void doRestoreFromGroupClone(StorageSystem storageSystem, List<URI> clones, TaskCompleter taskCompleter) { _cloneOperations.restoreGroupClones(storageSystem, clones, taskCompleter); } @Override public void doActivateGroupFullCopy(StorageSystem storage, List<URI> fullCopy, TaskCompleter completer) { _cloneOperations.activateGroupClones(storage, fullCopy, completer); } @Override public void doResyncGroupClone(StorageSystem storageDevice, List<URI> clone, TaskCompleter completer) throws Exception { _cloneOperations.resyncGroupClones(storageDevice, clone, completer); } @Override public void doFractureGroupClone(StorageSystem storageDevice, List<URI> clone, TaskCompleter completer) throws Exception { _cloneOperations.fractureGroupClones(storageDevice, clone, completer); } @Override public void refreshStorageSystem(final URI systemURI, List<URI> volumeURIs) { _srdfOperations.refreshStorageSystem(systemURI, volumeURIs); } /** * Before the clone could be deleted, if the clone is from a CG, we will * remove the target group, then reset the replicationGroupInstance for the clones in the group. * * @param storage * @param clones * @throws Exception */ private void processClonesBeforeDeletion(StorageSystem storage, Set<Volume> clones) throws Exception { _log.info("process clones before deletion"); for (Volume clone : clones) { String groupName = clone.getReplicationGroupInstance(); if (storage.deviceIsType(Type.vmax) && NullColumnValueGetter.isNotNullValue(groupName)) { CIMObjectPath cgPath = _cimPath.getReplicationGroupPath(storage, clone.getReplicationGroupInstance()); ReplicationUtils.deleteTargetDeviceGroup(storage, cgPath, _dbClient, _helper, _cimPath); URIQueryResultList queryResults = new URIQueryResultList(); _dbClient.queryByConstraint(AlternateIdConstraint.Factory .getVolumeReplicationGroupInstanceConstraint(clone .getReplicationGroupInstance()), queryResults); Iterator<URI> resultsIter = queryResults.iterator(); while (resultsIter.hasNext()) { URI cloneUri = resultsIter.next(); Volume theClone = _dbClient.queryObject(Volume.class, cloneUri); theClone.setReplicationGroupInstance(NullColumnValueGetter.getNullStr()); _dbClient.persistObject(theClone); } } } } @Override public void doChangeCopyMode(StorageSystem system, Volume target, TaskCompleter completer) { _srdfOperations.performChangeCopyMode(system, target, completer); } @Override public void doCreateListReplica(StorageSystem storage, List<URI> replicaList, Boolean createInactive, TaskCompleter taskCompleter) { _replicaOperations.createListReplica(storage, replicaList, createInactive, taskCompleter); } @Override public void doDetachListReplica(StorageSystem storage, List<URI> replicaList, TaskCompleter taskCompleter) { _replicaOperations.detachListReplica(storage, replicaList, taskCompleter); } @Override public void doFractureListReplica(StorageSystem storage, List<URI> replicaList, Boolean sync, TaskCompleter taskCompleter) { _replicaOperations.fractureListReplica(storage, replicaList, sync, taskCompleter); } @Override public void doDeleteListReplica(StorageSystem storage, List<URI> replicaList, TaskCompleter taskCompleter) { _replicaOperations.deleteListReplica(storage, replicaList, taskCompleter); } @Override public Map<URI, Integer> getExportMaskHLUs(StorageSystem storage, ExportMask exportMask) { return _exportMaskOperationsHelper.getExportMaskHLUs(storage, exportMask); } /** * This method tests if the array represented by 'storageSystem' supports volume expand. * * @param storageSystem * [IN] - StorageSystem object to check the volume expand capabilities * @return true iff The array indicates that it supports volume expand. */ private boolean doesStorageSystemSupportVolumeExpand(StorageSystem storageSystem) throws WBEMException { boolean expandSupported = false; CloseableIterator<CIMInstance> cimInstances = null; CIMObjectPath storageConfigServicePath = _cimPath.getConfigSvcPath(storageSystem); try { cimInstances = _helper.getAssociatorInstances(storageSystem, storageConfigServicePath, null, SmisConstants.EMC_STORAGE_CONFIGURATION_CAPABILITIES, null, null, SmisConstants.PS_SUPPORTED_STORAGE_ELEMENT_FEATURES); if (cimInstances != null) { while (cimInstances.hasNext()) { CIMInstance capabilitiesInstance = cimInstances.next(); UnsignedInteger16[] supportedFeatures = (UnsignedInteger16[]) capabilitiesInstance .getPropertyValue(SmisConstants.CP_SUPPORTED_STORAGE_ELEMENT_FEATURES); for (UnsignedInteger16 supportedFeatureEntry : supportedFeatures) { if (supportedFeatureEntry.intValue() == SmisConstants.STORAGE_ELEMENT_CAPACITY_EXPANSION_VALUE) { expandSupported = true; return true; } } } } } finally { if (cimInstances != null) { cimInstances.close(); } _log.info(String.format("StorageSystem %s %s volume expand", storageSystem.getNativeGuid(), (expandSupported) ? "supports" : "does not support")); } return false; } @Override public void refreshVolumeProperties(URI systemURI, List<URI> volumeURIs) throws Exception { _srdfOperations.refreshVolumeProperties(systemURI, volumeURIs); } @Override public void doUntagVolumes(StorageSystem storageSystem, String opId, List<Volume> volumes, TaskCompleter taskCompleter) throws DeviceControllerException { try { int volumeCount = 0; String[] volumeNativeIds = new String[volumes.size()]; StringBuilder logMsgBuilder = new StringBuilder(String.format( "Untag Volume Start - Array:%s", storageSystem.getSerialNumber())); MultiVolumeTaskCompleter multiVolumeTaskCompleter = (MultiVolumeTaskCompleter) taskCompleter; for (Volume volume : volumes) { logMsgBuilder.append(String.format("%nVolume:%s", volume.getLabel())); _helper.doApplyRecoverPointTag(storageSystem, volume, false); } } catch (WBEMException e) { _log.error("Problem making SMI-S call: ", e); ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e .getMessage()); taskCompleter.error(_dbClient, error); } catch (Exception e) { _log.error("Problem in doUntagVolume: ", e); ServiceError error = DeviceControllerErrors.smis.methodFailed("doUntagVolume", e.getMessage()); taskCompleter.error(_dbClient, error); } StringBuilder logMsgBuilder = new StringBuilder(String.format( "Untag Volume End - Array: %s", storageSystem.getSerialNumber())); for (Volume volume : volumes) { logMsgBuilder.append(String.format("%nVolume:%s", volume.getLabel())); } _log.info(logMsgBuilder.toString()); } /** * {@inheritDoc} */ @Override public void doCreateSnapshotSession(StorageSystem system, URI snapSessionURI, String groupName, TaskCompleter completer) throws DeviceControllerException { try { if (checkSnapshotSessionConsistencyGroup(snapSessionURI, _dbClient, completer)) { // Note that this will need to be changed when we add group support. // Because RP+VPLEX requires groups, even if we aren't really doing // a group operation, it will be determined this is a group operation. // For now we just call the single snapshot session create. _snapshotOperations.createGroupSnapshotSession(system, snapSessionURI, groupName, completer); } else { _snapshotOperations.createSnapshotSession(system, snapSessionURI, completer); } } catch (Exception e) { _log.error(String.format("Exception trying to create snapshot session(s) on array %s", system.getSerialNumber()), e); ServiceError error = DeviceControllerErrors.smis.methodFailed("doCreateSnapshotSession", e.getMessage()); completer.error(_dbClient, error); } } /** * {@inheritDoc} */ @Override public void doLinkBlockSnapshotSessionTarget(StorageSystem system, URI snapSessionURI, URI snapshotURI, String copyMode, Boolean targetExists, TaskCompleter completer) throws DeviceControllerException { try { _snapshotOperations.linkSnapshotSessionTarget(system, snapSessionURI, snapshotURI, copyMode, targetExists, completer); } catch (Exception e) { _log.error(String.format("Exception trying to create and link new target to block snapshot session %s on array %s", snapSessionURI, system.getSerialNumber()), e); ServiceError error = DeviceControllerErrors.smis.methodFailed("doLinkBlockSnapshotSessionTarget", e.getMessage()); completer.error(_dbClient, error); } } @Override public void doLinkBlockSnapshotSessionTargetGroup(StorageSystem system, URI snapshotSessionURI, List<URI> snapSessionSnapshotURIs, String copyMode, Boolean targetsExist, TaskCompleter completer) throws DeviceControllerException { try { _snapshotOperations.linkSnapshotSessionTargetGroup(system, snapshotSessionURI, snapSessionSnapshotURIs, copyMode, targetsExist, completer); } catch (Exception e) { // TODO Fix error message _log.error(String.format("Exception trying to create and link new target to block snapshot session %s on array %s", "TODO", system.getSerialNumber()), e); ServiceError error = DeviceControllerErrors.smis.methodFailed("doLinkBlockSnapshotSessionTarget", e.getMessage()); completer.error(_dbClient, error); } } /** * {@inheritDoc} */ @Override public void doRelinkBlockSnapshotSessionTarget(StorageSystem system, URI tgtSnapSessionURI, URI snapshotURI, TaskCompleter completer) throws DeviceControllerException { try { if (checkSnapshotSessionConsistencyGroup(tgtSnapSessionURI, _dbClient, completer)) { _snapshotOperations.relinkSnapshotSessionTargetGroup(system, tgtSnapSessionURI, snapshotURI, completer); } else { _snapshotOperations.relinkSnapshotSessionTarget(system, tgtSnapSessionURI, snapshotURI, completer); } } catch (Exception e) { _log.error(String.format("Exception trying to re-link target to block snapshot session %s on array %s", tgtSnapSessionURI, system.getSerialNumber()), e); ServiceError error = DeviceControllerErrors.smis.methodFailed("doRelinkBlockSnapshotSessionTarget", e.getMessage()); completer.error(_dbClient, error); } } /** * {@inheritDoc} */ @Override public void doUnlinkBlockSnapshotSessionTarget(StorageSystem system, URI snapSessionURI, URI snapshotURI, Boolean deleteTarget, TaskCompleter completer) throws DeviceControllerException { try { _snapshotOperations.unlinkSnapshotSessionTarget(system, snapSessionURI, snapshotURI, deleteTarget, completer); } catch (Exception e) { _log.error(String.format("Exception trying to unlink target from block snapshot session %s on array %s", snapSessionURI, system.getSerialNumber()), e); ServiceError error = DeviceControllerErrors.smis.methodFailed("doUnlinkBlockSnapshotSessionTarget", e.getMessage()); completer.error(_dbClient, error); } } /** * {@inheritDoc} */ @Override public void doRestoreBlockSnapshotSession(StorageSystem system, URI snapSessionURI, TaskCompleter completer) throws DeviceControllerException { try { _snapshotOperations.restoreSnapshotSession(system, snapSessionURI, completer); } catch (Exception e) { _log.error(String.format("Exception trying to restore block snapshot session %s on array %s", snapSessionURI, system.getSerialNumber()), e); ServiceError error = DeviceControllerErrors.smis.methodFailed("doRestoreBlockSnapshotSession", e.getMessage()); completer.error(_dbClient, error); } } /** * {@inheritDoc} */ @Override public void doDeleteBlockSnapshotSession(StorageSystem system, URI snapSessionURI, String groupName, TaskCompleter completer) throws DeviceControllerException { try { _snapshotOperations.deleteSnapshotSession(system, snapSessionURI, groupName, completer); } catch (Exception e) { _log.error(String.format("Exception trying to delete block snapshot session %s on array %s", snapSessionURI, system.getSerialNumber()), e); ServiceError error = DeviceControllerErrors.smis.methodFailed("doDeleteBlockSnapshotSession", e.getMessage()); completer.error(_dbClient, error); } } /** * Determines whether we do group snapshot session creation on the array. * * @param snapSessions * The session(s) to be created. * * @return true to do group creation, false otherwise. */ private boolean doGroupSnapshotSessionCreation(List<BlockSnapshotSession> snapSessions) { boolean doGroupCreation = false; if (snapSessions.size() > 1) { doGroupCreation = true; } else { BlockSnapshotSession snapSession = snapSessions.get(0); URI sourceObjURI = snapSession.getParent().getURI(); BlockObject sourceObj = BlockObject.fetch(_dbClient, sourceObjURI); URI cgURI = sourceObj.getConsistencyGroup(); if (!NullColumnValueGetter.isNullURI(cgURI)) { doGroupCreation = true; } } return doGroupCreation; } @Override public void doAddSnapshotSessionsToConsistencyGroup(StorageSystem storageSystem, URI consistencyGroup, List<URI> volumes, TaskCompleter taskCompleter) { List<? extends BlockObject> blockObjects = BlockObject.fetch(_dbClient, volumes); BlockConsistencyGroup cg = _dbClient.queryObject(BlockConsistencyGroup.class, consistencyGroup); Map<String, List<BlockSnapshotSession>> sessionLabelsMap = new HashMap<>(); for (BlockObject blockObject : blockObjects) { List<BlockSnapshotSession> sessions = CustomQueryUtility.queryActiveResourcesByConstraint(_dbClient, BlockSnapshotSession.class, ContainmentConstraint.Factory.getParentSnapshotSessionConstraint(blockObject.getId())); if (!sessions.isEmpty()) { for (BlockSnapshotSession session : sessions) { if (!sessionLabelsMap.containsKey(session.getSessionLabel())) { sessionLabelsMap.put(session.getSessionLabel(), new ArrayList<BlockSnapshotSession>()); } sessionLabelsMap.get(session.getSessionLabel()).add(session); } } } try { fabricateSourceGroupAspects(storageSystem, cg, sessionLabelsMap); taskCompleter.ready(_dbClient); } catch (WBEMException e) { _log.error("Problem in adding snapshot sessions to Consistency Group {}", consistencyGroup, e); taskCompleter.error(_dbClient, DeviceControllerException.exceptions .failedToAddMembersToConsistencyGroup(cg.getLabel(), cg.getCgNameOnStorageSystem(storageSystem.getId()), e.getMessage())); } } /** * This method will be used to set the Initiator Alias for a given initiator. * The SMI-S version that supports this operation is Version 8.2 onwards. * The initiator must be part of the an Initiator Group for the Value to be set * * @param storage * - StorageSystem object * @param initiator * - Initiator Object for which the Alias needs to be set * @param initiatorAlias * - User Friendly Name * @throws Exception */ public void doInitiatorAliasSet(StorageSystem storage, Initiator initiator, String initiatorAlias) throws Exception { try { checkIfProviderSupportsAliasOperations(storage); CIMObjectPath hwManagementIDSvcPath = _cimPath.getStorageHardwareIDManagementService(storage); CIMObjectPath shidPath = getSHIDPathForAliasOperation(storage, hwManagementIDSvcPath, initiator); CIMArgument[] inArgs = _helper.getEMCInitiatorAliasSetArgs(shidPath, initiatorAlias); CIMArgument[] outArgs = new CIMArgument[5]; _helper.invokeMethod(storage, hwManagementIDSvcPath, SmisConstants.INITIATOR_ALIAS_SET, inArgs, outArgs); } catch (WBEMException e) { _log.error("Problem making SMI-S call: ", e); throw e; } catch (Exception e) { _log.error("Unexpected error: EMCInitiatorAliasSet failed.", e); throw e; } } /** * This method will be used to get the Initiator Alias for a given initiator. * The SMI-S version that supports this operation is Version 8.2 onwards. * The initiator must be part of the an Initiator Group for the Value to be retrieved * If the Alias is not set, an EMPTY string will be returned * * @param storage * - StorageSystem object * @param initiator * - Initiator Object for which the Alias needs to be set * @return initiatorAlias - User Friendly Name * @throws Exception */ public String doInitiatorAliasGet(StorageSystem storage, Initiator initiator) throws Exception { String initiatorAlias = null; try { checkIfProviderSupportsAliasOperations(storage); CIMObjectPath hwManagementIDSvcPath = _cimPath.getStorageHardwareIDManagementService(storage); CIMObjectPath shidPath = getSHIDPathForAliasOperation(storage, hwManagementIDSvcPath, initiator); CIMArgument[] inArgs = _helper.getEMCInitiatorAliasGetArgs(shidPath); CIMArgument[] outArgs = new CIMArgument[5]; _helper.invokeMethod(storage, hwManagementIDSvcPath, SmisConstants.INITIATOR_ALIAS_GET, inArgs, outArgs); for (CIMArgument arg : outArgs) { if (arg != null && arg.getName().equalsIgnoreCase(SmisConstants.CP_ALIAS_STORAGEID)) { initiatorAlias = (String) arg.getValue(); } } } catch (WBEMException e) { _log.error("Problem making SMI-S call: ", e); throw e; } catch (Exception e) { _log.error("Unexpected error: EMCInitiatorAliasGet failed.", e); throw e; } return initiatorAlias; } /** * Get the StorageHardwareID CIM path when found on the Storage System * Throw an exception if it is not found. * * @param storage * storage system * @param hwManagementIDSvcPath * @param initiator * initiator Object * @return CIMObjectPath corresponding to that StorageHardwareID * @throws Exception */ private CIMObjectPath getSHIDPathForAliasOperation(StorageSystem storage, CIMObjectPath hwManagementIDSvcPath, Initiator initiator) throws Exception { // Multiple arrays can be managed by a single SMI-S instance. The SE_StorageHardwareID is // global to the provider, so we need to get the SE_StorageHardware_ID object that are // associated with a specific array. CIMObjectPath shidPath = null; String normalizedPortName = Initiator.normalizePort(initiator.getInitiatorPort()); CloseableIterator<CIMInstance> initiatorInstances = null; try { initiatorInstances = _helper.getAssociatorInstances(storage, hwManagementIDSvcPath, null, SmisConstants.CP_SE_STORAGE_HARDWARE_ID, null, null, SmisConstants.PS_STORAGE_ID); while (initiatorInstances.hasNext()) { CIMInstance initiatorInstance = initiatorInstances.next(); String storageId = CIMPropertyFactory.getPropertyValue(initiatorInstance, SmisConstants.CP_STORAGE_ID); if (normalizedPortName.equals(storageId)) { shidPath = initiatorInstance.getObjectPath(); break; } } } catch (WBEMException we) { _log.error("Caught an error will attempting to execute query and process query result. Query: ", we); } finally { initiatorInstances.close(); } if ((shidPath == null) || shidPath.toString().isEmpty()) { String errMsg = String.format("Supplied initiator: %s was not found on the Storage System: %s", normalizedPortName, storage.getSerialNumber()); _log.error(errMsg); throw DeviceControllerException.exceptions.couldNotPerformAliasOperation(errMsg); } return shidPath; } /** * This method return true if the SMI-S provider supports initiator alias operations. * If not, it will throw an exception * * @param storage * - StorageSystem object * @throws Exception */ private void checkIfProviderSupportsAliasOperations(StorageSystem storageSystem) throws Exception { String versionSubstring = null; if (storageSystem.checkIfVmax3() && storageSystem.getUsingSmis80()) { try { StorageProvider storageProvider = _dbClient.queryObject(StorageProvider.class, storageSystem.getActiveProviderURI()); String providerVersion = storageProvider.getVersionString(); versionSubstring = providerVersion.split("\\.")[1]; } catch (Exception e) { _log.error("Exception get provider version for the storage system {} {}.", storageSystem.getLabel(), storageSystem.getId()); throw e; } } if (NullColumnValueGetter.isNullValue(versionSubstring) || !(Integer.parseInt(versionSubstring) >= 2)) { String errMsg = String.format( "SMI-S Provider associated with Storage System %s does not support Initiator Alias operations", storageSystem.getSerialNumber()); _log.error(errMsg); throw DeviceControllerException.exceptions.couldNotPerformAliasOperation(errMsg); } } @Override public void doExportAddPaths(final StorageSystem storage, final URI exportMask, final Map<URI, List<URI>> newPaths, final TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("{} doExportAddPaths START ...", storage.getSerialNumber()); _exportMaskOperationsHelper.addPaths(storage, exportMask, newPaths, taskCompleter); _log.info("{} doExportAddPaths END ...", storage.getSerialNumber()); } @Override public void doExportRemovePaths(final StorageSystem storage, final URI exportMask, final Map<URI, List<URI>> adjustedPaths, final Map<URI, List<URI>> removePaths, final TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("{} doExportRemovePaths START ...", storage.getSerialNumber()); _exportMaskOperationsHelper.removePaths(storage, exportMask, adjustedPaths, removePaths, taskCompleter); _log.info("{} doExportRemovePaths END ...", storage.getSerialNumber()); } }