/* * Copyright (c) 2012 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.smis; import java.util.List; import javax.cim.CIMArgument; import javax.cim.CIMInstance; import javax.cim.CIMObjectPath; import javax.wbem.WBEMException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.model.DiscoveredDataObject; 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.exceptions.DatabaseException; import com.emc.storageos.exceptions.DeviceControllerErrors; import com.emc.storageos.exceptions.DeviceControllerException; import com.emc.storageos.protectioncontroller.impl.recoverpoint.RPHelper; import com.emc.storageos.svcs.errorhandling.model.ServiceError; import com.emc.storageos.volumecontroller.ControllerLockingService; import com.emc.storageos.volumecontroller.Job; import com.emc.storageos.volumecontroller.MetaVolumeOperations; import com.emc.storageos.volumecontroller.TaskCompleter; import com.emc.storageos.volumecontroller.impl.block.taskcompleter.MetaVolumeTaskCompleter; import com.emc.storageos.volumecontroller.impl.smis.job.SmisAbstractCreateVolumeJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisCreateMetaVolumeHeadJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisCreateMetaVolumeJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisCreateMetaVolumeMembersJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisCreateMultiVolumeJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisVolumeExpandJob; import com.emc.storageos.volumecontroller.impl.utils.VirtualPoolCapabilityValuesWrapper; public class SmisMetaVolumeOperations implements MetaVolumeOperations { // Max retries for RP expand operation private static final int MAX_RP_EXPAND_RETRIES = 20; // Wait 15 seconds before attempting another call to delete the RP associated XIO volume private static final int RP_EXPAND_WAIT_FOR_RETRY = 5000; private static final String EMC_IS_BOUND = SmisConstants.CP_EMC_IS_BOUND; private static final Logger _log = LoggerFactory.getLogger(MetaVolumeOperations.class); protected DbClient _dbClient; protected SmisCommandHelper _helper; protected CIMObjectPathFactory _cimPath; protected NameGenerator _nameGenerator; private SmisStorageDevicePreProcessor _smisStorageDevicePreProcessor; private ControllerLockingService _locker; public void setLocker(ControllerLockingService locker) { this._locker = locker; } 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; } /** * Create meta volume head device. Meta volume is represented by its head. * We create it as a regular bound volume. * * @param storageSystem * @param storagePool * @param metaHead * @param capacity * @param capabilities * @param metaVolumeTaskCompleter * @throws Exception */ @Override public void createMetaVolumeHead(StorageSystem storageSystem, StoragePool storagePool, Volume metaHead, long capacity, VirtualPoolCapabilityValuesWrapper capabilities, MetaVolumeTaskCompleter metaVolumeTaskCompleter) throws Exception { String label; _log.info(String.format( "Create Meta Volume Head Start - Array: %s, Pool: %s, %n Head: %s, IsThinlyProvisioned: %s, Capacity: %s", storageSystem.getSerialNumber(), storagePool.getNativeId(), metaHead.getLabel(), metaHead.getThinlyProvisioned(), capacity)); String tenantName = ""; try { TenantOrg tenant = _dbClient.queryObject(TenantOrg.class, metaHead.getTenant().getURI()); tenantName = tenant.getLabel(); } catch (DatabaseException e) { _log.error("Error lookup TenantOrb object", e); } label = _nameGenerator.generate(tenantName, metaHead.getLabel(), metaHead.getId().toString(), '-', SmisConstants.MAX_VOLUME_NAME_LENGTH); boolean isThinlyProvisioned = metaHead.getThinlyProvisioned(); // Thin stripe meta heads should be created unbound from pool on VMAX // Thin concatenated meta heads are created unbound from pool on vmax as well. // This is done to preallocate capacity later when meta volume is bound to pool. boolean isBoundToPool = !(isThinlyProvisioned && DiscoveredDataObject.Type.vmax.toString().equalsIgnoreCase(storageSystem.getSystemType())); try { CIMObjectPath configSvcPath = _cimPath.getConfigSvcPath(storageSystem); CIMArgument[] inArgs; // Prepare parameters and call method to create meta head // only for vnxBlock, we need to associate StoragePool Setting as Goal if (DiscoveredDataObject.Type.vnxblock.toString().equalsIgnoreCase( storageSystem.getSystemType())) { inArgs = _helper.getCreateVolumesInputArgumentsOnFastEnabledPool(storageSystem, storagePool, label, capacity, 1, isThinlyProvisioned, capabilities.getAutoTierPolicyName()); } else { inArgs = _helper.getCreateVolumesInputArguments(storageSystem, storagePool, label, capacity, 1, isThinlyProvisioned, null, isBoundToPool); } CIMArgument[] outArgs = new CIMArgument[5]; StorageSystem forProvider = _helper.getStorageSystemForProvider(storageSystem, metaHead); _log.info("Selected Provider : {}", forProvider.getNativeGuid()); SmisCreateMetaVolumeHeadJob smisJobCompleter = new SmisCreateMetaVolumeHeadJob(null, forProvider.getId(), metaVolumeTaskCompleter, metaHead.getId()); _helper.invokeMethodSynchronously(forProvider, configSvcPath, _helper.createVolumesMethodName(forProvider), inArgs, outArgs, smisJobCompleter); } catch (WBEMException e) { _log.error("Problem making SMI-S call: ", e); ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e.getMessage()); metaVolumeTaskCompleter.getVolumeTaskCompleter().error(_dbClient, error); throw e; } catch (Exception e) { _log.error("Problem in createMetaVolumeHead: " + metaHead.getLabel(), e); ServiceError error = DeviceControllerErrors.smis.methodFailed("createMetaVolumeHead", e.getMessage()); metaVolumeTaskCompleter.getVolumeTaskCompleter().error(_dbClient, error); throw e; } finally { _log.info(String.format( "Create Meta Volume Head End - Array:%s, Pool: %s, %n Head: %s", storageSystem.getSerialNumber(), storagePool.getNativeId(), metaHead.getLabel())); } } /** * Create meta volume member devices. These devices provide capacity to meta volume. * SMI-S requires that these devices be created unbound form a pool. * * @param storageSystem * @param storagePool * @param metaHead * @param memberCount * @param memberCapacity * @param metaVolumeTaskCompleter * @return list of native ids of meta member devices * @throws Exception */ @Override public List<String> createMetaVolumeMembers(StorageSystem storageSystem, StoragePool storagePool, Volume metaHead, int memberCount, long memberCapacity, MetaVolumeTaskCompleter metaVolumeTaskCompleter) throws Exception { _log.info(String.format( "Create Meta Volume Members Start - Array: %s, Pool: %s, %n Volume: %s, Count:%s, Member capacity: %s", storageSystem.getSerialNumber(), storagePool.getNativeId(), metaHead.getLabel(), memberCount, memberCapacity)); try { boolean isThinlyProvisioned = metaHead.getThinlyProvisioned(); CIMObjectPath configSvcPath = _cimPath.getConfigSvcPath(storageSystem); CIMArgument[] inArgs; // Prepare parameters and call method to create meta members inArgs = _helper.getCreateMetaVolumeMembersInputArguments(storageSystem, storagePool, memberCount, memberCapacity, isThinlyProvisioned); CIMArgument[] outArgs = new CIMArgument[5]; StorageSystem forProvider = _helper.getStorageSystemForProvider(storageSystem, metaHead); _log.info("Selected Provider : {}", forProvider.getNativeGuid()); SmisCreateMetaVolumeMembersJob smisJobCompleter = new SmisCreateMetaVolumeMembersJob(null, forProvider.getId(), metaHead, memberCount, metaVolumeTaskCompleter); _helper.invokeMethodSynchronously(forProvider, configSvcPath, SmisConstants.CREATE_OR_MODIFY_ELEMENT_FROM_STORAGE_POOL, inArgs, outArgs, smisJobCompleter); return smisJobCompleter.getMetaMembers(); } catch (WBEMException e) { _log.error("Problem making SMI-S call: ", e); ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e.getMessage()); metaVolumeTaskCompleter.getVolumeTaskCompleter().error(_dbClient, error); throw e; } catch (Exception e) { _log.error("Problem in createMetaVolumeMembers: ", e); ServiceError error = DeviceControllerErrors.smis.methodFailed("createMetaVolumeMemebers", e.getMessage()); metaVolumeTaskCompleter.getVolumeTaskCompleter().error(_dbClient, error); throw e; } finally { _log.info(String.format( "Create Meta Volume Members End - Array: %s, Pool: %s, %n Volume: %s", storageSystem.getSerialNumber(), storagePool.getNativeId(), metaHead.getLabel())); } } /** * Create meta volume from provided meta head and meta members * * @param storageSystem storageSystem * @param metaHead meta head * @param metaMembers list of native ids of meta volume members (not including meta head) * @param metaType meta volume type to create, concatenate or stripe * @param capabilities capabilities * @param metaVolumeTaskCompleter task completer */ @Override public void createMetaVolume(StorageSystem storageSystem, StoragePool storagePool, Volume metaHead, List<String> metaMembers, String metaType, VirtualPoolCapabilityValuesWrapper capabilities, MetaVolumeTaskCompleter metaVolumeTaskCompleter) throws Exception { String label = null; label = metaHead.getLabel(); try { CIMObjectPath elementCompositionServicePath = _cimPath.getElementCompositionSvcPath(storageSystem); // Check if meta head is bound to pool. The binding state is not changed by create meta volume call below, so we can know in // advance if we need // to bind element after this call completes. CIMInstance cimVolume = null; CIMObjectPath volumePath = _cimPath.getBlockObjectPath(storageSystem, metaHead); cimVolume = _helper.getInstance(storageSystem, volumePath, false, false, new String[] { EMC_IS_BOUND }); String isBoundStr = cimVolume.getPropertyValue(EMC_IS_BOUND).toString(); Boolean isBound = Boolean.parseBoolean(isBoundStr); // When isBound is true, create meta volume job is the last job in meta volume create sequence and we can complete this task. // Otherwise, we can complete this task only after binding is executed. Boolean isLastJob = isBound; _log.info(String.format( "Create Meta Volume Start - Array: %s, Head: %s, Type: %s %n Members:%s, isLastJob: %s", storageSystem.getSerialNumber(), metaHead.getLabel(), metaType, metaMembers, isLastJob)); CIMArgument[] inArgs; // Should not change meta head binding state. inArgs = _helper.getCreateMetaVolumeInputArguments(storageSystem, label, metaHead, metaMembers, metaType, false); CIMArgument[] outArgs = new CIMArgument[5]; StorageSystem forProvider = _helper.getStorageSystemForProvider(storageSystem, metaHead); _log.info("Selected Provider : {}", forProvider.getNativeGuid()); SmisJob smisJobCompleter = new SmisCreateMetaVolumeJob(null, forProvider.getId(), storagePool.getId(), metaHead, metaVolumeTaskCompleter, isLastJob); _helper.invokeMethodSynchronously(forProvider, elementCompositionServicePath, SmisConstants.CREATE_OR_MODIFY_COMPOSITE_ELEMENT, inArgs, outArgs, smisJobCompleter); // check if volume has to be bound to pool // thin meta heads are created unbound from pool on VMAX if (metaVolumeTaskCompleter.getLastStepStatus() == Job.JobStatus.SUCCESS) { if (!isBound) { // Set thin meta volume preallocate size when thin meta is bound to pool bindMetaVolumeToPool(storageSystem, storagePool, metaHead, metaVolumeTaskCompleter, true); } } } catch (WBEMException e) { _log.error("Problem making SMI-S call: ", e); ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e.getMessage()); metaVolumeTaskCompleter.getVolumeTaskCompleter().error(_dbClient, error); throw e; } catch (Exception e) { _log.error("Problem in createMetaVolume: ", e); ServiceError error = DeviceControllerErrors.smis.methodFailed("createMetaVolume", e.getMessage()); metaVolumeTaskCompleter.getVolumeTaskCompleter().error(_dbClient, error); throw e; } _log.info(String.format( "Create Meta Volume End - Array:%s, Head:%s, %n Head device ID: %s, Members:%s", storageSystem.getSerialNumber(), metaHead.getLabel(), metaHead.getNativeId(), metaMembers)); } /** * Create meta volumes. * * @param storageSystem storageSystem * @param metaHead meta head * @param metaMembers list of native ids of meta volume members (not including meta head) * @param metaType meta volume type to create, concatenate or stripe * @param capabilities capabilities * @param metaVolumeTaskCompleter task completer */ /** * Create meta volumes * * @param storageSystem * @param storagePool * @param volumes * @param capabilities * @param taskCompleter * @throws Exception */ @Override public void createMetaVolumes(StorageSystem storageSystem, StoragePool storagePool, List<Volume> volumes, VirtualPoolCapabilityValuesWrapper capabilities, TaskCompleter taskCompleter) throws Exception { String label = null; Volume volume = volumes.get(0); // We don't need a label when we are to create more than // one volume. In fact we can't set the label in this // case for VMAX, else the request will fail. // TODO there is a bug in smis --- the request to element composition service fails when name is set even for a single volume. // todo: the opt 450103 was opened on 05/30 Long capacity = volume.getTotalMetaMemberCapacity(); Integer metaMemberCount = volume.getMetaMemberCount(); String metaVolumeType = volume.getCompositionType(); boolean opCreationFailed = false; try { CIMObjectPath elementCompositionServicePath = _cimPath.getElementCompositionSvcPath(storageSystem); boolean isThinlyProvisioned = volume.getThinlyProvisioned(); _log.info(String.format( "Create Meta Volumes Start - Array: %s, Count: %s, MetaType: %s", storageSystem.getSerialNumber(), volumes.size(), metaVolumeType)); CIMArgument[] inArgs; CIMInstance poolSetting = null; // set preallocate size if needed if (isThinlyProvisioned && volume.getThinVolumePreAllocationSize() > 0) { poolSetting = _smisStorageDevicePreProcessor.createStoragePoolSetting( storageSystem, storagePool, volume.getThinVolumePreAllocationSize()); } inArgs = _helper.getCreateMetaVolumesInputArguments(storageSystem, storagePool, label, capacity, volumes.size(), isThinlyProvisioned, metaVolumeType, metaMemberCount, poolSetting); CIMArgument[] outArgs = new CIMArgument[5]; StorageSystem forProvider = _helper.getStorageSystemForProvider(storageSystem, volume); _log.info("Selected Provider : {}", forProvider.getNativeGuid()); // can not invoke async --- cimPath is not serializable // todo: before opt 450103 is fixed always use multi-volume job SmisAbstractCreateVolumeJob smisJobCompleter = new SmisCreateMultiVolumeJob(null, forProvider.getId(), storagePool.getId(), volumes.size(), taskCompleter); // SmisAbstractCreateVolumeJob smisJobCompleter = volumes.size() > 1 ? new SmisCreateMultiVolumeJob(null, // forProvider.getId(), storagePool.getId(), volumes.size(), taskCompleter) // : new SmisCreateVolumeJob(null, forProvider.getId(), storagePool.getId(), // taskCompleter); smisJobCompleter.setCimPath(_cimPath); _helper.invokeMethodSynchronously(forProvider, elementCompositionServicePath, SmisConstants.CREATE_OR_MODIFY_COMPOSITE_ELEMENT, inArgs, outArgs, smisJobCompleter); } 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 createMetaVolumes: ", e); opCreationFailed = true; ServiceError serviceError = DeviceControllerErrors.smis.methodFailed("createMetaVolumes", e.getMessage()); taskCompleter.error(_dbClient, serviceError); } if (opCreationFailed) { for (Volume vol : volumes) { vol.setInactive(true); _dbClient.persistObject(vol); } } StringBuilder logMsgBuilder = new StringBuilder(String.format("Create meta volumes End - Array:%s, Pool:%s", storageSystem.getSerialNumber(), storagePool.getNativeGuid())); for (Volume vol : volumes) { logMsgBuilder.append(String.format("%nVolume:%s", vol.getLabel())); } _log.info(logMsgBuilder.toString()); } /** * Expand regular volume as a meta volume. * * @param storageSystem * @param metaHead * @param metaMembers * @param metaType * @param metaVolumeTaskCompleter * @throws DeviceControllerException */ @Override public void expandVolumeAsMetaVolume(StorageSystem storageSystem, StoragePool storagePool, Volume metaHead, List<String> metaMembers, String metaType, MetaVolumeTaskCompleter metaVolumeTaskCompleter) throws DeviceControllerException { String label = null; _log.info(String.format( "Expand Volume as Meta Volume Start - Array: %s, Head: %s, Recommended meta type: %s %n Members:%s", storageSystem.getSerialNumber(), metaHead.getLabel(), metaType, metaMembers)); label = metaHead.getLabel(); boolean isRPVolume = false; if (metaHead != null) { // A volume is of type RP if the volume has an RP copy name or it's a VPlex backing volume associated to a // VPlex RP source volume. isRPVolume = metaHead.checkForRp() || RPHelper.isAssociatedToAnyRpVplexTypes(metaHead, _dbClient); } // initialize the retry/attempt variables int attempt = 0; int retries = 1; if (isRPVolume) { // if we are dealing with an RP volume, we need to set the retry count appropriately retries = MAX_RP_EXPAND_RETRIES; } // Execute one-to-many expand attempts depending on if this is an RP volume or not. If the // volume is RP, retry if we get the "The requested device has active sessions" error. This is // because RP has issued an asynchronous call to the array to terminate the active session but it // has not been received or processed yet. while (attempt++ <= retries) { try { CIMObjectPath elementCompositionServicePath = _cimPath.getElementCompositionSvcPath(storageSystem); CIMArgument[] inArgs; inArgs = _helper.getCreateMetaVolumeInputArguments(storageSystem, label, metaHead, metaMembers, metaType, true); CIMArgument[] outArgs = new CIMArgument[5]; // TODO evaluate use of asunc call for the last operation in extend sequence // _helper.invokeMethod(storageSystem, elementCompositionServicePath, SmisConstants.CREATE_OR_MODIFY_COMPOSITE_ELEMENT, // inArgs, // outArgs); // CIMObjectPath job = _cimPath.getCimObjectPathFromOutputArgs(outArgs, SmisConstants.JOB); // if (job != null) { // ControllerServiceImpl.enqueueJob(new QueueJob(new SmisVolumeExpandJob(job, storageSystem.getId(), // taskCompleter, "ExpandAsMetaVolume"))); // } // StorageSystem forProvider = _helper.getStorageSystemForProvider(storageSystem, metaHead); _log.info("Selected Provider : {}", forProvider.getNativeGuid()); SmisJob smisJobCompleter = new SmisVolumeExpandJob(null, forProvider.getId(), storagePool.getId(), metaVolumeTaskCompleter, "ExpandAsMetaVolume"); if (isRPVolume) { _log.info(String.format("Attempt %s/%s to expand volume %s, which is associated with RecoverPoint", attempt, MAX_RP_EXPAND_RETRIES, metaHead.getLabel())); } _helper.invokeMethodSynchronously(forProvider, elementCompositionServicePath, SmisConstants.CREATE_OR_MODIFY_COMPOSITE_ELEMENT, inArgs, outArgs, smisJobCompleter); // No exceptions so break out of the retry loop break; } catch (WBEMException e) { _log.error("Problem making SMI-S call: ", e); ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e.getMessage()); metaVolumeTaskCompleter.getVolumeTaskCompleter().error(_dbClient, _locker, error); } catch (Exception e) { if (attempt != retries && isRPVolume && e.getMessage().contains("The requested device has active sessions")) { // RP has issued an async request to terminate the active session so we just need to wait // and retry the expand. _log.warn(String .format("Encountered exception attempting to expand RP volume %s. Waiting %s milliseconds before trying again. Error: %s", metaHead.getLabel(), RP_EXPAND_WAIT_FOR_RETRY, e.getMessage())); try { Thread.sleep(RP_EXPAND_WAIT_FOR_RETRY); } catch (InterruptedException e1) { Thread.currentThread().interrupt(); } } else { _log.error("Problem in expandVolumeAsMetaVolume: ", e); ServiceError error = DeviceControllerErrors.smis.methodFailed("expandVolumeAsMetaVolume", e.getMessage()); metaVolumeTaskCompleter.getVolumeTaskCompleter().error(_dbClient, _locker, error); // Break out of the retry loop break; } } } _log.info(String.format( "Expand Volume as Meta Volume End - Array:%s, Head:%s, %n Head device ID: %s, Members:%s", storageSystem.getSerialNumber(), metaHead.getLabel(), metaHead.getNativeId(), metaMembers)); } /** * Expand meta volume. * * @param storageSystem * @param metaHead * @param newMetaMembers * @param metaVolumeTaskCompleter * @throws DeviceControllerException */ @Override public void expandMetaVolume(StorageSystem storageSystem, StoragePool storagePool, Volume metaHead, List<String> newMetaMembers, MetaVolumeTaskCompleter metaVolumeTaskCompleter) throws DeviceControllerException { _log.info(String.format( "Expand Meta Volume Start - Array: %s, Head: %s, %n New members:%s", storageSystem.getSerialNumber(), metaHead.getLabel(), newMetaMembers)); boolean isRPVolume = false; if (metaHead != null) { // A volume is of type RP if the volume has an RP copy name or it's a VPlex backing volume associated to a // VPlex RP source volume. isRPVolume = metaHead.checkForRp() || RPHelper.isAssociatedToAnyRpVplexTypes(metaHead, _dbClient); } // initialize the retry/attempt variables int attempt = 0; int retries = 1; if (isRPVolume) { // if we are dealing with an RP volume, we need to set the retry count appropriately retries = MAX_RP_EXPAND_RETRIES; } // Execute one-to-many expand attempts depending on if this is an RP volume or not. If the // volume is RP, retry if we get the "The requested device has active sessions" error. This is // because RP has issued an asynchronous call to the array to terminate the active session but it // has not been received or processed yet. while (attempt++ <= retries) { try { CIMObjectPath elementCompositionServicePath = _cimPath.getElementCompositionSvcPath(storageSystem); CIMArgument[] inArgs; inArgs = _helper.getExpandMetaVolumeInputArguments(storageSystem, metaHead, newMetaMembers); CIMArgument[] outArgs = new CIMArgument[5]; // TODO evaluate use of asunc call for the last operation in extend sequence // _helper.invokeMethod(storageSystem, elementCompositionServicePath, SmisConstants.CREATE_OR_MODIFY_COMPOSITE_ELEMENT, // inArgs, // outArgs); // CIMObjectPath job = _cimPath.getCimObjectPathFromOutputArgs(outArgs, SmisConstants.JOB); // if (job != null) { // ControllerServiceImpl.enqueueJob(new QueueJob(new SmisVolumeExpandJob(job, storageSystem.getId(), // taskCompleter, "ExpandMetaVolume"))); // } StorageSystem forProvider = _helper.getStorageSystemForProvider(storageSystem, metaHead); _log.info("Selected Provider : {}", forProvider.getNativeGuid()); SmisJob smisJobCompleter = new SmisVolumeExpandJob(null, forProvider.getId(), storagePool.getId(), metaVolumeTaskCompleter, "ExpandMetaVolume"); if (isRPVolume) { _log.info(String.format("Attempt %s/%s to expand volume %s, which is associated with RecoverPoint", attempt, MAX_RP_EXPAND_RETRIES, metaHead.getLabel())); } _helper.invokeMethodSynchronously(forProvider, elementCompositionServicePath, SmisConstants.CREATE_OR_MODIFY_COMPOSITE_ELEMENT, inArgs, outArgs, smisJobCompleter); // No exceptions so break out of the retry loop break; } catch (WBEMException e) { _log.error("Problem making SMI-S call: ", e); ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e.getMessage()); metaVolumeTaskCompleter.getVolumeTaskCompleter().error(_dbClient, _locker, error); } catch (Exception e) { if (attempt != retries && isRPVolume && e.getMessage().contains("The requested device has active sessions")) { // RP has issued an async request to terminate the active session so we just need to wait // and retry the expand. _log.warn(String .format("Encountered exception attempting to expand RP volume %s. Waiting %s milliseconds before trying again. Error: %s", metaHead.getLabel(), RP_EXPAND_WAIT_FOR_RETRY, e.getMessage())); try { Thread.sleep(RP_EXPAND_WAIT_FOR_RETRY); } catch (InterruptedException e1) { Thread.currentThread().interrupt(); } } else { _log.error("Problem in expandMetaVolume: ", e); ServiceError error = DeviceControllerErrors.smis.methodFailed("expandVolume", e.getMessage()); metaVolumeTaskCompleter.getVolumeTaskCompleter().error(_dbClient, _locker, error); // Break out of the retry loop break; } } } _log.info(String.format( "Expand Meta Volume End - Array:%s, Head:%s, %n Head device ID: %s, New members:%s", storageSystem.getSerialNumber(), metaHead.getLabel(), metaHead.getNativeId(), newMetaMembers)); } private void bindMetaVolumeToPool(StorageSystem storageSystem, StoragePool storagePool, Volume volume, MetaVolumeTaskCompleter metaVolumeTaskCompleter, Boolean isLastJob) throws Exception { long thinMetaVolumePreAllocateSize = 0; if (volume.getThinVolumePreAllocationSize() != null && volume.getThinVolumePreAllocationSize() > 0) { thinMetaVolumePreAllocateSize = volume.getThinVolumePreAllocationSize(); } _log.info(String.format( "Bind Meta Volume to Pool Start - Array: %s, Pool: %s, %n Volume: %s, ThinMetaVolumePreAllocateSize: %s, isLastJob: %s", storageSystem.getSerialNumber(), storagePool.getNativeId(), volume.getNativeId(), thinMetaVolumePreAllocateSize, isLastJob)); try { CIMObjectPath configSvcPath = _cimPath.getConfigSvcPath(storageSystem); CIMArgument[] inArgs; // Prepare parameters and call method to bind meta head inArgs = _helper.getBindVolumeInputArguments(storageSystem, storagePool, volume, thinMetaVolumePreAllocateSize); CIMArgument[] outArgs = new CIMArgument[5]; // SmisJob smisJobCompleter = // new SmisJob(null, storageSystem.getId(), metaVolumeTaskCompleter.getVolumeTaskCompleter(), "Bind volume to pool job"); StorageSystem forProvider = _helper.getStorageSystemForProvider(storageSystem, volume); _log.info("Selected Provider : {}", forProvider.getNativeGuid()); SmisJob smisJobCompleter = new SmisCreateMetaVolumeJob(null, forProvider.getId(), storagePool.getId(), volume, metaVolumeTaskCompleter, isLastJob); _helper.invokeMethodSynchronously(forProvider, configSvcPath, SmisConstants.EMC_BIND_ELEMENT, inArgs, outArgs, smisJobCompleter); } catch (WBEMException e) { _log.error("Problem making SMI-S call: ", e); ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e.getMessage()); metaVolumeTaskCompleter.getVolumeTaskCompleter().error(_dbClient, error); throw e; } catch (Exception e) { _log.error("Problem in bindVolumeToPool: " + volume.getLabel(), e); ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e.getMessage()); metaVolumeTaskCompleter.getVolumeTaskCompleter().error(_dbClient, error); throw e; } finally { _log.info(String.format( "Bind Meta Volume to Pool End - Array:%s, Pool: %s, %n Volume: %s", storageSystem.getSerialNumber(), storagePool.getNativeId(), volume.getNativeId())); } } /** * Deletes 'SMI_BCV_META_...' helper volume form array. * * @param storageSystem * @param volume * @throws Exception */ @Override public void deleteBCVHelperVolume(StorageSystem storageSystem, Volume volume) throws Exception { _log.info(String.format("Start executing BCV helper volume from array: %s, for volume: %s", storageSystem.getId(), volume.getId())); try { // Find BCV volume instance based on volume device name. String deviceName = volume.getNativeId(); // Temporary before OPT: is fixed --- // try to find volume based on device id with removed leading zeros, if failed try complete device id. String deviceNameWithoutLeadingZeros = deviceName.replaceAll("^0*", ""); String query = String .format("SELECT CIM_StorageVolume.%s, CIM_StorageVolume.%s FROM CIM_StorageVolume where CIM_StorageVolume.%s ='SMI_BCV_META_%s'", SmisConstants.CP_ELEMENT_NAME, SmisConstants.CP_DEVICE_ID, SmisConstants.CP_ELEMENT_NAME, deviceNameWithoutLeadingZeros); String queryLanguage = "CQL"; List<CIMInstance> bcvVolumeInstanceList = _helper.executeQuery(storageSystem, query, queryLanguage); if (bcvVolumeInstanceList == null || bcvVolumeInstanceList.isEmpty()) { // Execute query for unmodified device name query = String .format("SELECT CIM_StorageVolume.%s, CIM_StorageVolume.%s FROM CIM_StorageVolume where CIM_StorageVolume.%s ='SMI_BCV_META_%s'", SmisConstants.CP_ELEMENT_NAME, SmisConstants.CP_DEVICE_ID, SmisConstants.CP_ELEMENT_NAME, deviceName); bcvVolumeInstanceList = _helper.executeQuery(storageSystem, query, queryLanguage); } String elementName = null; String nativeId = null; CIMInstance bcvVolumeInstance = null; if (bcvVolumeInstanceList != null && !bcvVolumeInstanceList.isEmpty()) { bcvVolumeInstance = bcvVolumeInstanceList.get(0); elementName = CIMPropertyFactory.getPropertyValue(bcvVolumeInstance, SmisConstants.CP_ELEMENT_NAME); nativeId = CIMPropertyFactory.getPropertyValue(bcvVolumeInstance, SmisConstants.CP_DEVICE_ID); _log.info(String.format("Found BCV helper volume: %s, nativeId: %s", elementName, nativeId)); } else { _log.warn(String.format("Could not find BCV helper volume for volume: %s, nativeId: %s", volume.getId(), volume.getNativeId())); return; } // Delete BCV volume from array _log.info(String.format("Executing delete of BCV helper volume: " + nativeId)); String[] nativeIds = new String[] { nativeId }; // 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]; String returnElementsMethod; if (storageSystem.getUsingSmis80()) { returnElementsMethod = SmisConstants.RETURN_ELEMENTS_TO_STORAGE_POOL; } else { returnElementsMethod = SmisConstants.EMC_RETURN_TO_STORAGE_POOL; } _helper.invokeMethodSynchronously(storageSystem, configSvcPath, returnElementsMethod, inArgs, outArgs, null); _log.info(String.format("Deleted BCV helper volume: " + nativeId)); } catch (Exception ex) { _log.error(String.format("Failed to delete BCV helper volume from array: %s, for volume: %s", storageSystem.getId(), volume.getId())); } } @Override public String defineExpansionType(StorageSystem storageSystem, Volume volume, String recommendedMetaVolumeType, MetaVolumeTaskCompleter metaVolumeTaskCompleter) throws Exception { String expansionType = null; Boolean isBound = null; try { CIMInstance cimVolume = null; CIMObjectPath volumePath = _cimPath.getBlockObjectPath(storageSystem, volume); cimVolume = _helper.getInstance(storageSystem, volumePath, false, false, new String[] { EMC_IS_BOUND }); String isBoundStr = cimVolume.getPropertyValue(EMC_IS_BOUND).toString(); isBound = Boolean.parseBoolean(isBoundStr); String deviceType = storageSystem.getSystemType(); // If a volume is composite volume, use its meta type, // otherwise for vmax bound regular volumes always use concatenated type for expansion. // The reason: not all microcode versions allows to form striped meta volumes with bound meta head. // See Notes in smis provider guide 4.6.1 p. 424 expansionType = recommendedMetaVolumeType; if (volume.getIsComposite()) { expansionType = volume.getCompositionType(); } else if (deviceType.equals(StorageSystem.Type.vmax.toString()) && isBound) { expansionType = Volume.CompositionType.CONCATENATED.toString(); } return expansionType; } catch (WBEMException e) { _log.error("Problem making SMI-S call: ", e); ServiceError error = DeviceControllerErrors.smis.unableToCallStorageProvider(e.getMessage()); metaVolumeTaskCompleter.getVolumeTaskCompleter().error(_dbClient, error); throw e; } catch (Exception e) { _log.error("Problem in defineExpansionType: " + volume.getLabel(), e); ServiceError error = DeviceControllerErrors.smis.methodFailed("defineExpansionType", e.getMessage()); metaVolumeTaskCompleter.getVolumeTaskCompleter().error(_dbClient, error); throw e; } finally { _log.info(String.format( "defineExpansionType End - Volume: %s, IsMeta: %s, isBound: %s, " + "\n Array:%s, Array type: %s, Meta type for expansion: %s", volume.getNativeId(), volume.getIsComposite(), isBound, storageSystem.getSerialNumber(), storageSystem.getSystemType(), expansionType)); } } public void setSmisStorageDevicePreProcessor( final SmisStorageDevicePreProcessor smisStorageDevicePreProcessor) { _smisStorageDevicePreProcessor = smisStorageDevicePreProcessor; } }