/* * Copyright (c) 2008-2013 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl; import static com.emc.storageos.db.client.constraint.AlternateIdConstraint.Factory.getBlockSnapshotSessionBySessionInstance; import static com.emc.storageos.db.client.constraint.ContainmentConstraint.Factory.getVolumesByConsistencyGroup; import static com.emc.storageos.db.client.util.CommonTransformerFunctions.fctnDataObjectToID; import static com.google.common.collect.Collections2.transform; import static com.google.common.collect.Lists.newArrayList; import java.math.BigDecimal; import java.math.BigInteger; import java.math.RoundingMode; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.CollectionUtils; import com.emc.storageos.coordinator.client.service.CoordinatorClient; import com.emc.storageos.db.client.DbClient; import com.emc.storageos.db.client.URIUtil; import com.emc.storageos.db.client.constraint.AlternateIdConstraint; import com.emc.storageos.db.client.constraint.ContainmentConstraint; import com.emc.storageos.db.client.constraint.URIQueryResultList; import com.emc.storageos.db.client.model.AutoTieringPolicy; 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.BlockSnapshotSession; import com.emc.storageos.db.client.model.DataObject; import com.emc.storageos.db.client.model.DataObject.Flag; import com.emc.storageos.db.client.model.DiscoveredDataObject; import com.emc.storageos.db.client.model.DiscoveredDataObject.Type; import com.emc.storageos.db.client.model.Event; import com.emc.storageos.db.client.model.ExportGroup; import com.emc.storageos.db.client.model.FCZoneReference; import com.emc.storageos.db.client.model.FileShare; import com.emc.storageos.db.client.model.Network; import com.emc.storageos.db.client.model.OpStatusMap; import com.emc.storageos.db.client.model.Operation; import com.emc.storageos.db.client.model.Project; import com.emc.storageos.db.client.model.StoragePool; import com.emc.storageos.db.client.model.StoragePort; import com.emc.storageos.db.client.model.StorageProvider; import com.emc.storageos.db.client.model.StorageSystem; import com.emc.storageos.db.client.model.StringSet; import com.emc.storageos.db.client.model.TenantOrg; import com.emc.storageos.db.client.model.VirtualPool; import com.emc.storageos.db.client.model.Volume; import com.emc.storageos.db.client.model.VolumeGroup; import com.emc.storageos.db.client.model.VplexMirror; import com.emc.storageos.db.client.util.CustomQueryUtility; import com.emc.storageos.db.client.util.NullColumnValueGetter; import com.emc.storageos.db.exceptions.DatabaseException; import com.emc.storageos.plugins.common.Constants; import com.emc.storageos.protectioncontroller.impl.recoverpoint.RPHelper; import com.emc.storageos.util.VPlexUtil; import com.emc.storageos.volumecontroller.TaskCompleter; import com.emc.storageos.volumecontroller.impl.monitoring.RecordableBourneEvent; import com.emc.storageos.volumecontroller.impl.monitoring.RecordableEvent; import com.emc.storageos.volumecontroller.impl.utils.ConsistencyGroupUtils; import com.emc.storageos.volumecontroller.impl.utils.VirtualPoolCapabilityValuesWrapper; import com.emc.storageos.volumecontroller.logging.BournePatternConverter; import com.google.common.base.Joiner; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.HashBasedTable; import com.google.common.collect.ListMultimap; import com.google.common.collect.Table; /** * Utilities class encapsulates controller utility methods. */ public class ControllerUtils { // Logger reference. private static final Logger s_logger = LoggerFactory.getLogger(ControllerUtils.class); // Constant that represents BLOCK_EVENT_SOURCE public static final String BLOCK_EVENT_SOURCE = "Block Controller"; // Constant that represents BLOCK_EVENT_SERVICE public static final String BLOCK_EVENT_SERVICE = "block"; private static final String KILOBYTECONVERTERVALUE = "1024"; private static final VolumeURIHLU[] EMPTY_VOLUME_URI_HLU_ARRAY = new VolumeURIHLU[0]; private static final String LABEL_DELIMITER = "-"; private static final int SMIS_MAJOR_VERSION = 8; private static final int SMIS_MINOR_VERSION = 1; /** * Gets the URI of the tenant organization for the project with the passed * URI. * * @param dbClient A reference to the database client. * @param projectURI The URI for the project. * * @return The URI of the tenant organization. */ public static URI getProjectTenantOrgURI(DbClient dbClient, URI projectURI) { URI tenantOrgURI = null; try { s_logger.debug("Getting the URI of the tenant for project {}.", projectURI); // Get the Project with the passed URI from the database and extract // the tenant organization for the project. Project project = dbClient.queryObject(Project.class, projectURI); if (project != null) { tenantOrgURI = project.getTenantOrg().getURI(); if (tenantOrgURI == null) { s_logger.warn("The tenant URI is null for project {}.", projectURI); } } else { s_logger.warn("The database returned a null project for URI {}.", projectURI); } } catch (Exception e) { s_logger.warn("Exception fetching project {} from the database.", projectURI, e); } // Use the default provider tenant if the tenant cannot be determined. if (tenantOrgURI == null) { tenantOrgURI = URI.create(TenantOrg.PROVIDER_TENANT_ORG); } s_logger.debug("Returning tenant {} for project {}.", new Object[] { tenantOrgURI, projectURI }); return tenantOrgURI; } /** * Sets data to be included in log messages while an operation is executed. * The data to be included in the log message is the id of the operation and * the id of the resource impacted by the operation. * * @param resourceId The urn of the resource impacted by the operation. * @param opId The identifier for the operation being executed. */ public static void setThreadLocalLogData(URI resourceId, String opId) { ArrayList<String> patternData = new ArrayList<String>(); if ((opId != null) && (opId.length() != 0)) { patternData.add(opId); } if (resourceId != null) { patternData.add(resourceId.toString()); } BournePatternConverter.s_patternData.set(patternData); } /** * Clears the data to be included in log messages. Typically called after * the operation has completed. */ public static void clearThreadLocalLogData() { BournePatternConverter.s_patternData.set(new ArrayList<String>()); } /** * This function looks first at the logical pools and updates them with physical * capacity information, then updates the physical pools. * If physical pools are removed from the storage system it marks them inactive. * * @param storage * @param physicalHardware * @return * * public static * boolean reconcilePhysicalHardware(URI storage, * List<Object> physicalHardware, * DbClient dbClient) { * Logger log = LoggerFactory.getLogger(ControllerUtils.class); * try { * * // First update the logical pools represented by the physical pool * * List<URI> poolURIs = dbClient.queryByConstraint * (ContainmentConstraint.Factory * .getStorageDeviceStoragePoolConstraint(storage)); * List<StoragePool> pools = dbClient.queryObject(StoragePool * .class, poolURIs); * boolean poolFound; * for(StoragePool pool : pools){ * poolFound = false; * for(Object obj : physicalHardware){ * if (obj instanceof PhysicalStoragePool) { * // the type and ID must match * PhysicalStoragePool psp = (PhysicalStoragePool) obj; * if (pool.getControllerParams().get(StoragePool.ControllerParam.NativeId.name()).equals(psp.getNativeId())&& * pool.getControllerParams().get(StoragePool.ControllerParam.PoolType.name()).equals(psp.getType())) { * pool.setFreeCapacity(psp.getFreeCapacity()); * pool.setTotalCapacity(psp.getTotalCapacity()); * pool.setLargestContiguousBlock(psp * .getLargestContiguousBlock()); * pool.setSubscribedCapacity(psp.getSubscribedCapacity()); * log.info(String.format("Logical pool %1$s updated by " + * "physical storage pool %2$s/%3$s", * pool.getId().toString(), * psp.getType(), psp.getNativeId())); * dbClient.persistObject(pool); * poolFound = true; * break; * } * } * } * if(poolFound == false){ * // probably a good indication this pool is not valid * //pool.setInactive(true); * //dbClient.persistObject(pool); * log.warn(String.format("Logical pool %1$s not found on storage system", * pool.getId().toString())); * } * } * * // Now update the physical pools obtained from controller * * poolURIs = dbClient.queryByConstraint(ContainmentConstraint.Factory * .getStorageDevicePhysicalPoolConstraint(storage)); * List<PhysicalStoragePool> physicalPools = dbClient.queryObject(PhysicalStoragePool.class, poolURIs); * Map<URI,PhysicalStoragePool> newPools = new HashMap<URI,PhysicalStoragePool>(); * // save the set of physical pools so we can tell if there are new ones * for (Object obj : physicalHardware) { * if (obj instanceof PhysicalStoragePool) { * PhysicalStoragePool psp = (PhysicalStoragePool) obj; * psp.setId(URIUtil.createId(PhysicalStoragePool.class)); * psp.setInactive(false); * psp.setStorageDevice(storage); * newPools.put(psp.getId(),psp); * } * } * for (PhysicalStoragePool pool : physicalPools) { * poolFound = false; * for (Object obj : physicalHardware) { * if (obj instanceof PhysicalStoragePool) { * PhysicalStoragePool psp = (PhysicalStoragePool) obj; * // native ID and type must match * if (pool.getNativeId().equals(psp.getNativeId()) && * pool.getType().equals(psp.getType())) { * newPools.remove(psp.getId()); * psp.setId(pool.getId()); * log.info(String.format("Updated physical storage pool %1$s/%2$s:%3$s %4$s", * psp.getType(), psp.getNativeId(), * pool.getId().toString(), * pool.getLabel())); * dbClient.persistObject(psp); * poolFound = true; * break; * } * } * } * if(poolFound==false){ * // this pool is no longer on array * log.info(String.format("Inactivated Pool %1$s", pool.getId())); * dbClient.markForDeletion(pool); * } * } * * // add new pools * Iterator<Map.Entry<URI,PhysicalStoragePool>> itr = newPools.entrySet().iterator(); * while(itr.hasNext()){ * Map.Entry<URI, PhysicalStoragePool> entry = itr.next(); * PhysicalStoragePool psp = entry.getValue(); * log.info(String.format("New physical storage pool %1$s/%2$s:%3$s %4$s", * psp.getType(),psp.getNativeId(), * psp.getId().toString(), * psp.getLabel())); * dbClient.persistObject(psp); * } * return true; * } catch (IOException e) { * log.error("Exception while trying to handle results from " + * "getPhysicalInventory", e); * } * return false; * } */ /** * returns if operation (besides opId) is pending * * @param id id of resource * @param opId operation id for current operation * @param resource instance of resource * @return */ public static boolean isOperationInProgress(URI id, String opId, DataObject resource) { OpStatusMap ops = resource.getOpStatus(); Set<Map.Entry<String, Operation>> opSet = ops.entrySet(); Iterator<Map.Entry<String, Operation>> opItr = opSet.iterator(); while (opItr.hasNext()) { Map.Entry<String, Operation> entry = opItr.next(); if (entry.getValue().getStatus().equals(Operation.Status.pending.toString())) { if (entry.getKey().equals(opId)) { // our operation, pass continue; } // // Logger log = LoggerFactory.getLogger(ControllerUtils.class); // log.debug("operation in progress"); // return true; } } return false; } /** * Converts a RecordableEvent to an Event Model * * @param event * @return */ public static Event convertToEvent(RecordableEvent event) { Event dbEvent = new Event(); dbEvent.setTimeInMillis(event.getTimestamp()); dbEvent.setEventType(event.getType()); dbEvent.setTenantId(event.getTenantId()); dbEvent.setProjectId(event.getProjectId()); dbEvent.setUserId(event.getUserId()); dbEvent.setVirtualPool(event.getVirtualPool()); dbEvent.setService(event.getService()); dbEvent.setResourceId(event.getResourceId()); dbEvent.setSeverity(event.getSeverity()); dbEvent.setDescription(event.getDescription()); dbEvent.setExtensions(event.getExtensions()); dbEvent.setEventId(event.getEventId()); dbEvent.setAlertType(event.getAlertType()); dbEvent.setRecordType(event.getRecordType()); dbEvent.setNativeGuid(event.getNativeGuid()); dbEvent.setOperationalStatusCodes(event.getOperationalStatusCodes()); dbEvent.setOperationalStatusDescriptions(event.getOperationalStatusDescriptions()); dbEvent.setEventSource(event.getSource()); return dbEvent; } /** * Create a new instance of RecordableBourneEvent with the given resource * and properties. * * @param resource * - Type of Resource - File or Volume * @param type * - Event Type Enum * @param description * - Description of event if available * @param extensions * - Extensions mapped with Event Model Extensions * @param eventServiceSource * - URI of the Project * @param dbClient * - DBClient reference * @param evtServiceType * - Service Type * @param recordType * - Type of Indication * @return RecordableBourneEvent */ public static RecordableBourneEvent convertToRecordableBourneEvent( DataObject resource, String type, String description, String extensions, DbClient dbClient, String evtServiceType, String recordType, String eventServiceSource) { URI cos = null; URI id = null; String nativeGuid = null; URI projectURI = null; URI tenantURI = null; RecordableBourneEvent event = null; if (resource != null) { if (resource instanceof Volume) { Volume volume = (Volume) resource; cos = volume.getVirtualPool(); id = volume.getId(); nativeGuid = volume.getNativeGuid(); projectURI = volume.getProject().getURI(); tenantURI = volume.getTenant().getURI(); } else if (resource instanceof FileShare) { FileShare fs = (FileShare) resource; cos = fs.getVirtualPool(); id = fs.getId(); nativeGuid = fs.getNativeGuid(); projectURI = (fs.getProject() != null) ? fs.getProject().getURI() : null; tenantURI = (fs.getTenant() != null) ? fs.getTenant().getURI() : null; } else if (resource instanceof VplexMirror) { VplexMirror vplexMirror = (VplexMirror) resource; cos = vplexMirror.getVirtualPool(); id = vplexMirror.getId(); projectURI = vplexMirror.getProject().getURI(); tenantURI = vplexMirror.getTenant().getURI(); } else if (resource instanceof BlockSnapshot) { BlockSnapshot snapshot = (BlockSnapshot) resource; try { if (!NullColumnValueGetter.isNullNamedURI(snapshot.getParent())) { Volume volume = dbClient.queryObject(Volume.class, snapshot.getParent()); cos = volume.getVirtualPool(); tenantURI = (volume.getTenant() != null) ? volume.getTenant().getURI() : null; } id = snapshot.getId(); nativeGuid = snapshot.getNativeGuid(); projectURI = snapshot.getProject().getURI(); } catch (DatabaseException e) { s_logger.error("Exception caught", e); } } else if (resource instanceof BlockSnapshotSession) { BlockSnapshotSession session = (BlockSnapshotSession) resource; try { id = session.getId(); projectURI = session.getProject().getURI(); } catch (DatabaseException e) { s_logger.error("Exception caught", e); } } else if (resource instanceof ExportGroup) { ExportGroup exportGroup = (ExportGroup) resource; try { id = exportGroup.getId(); projectURI = exportGroup.getProject().getURI(); tenantURI = (exportGroup.getTenant() != null) ? exportGroup.getTenant().getURI() : null; } catch (Exception e) { s_logger.error("Exception caught", e); } } else if (resource instanceof FCZoneReference) { FCZoneReference zone = (FCZoneReference) resource; try { id = zone.getId(); } catch (Exception e) { s_logger.error("Exception caught", e); } } else if (resource instanceof Network) { Network tz = (Network) resource; id = tz.getId(); nativeGuid = tz.getNativeGuid(); } else if (resource instanceof BlockConsistencyGroup) { BlockConsistencyGroup consistencyGroup = (BlockConsistencyGroup) resource; try { id = consistencyGroup.getId(); projectURI = consistencyGroup.getProject().getURI(); tenantURI = (consistencyGroup.getTenant() != null) ? consistencyGroup.getTenant() .getURI() : null; } catch (Exception e) { s_logger.error("Exception caught", e); } } else if (resource instanceof StoragePool) { StoragePool sp = (StoragePool) resource; id = sp.getId(); nativeGuid = sp.getNativeGuid(); } else { s_logger.info( "Error getting vpool,id,NativeGuid for event. Unexpected resource type {}.", resource.getClass().getName()); } // TODO fix the bogus tenant, user ID once we have AuthZ working if (tenantURI == null && projectURI != null) { tenantURI = ControllerUtils.getProjectTenantOrgURI(dbClient, projectURI); } event = new RecordableBourneEvent( type, tenantURI, URI.create("ViPR-User"), // user ID TODO when AAA // fixed projectURI, cos, evtServiceType, id, description, System.currentTimeMillis(), extensions, nativeGuid, recordType, eventServiceSource, "", ""); } return event; } /** * convert Bytes to KiloBytes * * @param value * @return */ public static Long convertBytesToKBytes(String value) { if (null == value) { return 0L; } BigDecimal val = new BigDecimal(value); BigDecimal kbconverter = new BigDecimal(KILOBYTECONVERTERVALUE); BigDecimal result = val.divide(kbconverter, RoundingMode.CEILING); // if the passed in Value from Provider is less than 1024 bytes, then by // default make it to 1 KB. if (result.longValue() == 0) { return 1L; } return result.longValue(); } /** * If the returned value from Provider cannot be accommodated within Long, then make it to 0. * as this is not a valid stat.The only possibility to get a high number is ,Provider initializes * all stat property values with a default value of uint64. (18444......) * Once stats collected, values will then accommodated within Long. * * @param value * @return */ public static Long getLongValue(String value) { try { return Long.parseLong(value); } catch (Exception e) { s_logger.warn("Not parse String to get Long value"); } return 0L; } static final BigInteger modValue = BigInteger.valueOf(Long.MAX_VALUE).add(BigInteger.ONE); /** * Get a modulo long value from a potentially bigger number by creating a BigInteger and * MODing by Long.MAX_VALUE + 1 * * @param value - String value of arbitrarily large integer * @return Long value computed by BigInteger MOD (Long.MAX_VALUE+1), 0 in case of Exception */ public static Long getModLongValue(String value) { try { BigInteger bigValue = new BigInteger(value); bigValue = bigValue.mod(modValue); return bigValue.longValue(); } catch (Exception e) { s_logger.warn("Not parse String to get Long value"); } return 0L; } /** * Returen a double vaule. Returns 0.0 if mal-formatted. * * @param value -- String * @return Double value */ public static Double getDoubleValue(String value) { try { return Double.parseDouble(value); } catch (Exception e) { s_logger.warn("Not parse String to get Double value"); } return 0.0; } public static VolumeURIHLU[] getVolumeURIHLUArray(String storageType, Map<URI, Integer> volumeMap, DbClient dbClient) { VolumeURIHLU[] volURIsHlus = EMPTY_VOLUME_URI_HLU_ARRAY; // Have a non-null default value if (volumeMap != null && !volumeMap.keySet().isEmpty()) { boolean convertFromHex = storageType.equals(DiscoveredDataObject.Type.vmax.name()); int entryCount = volumeMap.keySet().size(); volURIsHlus = new VolumeURIHLU[entryCount]; int index = 0; Map<URI, String> blockURIToLabelMap = new HashMap<URI, String>(); Map<String, URI> nativeIdToURIMap = new HashMap<String, URI>(); for (URI uri : volumeMap.keySet()) { BlockObject blockObject = BlockObject.fetch(dbClient, uri); Integer nativeId; String nativeIdString = blockObject.getNativeId(); if (convertFromHex) { nativeId = Integer.parseInt(blockObject.getNativeId(), 16); nativeIdString = String.format("%04d", nativeId); } else if (!storageType.equals(DiscoveredDataObject.Type.vnxe.name()) && !storageType.equals(DiscoveredDataObject.Type.unity.name()) && blockObject.getNativeId().matches("\\d+")) { nativeId = Integer.parseInt(blockObject.getNativeId()); nativeIdString = String.format("%04d", nativeId); } nativeIdToURIMap.put(nativeIdString, blockObject.getId()); blockURIToLabelMap.put(blockObject.getId(), blockObject.getLabel()); } Set<String> orderedByNativeId = new TreeSet<String>(nativeIdToURIMap.keySet()); for (String nativeId : orderedByNativeId) { URI uri = nativeIdToURIMap.get(nativeId); Integer entryHLU = volumeMap.get(uri); String hluString = null; if (storageType.equals(DiscoveredDataObject.Type.unity.name())) { // Don't change to hex string for Unity hluString = (entryHLU != null) ? Integer.toString(entryHLU) : ExportGroup.LUN_UNASSIGNED_STR; } else { hluString = (entryHLU != null) ? Integer.toHexString(entryHLU) : ExportGroup.LUN_UNASSIGNED_STR; } String volLabel = blockURIToLabelMap.get(uri); if (storageType.equals(DiscoveredDataObject.Type.hds.name()) || storageType.equals(DiscoveredDataObject.Type.xtremio.name())) { // @TODO setting the policy name as null for now. We should handle when we support tiering. volURIsHlus[index++] = new VolumeURIHLU(uri, String.valueOf(entryHLU), null, volLabel); } else { String policyName = getAutoTieringPolicyName(uri, dbClient); VolumeURIHLU volumeURLHLU = new VolumeURIHLU(uri, hluString, policyName, volLabel); if (storageType.equals(DiscoveredDataObject.Type.vmax.name())) { BlockObject blockObject = BlockObject.fetch(dbClient, uri); if (blockObject instanceof Volume) { Volume volume = (Volume) blockObject; VirtualPool virtualPool = dbClient.queryObject(VirtualPool.class, volume.getVirtualPool()); volumeURLHLU = new VolumeURIHLU(uri, hluString, policyName, volLabel, virtualPool.getHostIOLimitBandwidth(), virtualPool.getHostIOLimitIOPs()); } } volURIsHlus[index++] = volumeURLHLU; } } s_logger.info(String.format("getVolumeURIHLUArray = %s", Joiner.on(',').join(volURIsHlus))); } return volURIsHlus; } public static String getAutoTieringPolicyName(URI uri, DbClient dbClient) { String policyName = Constants.NONE; if (URIUtil.isType(uri, Volume.class)) { Volume volume = dbClient.queryObject(Volume.class, uri); URI policyURI = volume.getAutoTieringPolicyUri(); if (!NullColumnValueGetter.isNullURI(policyURI)) { AutoTieringPolicy policy = dbClient.queryObject(AutoTieringPolicy.class, policyURI); policyName = policy.getPolicyName(); } } else if (URIUtil.isType(uri, BlockSnapshot.class)) { BlockSnapshot snapshot = dbClient.queryObject(BlockSnapshot.class, uri); StorageSystem storage = dbClient.queryObject(StorageSystem.class, snapshot.getStorageController()); if (storage.checkIfVmax3()) { Volume volume = dbClient.queryObject(Volume.class, snapshot.getParent()); URI policyURI = volume.getAutoTieringPolicyUri(); if (!NullColumnValueGetter.isNullURI(policyURI)) { AutoTieringPolicy policy = dbClient.queryObject(AutoTieringPolicy.class, policyURI); policyName = policy.getPolicyName(); } } } else if (URIUtil.isType(uri, BlockMirror.class)) { BlockMirror mirror = dbClient.queryObject(BlockMirror.class, uri); if (!NullColumnValueGetter.isNullURI(mirror.getAutoTieringPolicyUri())) { AutoTieringPolicy policy = dbClient.queryObject(AutoTieringPolicy.class, mirror.getAutoTieringPolicyUri()); policyName = policy.getPolicyName(); } } return policyName; } /** * Gets the URI of auto tiering policy associated with from virtual pool. * * @param vPool the virtual pool * @param storage the storage system * @param dbClient the db client * @return the auto tiering policy uri */ public static URI getAutoTieringPolicyURIFromVirtualPool(VirtualPool vPool, StorageSystem storage, DbClient dbClient) { /** * for VMAX: * if unique tiering policy is enabled on Virtual Pool, it has policy's * name. else it has policy's nativeGuid. * * for VNX: * Unique tiering policy field is not available. * So, it always has the policy's name. */ String policyNameInVpool = vPool.getAutoTierPolicyName(); if (policyNameInVpool != null) { URIQueryResultList result = new URIQueryResultList(); if (vPool.getUniquePolicyNames()) { dbClient.queryByConstraint(AlternateIdConstraint.Factory .getFASTPolicyByNameConstraint(policyNameInVpool), result); } else { StringSet systemType = new StringSet(); if (vPool.getArrayInfo() != null) { systemType.addAll(vPool.getArrayInfo().get(VirtualPoolCapabilityValuesWrapper.SYSTEM_TYPE)); } if (systemType.contains(DiscoveredDataObject.Type.vnxblock.name())) { dbClient.queryByConstraint(AlternateIdConstraint.Factory .getFASTPolicyByNameConstraint(policyNameInVpool), result); } else { dbClient.queryByConstraint(AlternateIdConstraint.Factory .getAutoTieringPolicyByNativeGuidConstraint(policyNameInVpool), result); } } Iterator<URI> iterator = result.iterator(); // if virtual pool is set with a unique policy name, it returns all // policies with that name from different arrays. while (iterator.hasNext()) { URI policyURI = iterator.next(); AutoTieringPolicy policy = dbClient.queryObject( AutoTieringPolicy.class, policyURI); if (policy.getStorageSystem().equals(storage.getId())) { return policyURI; } } } return null; } /** * grouping volumes based on fast Policy * * @param volumeMap * @param dbClient * @return */ public static Map<String, Map<URI, Integer>> groupVolumeBasedOnPolicy( Map<URI, Integer> volumeMap, DbClient dbClient) { Map<String, Map<URI, Integer>> volumeGroup = new HashMap<String, Map<URI, Integer>>(); if (volumeMap != null && !volumeMap.keySet().isEmpty()) { for (Map.Entry<URI, Integer> entry : volumeMap.entrySet()) { String policyName = getAutoTieringPolicyName(entry.getKey(), dbClient); Map<URI, Integer> volumeUris = volumeGroup.get(policyName); if (null == volumeUris) { volumeUris = new HashMap<URI, Integer>(); volumeGroup.put(policyName, volumeUris); } volumeUris.put(entry.getKey(), entry.getValue()); } } return volumeGroup; } /** * get Volume NativeGuids from volume Map * * @param volumeMap * @param dbClient * @return */ public static ListMultimap<String, VolumeURIHLU> getVolumeNativeGuids( Collection<VolumeURIHLU> volumeMap, DbClient dbClient) { ListMultimap<String, VolumeURIHLU> nativeGuidToVolumeUriHLU = ArrayListMultimap .create(); for (VolumeURIHLU volumeURIHLU : volumeMap) { BlockObject blockObject = BlockObject.fetch(dbClient, volumeURIHLU.getVolumeURI()); nativeGuidToVolumeUriHLU.put(blockObject.getNativeGuid(), volumeURIHLU); } return nativeGuidToVolumeUriHLU; } public static VolumeURIHLU[] constructVolumeUriHLUs(Set<String> diff, ListMultimap<String, VolumeURIHLU> nativeGuidToVolumeHluMap) { List<VolumeURIHLU> volumeUriHLUs = new ArrayList<VolumeURIHLU>(); for (String nativeGuid : diff) { Collection<VolumeURIHLU> volumeUriHLU = nativeGuidToVolumeHluMap.asMap().get(nativeGuid); volumeUriHLUs.addAll(volumeUriHLU); } VolumeURIHLU[] volumeURIHLUArr = new VolumeURIHLU[volumeUriHLUs.size()]; return volumeUriHLUs.toArray(volumeURIHLUArr); } /** * Gets the property value from coordinator. * * @param coordinator * @param key * @return the property value */ public static String getPropertyValueFromCoordinator(CoordinatorClient coordinator, String key) { return coordinator.getPropertyInfo().getProperty(key); } /** * Query database to get storage ports of given storage systems * * @param dbClient * @param systemURI * @return list of storage system's storage ports */ public static List<StoragePort> getSystemPortsOfSystem(final DbClient dbClient, final URI systemURI) { List<StoragePort> systemPorts = new ArrayList<StoragePort>(); URIQueryResultList portQueryResult = new URIQueryResultList(); try { dbClient.queryByConstraint(ContainmentConstraint.Factory.getStorageDeviceStoragePortConstraint(systemURI), portQueryResult); for (Iterator<URI> portResultItr = portQueryResult.iterator(); portResultItr.hasNext();) { StoragePort port = dbClient.queryObject(StoragePort.class, portResultItr.next()); systemPorts.add(port); } } catch (DatabaseException e) { // Even if one volume fails, no need to throw exception instead // continue processing other volumes s_logger.error("Unable to retrieve ports for system: {}", systemURI); } return systemPorts; } /** * Convenient method to get policy name from a virtual pool * * @param _dbClient * @param storage * @param vpool * @return */ public static String getFastPolicyNameFromVirtualPool(DbClient _dbClient, StorageSystem storage, VirtualPool vpool) { String policyName = Constants.NONE; URI policyURI = ControllerUtils.getAutoTieringPolicyURIFromVirtualPool(vpool, storage, _dbClient); if (policyURI != null) { AutoTieringPolicy policy = _dbClient.queryObject(AutoTieringPolicy.class, policyURI); policyName = policy.getPolicyName(); } return policyName; } /** * Takes in a list of URIs, queries using Iterative method and returns list of volume objects. * * @param dbClient the db client * @param volumeURIs the volume uris * @return the list of volume objects */ public static List<Volume> queryVolumesByIterativeQuery(DbClient dbClient, List<URI> volumeURIs) { List<Volume> volumes = new ArrayList<Volume>(); @SuppressWarnings("unchecked") Iterator<Volume> volumeIterator = dbClient.queryIterativeObjects(Volume.class, volumeURIs); while (volumeIterator.hasNext()) { volumes.add(volumeIterator.next()); } return volumes; } /** * Utility method which will filter the snapshots from getBlockSnapshotsBySnapsetLabel query by the * snapshot's project * * @param snapshot * @param dbClient * @return */ public static List<BlockSnapshot> getBlockSnapshotsBySnapsetLabelForProject(BlockSnapshot snapshot, DbClient dbClient) { URIQueryResultList list = new URIQueryResultList(); dbClient.queryByConstraint(AlternateIdConstraint.Factory. getBlockSnapshotsBySnapsetLabel(snapshot.getSnapsetLabel()), list); Iterator<BlockSnapshot> resultsIt = dbClient.queryIterativeObjects(BlockSnapshot.class, list); List<BlockSnapshot> snapshots = new ArrayList<BlockSnapshot>(); while (resultsIt.hasNext()) { BlockSnapshot snap = resultsIt.next(); if (snapshot.getProject() != null && snapshot.getProject().getURI().equals(snap.getProject().getURI())) { snapshots.add(snap); } } return snapshots; } /** * Determines if the passed volume is a full copy. * * @param volume A reference to a volume. * @param dbClient A reference to database client. * * @return true if the volume is a full copy, false otherwise. */ public static boolean isVolumeFullCopy(Volume volume, DbClient dbClient) { boolean isFullCopy = false; URI fcSourceObjURI = volume.getAssociatedSourceVolume(); if (!NullColumnValueGetter.isNullURI(fcSourceObjURI)) { BlockObject fcSourceObj = BlockObject.fetch(dbClient, fcSourceObjURI); if ((fcSourceObj != null) && (!fcSourceObj.getInactive())) { // The volume has a valid source object, so it // is a full copy volume. We check the source, // because the full copy mat have been detached // from the source and the source may have been // deleted. isFullCopy = true; } } return isFullCopy; } /** * Gets the volumes part of a given consistency group. * */ public static List<Volume> getVolumesPartOfCG(URI cgURI, DbClient dbClient) { List<Volume> volumes = new ArrayList<Volume>(); final URIQueryResultList uriQueryResultList = new URIQueryResultList(); dbClient.queryByConstraint(AlternateIdConstraint.Factory .getBlockObjectsByConsistencyGroup(cgURI.toString()), uriQueryResultList); Iterator<Volume> volumeIterator = dbClient.queryIterativeObjects(Volume.class, uriQueryResultList); while (volumeIterator.hasNext()) { Volume volume = volumeIterator.next(); if (volume != null && !volume.getInactive()) { volumes.add(volume); } } return volumes; } /** * Gets the volumes part of a given replication group. * and add system check. * * @param system the storage system where the replication group resides * @param replicationGroupInstance the replication group instance * @param dbClient the db client * @return the volumes part of replication group */ public static List<Volume> getVolumesPartOfRG(URI system, String replicationGroupInstance, DbClient dbClient) { List<Volume> volumes = new ArrayList<Volume>(); URIQueryResultList uriQueryResultList = new URIQueryResultList(); dbClient.queryByConstraint(AlternateIdConstraint.Factory .getVolumeReplicationGroupInstanceConstraint(replicationGroupInstance), uriQueryResultList); Iterator<Volume> volumeIterator = dbClient.queryIterativeObjects(Volume.class, uriQueryResultList, true); while (volumeIterator.hasNext()) { Volume volume = volumeIterator.next(); if (volume != null && system.toString().equals(volume.getStorageController().toString())) { volumes.add(volume); } } return volumes; } /** * Gets the mirrors part of a given replication group. */ public static List<BlockMirror> getMirrorsPartOfReplicationGroup( String replicationGroupInstance, DbClient dbClient) { List<BlockMirror> mirrors = new ArrayList<BlockMirror>(); URIQueryResultList uriQueryResultList = new URIQueryResultList(); dbClient.queryByConstraint(AlternateIdConstraint.Factory .getMirrorReplicationGroupInstanceConstraint(replicationGroupInstance), uriQueryResultList); Iterator<BlockMirror> mirrorIterator = dbClient.queryIterativeObjects(BlockMirror.class, uriQueryResultList); while (mirrorIterator.hasNext()) { BlockMirror mirror = mirrorIterator.next(); if (mirror != null && !mirror.getInactive()) { mirrors.add(mirror); } } return mirrors; } /** * Gets the full copies part of a given replication group. */ public static List<Volume> getFullCopiesPartOfReplicationGroup( String replicationGroupInstance, DbClient dbClient) { List<Volume> fullCopies = new ArrayList<Volume>(); URIQueryResultList uriQueryResultList = new URIQueryResultList(); dbClient.queryByConstraint(AlternateIdConstraint.Factory .getVolumeReplicationGroupInstanceConstraint(replicationGroupInstance), uriQueryResultList); Iterator<Volume> itr = dbClient.queryIterativeObjects(Volume.class, uriQueryResultList); while (itr.hasNext()) { Volume fullCopy = itr.next(); if (fullCopy != null && !fullCopy.getInactive()) { fullCopies.add(fullCopy); } } return fullCopies; } /** * Gets the snapshots part of a given replication group. * Check storage system just in case same replication group name could be found on different arrays * * @param replicationGroupInstance * @param storage * @param dbClient */ public static List<BlockSnapshot> getSnapshotsPartOfReplicationGroup( String replicationGroupInstance, URI storage, DbClient dbClient) { List<BlockSnapshot> snapshots = new ArrayList<BlockSnapshot>(); URIQueryResultList uriQueryResultList = new URIQueryResultList(); dbClient.queryByConstraint(AlternateIdConstraint.Factory .getSnapshotReplicationGroupInstanceConstraint(replicationGroupInstance), uriQueryResultList); Iterator<BlockSnapshot> snapIterator = dbClient.queryIterativeObjects(BlockSnapshot.class, uriQueryResultList); while (snapIterator.hasNext()) { BlockSnapshot snapshot = snapIterator.next(); if (snapshot != null && !snapshot.getInactive() && storage.equals(snapshot.getStorageController())) { snapshots.add(snapshot); } } return snapshots; } /** * Gets the snapshots part of a given replication group. * * @param snapshot * @param dbClient * @return snapshot list */ public static List<BlockSnapshot> getSnapshotsPartOfReplicationGroup(BlockSnapshot snapshot, DbClient dbClient) { if (NullColumnValueGetter.isNotNullValue(snapshot.getReplicationGroupInstance())) { return getSnapshotsPartOfReplicationGroup(snapshot.getReplicationGroupInstance(), snapshot.getStorageController(), dbClient); } else if (!NullColumnValueGetter.isNullURI(snapshot.getConsistencyGroup()) && NullColumnValueGetter.isNotNullValue(snapshot.getSnapsetLabel())) { StorageSystem system = dbClient.queryObject(StorageSystem.class, snapshot.getStorageController()); if (!system.getSystemType().equals(Type.vmax) && !system.getSystemType().equals(Type.vmax3) && !system.getSystemType().equals(Type.vnxblock) && !system.getSystemType().equals(Type.xtremio) ) { URIQueryResultList uriQueryResultList = new URIQueryResultList(); dbClient.queryByConstraint(AlternateIdConstraint.Factory.getBlockSnapshotsBySnapsetLabel(snapshot.getSnapsetLabel()), uriQueryResultList); List<BlockSnapshot> snapshots = new ArrayList<BlockSnapshot>(); Iterator<BlockSnapshot> snapshotItr = dbClient.queryIterativeObjects(BlockSnapshot.class, uriQueryResultList); while (snapshotItr.hasNext()) { BlockSnapshot snap = snapshotItr.next(); if (snapshot.getProject() != null && snapshot.getProject().getURI().equals(snap.getProject().getURI())) { snapshots.add(snap); } } return snapshots; } } return new ArrayList<BlockSnapshot>(Arrays.asList(snapshot)); } /** * From the passed list group snapshot URIs, filters the list so that only one snapshot * per replication group is in the returned, filtered list. * * @param snapshotURIs A list of group snapshots * @param dbClient A reference to a database client. * * @return The filtered list of snapshot URIs. */ public static List<URI> ensureOneSnapshotPerReplicationGroup(List<URI> snapshotURIs, DbClient dbClient) { List<URI> filteredSnapshotURIs = new ArrayList<>(); Set<String> replicationGroups = new HashSet<>(); Iterator<BlockSnapshot> snapshotIter = dbClient.queryIterativeObjects(BlockSnapshot.class, snapshotURIs); while (snapshotIter.hasNext()) { BlockSnapshot snapshot = snapshotIter.next(); String repGrpInstance = snapshot.getReplicationGroupInstance(); if (replicationGroups.contains(repGrpInstance)) { continue; } replicationGroups.add(repGrpInstance); filteredSnapshotURIs.add(snapshot.getId()); } return filteredSnapshotURIs; } /** * BlockSnapshot instances associated to an BlockSnapshotSession will have its replicationGroupName field set in a * different format than regular BlockSnapshot instances, e.g. system-serial+groupName. * * This method will extract and return only the group name, if required. * * @param groupName Replication group name, possibly containing the system serial. * @return Replication group name. */ public static String extractGroupName(String groupName) { Pattern p = Pattern.compile("^\\S+\\+(\\S+)$"); Matcher matcher = p.matcher(groupName); if (matcher.matches()) { return matcher.group(1); } return groupName; } /** * Filters the CG volumes by given Replication Group name and system. * * @param cgVolumes the cg volumes * @param rgName the replication group name * @param system the system * @param dbClient the db client * @return volumes belonging to given replication group and system in a CG */ public static List<BlockObject> getAllVolumesForRGInCG(List<Volume> cgVolumes, String rgName, URI system, DbClient dbClient) { List<BlockObject> cgVolumesInRG = new ArrayList<BlockObject>(); // get only those volumes belonging to given RG name if (NullColumnValueGetter.isNotNullValue(rgName)) { for (Volume vol : cgVolumes) { String volRGName = vol.getReplicationGroupInstance(); URI systemForVolume = vol.getStorageController(); if (vol.isVPlexVolume(dbClient)) { // get RG name from back end volume Volume srcBEVolume = VPlexUtil.getVPLEXBackendVolume(vol, true, dbClient); volRGName = srcBEVolume.getReplicationGroupInstance(); systemForVolume = srcBEVolume.getStorageController(); } if (rgName.equals(volRGName) && systemForVolume.toString().equals(system.toString())) { cgVolumesInRG.add(vol); } } } return cgVolumesInRG; } /** * Gets the replication group name from replicas of all volumes in CG. */ public static String getGroupNameFromReplicas(List<URI> replicas, BlockConsistencyGroup consistencyGroup, DbClient dbClient) { URI replicaURI = replicas.iterator().next(); // get volumes part of this CG List<Volume> volumes = ControllerUtils. getVolumesPartOfCG(consistencyGroup.getId(), dbClient); // check if replica of any of these volumes have replicationGroupInstance set for (Volume volume : volumes) { if (URIUtil.isType(replicaURI, BlockSnapshot.class)) { URIQueryResultList list = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory .getVolumeSnapshotConstraint(volume.getId()), list); Iterator<URI> it = list.iterator(); while (it.hasNext()) { URI snapshotID = it.next(); BlockSnapshot snapshot = dbClient.queryObject(BlockSnapshot.class, snapshotID); if (snapshot != null && !snapshot.getInactive() && NullColumnValueGetter.isNotNullValue(snapshot.getReplicationGroupInstance())) { return snapshot.getReplicationGroupInstance(); } } } else if (URIUtil.isType(replicaURI, Volume.class)) { URIQueryResultList cloneList = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory .getAssociatedSourceVolumeConstraint(volume.getId()), cloneList); Iterator<URI> iter = cloneList.iterator(); while (iter.hasNext()) { URI cloneID = iter.next(); Volume clone = dbClient.queryObject(Volume.class, cloneID); if (clone != null && !clone.getInactive() && NullColumnValueGetter.isNotNullValue(clone.getReplicationGroupInstance())) { return clone.getReplicationGroupInstance(); } } } else if (URIUtil.isType(replicaURI, BlockMirror.class)) { URIQueryResultList mirrorList = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory .getVolumeBlockMirrorConstraint(volume.getId()), mirrorList); Iterator<URI> itr = mirrorList.iterator(); while (itr.hasNext()) { URI mirrorID = itr.next(); BlockMirror mirror = dbClient.queryObject(BlockMirror.class, mirrorID); if (mirror != null && !mirror.getInactive() && NullColumnValueGetter.isNotNullValue(mirror.getReplicationGroupInstance())) { return mirror.getReplicationGroupInstance(); } } } } return null; } /** * Check if CG has any group relationship * * Note - on array side, if replica has been removed from replication group, but source volume has not been removed from CG yet, * the CG will not have group relationship until the source volume get removed from the CG. * * As a result, getting associator names cannot be used to check if CG has group relationship. */ public static boolean checkCGHasGroupRelationship(StorageSystem storage, URI cgURI, DbClient dbClient) { // get volumes part of this CG List<Volume> volumes = ControllerUtils.getVolumesPartOfCG(cgURI, dbClient); boolean isVNX = storage.deviceIsType(Type.vnxblock); // check if replica of any of these volumes have replicationGroupInstance set for (Volume volume : volumes) { if (NullColumnValueGetter.isNotNullValue(volume.getReplicationGroupInstance())) { if (!isVNX) { // VNX doesn't have group clones/mirrors // clone URIQueryResultList cloneList = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory .getAssociatedSourceVolumeConstraint(volume.getId()), cloneList); Iterator<URI> iter = cloneList.iterator(); while (iter.hasNext()) { URI cloneID = iter.next(); Volume clone = dbClient.queryObject(Volume.class, cloneID); if (clone != null && !clone.getInactive()) { return true; } } // mirror URIQueryResultList mirrorList = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory .getVolumeBlockMirrorConstraint(volume.getId()), mirrorList); Iterator<URI> itr = mirrorList.iterator(); while (itr.hasNext()) { URI mirrorID = itr.next(); BlockMirror mirror = dbClient.queryObject(BlockMirror.class, mirrorID); if (mirror != null && !mirror.getInactive()) { return true; } } } // snapshot URIQueryResultList list = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory .getVolumeSnapshotConstraint(volume.getId()), list); Iterator<URI> it = list.iterator(); while (it.hasNext()) { URI snapshotID = it.next(); BlockSnapshot snapshot = dbClient.queryObject(BlockSnapshot.class, snapshotID); if (snapshot != null && !snapshot.getInactive()) { return true; } } // snapshot session if (storage.checkIfVmax3()) { URIQueryResultList sessionList = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory. getBlockSnapshotSessionByConsistencyGroup(cgURI), sessionList); Iterator<URI> itr = sessionList.iterator(); while (itr.hasNext()) { URI sessionID = itr.next(); BlockSnapshotSession session = dbClient.queryObject(BlockSnapshotSession.class, sessionID); if (session != null && !session.getInactive()) { return true; } } } } } return false; } /** * Gets snapshot replication group names from source volumes in CG. * * @param volumes * @param dbClient * @return */ public static Set<String> getSnapshotReplicationGroupNames(List<Volume> volumes, DbClient dbClient) { Set<String> groupNames = new HashSet<>(); // check if replica of any of these volumes have replicationGroupInstance set for (Volume volume : volumes) { URIQueryResultList snapshotList = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory.getVolumeSnapshotConstraint(volume.getId()), snapshotList); Iterator<URI> iter = snapshotList.iterator(); while (iter.hasNext()) { URI snapshotID = iter.next(); BlockSnapshot snapshot = dbClient.queryObject(BlockSnapshot.class, snapshotID); if (snapshot != null && !snapshot.getInactive() && NullColumnValueGetter.isNotNullValue(snapshot.getReplicationGroupInstance())) { groupNames.add(snapshot.getReplicationGroupInstance()); } } if (!groupNames.isEmpty()) { // no need to check other CG members break; } } return groupNames; } /** * Gets snapshot replication group names for given source volumes in Replication Group and snap session. * * @param volumes the volumes * @param snapSession the snap session * @param dbClient the db client * @return the snapshot replication group names for snap session */ public static Set<String> getSnapshotReplicationGroupNamesForSnapSession(List<Volume> volumes, BlockSnapshotSession snapSession, DbClient dbClient) { Set<String> groupNames = new HashSet<>(); StringSet linkedTargets = snapSession.getLinkedTargets(); // check if replica of any of these volumes have replicationGroupInstance set for (Volume volume : volumes) { URIQueryResultList snapshotList = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory.getVolumeSnapshotConstraint(volume.getId()), snapshotList); Iterator<URI> iter = snapshotList.iterator(); while (iter.hasNext()) { URI snapshotID = iter.next(); BlockSnapshot snapshot = dbClient.queryObject(BlockSnapshot.class, snapshotID); if (snapshot != null && !snapshot.getInactive() && linkedTargets != null && linkedTargets.contains(snapshotID.toString()) && NullColumnValueGetter.isNotNullValue(snapshot.getReplicationGroupInstance())) { groupNames.add(snapshot.getReplicationGroupInstance()); } } if (!groupNames.isEmpty()) { // no need to check other CG members break; } } return groupNames; } /** * Gets copy mode for snapshots in snapshot replication group. * * @param snapGroupName the snap group name * @param storage the storage * @param dbClient the db client * @return the copy mode from snapshot group */ public static String getCopyModeFromSnapshotGroup(String snapGroupName, URI storage, DbClient dbClient) { List<BlockSnapshot> snapshots = getSnapshotsPartOfReplicationGroup(snapGroupName, storage, dbClient); return snapshots.get(0).getCopyMode(); } /** * Gets clone replication group names from clones of all volumes in CG. */ public static Set<String> getCloneReplicationGroupNames(List<Volume> volumes, DbClient dbClient) { Set<String> groupNames = new HashSet<String>(); // check if replica of any of these volumes have replicationGroupInstance set for (Volume volume : volumes) { URIQueryResultList cloneList = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory .getAssociatedSourceVolumeConstraint(volume.getId()), cloneList); Iterator<URI> iter = cloneList.iterator(); while (iter.hasNext()) { URI cloneID = iter.next(); Volume clone = dbClient.queryObject(Volume.class, cloneID); if (clone != null && !clone.getInactive() && NullColumnValueGetter.isNotNullValue(clone.getReplicationGroupInstance())) { groupNames.add(clone.getReplicationGroupInstance()); } } if (!groupNames.isEmpty()) { // no need to check other CG members break; } } return groupNames; } /** * Gets mirror replication group names from mirrors of all volumes in CG. */ public static Set<String> getMirrorReplicationGroupNames(List<Volume> volumes, DbClient dbClient) { Set<String> groupNames = new HashSet<String>(); // check if replica of any of these volumes have replicationGroupInstance set for (Volume volume : volumes) { URIQueryResultList mirrorList = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory .getVolumeBlockMirrorConstraint(volume.getId()), mirrorList); Iterator<URI> iter = mirrorList.iterator(); while (iter.hasNext()) { URI mirrorID = iter.next(); BlockMirror mirror = dbClient.queryObject(BlockMirror.class, mirrorID); if (mirror != null && !mirror.getInactive() && NullColumnValueGetter.isNotNullValue(mirror.getReplicationGroupInstance())) { groupNames.add(mirror.getReplicationGroupInstance()); } } if (!groupNames.isEmpty()) { // no need to check other CG members break; } } return groupNames; } public static String getMirrorLabel(String sourceLabel, String mirrorLabel) { return sourceLabel + LABEL_DELIMITER + mirrorLabel; } public static String getMirrorLabel(String mirrorLabel, int counter) { return mirrorLabel + LABEL_DELIMITER + counter; } public static String generateLabel(String sourceLabel, String mirrorLabel) { if (mirrorLabel.startsWith(sourceLabel + LABEL_DELIMITER) && mirrorLabel.length() > sourceLabel.length() + 1) { return mirrorLabel.substring(sourceLabel.length() + 1); } else { return mirrorLabel; } } /** * Returns true, if a snapshot is part of a consistency group, false otherwise. * In addition to this, if a non-null {@link TaskCompleter} is provided the {@BlockConsistencyGroup} instance * added to it. * * @param snapshots List of snapshot URI's * @param dbClient DbClient instance * @param completer Optional TaskCompleter instance. * @return true/false dependent on a snapshot being part of a consistency group. */ public static boolean checkSnapshotsInConsistencyGroup(List<BlockSnapshot> snapshots, DbClient dbClient, TaskCompleter completer) { BlockConsistencyGroup group = ConsistencyGroupUtils.getSnapshotsConsistencyGroup(snapshots, dbClient); if (group != null) { if (completer != null) { completer.addConsistencyGroupId(group.getId()); } return true; } return false; } /** * Returns true, if the clone is part of a consistency group, false otherwise. * In addition to this, if a non-null {@link TaskCompleter} is provided the {@BlockConsistencyGroup} instance * added to it. * * @param clone URI of the clone/fullcopy * @param dbClient DbClient instance * @param completer Optional TaskCompleter instance. * @return true/false dependent on the clone being part of a consistency group. */ public static boolean checkCloneConsistencyGroup(URI clone, DbClient dbClient, TaskCompleter completer) { BlockConsistencyGroup group = ConsistencyGroupUtils.getCloneConsistencyGroup(clone, dbClient); if (group != null) { if (completer != null) { completer.addConsistencyGroupId(group.getId()); } return true; } return false; } public static boolean checkSnapshotSessionConsistencyGroup(URI snapshotSession, DbClient dbClient, TaskCompleter completer) { BlockConsistencyGroup group = ConsistencyGroupUtils.getSnapshotSessionConsistencyGroup(snapshotSession, dbClient); if (group != null) { if (completer != null) { completer.addConsistencyGroupId(group.getId()); } return true; } return false; } /** * Check whether the given volume is vmax volume and vmax managed by SMI 8.0.3 * * @param mirrors * @param dbClient * @param completer Optional TaskCompleter instance. * @return true/false dependent on the clone being part of a consistency group. */ public static boolean checkMirrorConsistencyGroup(List<URI> mirrors, DbClient dbClient, TaskCompleter completer) { BlockConsistencyGroup group = ConsistencyGroupUtils.getMirrorsConsistencyGroup(mirrors, dbClient); if (group != null) { if (completer != null) { completer.addConsistencyGroupId(group.getId()); } return true; } return false; } /** * Check whether the given volume is vmax volume and vmax managed by SMI 8.0.3 * * @param volume * @param dbClient * @return */ public static boolean isVmaxVolumeUsing803SMIS(Volume volume, DbClient dbClient) { StorageSystem storage = dbClient.queryObject(StorageSystem.class, volume.getStorageController()); return (storage != null && storage.deviceIsType(Type.vmax) && storage.getUsingSmis80()); } /** * Check whether the given volume is VNX volume * * @param volume * @param dbClient * @return */ public static boolean isVnxVolume(Volume volume, DbClient dbClient) { StorageSystem storage = dbClient.queryObject(StorageSystem.class, volume.getStorageController()); return storage != null && storage.deviceIsType(Type.vnxblock); } /** * Check whether the given volume is XtremIO volume * * @param volume * @param dbClient * @return */ public static boolean isXtremIOVolume(Volume volume, DbClient dbClient) { StorageSystem storage = dbClient.queryObject(StorageSystem.class, volume.getStorageController()); return storage != null && storage.deviceIsType(Type.xtremio); } /** * Check whether the given volume is not in a real replication group * * @param volume * @param dbClient * @return */ public static boolean isNotInRealVNXRG(Volume volume, DbClient dbClient) { if (volume != null && volume.isInCG() && ControllerUtils.isVnxVolume(volume, dbClient)) { BlockConsistencyGroup consistencyGroup = dbClient.queryObject(BlockConsistencyGroup.class, volume.getConsistencyGroup()); if (consistencyGroup != null && !consistencyGroup.getInactive()) { return !consistencyGroup.getArrayConsistency(); } } return false; } public static String generateReplicationGroupName(StorageSystem storage, URI cgUri, String replicationGroupName, DbClient dbClient) { BlockConsistencyGroup cg = dbClient.queryObject(BlockConsistencyGroup.class, cgUri); if (cg == null || cg.getInactive()) { s_logger.warn(String.format("BlockConsistencyGroup with uri %s does not exist or is inactive", cgUri.toString())); } return generateReplicationGroupName(storage, cg, replicationGroupName, dbClient); } public static String generateReplicationGroupName(StorageSystem storage, BlockConsistencyGroup cg, String replicationGroupName, DbClient dbClient) { if (storage.deviceIsType(Type.vnxblock) && cg.getArrayConsistency()) { return cg.getCgNameOnStorageSystem(storage.getId()); } String groupName = replicationGroupName; if (groupName == null && cg != null) { //TEMPORARY FIX to solve both Application & Non-application use cases // Check to see if there's already a groupName associated with the existing volumes // Get all of the volumes associated with this consistency group, look for your storage system // If the replicationGroupInstance is filled-in, go with that. List<Volume> volumes = RPHelper.getAllCgVolumes(cg.getId(), dbClient); for (Volume volume : volumes) { if (volume.getStorageController().equals(storage.getId())) { String volumeCGName = ConsistencyGroupUtils.getSourceConsistencyGroupName(volume, dbClient); if (NullColumnValueGetter.isNotNullValue(volumeCGName)) { return volumeCGName; } } } // if there is only one system cg name for this storage system, use this; it may be different than the label if (cg.getSystemConsistencyGroups() != null && storage != null) { StringSet cgsforStorage = cg.getSystemConsistencyGroups().get(storage.getId().toString()); if (cgsforStorage != null && cgsforStorage.size() == 1) { groupName = cgsforStorage.iterator().next(); } else { groupName = (cg.getAlternateLabel() != null) ? cg.getAlternateLabel() : cg.getLabel(); } } else { groupName = (cg.getAlternateLabel() != null) ? cg.getAlternateLabel() : cg.getLabel(); } } return groupName; } /** * This utility method returns the snapsetLabel of the existing snapshots. * This is required when we try to create a new snapshot when the existing source volumes have snapshots. * * @param repGroupName * @param storage * @param dbClient * @return */ public static String getSnapSetLabelFromExistingSnaps(String repGroupName, URI storage, DbClient dbClient) { List<BlockSnapshot> snapshots = getSnapshotsPartOfReplicationGroup(repGroupName, storage, dbClient); String existingSnapSnapSetLabel = null; if (!CollectionUtils.isEmpty(snapshots)) { existingSnapSnapSetLabel = snapshots.get(0).getSnapsetLabel(); } return existingSnapSnapSetLabel; } public static Set<String> getSnapshotLabelsFromExistingSnaps(String repGroupName, BlockObject source, DbClient dbClient) { List<BlockSnapshot> snapshots = getSnapshotsPartOfReplicationGroup(repGroupName, source.getStorageController(), dbClient); Set<String> snapLables = new HashSet<>(); if (!CollectionUtils.isEmpty(snapshots)) { for (BlockSnapshot snapshot : snapshots) { snapLables.add(String.format("%s-%s", snapshot.getSnapsetLabel(), source.getLabel())); } } return snapLables; } /** * Check whether the given storage system is managed by SMI 8.1 or later * * @param storage * @param dbClient * @return true if the version is at least 8.1 */ public static boolean isVmaxUsing81SMIS(StorageSystem storage, DbClient dbClient) { if (storage != null && !NullColumnValueGetter.isNullURI(storage.getActiveProviderURI())) { StorageProvider provider = dbClient.queryObject(StorageProvider.class, storage.getActiveProviderURI()); if (provider != null && provider.getVersionString() != null) { String providerVersion = provider.getVersionString().replaceFirst("[^\\d]", ""); String provStr[] = providerVersion.split(Constants.SMIS_DOT_REGEX); int major = Integer.parseInt(provStr[0]); int minor = Integer.parseInt(provStr[1]); return major > SMIS_MAJOR_VERSION || major == SMIS_MAJOR_VERSION && minor >= SMIS_MINOR_VERSION; } } return false; } /** * return the cause of the exception. * * @param ex * @return */ public static String getMessage(final Exception ex) { String cause = ex.getCause() != null ? ex.getCause().toString() : ""; String message = ex.getMessage() != null ? ex.getMessage() : ""; String error = ""; if (!cause.isEmpty()) { error = cause; } if (!message.isEmpty()) { error = error + "-" + message; } return error; } /* * Check CG contains all and only volumes provided * * Assumption - all volumes provided are in the CG * * @param dbClient * * @param cg * * @param volumes * * @return boolean */ public static boolean cgHasNoOtherVolume(DbClient dbClient, URI cg, List<?> volumes) { URIQueryResultList cgVolumeList = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory .getVolumesByConsistencyGroup(cg), cgVolumeList); int totalVolumeCount = 0; while (cgVolumeList.iterator().hasNext()) { Volume cgSourceVolume = dbClient.queryObject(Volume.class, cgVolumeList.iterator().next()); if (cgSourceVolume != null) { totalVolumeCount++; } } s_logger.info("totalVolumeCount {} volume size {}", totalVolumeCount, volumes.size()); return totalVolumeCount == volumes.size(); } /** * Check back end cg created on array or not for the given volume * * @param volume * @return */ public static boolean checkCGCreatedOnBackEndArray(Volume volume) { return (volume != null && NullColumnValueGetter.isNotNullValue(volume.getReplicationGroupInstance())); } /** * Returns true if the request is made for subset of array groups within the Volume Group. * For Partial request, PARTIAL Flag was set on the requested Volume. * * @param dbClient the db client * @param volume the volume * @return true, if the request is Partial */ public static boolean checkVolumeForVolumeGroupPartialRequest(DbClient dbClient, Volume volume) { boolean partial = false; if (volume.checkInternalFlags(Flag.VOLUME_GROUP_PARTIAL_REQUEST)) { partial = true; } else { // check on other volumes part of the array group. List<Volume> volumes = new ArrayList<Volume>(); String rgName = volume.getReplicationGroupInstance(); if (volume.isVPlexVolume(dbClient)) { // get backend source volume Volume backedVol = VPlexUtil.getVPLEXBackendVolume(volume, true, dbClient); if (backedVol != null) { rgName = backedVol.getReplicationGroupInstance(); if (rgName != null) { List<Volume> backendVolumes = getVolumesPartOfRG(backedVol.getStorageController(), rgName, dbClient); for (Volume backendVolume : backendVolumes) { Volume vplexVolume = Volume.fetchVplexVolume(dbClient, backendVolume); volumes.add(vplexVolume); } } } } else if (NullColumnValueGetter.isNotNullValue(rgName)) { volumes = getVolumesPartOfRG(volume.getStorageController(), rgName, dbClient); } for (Volume vol : volumes) { if (vol.checkInternalFlags(Flag.VOLUME_GROUP_PARTIAL_REQUEST)) { partial = true; break; } } } return partial; } /** * Returns true if the request is made for subset of array groups within the Volume Group. * For Partial request, PARTIAL Flag was set on the requested Volume. * * @param dbClient the db client * @param volumes the volumes * @return true, if the request is Partial */ public static boolean checkVolumesForVolumeGroupPartialRequest(DbClient dbClient, List<BlockObject> volumes) { boolean partial = false; for (BlockObject volume : volumes) { if (volume.checkInternalFlags(Flag.VOLUME_GROUP_PARTIAL_REQUEST)) { partial = true; break; } } return partial; } /** * Get volume group's volumes. * * @param volumeGroup * @return The list of volumes in volume group */ public static List<Volume> getVolumeGroupVolumes(DbClient dbClient, VolumeGroup volumeGroup) { return CustomQueryUtility .queryActiveResourcesByConstraint(dbClient, Volume.class, AlternateIdConstraint.Factory.getVolumesByVolumeGroupId(volumeGroup.getId().toString())); } /** * Group volumes by array group + storage system Id. For VPLEX virtual volumes, group them by backend src volumes's array group. * * @param volumes the volumes * @param dbClient dbClient instance * @return the map of array group to volumes */ public static Map<String, List<Volume>> groupVolumesByArrayGroup(List<Volume> volumes, DbClient dbClient) { Map<String, List<Volume>> arrayGroupToVolumes = new HashMap<String, List<Volume>>(); for (Volume volume : volumes) { String storage = volume.getStorageController().toString(); String repGroupName = volume.getReplicationGroupInstance(); if (volume.isVPlexVolume(dbClient)) { // get backend source volume Volume backedVol = VPlexUtil.getVPLEXBackendVolume(volume, true, dbClient); if (backedVol != null) { repGroupName = backedVol.getReplicationGroupInstance(); storage = backedVol.getStorageController().toString(); } } if (NullColumnValueGetter.isNullValue(repGroupName)) { repGroupName = ""; } String key = repGroupName + storage; if (arrayGroupToVolumes.get(key) == null) { arrayGroupToVolumes.put(key, new ArrayList<Volume>()); } arrayGroupToVolumes.get(key).add(volume); } return arrayGroupToVolumes; } /** * Group volume URIs by consistency group. * * @param volumes the volumes * @return the map of consistency group to volume URIs */ public static Map<URI, List<URI>> groupVolumeURIsByCG(List<Volume> volumes) { Map<URI, List<URI>> cgToVolUris = new HashMap<URI, List<URI>>(); for (Volume volume : volumes) { if (volume.isInCG()) { URI cg = volume.getConsistencyGroup(); if (!cgToVolUris.containsKey(cg)) { cgToVolUris.put(cg, new ArrayList<URI>()); } cgToVolUris.get(cg).add(volume.getId()); } } return cgToVolUris; } /** * Gets all snapshots for the given set name. */ public static List<BlockSnapshot> getVolumeGroupSnapshots(URI volumeGroupId, String snapsetLabel, DbClient dbClient) { List<BlockSnapshot> snapshots = new ArrayList<BlockSnapshot>(); if (snapsetLabel != null) { URIQueryResultList list = new URIQueryResultList(); dbClient.queryByConstraint(AlternateIdConstraint.Factory. getBlockSnapshotsBySnapsetLabel(snapsetLabel), list); Iterator<BlockSnapshot> iter = dbClient.queryIterativeObjects(BlockSnapshot.class, list); while (iter.hasNext()) { BlockSnapshot snapshot = iter.next(); if (isSourceInVoumeGroup(snapshot, volumeGroupId, dbClient)) { snapshots.add(snapshot); } } } return snapshots; } /* * For each storage system and RG, get one snapshot * * @param snapshots List of snapshots * * @return table with storage URI, replication group name, and snapshot */ public static Table<URI, String, BlockSnapshot> getSnapshotForStorageReplicationGroup(List<BlockSnapshot> snapshots) { Table<URI, String, BlockSnapshot> storageRgToSnapshot = HashBasedTable.create(); for (BlockSnapshot snapshot : snapshots) { URI storage = snapshot.getStorageController(); String rgName = snapshot.getReplicationGroupInstance(); if (!storageRgToSnapshot.contains(storage, rgName)) { storageRgToSnapshot.put(storage, rgName, snapshot); } } return storageRgToSnapshot; } /* * For each storage system and RG, get one snapshot session * * @param snapshot sessions List of snapshot sessions * * @return table with storage URI, replication group name, and snapshot session */ public static Table<URI, String, BlockSnapshotSession> getSnapshotSessionForStorageReplicationGroup(List<BlockSnapshotSession> sessions, DbClient dbClient) { Table<URI, String, BlockSnapshotSession> storageRgToSession = HashBasedTable.create(); for (BlockSnapshotSession session : sessions) { URI storage = session.getStorageController(); String rgName = session.getReplicationGroupInstance(); if (!storageRgToSession.contains(storage, rgName)) { storageRgToSession.put(storage, rgName, session); } } return storageRgToSession; } /** * Check if source of the snapshot is in the volume group * * @param snapshot * @param voluemGroupId * @param dbClient */ public static boolean isSourceInVoumeGroup(BlockSnapshot snapshot, URI volumeGroupId, DbClient dbClient) { Volume volume = dbClient.queryObject(Volume.class, snapshot.getParent()); if (volume != null && !volume.getInactive()) { if (volume.getVolumeGroupIds().contains(volumeGroupId.toString())) { return true; } Volume vplexVolume = Volume.fetchVplexVolume(dbClient, volume); if (vplexVolume != null && !vplexVolume.getInactive() && vplexVolume.getVolumeGroupIds().contains(volumeGroupId.toString())) { return true; } } return false; } /** * Returns the project for the snapshot session source. * * @param sourceObj A reference to the Volume or BlockSnapshot instance. * @param dbClient A reference to a database client. * * @return A reference to the project for the snapshot session source. */ public static URI querySnapshotSessionSourceProject(BlockObject sourceObj, DbClient dbClient) { URI projectURI = null; if (sourceObj instanceof Volume) { projectURI = ((Volume) sourceObj).getProject().getURI(); } else if (sourceObj instanceof BlockSnapshot) { projectURI = ((BlockSnapshot) sourceObj).getProject().getURI(); } return projectURI; } /* * Check replicationGroup contains all and only volumes provided * * Assumption - all volumes provided are in the same replicationGroup * * @param dbClient * * @param rpName replication group name * * @param volumes volumes in the same replication group * * @return boolean */ public static boolean replicationGroupHasNoOtherVolume(DbClient dbClient, String rpName, Collection<URI> volumes, URI storage) { List<Volume> rpVolumes = CustomQueryUtility .queryActiveResourcesByConstraint(dbClient, Volume.class, AlternateIdConstraint.Factory.getVolumeReplicationGroupInstanceConstraint(rpName)); int rpVolumeCount = 0; for (Volume rpVol : rpVolumes) { URI storageUri = rpVol.getStorageController(); if (storageUri.toString().equals(storage.toString())) { rpVolumeCount++; } } s_logger.info("rpVolumeCount {} volume size {}", rpVolumeCount, volumes.size()); return rpVolumeCount == volumes.size(); } /** * Returns true if the Replication group has no snapshot other than the given ones. * * @param dbClient the db client * @param rgName the RG name * @param snapshots the snapshots * @param storage the storage * @return true, if successful */ public static boolean replicationGroupHasNoOtherSnapshot(DbClient dbClient, String rgName, Collection<URI> snapshots, URI storage) { List<BlockSnapshot> snapshotsInRG = getSnapshotsPartOfReplicationGroup(rgName, storage, dbClient); List<URI> snapshotURsInRG = newArrayList(transform(snapshotsInRG, fctnDataObjectToID())); s_logger.info("Snapshot count in RG: {}, given snapshots count: {}", snapshotsInRG.size(), snapshots.size()); snapshotURsInRG.removeAll(snapshots); return snapshotURsInRG.isEmpty(); } /** * gets the application volume group for this CG and group name if it exists * * @param dbClient * dbClient to query objects from db * @param consistencyGroup * consistency group object * @param cgNameOnArray * cg name to check * @return a VolumeGroup object or null if this CG and group name are not associated with an application */ public static VolumeGroup getApplicationForCG(DbClient dbClient, BlockConsistencyGroup consistencyGroup, String cgNameOnArray) { VolumeGroup volumeGroup = null; URIQueryResultList uriQueryResultList = new URIQueryResultList(); dbClient.queryByConstraint(getVolumesByConsistencyGroup(consistencyGroup.getId()), uriQueryResultList); Iterator<Volume> volumeIterator = dbClient.queryIterativeObjects(Volume.class, uriQueryResultList); while (volumeIterator.hasNext()) { Volume volume = volumeIterator.next(); if (NullColumnValueGetter.isNotNullValue(volume.getReplicationGroupInstance()) && volume.getReplicationGroupInstance().equals(cgNameOnArray)) { volumeGroup = volume.getApplication(dbClient); if (volumeGroup != null) { break; } } } return volumeGroup; } public static boolean checkIfVolumeHasSnapshot(Volume volume, DbClient dbClient) { URIQueryResultList list = new URIQueryResultList(); dbClient.queryByConstraint(ContainmentConstraint.Factory.getVolumeSnapshotConstraint(volume.getId()), list); Iterator<URI> it = list.iterator(); while (it.hasNext()) { URI snapshotID = it.next(); BlockSnapshot snapshot = dbClient.queryObject(BlockSnapshot.class, snapshotID); if (snapshot != null & !snapshot.getInactive()) { s_logger.debug("Volume {} has snapshot", volume.getId()); return true; } } return false; } /* * Check if non CG volume has snapshot session * * @param volumeUri * @param dbClient * @return true if has session, false otherwise */ public static boolean checkIfVolumeHasSnapshotSession(URI volumeUri, DbClient dbClient) { List<BlockSnapshotSession> sessions = CustomQueryUtility.queryActiveResourcesByConstraint(dbClient, BlockSnapshotSession.class, ContainmentConstraint.Factory.getParentSnapshotSessionConstraint(volumeUri)); return !sessions.isEmpty(); } /** * Return snapshot sessions based on the given snapshot session instance. * * @param instance * @param dbClient * @return */ public static List<URI> getSnapshotSessionsByInstance(String instance, DbClient dbClient) { URIQueryResultList resultList = new URIQueryResultList(); dbClient.queryByConstraint(getBlockSnapshotSessionBySessionInstance(instance), resultList); return newArrayList(resultList.iterator()); } /** * get the list of applications for a list of full copy volumes * * @param fcVolumeIds full copy volume ids * @param dbClient * @return */ public static List<URI> getApplicationsForFullCopies(List<URI> fcVolumeIds, DbClient dbClient) { Set<URI> volumeGroupIds = new HashSet<URI>(); Iterator<Volume> fcVolumes = dbClient.queryIterativeObjects(Volume.class, fcVolumeIds); while (fcVolumes.hasNext()) { Volume fcVolume = fcVolumes.next(); if (!NullColumnValueGetter.isNullURI(fcVolume.getAssociatedSourceVolume())) { BlockObject sourceObj = BlockObject.fetch(dbClient, fcVolume.getAssociatedSourceVolume()); if (sourceObj instanceof Volume) { for (String appId : ((Volume) sourceObj).getVolumeGroupIds()) { volumeGroupIds.add(URI.create(appId)); } } } } Iterator<VolumeGroup> volumeGroups = dbClient.queryIterativeObjects(VolumeGroup.class, volumeGroupIds); List<URI> applicationIds = new ArrayList<URI>(); while (volumeGroups.hasNext()) { VolumeGroup app = volumeGroups.next(); if (app.getRoles().contains(VolumeGroup.VolumeGroupRole.COPY.toString())) { applicationIds.add(app.getId()); } } return applicationIds; } /** * returns true if this replication group has already been created * * @param storage * @param replicationGroupName * @return */ public static boolean replicationGroupExists(URI storage, String replicationGroupName, DbClient dbClient) { Iterator<BlockConsistencyGroup> allCgs = dbClient.queryIterativeObjects(BlockConsistencyGroup.class, dbClient.queryByType(BlockConsistencyGroup.class, true)); while (allCgs.hasNext()) { BlockConsistencyGroup cg = allCgs.next(); if (cg.created(storage, replicationGroupName)) { return true; } } return false; } /** * Convert objects (like URIQueryResultList) that only implement iterator to fully implemented Collection * * @param collectionIn * @return collectionOut */ public static <T> Collection<T> getFullyImplementedCollection(Collection<T> collectionIn) { // Convert objects (like URIQueryResultList) that only implement iterator to // fully implemented Collection Collection<T> collectionOut = new ArrayList<>(); Iterator<T> iter = collectionIn.iterator(); while (iter.hasNext()) { collectionOut.add(iter.next()); } return collectionOut; } }