/* * Copyright (c) 2015 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.smis; import static com.emc.storageos.volumecontroller.impl.smis.SmisConstants.JOB_COMPLETED_NO_ERROR; import java.net.URI; import java.util.List; import java.util.Set; import javax.cim.CIMArgument; import javax.cim.CIMInstance; import javax.cim.CIMObjectPath; import javax.cim.UnsignedInteger32; import javax.wbem.CloseableIterator; import javax.wbem.WBEMException; import com.emc.storageos.db.client.model.SynchronizationState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.model.BlockMirror; import com.emc.storageos.db.client.model.DiscoveredDataObject.Type; import com.emc.storageos.db.client.model.StoragePool; import com.emc.storageos.db.client.model.StorageSystem; import com.emc.storageos.db.client.model.TenantOrg; import com.emc.storageos.db.client.model.Volume; import com.emc.storageos.db.client.util.NameGenerator; import com.emc.storageos.db.client.util.NullColumnValueGetter; import com.emc.storageos.exceptions.DeviceControllerErrors; import com.emc.storageos.exceptions.DeviceControllerException; import com.emc.storageos.svcs.errorhandling.model.ServiceError; import com.emc.storageos.svcs.errorhandling.resources.InternalException; import com.emc.storageos.volumecontroller.TaskCompleter; import com.emc.storageos.volumecontroller.impl.ControllerServiceImpl; import com.emc.storageos.volumecontroller.impl.job.QueueJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisBlockCreateMirrorJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisBlockDeleteCGMirrorJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisBlockDeleteMirrorJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisBlockResumeMirrorJob; /** * A class to provide common, array-independent mirror implementations */ public abstract class AbstractMirrorOperations implements MirrorOperations { private static final Logger _log = LoggerFactory.getLogger(AbstractMirrorOperations.class); protected DbClient _dbClient; protected SmisCommandHelper _helper; protected CIMObjectPathFactory _cimPath; protected NameGenerator _nameGenerator; public void setCimObjectPathFactory(CIMObjectPathFactory cimObjectPathFactory) { _cimPath = cimObjectPathFactory; } public void setDbClient(DbClient dbClient) { _dbClient = dbClient; } public void setSmisCommandHelper(SmisCommandHelper smisCommandHelper) { _helper = smisCommandHelper; } public void setNameGenerator(NameGenerator nameGenerator) { _nameGenerator = nameGenerator; } @Override public void createSingleVolumeMirror(StorageSystem storage, URI mirror, Boolean createInactive, TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("createSingleVolumeMirror operation START"); try { BlockMirror mirrorObj = _dbClient.queryObject(BlockMirror.class, mirror); StoragePool targetPool = _dbClient.queryObject(StoragePool.class, mirrorObj.getPool()); Volume source = _dbClient.queryObject(Volume.class, mirrorObj.getSource()); TenantOrg tenant = _dbClient.queryObject(TenantOrg.class, source.getTenant().getURI()); String tenantName = tenant.getLabel(); String targetLabelToUse = _nameGenerator.generate(tenantName, mirrorObj.getLabel(), mirror.toString(), '-', SmisConstants.MAX_VOLUME_NAME_LENGTH); CIMObjectPath replicationSvcPath = _cimPath.getControllerReplicationSvcPath(storage); CIMArgument[] inArgs = null; if (storage.checkIfVmax3()) { CIMObjectPath volumeGroupPath = _helper.getVolumeGroupPath(storage, storage, source, targetPool); CIMInstance replicaSettingData = getDefaultReplicationSettingData(storage); inArgs = _helper.getCreateElementReplicaMirrorInputArguments(storage, source, targetPool, createInactive, targetLabelToUse, volumeGroupPath, replicaSettingData); } else { inArgs = _helper.getCreateElementReplicaMirrorInputArguments(storage, source, targetPool, createInactive, targetLabelToUse); } CIMArgument[] outArgs = new CIMArgument[5]; _helper.invokeMethod(storage, replicationSvcPath, SmisConstants.CREATE_ELEMENT_REPLICA, inArgs, outArgs); CIMObjectPath job = _cimPath.getCimObjectPathFromOutputArgs(outArgs, SmisConstants.JOB); if (job != null) { ControllerServiceImpl.enqueueJob(new QueueJob(new SmisBlockCreateMirrorJob(job, storage.getId(), !createInactive, taskCompleter))); // Resynchronizing state applies to the initial copy as well as future // re-synchronization's. mirrorObj.setSyncState(SynchronizationState.RESYNCHRONIZING.toString()); _dbClient.persistObject(mirrorObj); } } catch (final InternalException e) { _log.info("Problem making SMI-S call: ", e); taskCompleter.error(_dbClient, e); } catch (Exception e) { _log.info("Problem making SMI-S call: ", e); ServiceError serviceError = DeviceControllerErrors.smis.unableToCallStorageProvider(e.getMessage()); taskCompleter.error(_dbClient, serviceError); } } @Override public void fractureSingleVolumeMirror(StorageSystem storage, URI mirror, Boolean sync, TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("fractureSingleVolumeMirror operation START"); CloseableIterator<CIMObjectPath> storageSyncRefs = null; try { BlockMirror mirrorObj = _dbClient.queryObject(BlockMirror.class, mirror); CIMObjectPath mirrorPath = _cimPath.getBlockObjectPath(storage, mirrorObj); // Get reference to the CIM_StorageSynchronized instance storageSyncRefs = _helper.getReference(storage, mirrorPath, SmisConstants.CIM_STORAGE_SYNCHRONIZED, null); boolean isVmax3 = storage.checkIfVmax3(); while (storageSyncRefs.hasNext()) { CIMObjectPath storageSync = storageSyncRefs.next(); CIMArgument[] inArgs = isVmax3 ? _helper.getFractureMirrorInputArgumentsWithCopyState(storageSync, sync) : _helper.getFractureMirrorInputArguments(storageSync, sync); CIMArgument[] outArgs = new CIMArgument[5]; // Invoke method to fracture the synchronization _helper.callModifyReplica(storage, inArgs, outArgs); taskCompleter.ready(_dbClient); } } catch (Exception e) { _log.info("Problem making SMI-S call", e); ServiceError serviceError = DeviceControllerException.errors.jobFailed(e); taskCompleter.error(_dbClient, serviceError); } finally { if (storageSyncRefs != null) { storageSyncRefs.close(); } } } @Override public void resumeSingleVolumeMirror(StorageSystem storage, URI mirror, TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("resumeSingleVolumeMirror operation START"); CloseableIterator<CIMObjectPath> storageSyncRefs = null; try { BlockMirror mirrorObj = _dbClient.queryObject(BlockMirror.class, mirror); CIMObjectPath mirrorPath = _cimPath.getBlockObjectPath(storage, mirrorObj); // Get reference to the CIM_StorageSynchronized instance storageSyncRefs = _helper.getReference(storage, mirrorPath, SmisConstants.CIM_STORAGE_SYNCHRONIZED, null); if (!storageSyncRefs.hasNext()) { _log.error("No synchronization instance found for {}", mirror); taskCompleter.error(_dbClient, DeviceControllerException.exceptions.resumeVolumeMirrorFailed(mirror)); return; } boolean isVmax3 = storage.checkIfVmax3(); while (storageSyncRefs.hasNext()) { CIMObjectPath storageSync = storageSyncRefs.next(); _log.debug(storageSync.toString()); /** * JIRA CTRL-11855 * User created mirror and did pause operation using SMI 4.6.2. * Then He upgraded to SMI 8.0.3. While doing mirror resume getting exception from SMI because of the * existing mirrorObj.getSynchronizedInstance() contains SystemName=\"SYMMETRIX+000195701573\"" * This is wrong with 8.0.3 as SystemName=\"SYMMETRIX-+-000195701573\"". * To resolve this issue setting new value collected from current smis provider here. * */ mirrorObj.setSynchronizedInstance(storageSync.toString()); _dbClient.persistObject(mirrorObj); CIMArgument[] inArgs = isVmax3 ? _helper.getResumeSynchronizationInputArgumentsWithCopyState(storageSync) : _helper.getResumeSynchronizationInputArguments(storageSync); CIMArgument[] outArgs = new CIMArgument[5]; _helper.callModifyReplica(storage, inArgs, outArgs); CIMObjectPath job = _cimPath.getCimObjectPathFromOutputArgs(outArgs, SmisConstants.JOB); if (job != null) { ControllerServiceImpl.enqueueJob(new QueueJob(new SmisBlockResumeMirrorJob(job, storage.getId(), taskCompleter))); } else { CIMInstance syncObject = _helper.getInstance(storage, storageSync, false, false, new String[] { SmisConstants.CP_SYNC_STATE }); mirrorObj.setSyncState(CIMPropertyFactory.getPropertyValue(syncObject, SmisConstants.CP_SYNC_STATE)); _dbClient.persistObject(mirrorObj); taskCompleter.ready(_dbClient); } } } catch (Exception e) { _log.error("Failed to resume single volume mirror: {}", mirror); ServiceError serviceError = DeviceControllerException.errors.jobFailed(e); taskCompleter.error(_dbClient, serviceError); } finally { if (storageSyncRefs != null) { storageSyncRefs.close(); } } } @Override public void establishVolumeNativeContinuousCopyGroupRelation(StorageSystem storage, URI sourceVolume, URI mirror, TaskCompleter taskCompleter) throws DeviceControllerException { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void detachSingleVolumeMirror(StorageSystem storage, URI mirror, TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("detachSingleVolumeMirror operation START"); try { BlockMirror mirrorObj = _dbClient.queryObject(BlockMirror.class, mirror); CIMArgument[] inArgs = _helper.getDetachSynchronizationInputArguments(storage, mirrorObj); CIMArgument[] outArgs = new CIMArgument[5]; // Invoke method to detach the local mirror UnsignedInteger32 result = (UnsignedInteger32) _helper.callModifyReplica(storage, inArgs, outArgs); if (JOB_COMPLETED_NO_ERROR.equals(result)) { taskCompleter.ready(_dbClient); } else { String msg = String.format("SMI-S call returned unsuccessfully: %s", result); taskCompleter.error(_dbClient, DeviceControllerException.errors.smis.jobFailed(msg)); } } catch (Exception e) { _log.error("Problem making SMI-S call: ", e); ServiceError serviceError = DeviceControllerException.errors.jobFailed(e); taskCompleter.error(_dbClient, serviceError); } } @Override public void deleteSingleVolumeMirror(StorageSystem storage, URI mirror, TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("deleteSingleVolumeMirror operation START"); try { BlockMirror mirrorObj = _dbClient.queryObject(BlockMirror.class, mirror); if (storage.checkIfVmax3()) { _helper.removeVolumeFromParkingSLOStorageGroup(storage, mirrorObj.getNativeId(), false); _log.info("Done invoking remove volume {} from parking SLO storage group", mirrorObj.getNativeId()); } CIMObjectPath mirrorPath = _cimPath.getBlockObjectPath(storage, mirrorObj); CIMObjectPath configSvcPath = _cimPath.getConfigSvcPath(storage); CIMArgument[] inArgs = _helper.getDeleteMirrorInputArguments(storage, mirrorPath); CIMArgument[] outArgs = new CIMArgument[5]; _helper.invokeMethod(storage, configSvcPath, SmisConstants.RETURN_TO_STORAGE_POOL, inArgs, outArgs); CIMObjectPath job = _cimPath.getCimObjectPathFromOutputArgs(outArgs, SmisConstants.JOB); if (job != null) { ControllerServiceImpl.enqueueJob(new QueueJob(new SmisBlockDeleteMirrorJob(job, storage.getId(), taskCompleter))); } } catch (Exception e) { _log.info("Problem making SMI-S call: ", e); ServiceError serviceError = DeviceControllerErrors.smis.unableToCallStorageProvider(e.getMessage()); taskCompleter.error(_dbClient, serviceError); } } // For VMAX V3 only for now public CIMInstance getDefaultReplicationSettingData(StorageSystem storage) throws WBEMException { CIMInstance defaultInstance = null; CloseableIterator<CIMInstance> repSvcCapIter = null; try { repSvcCapIter = _helper.getAssociatorInstances(storage, _cimPath.getControllerReplicationSvcPath(storage), null, _cimPath.prefixWithParamName(SmisConstants.REPLICATION_SERVICE_CAPABILTIES), null, null, null); if (repSvcCapIter != null && repSvcCapIter.hasNext()) { CIMInstance instance = repSvcCapIter.next(); CIMArgument[] in = _helper.getDefaultReplicationSettingDataInputArgumentsForLocalMirror(); CIMArgument[] out = new CIMArgument[5]; _helper.invokeMethod(storage, instance.getObjectPath(), SmisConstants.GET_DEFAULT_REPLICATION_SETTING_DATA, in, out); defaultInstance = (CIMInstance) _cimPath.getFromOutputArgs(out, SmisConstants.DEFAULT_INSTANCE); } } finally { if (repSvcCapIter != null) { repSvcCapIter.close(); } } return defaultInstance; } @Override public void createGroupMirrors(StorageSystem storage, List<URI> mirrorList, Boolean createInactive, TaskCompleter taskCompleter) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void fractureGroupMirrors(StorageSystem storage, List<URI> mirrorList, Boolean sync, TaskCompleter taskCompleter) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void resumeGroupMirrors(StorageSystem storage, List<URI> mirrorList, TaskCompleter taskCompleter) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void detachGroupMirrors(StorageSystem storage, List<URI> mirrorList, Boolean deleteGroup, TaskCompleter taskCompleter) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } @Override public void deleteGroupMirrors(StorageSystem storage, List<URI> mirrorList, TaskCompleter taskCompleter) throws DeviceControllerException { _log.info("deleteGroupMirrors operation START"); if (!((storage.getUsingSmis80() && storage.deviceIsType(Type.vmax)) || storage.deviceIsType(Type.vnxblock))) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } try { String[] deviceIds = null; BlockMirror firstMirror = _dbClient.queryObject(BlockMirror.class, mirrorList.get(0)); String repGroupName = firstMirror.getReplicationGroupInstance(); if (NullColumnValueGetter.isNotNullValue(repGroupName)) { CIMObjectPath repGroupPath = _cimPath.getReplicationGroupPath(storage, repGroupName); Set<String> deviceIdsSet = _helper.getVolumeDeviceIdsFromStorageGroup(storage, repGroupPath); deviceIds = deviceIdsSet.toArray(new String[deviceIdsSet.size()]); // Delete replication group ReplicationUtils.deleteReplicationGroup(storage, repGroupName, _dbClient, _helper, _cimPath); // Set mirrors replication group to null List<BlockMirror> mirrors = _dbClient.queryObject(BlockMirror.class, mirrorList); for (BlockMirror mirror : mirrors) { mirror.setConsistencyGroup(NullColumnValueGetter.getNullURI()); mirror.setReplicationGroupInstance(NullColumnValueGetter.getNullStr()); } _dbClient.persistObject(mirrors); } else { deviceIds = _helper.getBlockObjectNativeIds(mirrorList); } if (storage.checkIfVmax3()) { for (String deviceId : deviceIds) { _helper.removeVolumeFromParkingSLOStorageGroup(storage, deviceId, false); _log.info("Done invoking remove volume {} from parking SLO storage group", deviceId); } } CIMObjectPath[] mirrorPaths = _cimPath.getVolumePaths(storage, deviceIds); CIMObjectPath configSvcPath = _cimPath.getConfigSvcPath(storage); CIMArgument[] inArgs = null; if (storage.deviceIsType(Type.vnxblock)) { inArgs = _helper.getReturnElementsToStoragePoolArguments(mirrorPaths); } else { inArgs = _helper.getReturnElementsToStoragePoolArguments(mirrorPaths, SmisConstants.CONTINUE_ON_NONEXISTENT_ELEMENT); } CIMArgument[] outArgs = new CIMArgument[5]; _helper.invokeMethod(storage, configSvcPath, SmisConstants.RETURN_ELEMENTS_TO_STORAGE_POOL, inArgs, outArgs); CIMObjectPath job = _cimPath.getCimObjectPathFromOutputArgs(outArgs, SmisConstants.JOB); ControllerServiceImpl.enqueueJob(new QueueJob(new SmisBlockDeleteCGMirrorJob(job, storage.getId(), taskCompleter))); } catch (Exception e) { _log.error("Problem making SMI-S call: ", e); ServiceError serviceError = DeviceControllerErrors.smis.unableToCallStorageProvider(e.getMessage()); taskCompleter.error(_dbClient, serviceError); } } @Override public void removeMirrorFromDeviceMaskingGroup(StorageSystem system, List<URI> mirrorList, TaskCompleter completer) { throw DeviceControllerException.exceptions.blockDeviceOperationNotSupported(); } }