/* * Copyright (c) 2008-2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.smis.ibm.xiv; import java.net.URI; import java.text.MessageFormat; 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 java.util.concurrent.ConcurrentHashMap; import javax.cim.CIMArgument; import javax.cim.CIMInstance; import javax.cim.CIMObjectPath; import javax.cim.CIMProperty; import javax.cim.UnsignedInteger16; import javax.wbem.CloseableIterator; import javax.wbem.WBEMException; import javax.wbem.client.EnumerateResponse; import javax.wbem.client.WBEMClient; import org.apache.commons.lang.math.NumberUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.cimadapter.connections.cim.CimConnection; import com.emc.storageos.cimadapter.connections.cim.CimObjectPathCreator; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.URIUtil; import com.emc.storageos.db.client.model.BlockConsistencyGroup; 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.DataObject; import com.emc.storageos.db.client.model.ExportGroup; import com.emc.storageos.db.client.model.ExportMask; import com.emc.storageos.db.client.model.Initiator; import com.emc.storageos.db.client.model.ScopedLabel; import com.emc.storageos.db.client.model.ScopedLabelSet; import com.emc.storageos.db.client.model.StoragePool; import com.emc.storageos.db.client.model.StorageSystem; import com.emc.storageos.db.client.model.Volume; import com.emc.storageos.exceptions.DeviceControllerException; import com.emc.storageos.plugins.common.Constants; import com.emc.storageos.volumecontroller.ControllerLockingService; import com.emc.storageos.volumecontroller.Job.JobStatus; import com.emc.storageos.volumecontroller.JobContext; import com.emc.storageos.volumecontroller.impl.JobPollResult; import com.emc.storageos.volumecontroller.impl.VolumeURIHLU; import com.emc.storageos.volumecontroller.impl.smis.CIMArgumentFactory; import com.emc.storageos.volumecontroller.impl.smis.CIMConnectionFactory; import com.emc.storageos.volumecontroller.impl.smis.CIMPropertyFactory; import com.emc.storageos.volumecontroller.impl.smis.SmisException; import com.emc.storageos.volumecontroller.impl.smis.ibm.IBMCIMObjectPathFactory; import com.emc.storageos.volumecontroller.impl.smis.ibm.IBMSmisConstants; import com.emc.storageos.volumecontroller.impl.smis.ibm.IBMSmisSynchSubTaskJob; import com.emc.storageos.volumecontroller.impl.smis.job.SmisJob; /** * Helper for SMI-S commands. */ public class XIVSmisCommandHelper implements IBMSmisConstants { private static final Logger _log = LoggerFactory .getLogger(XIVSmisCommandHelper.class); public static final ConcurrentHashMap<String, CIMObjectPath> CIM_OBJECT_PATH_HASH_MAP = new ConcurrentHashMap<String, CIMObjectPath>(); private static final int SYNC_WRAPPER_WAIT = 5000; private static final int SYNC_WRAPPER_TIME_OUT = 600000; // 10 minutes private static final int POLL_CYCLE_LIMIT = 100; private static final int INVALID_RETURN_CODE = -1; private static final int CIM_SUCCESS_CODE = 0; private static final int CIM_DUPLICATED_CG_NAME_CODE = 45314; // Consistency Group name already exists private static final int CIM_ONLY_ALLOWED_ON_EMPTY_CG_CODE = 45316; // This operation is only allowed on an empty Consistency Group. private static final int CIM_MAPPING_NOT_DEFINED = 45635; // The requested mapping is not defined private static final int CIM_DUPLICATED_HOST_NAME = 45504; // Host name already exists private static final int CIM_OPERATION_PARTIALLY_SUCCEEDED = 32769; // Operation partially succeeded private static final int CIM_MAX_RETRY_COUNT = 25; private static final int CIM_RETRY_WAIT_INTERVAL = 5000; // 5 seconds private static final String CIM_BAD_REQUEST = "HTTP 400 - Bad Request (CIMError: \"request-not-well-formed\", OpenPegasus Error: \"Bad opening element: on line 1\")"; private static final int MAXIMUM_LUN = 511; private static final String INVALID_LUN_ERROR_MSG = "Logical unit number provided (%d) is larger than allowed (%d)."; private static final String QUANTITY = "Quantity"; private static final String SIZE = "Size"; private static final String ELEMENT_NAMES = "ElementNames"; CIMArgumentFactory _cimArgument = null; CIMPropertyFactory _cimProperty = null; IBMCIMObjectPathFactory _cimPath = null; CIMConnectionFactory _cimConnection = null; DbClient _dbClient = null; // TODO - place holder for now ControllerLockingService _locker; public void setCimArgumentFactory(CIMArgumentFactory cimArgumentFactory) { _cimArgument = cimArgumentFactory; } public void setCimPropertyFactory(CIMPropertyFactory cimPropertyFactory) { _cimProperty = cimPropertyFactory; } public void setCimConnectionFactory(CIMConnectionFactory connectionFactory) { _cimConnection = connectionFactory; } public void setCimObjectPathFactory( IBMCIMObjectPathFactory cimObjectPathFactory) { _cimPath = cimObjectPathFactory; } public void setDbClient(DbClient dbClient) { _dbClient = dbClient; } public void setLocker(ControllerLockingService locker) { _locker = locker; } /** * Return CIM connection. */ public CimConnection getConnection(StorageSystem storageDevice) { return _cimConnection.getConnection(storageDevice); } /* * Validate connection */ public boolean validateStorageProviderConnection(String ipAddress, Integer portNumber) { boolean isConnectionValid = false; try { CimConnection connection = _cimConnection.getConnection(ipAddress, portNumber.toString()); isConnectionValid = (connection != null && _cimConnection.checkConnectionliveness(connection)); } catch (IllegalStateException ise) { _log.error(ise.getMessage()); } return isConnectionValid; } /** * Invoke CIM method. */ @SuppressWarnings("rawtypes") public void invokeMethod(StorageSystem storageDevice, CIMObjectPath objectPath, String methodName, CIMArgument[] inArg) throws Exception { invokeMethod(storageDevice, objectPath, methodName, inArg, new CIMArgument[5]); } /** * Invoke CIM method. */ @SuppressWarnings("rawtypes") public void invokeMethod(StorageSystem storageDevice, CIMObjectPath objectPath, String methodName, CIMArgument[] inArgs, CIMArgument[] outArgs) throws Exception { CimConnection connection = getConnection(storageDevice); WBEMClient client = connection.getCimClient(); int index = 0; StringBuilder inputInfoBuffer = new StringBuilder(); inputInfoBuffer.append("\nSMI-S Provider: ") .append(connection.getHost()) .append(" -- Attempting invokeMethod ").append(methodName) .append(" on\n").append(" objectPath=") .append(objectPath.toString()).append(" with arguments: \n"); for (CIMArgument arg : inArgs) { inputInfoBuffer.append(" inArg[").append(index++).append("]=") .append(arg.toString()).append('\n'); } _log.info(inputInfoBuffer.toString()); long start = System.nanoTime(); // workaround for CTRL-8024 (CIMError: "request-not-well-formed" ...) // retry CIM_MAX_RETRY_COUNT times if encounter the error // the invoke method will return quickly with the error, so extensive retry (e.g., 25 times), won't be a big overhead Object obj = null; int retryCount = 0; while (true) { try { _log.info("Invoke method {}, attempt {}", methodName, retryCount); obj = client.invokeMethod(objectPath, methodName, inArgs, outArgs); } catch (WBEMException e) { if (CIM_BAD_REQUEST.equals(e.getMessage())) { if (retryCount < CIM_MAX_RETRY_COUNT) { _log.warn("Encountered 'request-not-well-formed' error. Retry..."); retryCount++; try { Thread.sleep(CIM_RETRY_WAIT_INTERVAL); } catch (InterruptedException ie) { _log.warn("Thread: " + Thread.currentThread().getName() + " interrupted."); throw e; } continue; } _log.warn("Exhausted {} retries", CIM_MAX_RETRY_COUNT); } // other WBEMException, or reach the max retry count throw e; } // no exception break; } String total = String.format("%2.6f", ((System.nanoTime() - start) / 1000000000.0)); StringBuilder outputInfoBuffer = new StringBuilder(); outputInfoBuffer.append("\nSMI-S Provider: ") .append(connection.getHost()) .append(" -- Completed invokeMethod ").append(methodName) .append(" on\n").append(" objectPath=") .append(objectPath.toString()).append("\n Returned: ") .append(obj.toString()).append(" with output arguments: \n"); int returnCode = NumberUtils.toInt(obj.toString(), INVALID_RETURN_CODE); for (CIMArgument arg : outArgs) { if (arg != null) { if (returnCode == CIM_SUCCESS_CODE) { outputInfoBuffer.append(" outArg=").append(arg.toString()) .append('\n'); } else { outputInfoBuffer.append(" outArg=").append(arg.getName()) .append("=") .append(arg.getValue()) .append(" (Type ").append(arg.getDataType()) .append(")\n"); } } } outputInfoBuffer.append(" Execution time: ").append(total) .append(" seconds.\n"); _log.info(outputInfoBuffer.toString()); if (returnCode == CIM_MAPPING_NOT_DEFINED && methodName.equals(HIDE_PATHS)) { // ignore the error } else if (returnCode == this.CIM_DUPLICATED_HOST_NAME && methodName.equals(CREATE_HARDWARE_ID_COLLECTION)) { outArgs[0] = null; } else if (returnCode == this.CIM_OPERATION_PARTIALLY_SUCCEEDED && methodName.equals(ADD_HARDWARE_IDS_TO_COLLECTION)) { outArgs[0] = null; } else if (returnCode == CIM_DUPLICATED_CG_NAME_CODE) { throw new Exception(DUPLICATED_CG_NAME_ERROR); } else if (returnCode == CIM_ONLY_ALLOWED_ON_EMPTY_CG_CODE && methodName.equals(REMOVE_MEMBERS)) { // sometimes CIM call returns with code 45316 (This operation is // only allowed on an empty Consistency Group.) // it is a wrong error, also it appears that the volume is removed // from cg // throw exception, so that caller can send error if volumes are not deleted by checking CG members // after this call throw new Exception("Failed with return code: " + obj); } else if (returnCode != CIM_SUCCESS_CODE && methodName.equals(CREATE_OR_MODIFY_ELEMENTS_FROM_STORAGE_POOL) && checkIfVolumeSizeExceedingPoolSize(inArgs, outArgs)) { throw DeviceControllerException.exceptions.volumeSizeExceedingPoolSize(getVolumeName(inArgs)); } else if (returnCode != CIM_SUCCESS_CODE) { throw new Exception("Failed with return code: " + obj); } } /** * Method checks whether the requested volume size exceeds pool size * @param inArgs Requested args * @param outArgs Original args * @return boolean */ private boolean checkIfVolumeSizeExceedingPoolSize(CIMArgument[] inArgs, CIMArgument[] outArgs) { if (getVolumeSize(inArgs) > getVolumeSize(outArgs)) { return true; } return false; } /** * Method returns the requested volume name * @param inArgs * @return */ private String getVolumeName(CIMArgument[] inArgs) { String volumeName = null; for (CIMArgument arg : inArgs) { if (arg != null) { if (arg.getName().equals(ELEMENT_NAMES)) { String[] elements = (String[]) arg.getValue(); if (elements != null) { String tempName = elements[0]; if (elements.length == 1) { volumeName = tempName; } else { volumeName = tempName.substring(0, tempName.length() - 2); } } } } } return volumeName; } //Internal method to get volume size private Long getVolumeSize(CIMArgument[] inArgs) { int quantity = 0; Long size = 0L; for(CIMArgument arg : inArgs){ if (arg != null) { if (arg.getName().equals(QUANTITY)) { quantity = Integer.parseInt(arg.getValue().toString()); } if(arg.getName().equals(SIZE)) { size = Long.parseLong(arg.getValue().toString()); } } } return quantity*size ; } /** * Construct input arguments for creating volumes. */ public CIMArgument[] getCreateVolumesInputArguments( StorageSystem storageDevice, StoragePool pool, List<String> labels, Long capacity, int count, boolean isThinlyProvisioned) { ArrayList<CIMArgument> list = new ArrayList<CIMArgument>(); try { list.add(_cimArgument.stringArray(CP_ELEMENT_NAMES, labels.toArray(new String[labels.size()]))); // Use thick/thin volume type int volumeType = isThinlyProvisioned ? STORAGE_VOLUME_TYPE_THIN : STORAGE_VOLUME_VALUE; list.add(_cimArgument.uint16(CP_ELEMENT_TYPE, volumeType)); CIMProperty[] goalPropKeys = { _cimProperty.string(CP_INSTANCE_ID, SYSTEM_BLOCK_SIZE) }; CIMObjectPath goalPath = CimObjectPathCreator.createInstance( DATA_TYPE_SETTING, Constants.IBM_NAMESPACE, goalPropKeys); list.add(_cimArgument.reference(CP_GOAL, goalPath)); CIMProperty[] inPoolPropKeys = { _cimProperty.string( CP_INSTANCE_ID, pool.getNativeId()) }; CIMObjectPath inPoolPath = CimObjectPathCreator.createInstance( pool.getPoolClassName(), Constants.IBM_NAMESPACE, inPoolPropKeys); list.add(_cimArgument.reference(CP_IN_POOL, inPoolPath)); list.add(_cimArgument.uint32(CP_QUANTITY, count)); list.add(_cimArgument.uint64(CP_SIZE, capacity)); } catch (Exception e) { throw new IllegalStateException("Problem getting input arguments: " + storageDevice.getLabel()); } return list.toArray(new CIMArgument[list.size()]); } /* * Construct input arguments for expanding volume. */ public CIMArgument[] getExpandVolumeInputArguments( StorageSystem storageDevice, Volume volume, Long size) { ArrayList<CIMArgument> list = new ArrayList<CIMArgument>(); try { CIMObjectPath volumePath = _cimPath.getBlockObjectPath( storageDevice, volume); list.add(_cimArgument.reference(CP_THE_ELEMENT, volumePath)); list.add(_cimArgument.uint64(CP_SIZE, size)); CIMProperty[] goalPropKeys = { _cimProperty.string(CP_INSTANCE_ID, SYSTEM_BLOCK_SIZE) }; CIMObjectPath goalPath = CimObjectPathCreator.createInstance( DATA_TYPE_SETTING, Constants.IBM_NAMESPACE, goalPropKeys); list.add(_cimArgument.reference(CP_GOAL, goalPath)); } catch (Exception e) { throw new IllegalStateException("Problem getting input arguments: " + storageDevice.getLabel()); } return list.toArray(new CIMArgument[list.size()]); } /** * Construct input arguments for deleting volumes. */ public CIMArgument[] getDeleteVolumesInputArguments( StorageSystem storageDevice, String[] volumeNames) { CIMObjectPath[] volumePaths; try { volumePaths = _cimPath.getVolumePaths(storageDevice, volumeNames); } catch (Exception e) { throw new IllegalStateException("Problem deleting volumes: " + volumeNames.toString() + "on array: " + storageDevice.getSerialNumber()); } return new CIMArgument[] { _cimArgument.referenceArray(CP_THE_ELEMENTS, volumePaths) }; } /** * This method is a wrapper for the getInstance. If the object is not found, * it returns a null value instead of throwing an exception. * * @param storage * [required] - StorageSystem object to which an SMI-S connection * would be made * @param objectPath * [required] * @param propagated * [required] * @param includeClassOrigin * [required] * @return CIMInstance object that represents the existing object * @throws Exception */ public CIMInstance checkExists(StorageSystem storage, Volume volume, boolean propagated, boolean includeClassOrigin) throws Exception { CIMInstance instance = null; CIMObjectPath objectPath = _cimPath.getBlockObjectPath(storage, volume); try { if (objectPath != null) { _log.debug(String .format("checkExists(storage=%s, objectPath=%s, propagated=%s, includeClassOrigin=%s)", storage.getSerialNumber(), objectPath.toString(), String.valueOf(propagated), String.valueOf(includeClassOrigin))); instance = getInstance(storage, objectPath, propagated, includeClassOrigin, null); } } catch (WBEMException e) { // If we get an error indicating the object is not found, then // it's okay, we want to return null for this method if (e.getID() != WBEMException.CIM_ERR_NOT_FOUND) { throw e; } } catch (Exception e) { _log.error("checkExists call encountered an exception", e); throw e; } return instance; } /** * Executes query * * @param storageSystem * @param query * @param queryLanguage * @return list of matched instances */ public List<CIMInstance> executeQuery(StorageSystem storageSystem, CIMObjectPath objectPath, String query, String queryLanguage) { return _cimPath.executeQuery(storageSystem, objectPath, query, queryLanguage); } @SuppressWarnings("rawtypes") public void createHardwareIDCollection(StorageSystem storage, CIMObjectPath hwIdManagementSvc, String elementName, String[] initiators, CIMArgument[] outArgs) throws Exception { CIMArgument[] inArgs = getCreateHardwareIDCollectionInputArgs(elementName, initiators); invokeMethod(storage, hwIdManagementSvc, IBMSmisConstants.CREATE_HARDWARE_ID_COLLECTION, inArgs, outArgs); } /** * Construct input arguments for creating hardware Id collection. */ @SuppressWarnings("rawtypes") private CIMArgument[] getCreateHardwareIDCollectionInputArgs(String elementName, String[] initiators) throws Exception { List<CIMArgument> argsList = new ArrayList<CIMArgument>(); if (elementName != null) { argsList.add(_cimArgument.string(CP_ELEMENT_NAME, elementName)); } if (initiators != null && initiators.length > 0) { argsList.add(_cimArgument.stringArray(CP_HARDWARE_IDS, initiators)); } return argsList.toArray(new CIMArgument[argsList.size()]); } @SuppressWarnings("rawtypes") public void addHardwareIDsToCollection(StorageSystem storage, CIMObjectPath collectionPath, String[] initiators, CIMArgument[] outArgs) throws Exception { CIMArgument[] inArgs = getAddHardwareIDsToCollectionInputArgs(collectionPath, initiators); CIMObjectPath hwIdManagementSvc = _cimPath .getStorageHardwareIDManagementService(storage); invokeMethod(storage, hwIdManagementSvc, IBMSmisConstants.ADD_HARDWARE_IDS_TO_COLLECTION, inArgs, outArgs); } /** * Construct input arguments for adding storage hardware ID. */ @SuppressWarnings("rawtypes") private CIMArgument[] getAddHardwareIDsToCollectionInputArgs(CIMObjectPath collectionPath, String[] initiators) throws Exception { List<CIMArgument> argsList = new ArrayList<CIMArgument>(); argsList.add(_cimArgument.reference(CP_HARDWARE_ID_COLLECTION, collectionPath)); argsList.add(_cimArgument.stringArray(CP_HARDWARE_IDS, initiators)); return argsList.toArray(new CIMArgument[argsList.size()]); } /** * Construct input arguments for exposing paths. */ public CIMArgument[] getExposePathsInputArguments(VolumeURIHLU[] volumeURIHLUs, String[] initiators, CIMObjectPath protocolController) throws Exception { List<CIMArgument> argsList = new ArrayList<CIMArgument>(); if (volumeURIHLUs != null && volumeURIHLUs.length > 0) { String[] lunNames = new String[volumeURIHLUs.length]; List<String> deviceNumbers = new ArrayList<String>(); UnsignedInteger16[] deviceAccesses = new UnsignedInteger16[volumeURIHLUs.length]; for (int i = 0; i < volumeURIHLUs.length; i++) { lunNames[i] = getBlockObjectAlternateName(volumeURIHLUs[i].getVolumeURI()); String hlu = volumeURIHLUs[i].getHLU(); // Add the HLU to the list only if it is non-null and not the // LUN_UNASSIGNED value (as a hex string). if (hlu != null && !hlu.equalsIgnoreCase(ExportGroup.LUN_UNASSIGNED_STR)) { int hluDec = Integer.parseInt(hlu, 16); if (hluDec > MAXIMUM_LUN) { String errMsg = String.format(INVALID_LUN_ERROR_MSG, hluDec, MAXIMUM_LUN); _log.error(errMsg); throw new Exception(errMsg); } deviceNumbers.add(Integer.toString(hluDec)); } deviceAccesses[i] = READ_WRITE_UINT16; } argsList.add(_cimArgument.uint16Array(CP_DEVICE_ACCESSES, deviceAccesses)); argsList.add(_cimArgument.stringArray(CP_LU_NAMES, lunNames)); if (!deviceNumbers.isEmpty()) { String[] numbers = {}; argsList.add(_cimArgument.stringArray(CP_DEVICE_NUMBERS, deviceNumbers.toArray(numbers))); } } if (initiators != null && initiators.length > 0) { argsList.add(_cimArgument.stringArray(CP_INITIATOR_PORT_IDS, initiators)); } if (protocolController != null) { argsList.add(_cimArgument.referenceArray(CP_PROTOCOL_CONTROLLERS, new CIMObjectPath[] { protocolController })); } return argsList.toArray(new CIMArgument[argsList.size()]); } /** * Construct input arguments for exposing paths with given export mask. */ public CIMArgument[] getExposePathsInputArguments(StorageSystem storage, URI exportMask, VolumeURIHLU[] volumeURIHLUs, List<Initiator> initiatorList) throws Exception { CIMObjectPath protocolController = _cimPath.getSCSIProtocolControllerPath(storage, getExportMaskNativeId(exportMask)); return getExposePathsInputArguments(volumeURIHLUs, getInitiatorNames(initiatorList), protocolController); } /** * Construct input arguments for hiding paths. */ public CIMArgument[] getHidePathsInputArguments(StorageSystem storage, ExportMask exportMask, String[] volumeNames, String[] initiatorPortIDs) throws Exception { List<CIMArgument> argsList = new ArrayList<CIMArgument>(); if (volumeNames != null && volumeNames.length > 0) { argsList.add(_cimArgument.stringArray(CP_LU_NAMES, volumeNames)); } if (initiatorPortIDs != null && initiatorPortIDs.length > 0) { argsList.add(_cimArgument.stringArray(CP_INITIATOR_PORT_IDS, initiatorPortIDs)); } CIMObjectPath protocolController = _cimPath.getSCSIProtocolControllerPath(storage, exportMask.getNativeId()); if (protocolController != null) { argsList.add(_cimArgument.referenceArray(CP_PROTOCOL_CONTROLLERS, new CIMObjectPath[] { protocolController })); } return argsList.toArray(new CIMArgument[argsList.size()]); } /** * Construct input arguments for hiding paths. */ public CIMArgument[] getHidePathsInputArguments(StorageSystem storage, URI exportMask, List<URI> volumeURIList, List<Initiator> initiatorList) throws Exception { List<CIMArgument> argsList = new ArrayList<CIMArgument>(); String[] volumeNames = null; String[] initiatorPortIDs = null; if (volumeURIList != null && !volumeURIList.isEmpty()) { volumeNames = getBlockObjectAlternateNames(volumeURIList); argsList.add(_cimArgument.stringArray(CP_LU_NAMES, volumeNames)); } if (initiatorList != null && !initiatorList.isEmpty()) { initiatorPortIDs = getInitiatorNames(initiatorList); argsList.add(_cimArgument.stringArray(CP_INITIATOR_PORT_IDS, initiatorPortIDs)); } CIMObjectPath protocolController = _cimPath.getSCSIProtocolControllerPath(storage, getExportMaskNativeId(exportMask)); if (protocolController != null) { argsList.add(_cimArgument.referenceArray(CP_PROTOCOL_CONTROLLERS, new CIMObjectPath[] { protocolController })); } return argsList.toArray(new CIMArgument[argsList.size()]); } /** * Construct input arguments for deleting protocol controller. */ public CIMArgument[] getDeleteProtocolControllerInputArguments(CIMObjectPath protocolController) throws Exception { List<CIMArgument> argsList = new ArrayList<CIMArgument>(); argsList.add(_cimArgument.reference(CP_PROTOCOL_CONTROLLER, protocolController)); return argsList.toArray(new CIMArgument[argsList.size()]); } /** * Construct input arguments for deleting storage hardware ID. */ public CIMArgument[] getDeleteStorageHardwareIDInputArgs(StorageSystem storage, CIMObjectPath hwIdPath) throws Exception { return new CIMArgument[] { _cimArgument.reference(CP_HARDWARE_ID, hwIdPath) }; } /** * Returns a CloseableIterator for SCSIProtocolController * CIMInstance objects. * * @param storage * [in] - StorageSystem object. Used to look up SMIS connection. * @return CloseableIterator of CIMInstance objects * @throws Exception */ public CloseableIterator<CIMInstance> getSCSIProtocolControllers( StorageSystem storage) throws Exception { return getInstances(storage, Constants.IBM_NAMESPACE, CP_SCSI_PROTOCOL_CONTROLLER, true, false, false, PS_CNTRL_NAME_AND_ID); } /* * Return protocol controller instance represented by the export mask. */ public CIMInstance getSCSIProtocolController(StorageSystem storage, ExportMask exportMask) throws Exception { return getInstance(storage, _cimPath.getSCSIProtocolControllerPath(storage, exportMask.getNativeId()), false, true, null); } /** * Returns a map of the volume WWNs to their HLU values for a masking * container on the array. * * @param storage * [in] - StorageSystem that the masking belongs to * @param controllerPath * [in] - CIMObjectPath of IBMTSDS_SCSIProtocolController, holding a representation * of an array masking container. * @return - a map of the volume WWNs to their HLU values for an instance of * LunMasking container on the array. */ public Map<String, Integer> getVolumesFromScsiProtocolController(StorageSystem storage, CIMObjectPath controllerPath) { Map<String, Integer> wwnToHLU = new HashMap<String, Integer>(); CloseableIterator<CIMInstance> iterator = null; CloseableIterator<CIMInstance> protocolControllerForUnitIter = null; try { Map<String, Integer> deviceIdToHLU = new HashMap<String, Integer>(); WBEMClient client = getConnection(storage).getCimClient(); protocolControllerForUnitIter = client.referenceInstances(controllerPath, CIM_PROTOCOL_CONTROLLER_FOR_UNIT, null, false, PS_DEVICE_NUMBER); while (protocolControllerForUnitIter.hasNext()) { CIMInstance pcu = protocolControllerForUnitIter.next(); CIMObjectPath pcuPath = pcu.getObjectPath(); CIMProperty<CIMObjectPath> dependentVolumePropery = (CIMProperty<CIMObjectPath>) pcuPath.getKey(CP_DEPENDENT); CIMObjectPath dependentVolumePath = dependentVolumePropery.getValue(); String deviceId = dependentVolumePath.getKey(CP_DEVICE_ID).getValue() .toString(); String deviceNumber = CIMPropertyFactory.getPropertyValue(pcu, CP_DEVICE_NUMBER); Integer decimalHLU = (int) Long.parseLong(deviceNumber, 16); deviceIdToHLU.put(deviceId, decimalHLU); } iterator = client.associatorInstances(controllerPath, null, CP_STORAGE_VOLUME, null, null, false, PS_NAME); while (iterator.hasNext()) { CIMInstance cimInstance = iterator.next(); String deviceId = cimInstance.getObjectPath().getKey(CP_DEVICE_ID) .getValue().toString(); String wwn = CIMPropertyFactory.getPropertyValue(cimInstance, CP_NAME); wwnToHLU.put(wwn.toUpperCase(), deviceIdToHLU.get(deviceId)); } } catch (WBEMException we) { _log.error("Caught an error will attempting to get volume list from " + "masking instance", we); } finally { if (iterator != null) { iterator.close(); } if (protocolControllerForUnitIter != null) { protocolControllerForUnitIter.close(); } } return wwnToHLU; } /** * Returns a map of normalized port name to port path for the masking. * * @param storage * [in] - StorageSystem that the masking belongs to * @param controllerPath * [in] - CIMObjectPath of IBMTSDS_SCSIProtocolController, holding a representation * of an array masking container. * @return - a map of port name to port path for the container. */ public Map<String, CIMObjectPath> getInitiatorsFromScsiProtocolController(StorageSystem storage, CIMObjectPath controllerPath) { Map<String, CIMObjectPath> initiatorPortPaths = new HashMap<String, CIMObjectPath>(); CloseableIterator<CIMInstance> iterator = null; try { WBEMClient client = getConnection(storage).getCimClient(); iterator = client.associatorInstances(controllerPath, CP_SHWID_TO_SPC, CP_STORAGE_HARDWARE_ID, null, null, false, PS_STORAGE_ID); while (iterator.hasNext()) { CIMInstance cimInstance = iterator.next(); String initiator = CIMPropertyFactory.getPropertyValue(cimInstance, CP_STORAGE_ID); initiatorPortPaths.put(Initiator.normalizePort(initiator), cimInstance.getObjectPath()); } } catch (WBEMException we) { _log.error("Caught an error while attempting to get initiator list from " + "masking instance", we); } finally { if (iterator != null) { iterator.close(); } } return initiatorPortPaths; } /** * Given a CIMInstance of a IBMTSDS_SCSIProtocolController return a list of storage ports that * it references. * * @param storage * [in] - StorageSystem that the masking belongs to * @param controllerPath * [in] - CIMObjectPath of IBMTSDS_SCSIProtocolController, holding a representation * of an array masking container. * @return List of port name String values. The WWNs will have colons separating the hex digits. */ public List<String> getStoragePortsFromScsiProtocolController(StorageSystem storage, CIMObjectPath controllerPath) { List<String> storagePorts = new ArrayList<String>(); CloseableIterator<CIMObjectPath> iterator = null; try { WBEMClient client = getConnection(storage).getCimClient(); iterator = client.associatorNames(controllerPath, null, CIM_PROTOCOL_ENDPOINT, null, null); while (iterator.hasNext()) { CIMObjectPath endpointPath = iterator.next(); String portName = endpointPath.getKeyValue(CP_NAME).toString(); String fixedName = Initiator.toPortNetworkId(portName); storagePorts.add(fixedName); } } catch (WBEMException we) { _log.error("Caught an error while attempting to get storage ports from " + "masking instance", we); } finally { if (iterator != null) { iterator.close(); } } return storagePorts; } /* * Return associated instances. */ public CloseableIterator<CIMInstance> getAssociatorInstances( StorageSystem storageDevice, CIMObjectPath path, String assocClass, String resultClass, String role, String resultRole, String[] prop) throws WBEMException { return getConnection(storageDevice).getCimClient().associatorInstances( path, null, resultClass, null, null, false, prop); } public CloseableIterator<CIMInstance> getReferenceInstances( StorageSystem storageDevice, CIMObjectPath path, String resultClass, String role, String[] prop) throws WBEMException { return getConnection(storageDevice).getCimClient().referenceInstances(path, resultClass, role, false, prop); } /* * Return CIM instance if there is one, otherwise return null. */ public CIMInstance checkExists(StorageSystem storage, CIMObjectPath objectPath, boolean propagated, boolean includeClassOrigin) throws Exception { return checkExists(storage, objectPath, propagated, includeClassOrigin, null); } /** * Wrapper for the getInstance. If the object is not found, it returns a null * value instead of throwing an exception. * * @param storage * [required] - StorageSystem object to which an SMI-S connection would be made * @param objectPath * [required] * @param propagated * [required] * @param includeClassOrigin * [required] * @param propertyList * - An array of property names used to filter what is contained in the instances * returned. * @return CIMInstance object that represents the existing object * @throws Exception */ private CIMInstance checkExists(StorageSystem storage, CIMObjectPath objectPath, boolean propagated, boolean includeClassOrigin, String[] propertyList) throws Exception { CIMInstance instance = null; try { if (objectPath != null) { _log.debug(String.format("checkExists(storage=%s, objectPath=%s, propagated=%s, includeClassOrigin=%s)", storage.getSerialNumber(), objectPath.toString(), String.valueOf(propagated), String.valueOf(includeClassOrigin))); instance = getInstance(storage, objectPath, propagated, includeClassOrigin, propertyList); } } catch (WBEMException e) { // If we get an error indicating the object is not found, then // it's okay, we want to return null for this method if (e.getID() != WBEMException.CIM_ERR_NOT_FOUND) { throw e; } } catch (Exception e) { _log.error("checkExists call encountered an exception", e); throw e; } return instance; } /** * Wrapper for the WBEMClient enumerateInstances method. * * @param storage * - StorageArray reference, will be used to lookup SMI-S connection * @param namespace * - Namespace to use * @param className * - Name of the class on the provider to query * @param deep * - If true, this specifies that, for each returned Instance of the Class, all * properties of the Instance must be present (subject to constraints imposed by the * other parameters), including any which were added by subclassing the specified * Class. If false, each returned Instance includes only properties defined for the * specified Class in path. * @param localOnly * - If true, only elements values that were instantiated in the instance is * returned. * @param includeClassOrigin * - The class origin attribute is the name of the class that first defined the * property. If true, the class origin attribute will be present for each property on * all returned CIMInstances. If false, the class origin will not be present. * @param propertyList * - An array of property names used to filter what is contained in the instances * returned. Each instance returned only contains elements for the properties of the * names specified. Duplicate and invalid property names are ignored and the request * is otherwise processed normally. An empty array indicates that no properties * should be returned. A null value indicates that all properties should be returned. * @return - CloseableIterator of CIMInstance values representing the instances of the specified * class. * @throws Exception */ private CloseableIterator<CIMInstance> getInstances(StorageSystem storage, String namespace, String className, boolean deep, boolean localOnly, boolean includeClassOrigin, String[] propertyList) throws Exception { CloseableIterator<CIMInstance> cimInstances; CimConnection connection = _cimConnection.getConnection(storage); WBEMClient client = connection.getCimClient(); String classKey = namespace + className; CIMObjectPath cimObjectPath = CIM_OBJECT_PATH_HASH_MAP.get(classKey); if (cimObjectPath == null) { cimObjectPath = CimObjectPathCreator.createInstance(className, namespace); CIM_OBJECT_PATH_HASH_MAP.putIfAbsent(classKey, cimObjectPath); } cimInstances = client.enumerateInstances(cimObjectPath, deep, localOnly, includeClassOrigin, propertyList); return cimInstances; } /* * Return names of initiator instances. */ public String[] getInitiatorNames(List<Initiator> initiatorList) throws Exception { List<String> initiatorNameList = new ArrayList<String>(); if (initiatorList != null && !initiatorList.isEmpty()) { for (Initiator initiator : initiatorList) { initiatorNameList.add(Initiator.normalizePort(initiator .getInitiatorPort())); } } return initiatorNameList.toArray(new String[initiatorNameList.size()]); } /* * Return mapping of initiator name and initiator instance. */ public Map<String, Initiator> getInitiatorMap(List<Initiator> initiatorList) throws Exception { Map<String, Initiator> initiatorMap = new HashMap<String, Initiator>(); if (initiatorList != null && !initiatorList.isEmpty()) { for (Initiator initiator : initiatorList) { initiatorMap.put(Initiator.normalizePort(initiator .getInitiatorPort()), initiator); } } return initiatorMap; } /* * Return CIM instance of the given object path. */ public CIMInstance getInstance(StorageSystem storage, CIMObjectPath objectPath, boolean propagated, boolean includeClassOrigin, String[] propertyList) throws Exception { CimConnection connection = _cimConnection.getConnection(storage); WBEMClient client = connection.getCimClient(); // CTRL-9069 workaround CIM_BAD_REQUEST error CIMInstance instance = null; int retryCount = 0; while (true) { try { _log.info("Calling getInstance, attempt {}", retryCount); instance = client.getInstance(objectPath, propagated, includeClassOrigin, propertyList); } catch (WBEMException e) { if (CIM_BAD_REQUEST.equals(e.getMessage())) { if (retryCount < CIM_MAX_RETRY_COUNT) { _log.warn("Encountered 'request-not-well-formed' error. Retry..."); retryCount++; try { Thread.sleep(CIM_RETRY_WAIT_INTERVAL); } catch (InterruptedException ie) { _log.warn("Thread: " + Thread.currentThread().getName() + " interrupted."); throw e; } continue; } _log.warn("Exhausted {} retries", CIM_MAX_RETRY_COUNT); } // other WBEMException, or reach the max retry count throw e; } // no exception break; } return instance; } /** * Loop through the URI list and return a list of nativeIds for each of the * BlockObject objects to which the URI applies. * * @param uris * - Collection of URIs * @return Returns a list of nativeId String values * @throws Exception */ public String[] getBlockObjectAlternateNames(Collection<URI> uris) throws Exception { String[] results = {}; Set<String> names = new HashSet<String>(); for (URI uri : uris) { names.add(getBlockObjectAlternateName(uri)); } return names.toArray(results); } /** * This method will take a URI and return alternateName for the BlockObject object to which the * URI applies. * * @param uri * - URI * @return Returns a nativeId String value * @throws DeviceControllerException.exceptions.notAVolumeOrBlocksnapshotUri * if URI is not a Volume/BlockSnapshot URI */ private String getBlockObjectAlternateName(URI uri) throws Exception { String nativeId; if (URIUtil.isType(uri, Volume.class)) { Volume volume = _dbClient.queryObject(Volume.class, uri); nativeId = volume.getAlternateName(); } else if (URIUtil.isType(uri, BlockSnapshot.class)) { BlockSnapshot blockSnapshot = _dbClient.queryObject(BlockSnapshot.class, uri); nativeId = blockSnapshot.getAlternateName(); } else if (URIUtil.isType(uri, BlockMirror.class)) { BlockMirror blockMirror = _dbClient.queryObject(BlockMirror.class, uri); nativeId = blockMirror.getAlternateName(); } else { throw DeviceControllerException.exceptions.notAVolumeOrBlocksnapshotUri(uri); } return nativeId; } /* * Returns native ID of the export mask. */ private String getExportMaskNativeId(URI exportMaskURI) throws Exception { ExportMask exportMask = _dbClient.queryObject(ExportMask.class, exportMaskURI); return exportMask.getNativeId(); } @SuppressWarnings("rawtypes") public CIMArgument[] getCreateElementReplicaSnapInputArguments( StorageSystem storageDevice, Volume volume, boolean createInactive, String label) { return getCreateElementReplicaInputArguments(storageDevice, volume, null, createInactive, label, SNAPSHOT_VALUE); } @SuppressWarnings("rawtypes") public CIMArgument[] getCreateElementReplicaInputArguments( StorageSystem storageDevice, Volume volume, StoragePool pool, boolean createInactive, String label, int syncType) { int waitForCopyState = (createInactive) ? INACTIVE_VALUE : ACTIVATE_VALUE; CIMObjectPath volumePath = _cimPath.getBlockObjectPath(storageDevice, volume); List<CIMArgument> args = new ArrayList<CIMArgument>(); args.add(_cimArgument.string(CP_ELEMENT_NAME, label)); args.add(_cimArgument.uint16(CP_SYNC_TYPE, syncType)); args.add(_cimArgument.reference(CP_SOURCE_ELEMENT, volumePath)); if (waitForCopyState == ACTIVATE_VALUE) { args.add(_cimArgument.uint16(CP_WAIT_FOR_COPY_STATE, waitForCopyState)); } if (pool != null) { addTargetPoolToArgs(storageDevice, pool, args); } return args.toArray(new CIMArgument[] {}); } @SuppressWarnings("rawtypes") private void addTargetPoolToArgs(StorageSystem storageSystem, StoragePool pool, List<CIMArgument> args) { CIMProperty[] inPoolPropKeys = { _cimProperty.string(CP_INSTANCE_ID, pool.getNativeId()) }; CIMObjectPath inPoolPath = CimObjectPathCreator.createInstance( pool.getPoolClassName(), _cimConnection.getNamespace(storageSystem), inPoolPropKeys); args.add(_cimArgument.reference(CP_TARGET_POOL, inPoolPath)); } public String getConsistencyGroupName(BlockObject bo, StorageSystem storageSystem) { if (bo.getConsistencyGroup() == null) { return null; } final BlockConsistencyGroup group = _dbClient.queryObject(BlockConsistencyGroup.class, bo.getConsistencyGroup()); return getConsistencyGroupName(group, storageSystem); } public String getConsistencyGroupName(final BlockConsistencyGroup group, final StorageSystem storageSystem) { String groupName = null; if (group != null && storageSystem != null) { groupName = group.getCgNameOnStorageSystem(storageSystem.getId()); } return groupName; } @SuppressWarnings("rawtypes") public CIMArgument[] getDeleteSnapshotSynchronousInputArguments( CIMObjectPath syncObjectPath) { return new CIMArgument[] { _cimArgument.uint16(CP_OPERATION, DELETE_SNAPSHOT), _cimArgument.reference(CP_SYNCHRONIZATION, syncObjectPath) }; } @SuppressWarnings("rawtypes") public CIMArgument[] getCreateGroupReplicaInputArguments( StorageSystem storage, CIMObjectPath cgPath, boolean createInactive, String label) { final CIMArgument[] basicArgs = new CIMArgument[] { _cimArgument.uint16("Consistency", 3), _cimArgument.uint16("Mode", 3), _cimArgument.uint16(CP_SYNC_TYPE, SNAPSHOT_VALUE), _cimArgument.reference(CP_SOURCE_GROUP, cgPath), _cimArgument.string(RELATIONSHIP_NAME, label)}; final List<CIMArgument> args = new ArrayList<CIMArgument>( Arrays.asList(basicArgs)); // If active, add the RelationshipName if (!createInactive) { args.add(_cimArgument.uint16(CP_WAIT_FOR_COPY_STATE, ACTIVATE_VALUE)); } return args.toArray(new CIMArgument[args.size()]); } @SuppressWarnings("rawtypes") public CIMArgument[] getReturnGroupSyncToPoolInputArguments( CIMObjectPath syncObjectPath) { return new CIMArgument[] { _cimArgument.uint16(CP_OPERATION, RETURN_TO_RESOURCE_POOL), _cimArgument.reference(CP_SYNCHRONIZATION, syncObjectPath) }; } @SuppressWarnings("rawtypes") public CIMArgument[] getDeleteListSynchronizationInputArguments( StorageSystem storage, CIMObjectPath[] syncObjectPaths) { return new CIMArgument[] { _cimArgument.uint16(CP_OPERATION, RETURN_TO_RESOURCE_POOL), _cimArgument .referenceArray(CP_SYNCHRONIZATION, syncObjectPaths) }; } /** * Convenience method that wraps SMI-S replication service operation * * @param storage * [required] - StorageSystem object representing array * @param methodName * [required] - String of method name * @param inArgs * [required] - CIMArgument array containing operation's * arguments * @throws Exception */ @SuppressWarnings("rawtypes") public void callReplicationSvc(StorageSystem storage, String methodName, CIMArgument[] inArgs, CIMArgument[] outArgs) throws Exception { CIMObjectPath replicationSvcPath = _cimPath .getReplicationSvcPath(storage); invokeMethod(storage, replicationSvcPath, methodName, inArgs, outArgs); } /** * Convenience method that wraps SMI-S ModifyReplicatSynchronization * operation * * @param storage * [required] - StorageSystem object representing array * @param inArgs * [required] - CIMArgument array containing operation's * arguments * @throws Exception */ @SuppressWarnings("rawtypes") public void callModifyReplica(StorageSystem storage, CIMArgument[] inArgs) throws Exception { callReplicationSvc(storage, MODIFY_REPLICA_SYNCHRONIZATION, inArgs, new CIMArgument[5]); } /** * This method will loop through the URI list and return a list of nativeIds * for each of the BlockObject objects to which the URI applies. * * @param uris * - Collection of URIs * @return Returns a list of nativeId String values * @throws DeviceControllerException.exceptions.notAVolumeOrBlocksnapshotUri * f URI is not a Volume/BlockSnapshot URI */ public String[] getBlockObjectNativeIds(Collection<URI> uris) throws Exception { String[] results = {}; Set<String> nativeIds = new HashSet<String>(); for (URI uri : uris) { String nativeId; if (URIUtil.isType(uri, Volume.class)) { Volume volume = _dbClient.queryObject(Volume.class, uri); nativeId = volume.getNativeId(); } else if (URIUtil.isType(uri, BlockSnapshot.class)) { BlockSnapshot blockSnapshot = _dbClient.queryObject( BlockSnapshot.class, uri); nativeId = blockSnapshot.getNativeId(); } else if (URIUtil.isType(uri, BlockMirror.class)) { BlockMirror blockMirror = _dbClient.queryObject( BlockMirror.class, uri); nativeId = blockMirror.getAlternateName(); } else { throw DeviceControllerException.exceptions .notAVolumeOrBlocksnapshotUri(uri); } nativeIds.add(nativeId); } return nativeIds.toArray(results); } @SuppressWarnings("rawtypes") public CIMArgument[] getRestoreFromSnapshotInputArguments( CIMObjectPath syncObjectPath) { return new CIMArgument[] { _cimArgument.uint16(CP_OPERATION, RESTORE_FROM_REPLICA), _cimArgument.reference(CP_SYNCHRONIZATION, syncObjectPath), _cimArgument.bool(CP_FORCE, true) }; } @SuppressWarnings("rawtypes") public CIMArgument[] getCloneInputArguments(String label, CIMObjectPath sourceVolumePath, StorageSystem storageDevice, StoragePool pool, boolean createInactive) { int waitForCopyState = (createInactive) ? PREPARED_VALUE : ACTIVATE_VALUE; List<CIMArgument> args = new ArrayList<CIMArgument>(); args.add(_cimArgument.string(CP_ELEMENT_NAME, label)); args.add(_cimArgument.reference(CP_SOURCE_ELEMENT, sourceVolumePath)); args.add(_cimArgument.uint16(CP_SYNC_TYPE, CLONE_VALUE)); if (waitForCopyState == ACTIVATE_VALUE) { args.add(_cimArgument.uint16(CP_WAIT_FOR_COPY_STATE, waitForCopyState)); } if (pool != null) { addTargetPoolToArgs(storageDevice, pool, args); } return args.toArray(new CIMArgument[args.size()]); } @SuppressWarnings("rawtypes") private CIMArgument[] getAddMembersInputArguments(CIMObjectPath cgPath, CIMObjectPath[] volumePaths) { return new CIMArgument[] { _cimArgument.referenceArray(CP_MEMBERS, volumePaths), _cimArgument.reference(CP_REPLICATION_GROUP, cgPath) }; } @SuppressWarnings("rawtypes") public CIMArgument[] getRemoveMembersInputArguments(CIMObjectPath cgPath, CIMObjectPath[] volumePaths) { return new CIMArgument[] { _cimArgument.referenceArray(CP_MEMBERS, volumePaths), _cimArgument.reference(CP_REPLICATION_GROUP, cgPath), _cimArgument.bool(CP_DELETE_ON_EMPTY_ELEMENT, true) }; } /** * Wrapper for WBEM.associatorNames routine * * @param storageDevice * [required] * @param path * [required] * @param assocClass * [optional] - assocClass - This string MUST either contain a * valid CIM Association class name or be null. It filters the * Objects returned to contain only Objects associated to the * source Object via this CIM Association class or one of its * subclasses. * @param resultClass * [optional] - This string MUST either contain a valid CIM Class * name or be null. It filters the Objects returned to contain * only the Objects of this Class name or one of its subclasses. * @param role * [optional] - role - This string MUST either contain a valid * Property name or be null. It filters the Objects returned to * contain only Objects associated to the source Object via an * Association class in which the source Object plays the * specified role. (i.e. the Property name in the Association * class that refers to the source Object matches this value) If * "Antecedent" is specified, then only Associations in which the * source Object is the "Antecedent" reference are examined. * @param resultRole * [optional] - This string MUST either contain a valid Property * name or be null. It filters the Objects returned to contain * only Objects associated to the source Object via an * Association class in which the Object returned plays the * specified role. (i.e. the Property name in the Association * class that refers to the Object returned matches this value) * If "Dependent" is specified, then only Associations in which * the Object returned is the "Dependent" reference are examined. * @return CloseableIterator - iterator that can be used to enumerate the * associatorNames * @throws WBEMException */ public CloseableIterator<CIMObjectPath> getAssociatorNames( StorageSystem storageDevice, CIMObjectPath path, String assocClass, String resultClass, String role, String resultRole) throws WBEMException { return getConnection(storageDevice).getCimClient().associatorNames( path, assocClass, resultClass, role, resultRole); } @SuppressWarnings("rawtypes") public CIMArgument[] getCreateReplicationGroupInputArguments(StorageSystem storage, String groupName, List<URI> blockObjects) throws Exception { String[] blockObjectNames = getBlockObjectNativeIds(blockObjects); CIMObjectPath[] members = _cimPath.getVolumePaths(storage, blockObjectNames); return new CIMArgument[] { _cimArgument .string(CP_GROUP_NAME, groupName), _cimArgument.referenceArray(CP_MEMBERS, members) }; } @SuppressWarnings("rawtypes") public CIMArgument[] getDeleteReplicationGroupInputArguments( StorageSystem storage, CIMObjectPath groupPath) { return new CIMArgument[] { _cimArgument.reference(CP_REPLICATION_GROUP, groupPath), _cimArgument.bool(CP_REMOVE_ELEMENTS, true) }; } /** * Convenience method that wraps SMI-S AddMembers operation * * @param storage * [required] - StorageSystem object representing array * @param blockObjects * [required] - list of block object URIs * @param cgPath * [required] - CIMObjectPath of CG group * @throws Exception */ public void addVolumesToConsistencyGroup(StorageSystem storage, final List<URI> blockObjects, CIMObjectPath cgPath) throws Exception { String[] blockObjectNames = getBlockObjectNativeIds(blockObjects); CIMObjectPath[] members = _cimPath.getVolumePaths(storage, blockObjectNames); @SuppressWarnings("rawtypes") CIMArgument[] addMembersInput = getAddMembersInputArguments(cgPath, members); callReplicationSvc(storage, ADD_MEMBERS, addMembersInput, new CIMArgument[5]); } /* * Returns null if CG doesn't exist, or members of the CG */ public Set<String> getCGMembers(StorageSystem storage, CIMObjectPath cgPath) throws Exception { Set<String> members = new HashSet<String>(); CIMInstance cgPathInstance = checkExists(storage, cgPath, false, false); if (cgPathInstance == null) { return null; } CloseableIterator<CIMObjectPath> assocVolNamesIter = null; try { assocVolNamesIter = getAssociatorNames(storage, cgPath, null, IBMSmisConstants.CIM_STORAGE_VOLUME, null, null); while (assocVolNamesIter.hasNext()) { CIMObjectPath assocVolPath = assocVolNamesIter.next(); String deviceId = assocVolPath.getKeyValue(IBMSmisConstants.CP_DEVICE_ID).toString(); members.add(deviceId); // may have a timing issue, sometimes vol is returned, but is gone from CG } } finally { if (assocVolNamesIter != null) { assocVolNamesIter.close(); } } return members; } /* * Get snapshot group members. Note the members may not be available. * * Called from IBMSmisSynchSubTaskJob */ public List<CIMObjectPath> getSGMembers(StorageSystem storageDevice, String sgName) throws WBEMException { CimConnection connection = getConnection(storageDevice); WBEMClient client = connection.getCimClient(); CloseableIterator<CIMObjectPath> syncVolumeIter = null; List<CIMObjectPath> objectPaths = new ArrayList<CIMObjectPath>(); try { // find out the real object path for the SG // workaround for provider bug (CTRL-8947, provider doesn't keep InstanceID of snapshot group constant) CIMObjectPath sgPath = _cimPath.getSnapshotGroupPath(storageDevice, sgName); if (sgPath == null) { return objectPaths; } syncVolumeIter = client.associatorNames(sgPath, IBMSmisConstants.CP_SNAPSHOT_GROUP_TO_ORDERED_MEMBERS, IBMSmisConstants.CP_STORAGE_VOLUME, null, null); while (syncVolumeIter.hasNext()) { CIMObjectPath syncVolumePath = syncVolumeIter.next(); objectPaths.add(syncVolumePath); if (_log.isDebugEnabled()) { _log.debug("syncVolumePath - " + syncVolumePath); } } } finally { if (syncVolumeIter != null) { syncVolumeIter.close(); } } return objectPaths; } /* * Use IBMSmisSynchSubTaskJob to check removed CG members */ public Set<String> getCGMembers(StorageSystem storageSystem, CIMObjectPath cgPath, Set<String> removedMembers) throws SmisException { IBMSmisSynchSubTaskJob job = new IBMSmisSynchSubTaskJob( cgPath, storageSystem, IBMSmisSynchSubTaskJob.JobName.GetRemovedCGMembers, null, 0, removedMembers); waitForSmisJob(storageSystem, job, POLL_CYCLE_LIMIT); return job.getCGMembers(); } /* * Use IBMSmisSynchSubTaskJob to get snapshot members */ public List<CIMObjectPath> getSGMembers(StorageSystem storageSystem, CIMObjectPath replicationGroupPath, String sgName, int expectedObjCount) throws SmisException { IBMSmisSynchSubTaskJob job = new IBMSmisSynchSubTaskJob( replicationGroupPath, storageSystem, IBMSmisSynchSubTaskJob.JobName.GetNewSGMembers, sgName, expectedObjCount, null); waitForSmisJob(storageSystem, job, POLL_CYCLE_LIMIT); return job.getSGMemberPaths(); } private JobStatus waitForSmisJob(StorageSystem storageDevice, SmisJob job, int pollCycleLimit) throws SmisException { JobStatus status = JobStatus.IN_PROGRESS; JobContext jobContext = new JobContext(_dbClient, _cimConnection, null, null, null, null, null, this); long startTime = System.currentTimeMillis(); int pollCycleCount = 0; while (true) { JobPollResult result = job.poll(jobContext, SYNC_WRAPPER_WAIT); pollCycleCount++; if (result.getJobStatus().equals(JobStatus.IN_PROGRESS)) { if (pollCycleCount > pollCycleLimit) { throw new SmisException( "Reached maximum number of poll " + pollCycleLimit); } else if (System.currentTimeMillis() - startTime > SYNC_WRAPPER_TIME_OUT) { throw new SmisException( "Timed out waiting on smis job to complete after " + (System.currentTimeMillis() - startTime) + " milliseconds"); } else { try { Thread.sleep(SYNC_WRAPPER_WAIT); } catch (InterruptedException e) { _log.error("Thread waiting for smis job to complete was interrupted and will be resumed"); } } } else { status = result.getJobStatus(); if (!status.equals(JobStatus.SUCCESS)) { throw new SmisException("Smis job failed: " + result.getErrorDescription()); } break; } } return status; } public CIMObjectPath[] getGroupSyncObjectPaths(StorageSystem storage, CIMObjectPath cgPath) throws WBEMException { CimConnection connection = getConnection(storage); WBEMClient client = connection.getCimClient(); CloseableIterator<CIMObjectPath> groupSyncIter = null; List<CIMObjectPath> objPaths = new ArrayList<CIMObjectPath>(); try { groupSyncIter = client.referenceNames(cgPath, CP_GROUP_SYNCHRONIZED, CP_SYSTEM_ELEMENT); while (groupSyncIter.hasNext()) { objPaths.add(groupSyncIter.next()); } } finally { if (groupSyncIter != null) { groupSyncIter.close(); } } return objPaths.toArray(new CIMObjectPath[objPaths.size()]); } public String getReplicationGroupName(CIMObjectPath replicationGroupPath) { String instanceId = (String) replicationGroupPath.getKey( IBMSmisConstants.CP_INSTANCE_ID).getValue(); return instanceId.substring(instanceId.lastIndexOf("-") + 1); } public void setTag(DataObject object, String scope, String label) { if (label == null) { // shouldn't happen label = ""; } ScopedLabel newScopedLabel = new ScopedLabel(scope, label); ScopedLabelSet tagSet = object.getTag(); if (tagSet == null) { tagSet = new ScopedLabelSet(); tagSet.add(newScopedLabel); object.setTag(tagSet); } else if (tagSet.contains(newScopedLabel)) { return; } else { removeLabel(tagSet, scope); tagSet.add(newScopedLabel); } _dbClient.persistObject(object); } public void unsetTag(DataObject object, String scope) { ScopedLabelSet tagSet = object.getTag(); if (tagSet == null) { return; } removeLabel(tagSet, scope); _dbClient.persistObject(object); } private void removeLabel(ScopedLabelSet tagSet, String scope) { ScopedLabel oldScopedLabel = null; Iterator<ScopedLabel> itr = tagSet.iterator(); while (itr.hasNext()) { ScopedLabel scopedLabel = itr.next(); if (scope.equals(scopedLabel.getScope())) { oldScopedLabel = scopedLabel; break; } } if (oldScopedLabel != null) { tagSet.remove(oldScopedLabel); } } }